From b214f4d2e3b701c1088ee6c172376905afa64cf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20H=C3=BCbotter?= Date: Wed, 19 Aug 2020 14:36:51 +0200 Subject: [PATCH] [#131] Type constraints --- .yalc/tony-lang/.eslintrc.js | 30 +- .yalc/tony-lang/.prettierignore | 2 + .yalc/tony-lang/package.json | 8 +- .yalc/tony-lang/src/ast/Abstraction.ts | 16 + .yalc/tony-lang/src/ast/AbstractionBranch.ts | 23 + .yalc/tony-lang/src/ast/Access.ts | 25 + .yalc/tony-lang/src/ast/Application.ts | 23 + .yalc/tony-lang/src/ast/Argument.ts | 16 + .yalc/tony-lang/src/ast/Assignment.ts | 26 + .yalc/tony-lang/src/ast/Block.ts | 16 + .yalc/tony-lang/src/ast/Boolean.ts | 19 + .yalc/tony-lang/src/ast/Case.ts | 30 + .yalc/tony-lang/src/ast/Declaration.ts | 4 + .../tony-lang/src/ast/DestructuringPattern.ts | 14 + .yalc/tony-lang/src/ast/ElseIf.ts | 23 + .yalc/tony-lang/src/ast/Export.ts | 16 + .yalc/tony-lang/src/ast/Expression.ts | 40 + .yalc/tony-lang/src/ast/ExpressionPair.ts | 25 + .yalc/tony-lang/src/ast/Generator.ts | 39 + .yalc/tony-lang/src/ast/Identifier.ts | 28 + .yalc/tony-lang/src/ast/IdentifierPattern.ts | 28 + .yalc/tony-lang/src/ast/If.ts | 41 + .yalc/tony-lang/src/ast/Import.ts | 3 + .yalc/tony-lang/src/ast/InfixApplication.ts | 29 + .yalc/tony-lang/src/ast/Interpolation.ts | 16 + .yalc/tony-lang/src/ast/List.ts | 19 + .yalc/tony-lang/src/ast/ListComprehension.ts | 23 + .yalc/tony-lang/src/ast/ListPattern.ts | 16 + .yalc/tony-lang/src/ast/Literal.ts | 8 + .yalc/tony-lang/src/ast/LiteralPattern.ts | 14 + .yalc/tony-lang/src/ast/Map.ts | 20 + .yalc/tony-lang/src/ast/MapPattern.ts | 23 + .yalc/tony-lang/src/ast/Module.ts | 22 + .yalc/tony-lang/src/ast/Number.ts | 19 + .yalc/tony-lang/src/ast/Parameters.ts | 19 + .yalc/tony-lang/src/ast/ParametricType.ts | 15 + .yalc/tony-lang/src/ast/Pattern.ts | 5 + .yalc/tony-lang/src/ast/PatternPair.ts | 26 + .yalc/tony-lang/src/ast/Pipeline.ts | 22 + .yalc/tony-lang/src/ast/PrefixApplication.ts | 23 + .yalc/tony-lang/src/ast/Program.ts | 16 + .yalc/tony-lang/src/ast/Regex.ts | 19 + .yalc/tony-lang/src/ast/Rest.ts | 20 + .yalc/tony-lang/src/ast/Return.ts | 16 + .../src/ast/ShorthandAccessIdentifier.ts | 21 + .../src/ast/ShorthandPairIdentifier.ts | 21 + .../src/ast/ShorthandPairIdentifierPattern.ts | 3 + .yalc/tony-lang/src/ast/Spread.ts | 20 + .yalc/tony-lang/src/ast/String.ts | 22 + .yalc/tony-lang/src/ast/StringPattern.ts | 15 + .yalc/tony-lang/src/ast/SyntaxNode.ts | 1 + .yalc/tony-lang/src/ast/Tuple.ts | 19 + .yalc/tony-lang/src/ast/TuplePattern.ts | 16 + .yalc/tony-lang/src/ast/When.ts | 23 + .yalc/tony-lang/src/ast/index.ts | 51 + .../src/code_generation/GenerateCode.ts | 646 ++++------ .../services/CollectDefaultValues.ts | 51 +- .../code_generation/services/GenerateBlock.ts | 4 +- .../services/GenerateImport.ts | 18 +- .../services/GenerateProgram.ts | 14 +- .../services/ParseStringContent.ts | 3 +- .yalc/tony-lang/src/compile.ts | 2 +- .yalc/tony-lang/src/constants.ts | 4 - .yalc/tony-lang/src/errors/CompileError.ts | 17 +- .../src/errors/IndeterminateTypeError.ts | 16 + .../tony-lang/src/errors/InternalTypeError.ts | 28 + .../errors/InvalidExternalTypeImportError.ts | 16 + .../src/errors/InvalidModuleAccessError.ts | 22 + .../src/errors/InvalidPropertyAccessError.ts | 28 - .../errors/InvalidUseOfTypeAsValueError.ts | 16 + .../MissingExternalImportTypeHintError.ts | 16 + .yalc/tony-lang/src/errors/TypeError.ts | 45 +- .yalc/tony-lang/src/errors/index.ts | 7 +- .yalc/tony-lang/src/index.ts | 7 +- .../src/symbol_table/BuildFileModuleScope.ts | 175 ++- .../src/symbol_table/WalkFileModuleScope.ts | 12 +- .yalc/tony-lang/src/symbol_table/index.ts | 7 +- .../symbol_table/models/BasicTypeBinding.ts | 21 +- .../src/symbol_table/models/Binding.ts | 5 + .../symbol_table/models/BindingTemplate.ts | 13 + .../symbol_table/models/FileModuleScope.ts | 36 + .../symbol_table/models/IdentifierBinding.ts | 61 +- .../models/IdentifierBindingTemplate.ts | 89 ++ .../symbol_table/models/IdentifierImport.ts | 45 + .../src/symbol_table/models/Import.ts | 8 + .../src/symbol_table/models/ImportBinding.ts | 7 - .../models/ImportIdentifierBinding.ts | 41 - .../symbol_table/models/ImportTypeBinding.ts | 44 - .../src/symbol_table/models/ModuleBinding.ts | 76 ++ .../src/symbol_table/models/ModuleImport.ts | 44 + .../src/symbol_table/models/NestedScope.ts | 96 +- .../src/symbol_table/models/TypeBinding.ts | 47 - .../src/symbol_table/models/index.ts | 10 +- .../services/BuildImportBindings.ts | 101 -- .../src/symbol_table/services/BuildImports.ts | 90 ++ .../services/BuildPatternBindings.ts | 16 +- .../services/UnifyPatternBindings.ts | 6 +- .../src/symbol_table/services/index.ts | 2 +- .../src/type_inference/InferTypes.ts | 1147 +++++++++++------ .../models/AccumulatedAnswer.ts | 23 + .../models/AccumulatedDisjunction.ts | 6 + .../src/type_inference/models/Answer.ts | 29 + .../src/type_inference/models/Disjunction.ts | 19 + .../models/GeneralizedDisjunction.ts | 16 + .../src/type_inference/models/index.ts | 5 + .../services/AccumulateTypeDisjunction.ts | 35 + .../services/DistributeTypeDisjunction.ts | 52 + .../services/GetModuleTypeRepresentation.ts | 19 + .../services/InferAbstractionBranchType.ts | 50 + .../services/InferAccessType.ts | 296 +++-- .../services/InferApplicationType.ts | 305 +++-- .../services/InferAssignmentType.ts | 41 + .../type_inference/services/InferBlockType.ts | 40 + .../services/InferBranchType.ts | 40 +- .../services/InferExpressionPairType.ts | 43 + .../services/InferGeneratorType.ts | 152 +++ .../services/InferIdentifierPatternType.ts | 205 +++ .../services/InferImportBindingTypes.ts | 163 +++ .../type_inference/services/InferListType.ts | 94 +- .../type_inference/services/InferMapType.ts | 64 +- .../services/InferPatternBindingTypes.ts | 415 +++--- .../services/InferPatternPairType.ts | 61 + .../services/InferStringType.ts | 53 + .../type_inference/services/InferTupleType.ts | 87 +- .../MergeAccumulatedTypeDisjunction.ts | 48 + .../services/MergeTypeDisjunction.ts | 43 + .../src/type_inference/services/index.ts | 14 + .yalc/tony-lang/src/types/index.ts | 14 +- .../tony-lang/src/types/models/CurriedType.ts | 69 - .../src/types/models/IdentifierProperty.ts | 31 - .../tony-lang/src/types/models/ModuleType.ts | 44 + .../src/types/models/ParametricType.ts | 97 +- .yalc/tony-lang/src/types/models/Property.ts | 8 - .../src/types/models/Representation.ts | 34 - .yalc/tony-lang/src/types/models/Type.ts | 25 +- .../src/types/models/TypeConstraint.ts | 49 + .../src/types/models/TypeConstraints.ts | 30 - .../src/types/models/TypeEqualityGraph.ts | 148 +++ .../src/types/models/TypeEquivalenceClass.ts | 72 ++ .../src/types/models/TypeProperty.ts | 33 - .../src/types/models/TypeVariable.ts | 37 +- .yalc/tony-lang/src/types/models/index.ts | 8 +- .../src/types/services/BuildRepresentation.ts | 23 - .../tony-lang/src/types/services/BuildType.ts | 23 +- .yalc/tony-lang/src/types/services/index.ts | 1 - .yalc/tony-lang/test/abstraction/quicksort.tn | 2 +- .yalc/tony-lang/test/application/index.tn | 1 - .../test/assignment/duplicate_binding.tn | 2 + .../test/assignment/duplicate_binding.txt | 1 + .../tony-lang/test/assignment/overloading.tn | 5 + .../tony-lang/test/assignment/overloading.txt | 2 + .../test/assignment/pattern_matching.tn | 3 - .yalc/tony-lang/test/stdlib.tn | 2 +- .yalc/tony-lang/tony-lang.d.ts | 51 +- .yalc/tony-lang/yalc.sig | 2 +- src/CLI.ts | 8 +- src/ErrorHandler.ts | 18 +- ...s => InvalidModuleAccessErrorFormatter.ts} | 11 +- src/formatting/TypeErrorFormatter.ts | 29 +- src/formatting/index.ts | 2 +- test.js | 1 + test.tn | 30 + yalc.lock | 2 +- yarn.lock | 6 +- 164 files changed, 5539 insertions(+), 2059 deletions(-) create mode 100644 .yalc/tony-lang/.prettierignore create mode 100644 .yalc/tony-lang/src/ast/Abstraction.ts create mode 100644 .yalc/tony-lang/src/ast/AbstractionBranch.ts create mode 100644 .yalc/tony-lang/src/ast/Access.ts create mode 100644 .yalc/tony-lang/src/ast/Application.ts create mode 100644 .yalc/tony-lang/src/ast/Argument.ts create mode 100644 .yalc/tony-lang/src/ast/Assignment.ts create mode 100644 .yalc/tony-lang/src/ast/Block.ts create mode 100644 .yalc/tony-lang/src/ast/Boolean.ts create mode 100644 .yalc/tony-lang/src/ast/Case.ts create mode 100644 .yalc/tony-lang/src/ast/Declaration.ts create mode 100644 .yalc/tony-lang/src/ast/DestructuringPattern.ts create mode 100644 .yalc/tony-lang/src/ast/ElseIf.ts create mode 100644 .yalc/tony-lang/src/ast/Export.ts create mode 100644 .yalc/tony-lang/src/ast/Expression.ts create mode 100644 .yalc/tony-lang/src/ast/ExpressionPair.ts create mode 100644 .yalc/tony-lang/src/ast/Generator.ts create mode 100644 .yalc/tony-lang/src/ast/Identifier.ts create mode 100644 .yalc/tony-lang/src/ast/IdentifierPattern.ts create mode 100644 .yalc/tony-lang/src/ast/If.ts create mode 100644 .yalc/tony-lang/src/ast/Import.ts create mode 100644 .yalc/tony-lang/src/ast/InfixApplication.ts create mode 100644 .yalc/tony-lang/src/ast/Interpolation.ts create mode 100644 .yalc/tony-lang/src/ast/List.ts create mode 100644 .yalc/tony-lang/src/ast/ListComprehension.ts create mode 100644 .yalc/tony-lang/src/ast/ListPattern.ts create mode 100644 .yalc/tony-lang/src/ast/Literal.ts create mode 100644 .yalc/tony-lang/src/ast/LiteralPattern.ts create mode 100644 .yalc/tony-lang/src/ast/Map.ts create mode 100644 .yalc/tony-lang/src/ast/MapPattern.ts create mode 100644 .yalc/tony-lang/src/ast/Module.ts create mode 100644 .yalc/tony-lang/src/ast/Number.ts create mode 100644 .yalc/tony-lang/src/ast/Parameters.ts create mode 100644 .yalc/tony-lang/src/ast/ParametricType.ts create mode 100644 .yalc/tony-lang/src/ast/Pattern.ts create mode 100644 .yalc/tony-lang/src/ast/PatternPair.ts create mode 100644 .yalc/tony-lang/src/ast/Pipeline.ts create mode 100644 .yalc/tony-lang/src/ast/PrefixApplication.ts create mode 100644 .yalc/tony-lang/src/ast/Program.ts create mode 100644 .yalc/tony-lang/src/ast/Regex.ts create mode 100644 .yalc/tony-lang/src/ast/Rest.ts create mode 100644 .yalc/tony-lang/src/ast/Return.ts create mode 100644 .yalc/tony-lang/src/ast/ShorthandAccessIdentifier.ts create mode 100644 .yalc/tony-lang/src/ast/ShorthandPairIdentifier.ts create mode 100644 .yalc/tony-lang/src/ast/ShorthandPairIdentifierPattern.ts create mode 100644 .yalc/tony-lang/src/ast/Spread.ts create mode 100644 .yalc/tony-lang/src/ast/String.ts create mode 100644 .yalc/tony-lang/src/ast/StringPattern.ts create mode 100644 .yalc/tony-lang/src/ast/SyntaxNode.ts create mode 100644 .yalc/tony-lang/src/ast/Tuple.ts create mode 100644 .yalc/tony-lang/src/ast/TuplePattern.ts create mode 100644 .yalc/tony-lang/src/ast/When.ts create mode 100644 .yalc/tony-lang/src/ast/index.ts create mode 100644 .yalc/tony-lang/src/errors/IndeterminateTypeError.ts create mode 100644 .yalc/tony-lang/src/errors/InternalTypeError.ts create mode 100644 .yalc/tony-lang/src/errors/InvalidExternalTypeImportError.ts create mode 100644 .yalc/tony-lang/src/errors/InvalidModuleAccessError.ts delete mode 100644 .yalc/tony-lang/src/errors/InvalidPropertyAccessError.ts create mode 100644 .yalc/tony-lang/src/errors/InvalidUseOfTypeAsValueError.ts create mode 100644 .yalc/tony-lang/src/errors/MissingExternalImportTypeHintError.ts create mode 100644 .yalc/tony-lang/src/symbol_table/models/BindingTemplate.ts create mode 100644 .yalc/tony-lang/src/symbol_table/models/IdentifierBindingTemplate.ts create mode 100644 .yalc/tony-lang/src/symbol_table/models/IdentifierImport.ts create mode 100644 .yalc/tony-lang/src/symbol_table/models/Import.ts delete mode 100644 .yalc/tony-lang/src/symbol_table/models/ImportBinding.ts delete mode 100644 .yalc/tony-lang/src/symbol_table/models/ImportIdentifierBinding.ts delete mode 100644 .yalc/tony-lang/src/symbol_table/models/ImportTypeBinding.ts create mode 100644 .yalc/tony-lang/src/symbol_table/models/ModuleBinding.ts create mode 100644 .yalc/tony-lang/src/symbol_table/models/ModuleImport.ts delete mode 100644 .yalc/tony-lang/src/symbol_table/models/TypeBinding.ts delete mode 100644 .yalc/tony-lang/src/symbol_table/services/BuildImportBindings.ts create mode 100644 .yalc/tony-lang/src/symbol_table/services/BuildImports.ts create mode 100644 .yalc/tony-lang/src/type_inference/models/AccumulatedAnswer.ts create mode 100644 .yalc/tony-lang/src/type_inference/models/AccumulatedDisjunction.ts create mode 100644 .yalc/tony-lang/src/type_inference/models/Answer.ts create mode 100644 .yalc/tony-lang/src/type_inference/models/Disjunction.ts create mode 100644 .yalc/tony-lang/src/type_inference/models/GeneralizedDisjunction.ts create mode 100644 .yalc/tony-lang/src/type_inference/models/index.ts create mode 100644 .yalc/tony-lang/src/type_inference/services/AccumulateTypeDisjunction.ts create mode 100644 .yalc/tony-lang/src/type_inference/services/DistributeTypeDisjunction.ts create mode 100644 .yalc/tony-lang/src/type_inference/services/GetModuleTypeRepresentation.ts create mode 100644 .yalc/tony-lang/src/type_inference/services/InferAbstractionBranchType.ts create mode 100644 .yalc/tony-lang/src/type_inference/services/InferAssignmentType.ts create mode 100644 .yalc/tony-lang/src/type_inference/services/InferBlockType.ts create mode 100644 .yalc/tony-lang/src/type_inference/services/InferExpressionPairType.ts create mode 100644 .yalc/tony-lang/src/type_inference/services/InferGeneratorType.ts create mode 100644 .yalc/tony-lang/src/type_inference/services/InferIdentifierPatternType.ts create mode 100644 .yalc/tony-lang/src/type_inference/services/InferImportBindingTypes.ts create mode 100644 .yalc/tony-lang/src/type_inference/services/InferPatternPairType.ts create mode 100644 .yalc/tony-lang/src/type_inference/services/InferStringType.ts create mode 100644 .yalc/tony-lang/src/type_inference/services/MergeAccumulatedTypeDisjunction.ts create mode 100644 .yalc/tony-lang/src/type_inference/services/MergeTypeDisjunction.ts delete mode 100644 .yalc/tony-lang/src/types/models/CurriedType.ts delete mode 100644 .yalc/tony-lang/src/types/models/IdentifierProperty.ts create mode 100644 .yalc/tony-lang/src/types/models/ModuleType.ts delete mode 100644 .yalc/tony-lang/src/types/models/Property.ts delete mode 100644 .yalc/tony-lang/src/types/models/Representation.ts create mode 100644 .yalc/tony-lang/src/types/models/TypeConstraint.ts delete mode 100644 .yalc/tony-lang/src/types/models/TypeConstraints.ts create mode 100644 .yalc/tony-lang/src/types/models/TypeEqualityGraph.ts create mode 100644 .yalc/tony-lang/src/types/models/TypeEquivalenceClass.ts delete mode 100644 .yalc/tony-lang/src/types/models/TypeProperty.ts delete mode 100644 .yalc/tony-lang/src/types/services/BuildRepresentation.ts create mode 100644 .yalc/tony-lang/test/assignment/duplicate_binding.tn create mode 100644 .yalc/tony-lang/test/assignment/duplicate_binding.txt create mode 100644 .yalc/tony-lang/test/assignment/overloading.tn create mode 100644 .yalc/tony-lang/test/assignment/overloading.txt rename src/formatting/{InvalidPropertyAccessErrorFormatter.ts => InvalidModuleAccessErrorFormatter.ts} (55%) create mode 100644 test.js create mode 100644 test.tn diff --git a/.yalc/tony-lang/.eslintrc.js b/.yalc/tony-lang/.eslintrc.js index 2f04efd..7cef5a5 100644 --- a/.yalc/tony-lang/.eslintrc.js +++ b/.yalc/tony-lang/.eslintrc.js @@ -20,25 +20,31 @@ module.exports = { }, plugins: ['@typescript-eslint'], rules: { - 'max-lines-per-function': ['error', { - max: 20, - skipBlankLines: true, - skipComments: true, - }], + 'max-lines-per-function': [ + 'error', + { + max: 25, + skipBlankLines: true, + skipComments: true, + }, + ], 'max-params': ['error', 5], 'sort-imports': 'error', '@typescript-eslint/explicit-function-return-type': 'error', '@typescript-eslint/no-use-before-define': 'off', '@typescript-eslint/no-explicit-any': 'error', '@typescript-eslint/no-unused-vars': 'error', - '@typescript-eslint/member-delimiter-style': ['error', { - multiline: { - delimiter: 'none', + '@typescript-eslint/member-delimiter-style': [ + 'error', + { + multiline: { + delimiter: 'none', + }, + singleline: { + delimiter: 'semi', + }, }, - singleline: { - delimiter: 'semi', - } - }], + ], }, overrides: [ { diff --git a/.yalc/tony-lang/.prettierignore b/.yalc/tony-lang/.prettierignore new file mode 100644 index 0000000..de4d1f0 --- /dev/null +++ b/.yalc/tony-lang/.prettierignore @@ -0,0 +1,2 @@ +dist +node_modules diff --git a/.yalc/tony-lang/package.json b/.yalc/tony-lang/package.json index 8ba96e2..2b2da75 100644 --- a/.yalc/tony-lang/package.json +++ b/.yalc/tony-lang/package.json @@ -1,14 +1,14 @@ { "name": "tony-lang", - "version": "0.1.4-afbd7cb8", + "version": "0.1.4-dae48e8c", "description": "The Tony programming language", "main": "dist/src/index.js", "types": "tony-lang.d.ts", "scripts": { "build": "babel . --ignore dist,node_modules --out-dir dist --extensions .ts", "start": "nodemon --watch src --watch test --exec 'yarn build' -e ts", - "lint": "prettier --check src test && eslint src test --ext .ts", - "prettier": "prettier --write src test", + "lint": "prettier --check '**/*.{ts,js}' && eslint src test --ext .ts", + "prettier": "prettier --write '**/*.{ts,js}'", "test": "ava dist/test/index.test.js" }, "engines": { @@ -28,7 +28,7 @@ }, "homepage": "https://github.com/tony-lang/tony#readme", "dependencies": { - "core-js": "^3.6.5", + "core-js": "^3.6.4", "deep-equal": "^2.0.1", "inquirer": "^7.1.0", "mkdirp": "^1.0.3", diff --git a/.yalc/tony-lang/src/ast/Abstraction.ts b/.yalc/tony-lang/src/ast/Abstraction.ts new file mode 100644 index 0000000..cceeaef --- /dev/null +++ b/.yalc/tony-lang/src/ast/Abstraction.ts @@ -0,0 +1,16 @@ +import { AbstractionBranch } from './AbstractionBranch' +import { SyntaxNode } from './SyntaxNode' + +export class Abstraction extends SyntaxNode { + private _branches: AbstractionBranch[] + + constructor(branches: AbstractionBranch[]) { + super() + + this._branches = branches + } + + get branches(): AbstractionBranch[] { + return this._branches + } +} diff --git a/.yalc/tony-lang/src/ast/AbstractionBranch.ts b/.yalc/tony-lang/src/ast/AbstractionBranch.ts new file mode 100644 index 0000000..6cc3a06 --- /dev/null +++ b/.yalc/tony-lang/src/ast/AbstractionBranch.ts @@ -0,0 +1,23 @@ +import { Block } from './Block' +import { Parameters } from './Parameters' +import { SyntaxNode } from './SyntaxNode' + +export class AbstractionBranch extends SyntaxNode { + private _body: Block + private _parameters: Parameters + + constructor(parameters: Parameters, body: Block) { + super() + + this._parameters = parameters + this._body = body + } + + get body(): Block { + return this._body + } + + get parameters(): Parameters { + return this._parameters + } +} diff --git a/.yalc/tony-lang/src/ast/Access.ts b/.yalc/tony-lang/src/ast/Access.ts new file mode 100644 index 0000000..93570b3 --- /dev/null +++ b/.yalc/tony-lang/src/ast/Access.ts @@ -0,0 +1,25 @@ +import { Expression } from './Expression' +import { ShorthandAccessIdentifier } from './ShorthandAccessIdentifier' +import { SyntaxNode } from './SyntaxNode' + +type Accessor = ShorthandAccessIdentifier | Expression + +export class Access extends SyntaxNode { + private _value: Expression + private _accessor: Accessor + + constructor(value: Expression, accessor: Accessor) { + super() + + this._value = value + this._accessor = accessor + } + + get value(): Expression { + return this._value + } + + get accessor(): Accessor { + return this._accessor + } +} diff --git a/.yalc/tony-lang/src/ast/Application.ts b/.yalc/tony-lang/src/ast/Application.ts new file mode 100644 index 0000000..1125a73 --- /dev/null +++ b/.yalc/tony-lang/src/ast/Application.ts @@ -0,0 +1,23 @@ +import { Argument } from './Argument' +import { Expression } from './Expression' +import { SyntaxNode } from './SyntaxNode' + +export class Application extends SyntaxNode { + private _arguments: Argument[] + private _value: Expression + + constructor(value: Expression, args: Argument[]) { + super() + + this._arguments = args + this._value = value + } + + get arguments(): Argument[] { + return this._arguments + } + + get value(): Expression { + return this._value + } +} diff --git a/.yalc/tony-lang/src/ast/Argument.ts b/.yalc/tony-lang/src/ast/Argument.ts new file mode 100644 index 0000000..fdca5c9 --- /dev/null +++ b/.yalc/tony-lang/src/ast/Argument.ts @@ -0,0 +1,16 @@ +import { Expression } from './Expression' +import { SyntaxNode } from './SyntaxNode' + +export class Argument extends SyntaxNode { + private _value: Expression | undefined + + constructor(value?: Expression) { + super() + + this._value = value + } + + get value(): Expression | undefined { + return this._value + } +} diff --git a/.yalc/tony-lang/src/ast/Assignment.ts b/.yalc/tony-lang/src/ast/Assignment.ts new file mode 100644 index 0000000..c5cf9a0 --- /dev/null +++ b/.yalc/tony-lang/src/ast/Assignment.ts @@ -0,0 +1,26 @@ +import { DestructuringPattern } from './DestructuringPattern' +import { Expression } from './Expression' +import { IdentifierPattern } from './IdentifierPattern' +import { SyntaxNode } from './SyntaxNode' + +export type AssignablePattern = IdentifierPattern | DestructuringPattern + +export class Assignment extends SyntaxNode { + private _pattern: AssignablePattern + private _value: Expression + + constructor(pattern: AssignablePattern, value: Expression) { + super() + + this._pattern = pattern + this._value = value + } + + get pattern(): AssignablePattern { + return this._pattern + } + + get value(): Expression { + return this._value + } +} diff --git a/.yalc/tony-lang/src/ast/Block.ts b/.yalc/tony-lang/src/ast/Block.ts new file mode 100644 index 0000000..e09074b --- /dev/null +++ b/.yalc/tony-lang/src/ast/Block.ts @@ -0,0 +1,16 @@ +import { Expression } from './Expression' +import { SyntaxNode } from './SyntaxNode' + +export class Block extends SyntaxNode { + private _expressions: Expression[] + + constructor(expressions: Expression[]) { + super() + + this._expressions = expressions + } + + get expressions(): Expression[] { + return this._expressions + } +} diff --git a/.yalc/tony-lang/src/ast/Boolean.ts b/.yalc/tony-lang/src/ast/Boolean.ts new file mode 100644 index 0000000..6e5652a --- /dev/null +++ b/.yalc/tony-lang/src/ast/Boolean.ts @@ -0,0 +1,19 @@ +import { SyntaxNode } from './SyntaxNode' + +export class Boolean extends SyntaxNode { + private _text: string + + constructor(text: string) { + super() + + this._text = text + } + + get text(): string { + return this._text + } + + get value(): boolean { + return this.text === 'true' + } +} diff --git a/.yalc/tony-lang/src/ast/Case.ts b/.yalc/tony-lang/src/ast/Case.ts new file mode 100644 index 0000000..b5bf87c --- /dev/null +++ b/.yalc/tony-lang/src/ast/Case.ts @@ -0,0 +1,30 @@ +import { Block } from './Block' +import { Expression } from './Expression' +import { SyntaxNode } from './SyntaxNode' +import { When } from './When' + +export class Case extends SyntaxNode { + private _branches: When[] + private _else: Block | undefined + private _value: Expression + + constructor(value: Expression, branches: When[], els?: Block) { + super() + + this._branches = branches + this._else = els + this._value = value + } + + get branches(): When[] { + return this._branches + } + + get else(): Block | undefined { + return this._else + } + + get value(): Expression { + return this._value + } +} diff --git a/.yalc/tony-lang/src/ast/Declaration.ts b/.yalc/tony-lang/src/ast/Declaration.ts new file mode 100644 index 0000000..e2ab1fb --- /dev/null +++ b/.yalc/tony-lang/src/ast/Declaration.ts @@ -0,0 +1,4 @@ +import { Assignment } from './Assignment' +import { Module } from './Module' + +export type Declaration = Assignment | Module diff --git a/.yalc/tony-lang/src/ast/DestructuringPattern.ts b/.yalc/tony-lang/src/ast/DestructuringPattern.ts new file mode 100644 index 0000000..284f6dd --- /dev/null +++ b/.yalc/tony-lang/src/ast/DestructuringPattern.ts @@ -0,0 +1,14 @@ +import { ListPattern } from './ListPattern' +import { MapPattern } from './MapPattern' +import { PatternPair } from './PatternPair' +import { Rest } from './Rest' +import { ShorthandPairIdentifierPattern } from './ShorthandPairIdentifierPattern' +import { TuplePattern } from './TuplePattern' + +export type DestructuringPattern = + | ListPattern + | MapPattern + | PatternPair + | Rest + | ShorthandPairIdentifierPattern + | TuplePattern diff --git a/.yalc/tony-lang/src/ast/ElseIf.ts b/.yalc/tony-lang/src/ast/ElseIf.ts new file mode 100644 index 0000000..023dd2a --- /dev/null +++ b/.yalc/tony-lang/src/ast/ElseIf.ts @@ -0,0 +1,23 @@ +import { Block } from './Block' +import { Expression } from './Expression' +import { SyntaxNode } from './SyntaxNode' + +export class ElseIf extends SyntaxNode { + private _body: Block + private _condition: Expression + + constructor(condition: Expression, body: Block) { + super() + + this._body = body + this._condition = condition + } + + get body(): Block { + return this._body + } + + get condition(): Expression { + return this._condition + } +} diff --git a/.yalc/tony-lang/src/ast/Export.ts b/.yalc/tony-lang/src/ast/Export.ts new file mode 100644 index 0000000..bcefc2f --- /dev/null +++ b/.yalc/tony-lang/src/ast/Export.ts @@ -0,0 +1,16 @@ +import { Declaration } from './Declaration' +import { SyntaxNode } from './SyntaxNode' + +export class Export extends SyntaxNode { + private _declaration: Declaration + + constructor(declaration: Declaration) { + super() + + this._declaration = declaration + } + + get declaration(): Declaration { + return this._declaration + } +} diff --git a/.yalc/tony-lang/src/ast/Expression.ts b/.yalc/tony-lang/src/ast/Expression.ts new file mode 100644 index 0000000..a2d259a --- /dev/null +++ b/.yalc/tony-lang/src/ast/Expression.ts @@ -0,0 +1,40 @@ +import { Abstraction } from './Abstraction' +import { Access } from './Access' +import { Application } from './Application' +import { Assignment } from './Assignment' +import { Case } from './Case' +import { Export } from './Export' +import { Identifier } from './Identifier' +import { If } from './If' +import { Import } from './Import' +import { InfixApplication } from './InfixApplication' +import { List } from './List' +import { ListComprehension } from './ListComprehension' +import { Literal } from './Literal' +import { Map } from './Map' +import { Module } from './Module' +import { Pipeline } from './Pipeline' +import { PrefixApplication } from './PrefixApplication' +import { Return } from './Return' +import { Tuple } from './Tuple' + +export type Expression = + | Abstraction + | Access + | Application + | Assignment + | Case + | Export + | Identifier + | If + | Import + | InfixApplication + | List + | ListComprehension + | Literal + | Map + | Module + | Pipeline + | PrefixApplication + | Return + | Tuple diff --git a/.yalc/tony-lang/src/ast/ExpressionPair.ts b/.yalc/tony-lang/src/ast/ExpressionPair.ts new file mode 100644 index 0000000..94f03bb --- /dev/null +++ b/.yalc/tony-lang/src/ast/ExpressionPair.ts @@ -0,0 +1,25 @@ +import { Expression } from './Expression' +import { ShorthandAccessIdentifier } from './ShorthandAccessIdentifier' +import { SyntaxNode } from './SyntaxNode' + +type Key = ShorthandAccessIdentifier | Expression + +export class ExpressionPair extends SyntaxNode { + private _key: Key + private _value: Expression + + constructor(key: Key, value: Expression) { + super() + + this._key = key + this._value = value + } + + get key(): Key { + return this._key + } + + get value(): Expression { + return this._value + } +} diff --git a/.yalc/tony-lang/src/ast/Generator.ts b/.yalc/tony-lang/src/ast/Generator.ts new file mode 100644 index 0000000..a7c1230 --- /dev/null +++ b/.yalc/tony-lang/src/ast/Generator.ts @@ -0,0 +1,39 @@ +import { Expression } from './Expression' +import { SyntaxNode } from './SyntaxNode' + +export class Generator extends SyntaxNode { + private _condition: Expression | undefined + private _name: string + private _transformedName: string + private _value: Expression + + constructor( + name: string, + transformedName: string, + value: Expression, + condition?: Expression, + ) { + super() + + this._condition = condition + this._name = name + this._transformedName = transformedName + this._value = value + } + + get condition(): Expression | undefined { + return this._condition + } + + get name(): string { + return this._name + } + + get transformedName(): string { + return this._transformedName + } + + get value(): Expression { + return this._value + } +} diff --git a/.yalc/tony-lang/src/ast/Identifier.ts b/.yalc/tony-lang/src/ast/Identifier.ts new file mode 100644 index 0000000..dc733f1 --- /dev/null +++ b/.yalc/tony-lang/src/ast/Identifier.ts @@ -0,0 +1,28 @@ +import { SyntaxNode } from './SyntaxNode' +import { Type } from '../types' + +export class Identifier extends SyntaxNode { + private _name: string + private _transformedName: string + private _type: Type + + constructor(type: Type, name: string, transformedName: string) { + super() + + this._name = name + this._transformedName = transformedName + this._type = type + } + + get name(): string { + return this._name + } + + get transformedName(): string { + return this._transformedName + } + + get type(): Type { + return this._type + } +} diff --git a/.yalc/tony-lang/src/ast/IdentifierPattern.ts b/.yalc/tony-lang/src/ast/IdentifierPattern.ts new file mode 100644 index 0000000..0857905 --- /dev/null +++ b/.yalc/tony-lang/src/ast/IdentifierPattern.ts @@ -0,0 +1,28 @@ +import { Expression } from './Expression' +import { SyntaxNode } from './SyntaxNode' + +export class IdentifierPattern extends SyntaxNode { + private _default: Expression | undefined + private _name: string + private _transformedName: string + + constructor(name: string, transformedName: string, def?: Expression) { + super() + + this._default = def + this._name = name + this._transformedName = transformedName + } + + get default(): Expression | undefined { + return this._default + } + + get name(): string { + return this._name + } + + get transformedName(): string { + return this._transformedName + } +} diff --git a/.yalc/tony-lang/src/ast/If.ts b/.yalc/tony-lang/src/ast/If.ts new file mode 100644 index 0000000..76ff196 --- /dev/null +++ b/.yalc/tony-lang/src/ast/If.ts @@ -0,0 +1,41 @@ +import { Block } from './Block' +import { ElseIf } from './ElseIf' +import { Expression } from './Expression' +import { SyntaxNode } from './SyntaxNode' + +export class If extends SyntaxNode { + private _body: Block + private _condition: Expression + private _else: Block | undefined + private _elseIfs: ElseIf[] + + constructor( + condition: Expression, + body: Block, + elseIfs: ElseIf[] = [], + els?: Block, + ) { + super() + + this._body = body + this._condition = condition + this._else = els + this._elseIfs = elseIfs + } + + get body(): Block { + return this._body + } + + get condition(): Expression { + return this._condition + } + + get else(): Block | undefined { + return this._else + } + + get elseIfs(): ElseIf[] { + return this._elseIfs + } +} diff --git a/.yalc/tony-lang/src/ast/Import.ts b/.yalc/tony-lang/src/ast/Import.ts new file mode 100644 index 0000000..19cea30 --- /dev/null +++ b/.yalc/tony-lang/src/ast/Import.ts @@ -0,0 +1,3 @@ +import { SyntaxNode } from './SyntaxNode' + +export class Import extends SyntaxNode {} diff --git a/.yalc/tony-lang/src/ast/InfixApplication.ts b/.yalc/tony-lang/src/ast/InfixApplication.ts new file mode 100644 index 0000000..345633b --- /dev/null +++ b/.yalc/tony-lang/src/ast/InfixApplication.ts @@ -0,0 +1,29 @@ +import { Expression } from './Expression' +import { Identifier } from './Identifier' +import { SyntaxNode } from './SyntaxNode' + +export class InfixApplication extends SyntaxNode { + private _left: Expression + private _right: Expression + private _value: Identifier + + constructor(left: Expression, value: Identifier, right: Expression) { + super() + + this._left = left + this._right = right + this._value = value + } + + get left(): Expression { + return this._left + } + + get right(): Expression { + return this._right + } + + get value(): Identifier { + return this._value + } +} diff --git a/.yalc/tony-lang/src/ast/Interpolation.ts b/.yalc/tony-lang/src/ast/Interpolation.ts new file mode 100644 index 0000000..fa6d1c5 --- /dev/null +++ b/.yalc/tony-lang/src/ast/Interpolation.ts @@ -0,0 +1,16 @@ +import { Expression } from './Expression' +import { SyntaxNode } from './SyntaxNode' + +export class Interpolation extends SyntaxNode { + private _value: Expression + + constructor(value: Expression) { + super() + + this._value = value + } + + get value(): Expression { + return this._value + } +} diff --git a/.yalc/tony-lang/src/ast/List.ts b/.yalc/tony-lang/src/ast/List.ts new file mode 100644 index 0000000..d73ef24 --- /dev/null +++ b/.yalc/tony-lang/src/ast/List.ts @@ -0,0 +1,19 @@ +import { Expression } from './Expression' +import { SpreadList } from './Spread' +import { SyntaxNode } from './SyntaxNode' + +type Element = Expression | SpreadList + +export class List extends SyntaxNode { + private _elements: Element[] + + constructor(elements: Element[]) { + super() + + this._elements = elements + } + + get elements(): Element[] { + return this._elements + } +} diff --git a/.yalc/tony-lang/src/ast/ListComprehension.ts b/.yalc/tony-lang/src/ast/ListComprehension.ts new file mode 100644 index 0000000..7196dbb --- /dev/null +++ b/.yalc/tony-lang/src/ast/ListComprehension.ts @@ -0,0 +1,23 @@ +import { Block } from './Block' +import { Generator } from './Generator' +import { SyntaxNode } from './SyntaxNode' + +export class ListComprehension extends SyntaxNode { + private _body: Block + private _generators: Generator[] + + constructor(body: Block, generators: Generator[]) { + super() + + this._body = body + this._generators = generators + } + + get body(): Block { + return this._body + } + + get generators(): Generator[] { + return this._generators + } +} diff --git a/.yalc/tony-lang/src/ast/ListPattern.ts b/.yalc/tony-lang/src/ast/ListPattern.ts new file mode 100644 index 0000000..2551394 --- /dev/null +++ b/.yalc/tony-lang/src/ast/ListPattern.ts @@ -0,0 +1,16 @@ +import { Pattern } from './Pattern' +import { SyntaxNode } from './SyntaxNode' + +export class ListPattern extends SyntaxNode { + private _elements: Pattern[] + + constructor(elements: Pattern[]) { + super() + + this._elements = elements + } + + get elements(): Pattern[] { + return this._elements + } +} diff --git a/.yalc/tony-lang/src/ast/Literal.ts b/.yalc/tony-lang/src/ast/Literal.ts new file mode 100644 index 0000000..3c2b71d --- /dev/null +++ b/.yalc/tony-lang/src/ast/Literal.ts @@ -0,0 +1,8 @@ +import { Boolean } from './Boolean' +import { Number } from './Number' +import { ParametricType } from './ParametricType' +import { Regex } from './Regex' +import { String } from './String' + +// eslint-disable-next-line @typescript-eslint/ban-types +export type Literal = Boolean | Number | ParametricType | Regex | String diff --git a/.yalc/tony-lang/src/ast/LiteralPattern.ts b/.yalc/tony-lang/src/ast/LiteralPattern.ts new file mode 100644 index 0000000..e201294 --- /dev/null +++ b/.yalc/tony-lang/src/ast/LiteralPattern.ts @@ -0,0 +1,14 @@ +import { Boolean } from './Boolean' +import { Number } from './Number' +import { ParametricType } from './ParametricType' +import { Regex } from './Regex' +import { StringPattern } from './StringPattern' + +export type LiteralPattern = + // eslint-disable-next-line @typescript-eslint/ban-types + | Boolean + // eslint-disable-next-line @typescript-eslint/ban-types + | Number + | ParametricType + | Regex + | StringPattern diff --git a/.yalc/tony-lang/src/ast/Map.ts b/.yalc/tony-lang/src/ast/Map.ts new file mode 100644 index 0000000..2c260be --- /dev/null +++ b/.yalc/tony-lang/src/ast/Map.ts @@ -0,0 +1,20 @@ +import { ExpressionPair } from './ExpressionPair' +import { ShorthandPairIdentifier } from './ShorthandPairIdentifier' +import { SpreadMap } from './Spread' +import { SyntaxNode } from './SyntaxNode' + +export type MapElement = ExpressionPair | ShorthandPairIdentifier | SpreadMap + +export class Map extends SyntaxNode { + private _elements: MapElement[] + + constructor(elements: MapElement[]) { + super() + + this._elements = elements + } + + get elements(): MapElement[] { + return this._elements + } +} diff --git a/.yalc/tony-lang/src/ast/MapPattern.ts b/.yalc/tony-lang/src/ast/MapPattern.ts new file mode 100644 index 0000000..afe2089 --- /dev/null +++ b/.yalc/tony-lang/src/ast/MapPattern.ts @@ -0,0 +1,23 @@ +import { PatternPair } from './PatternPair' +import { RestMap } from './Rest' +import { ShorthandPairIdentifierPattern } from './ShorthandPairIdentifierPattern' +import { SyntaxNode } from './SyntaxNode' + +export type MapPatternElement = + | PatternPair + | ShorthandPairIdentifierPattern + | RestMap + +export class MapPattern extends SyntaxNode { + private _elements: MapPatternElement[] + + constructor(elements: MapPatternElement[]) { + super() + + this._elements = elements + } + + get elements(): MapPatternElement[] { + return this._elements + } +} diff --git a/.yalc/tony-lang/src/ast/Module.ts b/.yalc/tony-lang/src/ast/Module.ts new file mode 100644 index 0000000..bac6e7b --- /dev/null +++ b/.yalc/tony-lang/src/ast/Module.ts @@ -0,0 +1,22 @@ +import { Block } from './Block' +import { SyntaxNode } from './SyntaxNode' + +export class Module extends SyntaxNode { + private _body: Block + private _name: string + + constructor(name: string, body: Block) { + super() + + this._body = body + this._name = name + } + + get body(): Block { + return this._body + } + + get name(): string { + return this._name + } +} diff --git a/.yalc/tony-lang/src/ast/Number.ts b/.yalc/tony-lang/src/ast/Number.ts new file mode 100644 index 0000000..0d21faf --- /dev/null +++ b/.yalc/tony-lang/src/ast/Number.ts @@ -0,0 +1,19 @@ +import { SyntaxNode } from './SyntaxNode' + +export class Number extends SyntaxNode { + private _text: string + + constructor(text: string) { + super() + + this._text = text + } + + get text(): string { + return this._text + } + + get value(): number { + return parseInt(this.text) + } +} diff --git a/.yalc/tony-lang/src/ast/Parameters.ts b/.yalc/tony-lang/src/ast/Parameters.ts new file mode 100644 index 0000000..4f05d83 --- /dev/null +++ b/.yalc/tony-lang/src/ast/Parameters.ts @@ -0,0 +1,19 @@ +import { Pattern } from './Pattern' +import { RestTuple } from './Rest' +import { SyntaxNode } from './SyntaxNode' + +type Parameter = Pattern | RestTuple + +export class Parameters extends SyntaxNode { + private _parameters: Parameter[] + + constructor(parameters: Parameter[] = []) { + super() + + this._parameters = parameters + } + + get parameters(): Parameter[] { + return this._parameters + } +} diff --git a/.yalc/tony-lang/src/ast/ParametricType.ts b/.yalc/tony-lang/src/ast/ParametricType.ts new file mode 100644 index 0000000..0d6874a --- /dev/null +++ b/.yalc/tony-lang/src/ast/ParametricType.ts @@ -0,0 +1,15 @@ +import { SyntaxNode } from './SyntaxNode' + +export class ParametricType extends SyntaxNode { + private _name: string + + constructor(name: string) { + super() + + this._name = name + } + + get name(): string { + return this._name + } +} diff --git a/.yalc/tony-lang/src/ast/Pattern.ts b/.yalc/tony-lang/src/ast/Pattern.ts new file mode 100644 index 0000000..e593c9f --- /dev/null +++ b/.yalc/tony-lang/src/ast/Pattern.ts @@ -0,0 +1,5 @@ +import { DestructuringPattern } from './DestructuringPattern' +import { IdentifierPattern } from './IdentifierPattern' +import { LiteralPattern } from './LiteralPattern' + +export type Pattern = DestructuringPattern | IdentifierPattern | LiteralPattern diff --git a/.yalc/tony-lang/src/ast/PatternPair.ts b/.yalc/tony-lang/src/ast/PatternPair.ts new file mode 100644 index 0000000..3a92b93 --- /dev/null +++ b/.yalc/tony-lang/src/ast/PatternPair.ts @@ -0,0 +1,26 @@ +import { Expression } from './Expression' +import { Pattern } from './Pattern' +import { ShorthandAccessIdentifier } from './ShorthandAccessIdentifier' +import { SyntaxNode } from './SyntaxNode' + +type Key = ShorthandAccessIdentifier | Expression + +export class PatternPair extends SyntaxNode { + private _key: Key + private _value: Pattern + + constructor(key: Key, value: Pattern) { + super() + + this._key = key + this._value = value + } + + get key(): Key { + return this._key + } + + get value(): Pattern { + return this._value + } +} diff --git a/.yalc/tony-lang/src/ast/Pipeline.ts b/.yalc/tony-lang/src/ast/Pipeline.ts new file mode 100644 index 0000000..55bbcfb --- /dev/null +++ b/.yalc/tony-lang/src/ast/Pipeline.ts @@ -0,0 +1,22 @@ +import { Expression } from './Expression' +import { SyntaxNode } from './SyntaxNode' + +export class Pipeline extends SyntaxNode { + private _argument: Expression + private _value: Expression + + constructor(value: Expression, argument: Expression) { + super() + + this._argument = argument + this._value = value + } + + get argument(): Expression { + return this._argument + } + + get value(): Expression { + return this._value + } +} diff --git a/.yalc/tony-lang/src/ast/PrefixApplication.ts b/.yalc/tony-lang/src/ast/PrefixApplication.ts new file mode 100644 index 0000000..84f132a --- /dev/null +++ b/.yalc/tony-lang/src/ast/PrefixApplication.ts @@ -0,0 +1,23 @@ +import { Expression } from './Expression' +import { Identifier } from './Identifier' +import { SyntaxNode } from './SyntaxNode' + +export class PrefixApplication extends SyntaxNode { + private _argument: Expression + private _value: Identifier + + constructor(value: Identifier, argument: Expression) { + super() + + this._argument = argument + this._value = value + } + + get argument(): Expression { + return this._argument + } + + get value(): Identifier { + return this._value + } +} diff --git a/.yalc/tony-lang/src/ast/Program.ts b/.yalc/tony-lang/src/ast/Program.ts new file mode 100644 index 0000000..582db9e --- /dev/null +++ b/.yalc/tony-lang/src/ast/Program.ts @@ -0,0 +1,16 @@ +import { Expression } from './Expression' +import { SyntaxNode } from './SyntaxNode' + +export class Program extends SyntaxNode { + private _expressions: Expression[] + + constructor(expressions: Expression[]) { + super() + + this._expressions = expressions + } + + get expressions(): Expression[] { + return this._expressions + } +} diff --git a/.yalc/tony-lang/src/ast/Regex.ts b/.yalc/tony-lang/src/ast/Regex.ts new file mode 100644 index 0000000..f3da867 --- /dev/null +++ b/.yalc/tony-lang/src/ast/Regex.ts @@ -0,0 +1,19 @@ +import { SyntaxNode } from './SyntaxNode' + +export class Regex extends SyntaxNode { + private _text: string + + constructor(text: string) { + super() + + this._text = text + } + + get text(): string { + return this._text + } + + get value(): RegExp { + return new RegExp(this.text) + } +} diff --git a/.yalc/tony-lang/src/ast/Rest.ts b/.yalc/tony-lang/src/ast/Rest.ts new file mode 100644 index 0000000..f8ff4a6 --- /dev/null +++ b/.yalc/tony-lang/src/ast/Rest.ts @@ -0,0 +1,20 @@ +import { IdentifierPattern } from './IdentifierPattern' +import { SyntaxNode } from './SyntaxNode' + +export abstract class Rest extends SyntaxNode { + private _name: IdentifierPattern + + constructor(name: IdentifierPattern) { + super() + + this._name = name + } + + get name(): IdentifierPattern { + return this._name + } +} + +export class RestList extends Rest {} +export class RestMap extends Rest {} +export class RestTuple extends Rest {} diff --git a/.yalc/tony-lang/src/ast/Return.ts b/.yalc/tony-lang/src/ast/Return.ts new file mode 100644 index 0000000..3b7ea45 --- /dev/null +++ b/.yalc/tony-lang/src/ast/Return.ts @@ -0,0 +1,16 @@ +import { Expression } from './Expression' +import { SyntaxNode } from './SyntaxNode' + +export class Return extends SyntaxNode { + private _value: Expression | undefined + + constructor(value?: Expression) { + super() + + this._value = value + } + + get value(): Expression | undefined { + return this._value + } +} diff --git a/.yalc/tony-lang/src/ast/ShorthandAccessIdentifier.ts b/.yalc/tony-lang/src/ast/ShorthandAccessIdentifier.ts new file mode 100644 index 0000000..42257f4 --- /dev/null +++ b/.yalc/tony-lang/src/ast/ShorthandAccessIdentifier.ts @@ -0,0 +1,21 @@ +import { SyntaxNode } from './SyntaxNode' + +export class ShorthandAccessIdentifier extends SyntaxNode { + private _name: string + private _transformedName: string | undefined + + constructor(name: string, transformedName?: string) { + super() + + this._name = name + this._transformedName = transformedName + } + + get name(): string { + return this._name + } + + get transformedName(): string | undefined { + return this._transformedName + } +} diff --git a/.yalc/tony-lang/src/ast/ShorthandPairIdentifier.ts b/.yalc/tony-lang/src/ast/ShorthandPairIdentifier.ts new file mode 100644 index 0000000..57177ca --- /dev/null +++ b/.yalc/tony-lang/src/ast/ShorthandPairIdentifier.ts @@ -0,0 +1,21 @@ +import { SyntaxNode } from './SyntaxNode' + +export class ShorthandPairIdentifier extends SyntaxNode { + private _name: string + private _transformedName: string + + constructor(name: string, transformedName: string) { + super() + + this._name = name + this._transformedName = transformedName + } + + get name(): string { + return this._name + } + + get transformedName(): string { + return this._transformedName + } +} diff --git a/.yalc/tony-lang/src/ast/ShorthandPairIdentifierPattern.ts b/.yalc/tony-lang/src/ast/ShorthandPairIdentifierPattern.ts new file mode 100644 index 0000000..78548b8 --- /dev/null +++ b/.yalc/tony-lang/src/ast/ShorthandPairIdentifierPattern.ts @@ -0,0 +1,3 @@ +import { IdentifierPattern } from './IdentifierPattern' + +export class ShorthandPairIdentifierPattern extends IdentifierPattern {} diff --git a/.yalc/tony-lang/src/ast/Spread.ts b/.yalc/tony-lang/src/ast/Spread.ts new file mode 100644 index 0000000..1de8cea --- /dev/null +++ b/.yalc/tony-lang/src/ast/Spread.ts @@ -0,0 +1,20 @@ +import { Expression } from './Expression' +import { SyntaxNode } from './SyntaxNode' + +export abstract class Spread extends SyntaxNode { + private _value: Expression + + constructor(value: Expression) { + super() + + this._value = value + } + + get value(): Expression { + return this._value + } +} + +export class SpreadList extends Spread {} +export class SpreadMap extends Spread {} +export class SpreadTuple extends Spread {} diff --git a/.yalc/tony-lang/src/ast/String.ts b/.yalc/tony-lang/src/ast/String.ts new file mode 100644 index 0000000..d13fa66 --- /dev/null +++ b/.yalc/tony-lang/src/ast/String.ts @@ -0,0 +1,22 @@ +import { Interpolation } from './Interpolation' +import { SyntaxNode } from './SyntaxNode' + +export class String extends SyntaxNode { + private _content: string + private _interpolations: Interpolation[] + + constructor(content: string, interpolations: Interpolation[]) { + super() + + this._content = content + this._interpolations = interpolations + } + + get content(): string { + return this._content + } + + get interpolations(): Interpolation[] { + return this._interpolations + } +} diff --git a/.yalc/tony-lang/src/ast/StringPattern.ts b/.yalc/tony-lang/src/ast/StringPattern.ts new file mode 100644 index 0000000..f96a6bc --- /dev/null +++ b/.yalc/tony-lang/src/ast/StringPattern.ts @@ -0,0 +1,15 @@ +import { SyntaxNode } from './SyntaxNode' + +export class StringPattern extends SyntaxNode { + private _content: string + + constructor(content: string) { + super() + + this._content = content + } + + get content(): string { + return this._content + } +} diff --git a/.yalc/tony-lang/src/ast/SyntaxNode.ts b/.yalc/tony-lang/src/ast/SyntaxNode.ts new file mode 100644 index 0000000..f01b127 --- /dev/null +++ b/.yalc/tony-lang/src/ast/SyntaxNode.ts @@ -0,0 +1 @@ +export abstract class SyntaxNode {} diff --git a/.yalc/tony-lang/src/ast/Tuple.ts b/.yalc/tony-lang/src/ast/Tuple.ts new file mode 100644 index 0000000..a8e6949 --- /dev/null +++ b/.yalc/tony-lang/src/ast/Tuple.ts @@ -0,0 +1,19 @@ +import { Expression } from './Expression' +import { SpreadTuple } from './Spread' +import { SyntaxNode } from './SyntaxNode' + +type Element = Expression | SpreadTuple + +export class Tuple extends SyntaxNode { + private _elements: Element[] + + constructor(elements: Element[]) { + super() + + this._elements = elements + } + + get elements(): Element[] { + return this._elements + } +} diff --git a/.yalc/tony-lang/src/ast/TuplePattern.ts b/.yalc/tony-lang/src/ast/TuplePattern.ts new file mode 100644 index 0000000..f8ee3ca --- /dev/null +++ b/.yalc/tony-lang/src/ast/TuplePattern.ts @@ -0,0 +1,16 @@ +import { Pattern } from './Pattern' +import { SyntaxNode } from './SyntaxNode' + +export class TuplePattern extends SyntaxNode { + private _elements: Pattern[] + + constructor(elements: Pattern[]) { + super() + + this._elements = elements + } + + get elements(): Pattern[] { + return this._elements + } +} diff --git a/.yalc/tony-lang/src/ast/When.ts b/.yalc/tony-lang/src/ast/When.ts new file mode 100644 index 0000000..ff4f61a --- /dev/null +++ b/.yalc/tony-lang/src/ast/When.ts @@ -0,0 +1,23 @@ +import { Block } from './Block' +import { Pattern } from './Pattern' +import { SyntaxNode } from './SyntaxNode' + +export class When extends SyntaxNode { + private _body: Block + private _patterns: Pattern[] + + constructor(patterns: Pattern[], body: Block) { + super() + + this._body = body + this._patterns = patterns + } + + get body(): Block { + return this._body + } + + get patterns(): Pattern[] { + return this._patterns + } +} diff --git a/.yalc/tony-lang/src/ast/index.ts b/.yalc/tony-lang/src/ast/index.ts new file mode 100644 index 0000000..cce1319 --- /dev/null +++ b/.yalc/tony-lang/src/ast/index.ts @@ -0,0 +1,51 @@ +export { Abstraction } from './Abstraction' +export { AbstractionBranch } from './AbstractionBranch' +export { Access } from './Access' +export { Application } from './Application' +export { Argument } from './Argument' +export { Assignment, AssignablePattern } from './Assignment' +export { Block } from './Block' +export { Boolean } from './Boolean' +export { Case } from './Case' +export { Declaration } from './Declaration' +export { DestructuringPattern } from './DestructuringPattern' +export { ElseIf } from './ElseIf' +export { Export } from './Export' +export { Expression } from './Expression' +export { ExpressionPair } from './ExpressionPair' +export { Generator } from './Generator' +export { Identifier } from './Identifier' +export { IdentifierPattern } from './IdentifierPattern' +export { If } from './If' +export { Import } from './Import' +export { InfixApplication } from './InfixApplication' +export { Interpolation } from './Interpolation' +export { List } from './List' +export { ListComprehension } from './ListComprehension' +export { ListPattern } from './ListPattern' +export { Literal } from './Literal' +export { LiteralPattern } from './LiteralPattern' +export { Map, MapElement } from './Map' +export { MapPattern, MapPatternElement } from './MapPattern' +export { Module } from './Module' +export { Number } from './Number' +export { Parameters } from './Parameters' +export { ParametricType } from './ParametricType' +export { Pattern } from './Pattern' +export { PatternPair } from './PatternPair' +export { Pipeline } from './Pipeline' +export { PrefixApplication } from './PrefixApplication' +export { Program } from './Program' +export { Regex } from './Regex' +export { Rest, RestList, RestMap, RestTuple } from './Rest' +export { Return } from './Return' +export { ShorthandAccessIdentifier } from './ShorthandAccessIdentifier' +export { ShorthandPairIdentifier } from './ShorthandPairIdentifier' +export { ShorthandPairIdentifierPattern } from './ShorthandPairIdentifierPattern' +export { Spread, SpreadList, SpreadMap, SpreadTuple } from './Spread' +export { String } from './String' +export { StringPattern } from './StringPattern' +export { SyntaxNode } from './SyntaxNode' +export { Tuple } from './Tuple' +export { TuplePattern } from './TuplePattern' +export { When } from './When' diff --git a/.yalc/tony-lang/src/code_generation/GenerateCode.ts b/.yalc/tony-lang/src/code_generation/GenerateCode.ts index 9a6216a..bd12616 100644 --- a/.yalc/tony-lang/src/code_generation/GenerateCode.ts +++ b/.yalc/tony-lang/src/code_generation/GenerateCode.ts @@ -1,3 +1,4 @@ +import * as AST from '../ast' import { CollectDefaultValues, GenerateBlock, @@ -11,31 +12,26 @@ import { TRANSFORM_PLACEHOLDER_ARGUMENT, TRANSFORM_REST_PATTERN, } from '../constants' -import Parser from 'tree-sitter' -import { isNotUndefined } from '../utilities' export const INTERNAL_TEMP_TOKEN = Object.freeze('#TONY_INTERNAL_TEMP') export class GenerateCode { private _fileScope: FileModuleScope - private _listComprehensionGeneratorCountStack: number[] = [] - private _walkFileModuleScope: WalkFileModuleScope constructor(fileScope: FileModuleScope) { this._fileScope = fileScope - this._walkFileModuleScope = new WalkFileModuleScope(fileScope) } perform = (): string => { assert( - this._fileScope.tree !== undefined, - 'Syntax tree of file scope should be present.', + this._fileScope.annotatedTree !== undefined, + 'Annotated syntax tree of file scope should be present.', ) try { - return this.handleProgram(this._fileScope.tree.rootNode) + return this.handleProgram(this._fileScope.annotatedTree) } catch (error) { if (error instanceof CompileError) error.filePath = this._fileScope.filePath @@ -44,128 +40,110 @@ export class GenerateCode { } // eslint-disable-next-line max-lines-per-function - traverse = (node: Parser.SyntaxNode): string | undefined => { - switch (node.type) { - case 'abstraction': - return this.handleAbstraction(node) - case 'abstraction_branch': - return this.handleAbstractionBranch(node) - case 'access': - return this.handleAccess(node) - case 'application': - return this.handleApplication(node) - case 'argument': - return this.handleArgument(node) - case 'arguments': - return this.handleArguments(node) - case 'assignment': - return this.handleAssignment(node) - case 'block': - return this.handleBlock(node) - case 'boolean': - return node.text - case 'case': - return this.handleCase(node) - case 'comment': - return - case 'else_if_clause': - return this.handleElseIfClause(node) - case 'else_if_clauses': - return this.handleElseIfClauses(node) - case 'export': - return this.handleExport(node) - case 'expression_pair': - return this.handleExpressionPair(node) - case 'generator': - return this.handleGenerator(node) - case 'generators': - return this.handleGenerators(node) - case 'identifier': - return this.handleIdentifier(node) - case 'identifier_pattern': - return this.handleIdentifierPattern(node) - case 'identifier_pattern_name': - return this.handleIdentifierPatternName(node) - case 'if': - return this.handleIf(node) - case 'import': - return - case 'infix_application': - return this.handleInfixApplication(node) - case 'interpolation': - return this.handleInterpolation(node) - case 'list': - return this.handleList(node) - case 'list_comprehension': - return this.handleListComprehension(node) - case 'list_pattern': - return this.handleListPattern(node) - case 'map': - return this.handleMap(node) - case 'map_pattern': - return this.handleMapPattern(node) - case 'module': - return this.handleModule(node) - case 'number': - return node.text - case 'parameters': - return this.handleParameters(node) - case 'pattern_list': - return this.handlePatternList(node) - case 'pattern_pair': - return this.handlePatternPair(node) - case 'pipeline': - return this.handlePipeline(node) - case 'prefix_application': - return this.handlePrefixApplication(node) - case 'program': - return this.handleProgram(node) - case 'regex': - return node.text - case 'rest_list': - return this.handleRestList(node) - case 'rest_map': - return this.handleRestMap(node) - case 'rest_tuple': - return this.handleRestTuple(node) - case 'return': - return this.handleReturn(node) - case 'shorthand_access_identifier': - return this.handleShorthandAccessIdentifier(node) - case 'shorthand_pair_identifier': - return this.handleShorthandPairIdentifier(node) - case 'shorthand_pair_identifier_pattern': - return this.handleShorthandPairIdentifierPattern(node) - case 'spread_list': - case 'spread_map': - case 'spread_tuple': - return this.handleSpread(node) - case 'string': - return this.handleString(node) - case 'string_pattern': - return this.handleStringPattern(node) - case 'tuple': - return this.handleTuple(node) - case 'tuple_pattern': - return this.handleTuplePattern(node) - case 'type': - return this.handleType(node) - case 'when_clause': - return this.handleWhenClause(node) - case 'when_clauses': - return this.handleWhenClauses(node) - default: - throw new InternalError( - 'GenerateCode: Could not find generator for AST node ' + - `'${node.type}'.`, - ) - } + traverse = (node: AST.SyntaxNode): string => { + if (node.constructor === AST.Abstraction) + return this.handleAbstraction(node as AST.Abstraction) + else if (node.constructor === AST.AbstractionBranch) + return this.handleAbstractionBranch(node as AST.AbstractionBranch) + else if (node.constructor === AST.Access) + return this.handleAccess(node as AST.Access) + else if (node.constructor === AST.Application) + return this.handleApplication(node as AST.Application) + else if (node.constructor === AST.Argument) + return this.handleArgument(node as AST.Argument) + else if (node.constructor === AST.Assignment) + return this.handleAssignment(node as AST.Assignment) + else if (node.constructor === AST.Block) + return this.handleBlock(node as AST.Block) + else if (node.constructor === AST.Boolean) return (node as AST.Boolean).text + else if (node.constructor === AST.Case) + return this.handleCase(node as AST.Case) + else if (node.constructor === AST.ElseIf) + return this.handleElseIf(node as AST.ElseIf) + else if (node.constructor === AST.Export) + return this.handleExport(node as AST.Export) + else if (node.constructor === AST.ExpressionPair) + return this.handleExpressionPair(node as AST.ExpressionPair) + else if (node.constructor === AST.Generator) + return this.handleGenerator(node as AST.Generator) + else if (node.constructor === AST.Identifier) + return this.handleIdentifier(node as AST.Identifier) + else if (node.constructor === AST.IdentifierPattern) + return this.handleIdentifierPattern(node as AST.IdentifierPattern) + else if (node.constructor === AST.If) return this.handleIf(node as AST.If) + else if (node.constructor === AST.Import) return '' + else if (node.constructor === AST.InfixApplication) + return this.handleInfixApplication(node as AST.InfixApplication) + else if (node.constructor === AST.Interpolation) + return this.handleInterpolation(node as AST.Interpolation) + else if (node.constructor === AST.List) + return this.handleList(node as AST.List) + else if (node.constructor === AST.ListComprehension) + return this.handleListComprehension(node as AST.ListComprehension) + else if (node.constructor === AST.ListPattern) + return this.handleListPattern(node as AST.ListPattern) + else if (node.constructor === AST.Map) + return this.handleMap(node as AST.Map) + else if (node.constructor === AST.MapPattern) + return this.handleMapPattern(node as AST.MapPattern) + else if (node.constructor === AST.Module) + return this.handleModule(node as AST.Module) + else if (node.constructor === AST.Number) return (node as AST.Number).text + else if (node.constructor === AST.Parameters) + return this.handleParameters(node as AST.Parameters) + else if (node.constructor === AST.ParametricType) + return (node as AST.ParametricType).name + else if (node.constructor === AST.PatternPair) + return this.handlePatternPair(node as AST.PatternPair) + else if (node.constructor === AST.Pipeline) + return this.handlePipeline(node as AST.Pipeline) + else if (node.constructor === AST.PrefixApplication) + return this.handlePrefixApplication(node as AST.PrefixApplication) + else if (node.constructor === AST.Regex) return (node as AST.Regex).text + else if ( + node.constructor === AST.RestList || + node.constructor === AST.RestTuple + ) + return this.handleRestListOrRestTuple( + node as AST.RestList | AST.RestTuple, + ) + else if (node.constructor === AST.RestMap) + return this.handleRestMap(node as AST.RestMap) + else if (node.constructor === AST.Return) + return this.handleReturn(node as AST.Return) + else if (node.constructor === AST.ShorthandAccessIdentifier) + return this.handleShorthandAccessIdentifier( + node as AST.ShorthandAccessIdentifier, + ) + else if (node.constructor === AST.ShorthandPairIdentifier) + return this.handleShorthandPairIdentifier( + node as AST.ShorthandPairIdentifier, + ) + else if (node.constructor === AST.ShorthandPairIdentifierPattern) + return this.handleShorthandPairIdentifierPattern( + node as AST.ShorthandPairIdentifierPattern, + ) + else if (node.constructor === AST.Spread) + return this.handleSpread(node as AST.Spread) + else if (node.constructor === AST.String) + return this.handleString(node as AST.String) + else if (node.constructor === AST.StringPattern) + return this.handleStringPattern(node as AST.StringPattern) + else if (node.constructor === AST.Tuple) + return this.handleTuple(node as AST.Tuple) + else if (node.constructor === AST.TuplePattern) + return this.handleTuplePattern(node as AST.TuplePattern) + else if (node.constructor === AST.When) + return this.handleWhen(node as AST.When) + + throw new InternalError( + 'Could not find generator for AST node ' + `'${node.constructor.name}'.`, + ) } - private handleAbstraction = (node: Parser.SyntaxNode): string => { - const branches = node.namedChildren - .map((element) => this.traverse(element)) - .filter(isNotUndefined) + private handleAbstraction = (node: AST.Abstraction): string => { + const branches = node.branches + .map((branch) => this.traverse(branch)) .join(',') return ( @@ -174,14 +152,14 @@ export class GenerateCode { ) } - private handleAbstractionBranch = (node: Parser.SyntaxNode): string => { + private handleAbstractionBranch = (node: AST.AbstractionBranch): string => { this._walkFileModuleScope.peekBlock() - const parameters = this.traverse(node.namedChild(0)!)! + const parameters = this.traverse(node.parameters) this._walkFileModuleScope.leaveBlock() - const body = this.traverse(node.namedChild(1)!) + const body = this.traverse(node.body) const [pattern, identifiers] = ResolvePattern.perform(parameters) - const defaults = new CollectDefaultValues(this).perform(node.namedChild(0)!) + const defaults = new CollectDefaultValues(this).perform(node.parameters) return ( `[${pattern},${defaults},(match)=>{` + @@ -189,55 +167,42 @@ export class GenerateCode { ) } - private handleAccess = (node: Parser.SyntaxNode): string => { - const left = this.traverse(node.namedChild(0)!) - const right = this.traverse(node.namedChild(1)!) + private handleAccess = (node: AST.Access): string => + `${this.traverse(node.value)}[${this.traverse(node.accessor)}]` - return `${left}[${right}]` - } - - private handleApplication = (node: Parser.SyntaxNode): string => { - const abstraction = this.traverse(node.namedChild(0)!) - const args = this.traverse(node.namedChild(1)!) - - return `${abstraction}(${args})` - } - - private handleArgument = (node: Parser.SyntaxNode): string => { - if (node.namedChildCount == 0) return `"${TRANSFORM_PLACEHOLDER_ARGUMENT}"` - - const expression = this.traverse(node.namedChild(0)!)! - return expression - } - - private handleArguments = (node: Parser.SyntaxNode): string => { - const args = node.namedChildren + private handleApplication = (node: AST.Application): string => + `${this.traverse(node.value)}(${node.arguments .map((argument) => this.traverse(argument)) - .filter(isNotUndefined) - .join(',') + .join(',')})` + + private handleArgument = (node: AST.Argument): string => { + if (node.value === undefined) return `"${TRANSFORM_PLACEHOLDER_ARGUMENT}"` - return args + return this.traverse(node.value) } - private handleAssignment = (node: Parser.SyntaxNode): string => { - const left = this.traverse(node.namedChild(0)!)! - const right = this.traverse(node.namedChild(1)!) - const [pattern, identifiers] = ResolvePattern.perform(left) - const defaults = new CollectDefaultValues(this).perform(node.namedChild(0)!) + private handleAssignment = (node: AST.Assignment): string => { + const [pattern, identifiers] = ResolvePattern.perform( + this.traverse(node.pattern), + ) + const defaults = new CollectDefaultValues(this).perform(node.pattern) return ( - `(()=>{const value=${right};[${identifiers.join(',')}]=new stdlib.` + + `(()=>{const value=${this.traverse(node.value)};[${identifiers.join( + ',', + )}]=new stdlib.` + `PatternMatch({defaults:${defaults},overmatching:true}).perform(` + `${pattern},value);return value})()` ) } - private handleBlock = (node: Parser.SyntaxNode): string => { + private handleBlock = (node: AST.Block): string => { this._walkFileModuleScope.enterBlock() - const expressions = node.namedChildren - .map((expression) => this.traverse(expression)) - .filter(isNotUndefined) - const endsWithReturn = node.lastNamedChild!.type === 'return' + const expressions = node.expressions.map((expression) => + this.traverse(expression), + ) + const endsWithReturn = + node.expressions[node.expressions.length - 1] instanceof AST.Return const block = new GenerateBlock(this._walkFileModuleScope.scope).perform( expressions, endsWithReturn, @@ -247,235 +212,146 @@ export class GenerateCode { return block } - private handleCase = (node: Parser.SyntaxNode): string => { - const value = this.traverse(node.namedChild(0)!) - const branches = this.traverse(node.namedChild(1)!) - if (node.namedChildCount == 2) + private handleCase = (node: AST.Case): string => { + const value = this.traverse(node.value) + const branches = node.branches + .map((branch) => this.traverse(branch)) + .join(',') + if (node.else === undefined) return `stdlib.ResolveAbstractionBranch.perform(${value},[${branches}])` - const defaultValue = this.traverse(node.namedChild(2)!) + const defaultValue = this.traverse(node.else) return ( `stdlib.ResolveAbstractionBranch.perform(${value},[${branches}],` + `()=>${defaultValue},false)` ) } - private handleElseIfClause = (node: Parser.SyntaxNode): string => { - const condition = this.traverse(node.namedChild(0)!) - const consequence = this.traverse(node.namedChild(1)!) - - return `else if(${condition}){return ${consequence}}` - } - - private handleElseIfClauses = (node: Parser.SyntaxNode): string => { - const clauses = node.namedChildren - .map((clause) => this.traverse(clause)) - .filter(isNotUndefined) - .join('') - - return clauses - } - - private handleExport = (node: Parser.SyntaxNode): string => { - const declaration = this.traverse(node.namedChild(0)!)! - - return declaration - } - - private handleExpressionPair = (node: Parser.SyntaxNode): string => { - const left = this.traverse(node.namedChild(0)!) - const right = this.traverse(node.namedChild(1)!) + private handleElseIf = (node: AST.ElseIf): string => + `else if(${this.traverse(node.condition)}){return ${this.traverse( + node.body, + )}}` - return `[${left}]:${right}` - } - - private handleGenerator = (node: Parser.SyntaxNode): string => { - const name = node.namedChild(0)!.text - const binding = this._walkFileModuleScope.scope.resolveBinding(name) - assert(binding !== undefined, 'Binding should not be undefined.') - const value = this.traverse(node.namedChild(1)!) - if (node.namedChildCount == 2) - return `${value}.map((${binding.transformedName})=>` + private handleExport = (node: AST.Export): string => + this.traverse(node.declaration) - const condition = this.traverse(node.namedChild(2)!) - return `${value}.map((${binding.transformedName})=>!${condition} ? "${INTERNAL_TEMP_TOKEN}" : ` - } + private handleExpressionPair = (node: AST.ExpressionPair): string => + `[${this.traverse(node.key)}]:${this.traverse(node.value)}` - private handleGenerators = (node: Parser.SyntaxNode): string => { - const generators = node.namedChildren - .map((generator) => this.traverse(generator)) - .filter(isNotUndefined) - .join('') + private handleGenerator = (node: AST.Generator): string => { + const value = this.traverse(node.value) + if (node.condition === undefined) + return `${value}.map((${node.transformedName})=>` - this._listComprehensionGeneratorCountStack.push(node.namedChildCount) - return generators + return `${value}.map((${node.transformedName})=>!${this.traverse( + node.condition, + )} ? "${INTERNAL_TEMP_TOKEN}" : ` } - private handleIdentifier = (node: Parser.SyntaxNode): string => { - const name = node.text - const binding = this._walkFileModuleScope.scope.resolveBinding(name) + private handleIdentifier = (node: AST.Identifier): string => + node.transformedName - assert(binding !== undefined, 'Binding should not be undefined.') + private handleIdentifierPattern = (node: AST.IdentifierPattern): string => + `"${INTERNAL_TEMP_TOKEN}${node.transformedName}"` - return binding.transformedName - } + private handleIf = (node: AST.If): string => { + let result = '(()=>{' - private handleIdentifierPattern = (node: Parser.SyntaxNode): string => - this.traverse(node.namedChild(0)!)! + const condition = this.traverse(node.condition) + const body = this.traverse(node.body) + result += `if(${condition}){return ${body}}` - private handleIdentifierPatternName = (node: Parser.SyntaxNode): string => { - const name = node.text - const binding = this._walkFileModuleScope.scope.resolveBinding(name) + if (node.elseIfs.length > 0) + result += node.elseIfs.map((elseIf) => this.traverse(elseIf)).join('') - assert(binding !== undefined, 'Binding should not be undefined.') - - return `"${INTERNAL_TEMP_TOKEN}${binding.transformedName}"` - } - - // eslint-disable-next-line max-lines-per-function - private handleIf = (node: Parser.SyntaxNode): string => { - const condition = this.traverse(node.namedChild(0)!) - const consequence = this.traverse(node.namedChild(1)!) - if (node.namedChildCount == 2) - return `(()=>{if(${condition}){return ${consequence}}})()` - - if (node.namedChild(node.namedChildCount - 1)!.type === 'else_if_clauses') { - const clauses = this.traverse(node.namedChild(2)!) - - return `(()=>{if(${condition}){return ${consequence}}${clauses}})()` - } else if (node.namedChildCount == 3) { - const alternative = this.traverse(node.namedChild(2)!) - - return ( - `(()=>{if(${condition}){return ${consequence}}` + - `else{return ${alternative}}})()` - ) - } else { - const clauses = this.traverse(node.namedChild(2)!) - const alternative = this.traverse(node.namedChild(3)!) + if (node.else) result += `else{return ${this.traverse(node.else)}}` - return ( - `(()=>{if(${condition}){return ${consequence}}${clauses}` + - `else{return ${alternative}}})()` - ) - } + result += '})()' + return result } - private handleInfixApplication = (node: Parser.SyntaxNode): string => { - const abstraction = this.traverse(node.namedChild(1)!) - const left = this.traverse(node.namedChild(0)!) - const right = this.traverse(node.namedChild(2)!) - - return `${abstraction}(${left},${right})` - } + private handleInfixApplication = (node: AST.InfixApplication): string => + `${this.traverse(node.value)}(${this.traverse(node.left)},${this.traverse( + node.right, + )})` - private handleInterpolation = (node: Parser.SyntaxNode): string => - this.traverse(node.namedChild(0)!)! + private handleInterpolation = (node: AST.Interpolation): string => + this.traverse(node.value) - private handleList = (node: Parser.SyntaxNode): string => { - const elements = node.namedChildren + private handleList = (node: AST.List): string => { + const elements = node.elements .map((element) => this.traverse(element)) - .filter(isNotUndefined) .join(',') return `[${elements}]` } - private handleListComprehension = (node: Parser.SyntaxNode): string => { + private handleListComprehension = (node: AST.ListComprehension): string => { this._walkFileModuleScope.peekBlock() - const generators = this.traverse(node.namedChild(1)!) + const generators = node.generators + .map((generator) => this.traverse(generator)) + .join('') this._walkFileModuleScope.leaveBlock() - const body = this.traverse(node.namedChild(0)!) - const generatorCount = this._listComprehensionGeneratorCountStack.pop() - assert( - generatorCount !== undefined, - 'generatorCount should have been pushed when analyzing generators.', - ) + const body = this.traverse(node.body) return ( - `${generators}${body}${')'.repeat(generatorCount)}.flat(` + - `${generatorCount - 1}).filter(e=>e!=="${INTERNAL_TEMP_TOKEN}")` + `${generators}${body}${')'.repeat(node.generators.length)}.flat(` + + `${node.generators.length - 1}).filter(e=>e!=="${INTERNAL_TEMP_TOKEN}")` ) } - private handleListPattern = (node: Parser.SyntaxNode): string => { - const elements = node.namedChildren + private handleListPattern = (node: AST.ListPattern): string => { + const elements = node.elements .map((element) => this.traverse(element)) - .filter(isNotUndefined) .join(',') return `[${elements}]` } - private handleMap = (node: Parser.SyntaxNode): string => { - const elements = node.namedChildren + private handleMap = (node: AST.Map): string => { + const elements = node.elements .map((element) => this.traverse(element)) - .filter(isNotUndefined) .join(',') return `{${elements}}` } - private handleMapPattern = (node: Parser.SyntaxNode): string => { - const elements = node.namedChildren + private handleMapPattern = (node: AST.MapPattern): string => { + const elements = node.elements .map((element) => this.traverse(element)) - .filter(isNotUndefined) .join(',') return `{${elements}}` } - private handleModule = (node: Parser.SyntaxNode): string => { - const name = this.traverse(node.namedChild(0)!) - const body = this.traverse(node.namedChild(1)!) + private handleModule = (node: AST.Module): string => { + const name = this.traverse(node.name) - return `(()=>{${name}=${body};return ${name}})()` + return `(()=>{${name}=${this.traverse(node.body)};return ${name}})()` } - private handleParameters = (node: Parser.SyntaxNode): string => { - const parameters = node.namedChildren + private handleParameters = (node: AST.Parameters): string => { + const parameters = node.parameters .map((parameter) => this.traverse(parameter)) - .filter(isNotUndefined) .join(',') return `[${parameters}]` } - private handlePatternList = (node: Parser.SyntaxNode): string => { - const patterns = node.namedChildren - .map((pattern) => this.traverse(pattern)) - .filter(isNotUndefined) - .join(';') - - return patterns - } + private handlePatternPair = (node: AST.PatternPair): string => + `"[${this.traverse(node.key)}]":${this.traverse(node.value)}` - private handlePatternPair = (node: Parser.SyntaxNode): string => { - const left = this.traverse(node.namedChild(0)!) - const right = this.traverse(node.namedChild(1)!) + private handlePipeline = (node: AST.Pipeline): string => + `${this.traverse(node.value)}(${this.traverse(node.argument)})` - return `"[${left}]":${right}` - } + private handlePrefixApplication = (node: AST.PrefixApplication): string => + `${this.traverse(node.value)}(${this.traverse(node.argument)})` - private handlePipeline = (node: Parser.SyntaxNode): string => { - const left = this.traverse(node.namedChild(0)!) - const right = this.traverse(node.namedChild(1)!) - - return `${right}(${left})` - } - - private handlePrefixApplication = (node: Parser.SyntaxNode): string => { - const abstraction = this.traverse(node.namedChild(0)!) - const argument = this.traverse(node.namedChild(1)!) - - return `${abstraction}(${argument})` - } - - private handleProgram = (node: Parser.SyntaxNode): string => { - const expressions = node.namedChildren - .map((expression) => this.traverse(expression)) - .filter(isNotUndefined) + private handleProgram = (node: AST.Program): string => { + const expressions = node.expressions.map((expression) => + this.traverse(expression), + ) assert( this._walkFileModuleScope.scope instanceof FileModuleScope, @@ -487,114 +363,85 @@ export class GenerateCode { ) } - private handleRestList = (node: Parser.SyntaxNode): string => { - const name = this.traverse(node.namedChild(0)!)!.slice(1, -1) + private handleRestListOrRestTuple = ( + node: AST.RestList | AST.RestTuple, + ): string => { + const name = this.traverse(node.name).slice(1, -1) return `"${INTERNAL_TEMP_TOKEN}${name}"` } - private handleRestMap = (node: Parser.SyntaxNode): string => { - const name = this.traverse(node.namedChild(0)!)!.slice(1, -1) + private handleRestMap = (node: AST.RestMap): string => { + const name = this.traverse(node.name).slice(1, -1) return `"['${TRANSFORM_REST_PATTERN}']":"${name}"` } - private handleRestTuple = (node: Parser.SyntaxNode): string => { - const name = this.traverse(node.namedChild(0)!)!.slice(1, -1) + private handleReturn = (node: AST.Return): string => { + if (node.value === undefined) return 'return' - return `"${INTERNAL_TEMP_TOKEN}${name}"` - } - - private handleReturn = (node: Parser.SyntaxNode): string => { - if (node.namedChildCount == 0) return 'return' - - const value = this.traverse(node.namedChild(0)!) - return `return ${value}` + return `return ${this.traverse(node.value)}` } private handleShorthandAccessIdentifier = ( - node: Parser.SyntaxNode, - ): string => { - const name = node.text - - return `'${name}'` - } - - private handleShorthandPairIdentifier = (node: Parser.SyntaxNode): string => { - const name = node.text - const binding = this._walkFileModuleScope.scope.resolveBinding(name) + node: AST.ShorthandAccessIdentifier, + ): string => `'${node.transformedName || node.name}'` - assert(binding !== undefined, 'Binding should not be undefined.') - - return `${name}:${binding.transformedName}` - } + private handleShorthandPairIdentifier = ( + node: AST.ShorthandPairIdentifier, + ): string => `${node.name}:${node.transformedName}` private handleShorthandPairIdentifierPattern = ( - node: Parser.SyntaxNode, - ): string => { - const name = node.namedChild(0)!.text - const identifierPattern = this.traverse(node.namedChild(0)!)! - - return `"${name}":${identifierPattern}` - } - - private handleSpread = (node: Parser.SyntaxNode): string => { - const expression = this.traverse(node.namedChild(0)!) + node: AST.ShorthandPairIdentifierPattern, + ): string => `"${node.name}":"${INTERNAL_TEMP_TOKEN}${node.transformedName}"` - return `...${expression}` - } + private handleSpread = (node: AST.Spread): string => + `...${this.traverse(node.value)}` - private handleString = (node: Parser.SyntaxNode): string => { - const interpolations = node.namedChildren - .filter((child) => child.type === 'interpolation') - .map((child) => this.handleInterpolation(child)) - const content = ParseStringContent.perform(node.text) + private handleString = (node: AST.String): string => { + const interpolations = node.interpolations.map((child) => + this.traverse(child), + ) + const content = ParseStringContent.perform(node.content) .replace(/(? interpolations.shift()!) return `\`${content}\`` } - private handleStringPattern = (node: Parser.SyntaxNode): string => { - const content = ParseStringContent.perform(node.text, '"') + private handleStringPattern = (node: AST.StringPattern): string => { + const content = ParseStringContent.perform(node.content, '"') return `"${content}"` } - private handleTuple = (node: Parser.SyntaxNode): string => { - const elements = node.namedChildren + private handleTuple = (node: AST.Tuple): string => { + const elements = node.elements .map((element) => this.traverse(element)) - .filter(isNotUndefined) .join(',') return `[${elements}]` } - private handleTuplePattern = (node: Parser.SyntaxNode): string => { - const elements = node.namedChildren + private handleTuplePattern = (node: AST.TuplePattern): string => { + const elements = node.elements .map((element) => this.traverse(element)) - .filter(isNotUndefined) .join(',') return `[${elements}]` } - private handleType = (node: Parser.SyntaxNode): string => node.text - - // eslint-disable-next-line max-lines-per-function - private handleWhenClause = (node: Parser.SyntaxNode): string => { + private handleWhen = (node: AST.When): string => { this._walkFileModuleScope.peekBlock() - const patterns = this.traverse(node.namedChild(0)!)! - .split(';') - .map((pattern) => ResolvePattern.perform(pattern)) + const patterns = node.patterns.map((pattern) => + ResolvePattern.perform(this.traverse(pattern)), + ) this._walkFileModuleScope.leaveBlock() - const body = this.traverse(node.namedChild(1)!) - const defaults = node - .namedChild(0)! - .namedChildren.map((pattern) => - new CollectDefaultValues(this).perform(pattern), - ) + const body = this.traverse(node.body) + const defaults = node.patterns.map((pattern) => + new CollectDefaultValues(this).perform(pattern), + ) return patterns .map(([pattern, identifiers], i) => { @@ -605,13 +452,4 @@ export class GenerateCode { }) .join(',') } - - private handleWhenClauses = (node: Parser.SyntaxNode): string => { - const clauses = node.namedChildren - .map((clause) => this.traverse(clause)) - .filter(isNotUndefined) - .join(',') - - return clauses - } } diff --git a/.yalc/tony-lang/src/code_generation/services/CollectDefaultValues.ts b/.yalc/tony-lang/src/code_generation/services/CollectDefaultValues.ts index e866c1b..1557ab2 100644 --- a/.yalc/tony-lang/src/code_generation/services/CollectDefaultValues.ts +++ b/.yalc/tony-lang/src/code_generation/services/CollectDefaultValues.ts @@ -1,6 +1,6 @@ +import * as AST from '../../ast' import { GenerateCode } from '../GenerateCode' -import { NODE_TYPES_WITH_DEFAULT_VALUES } from '../../constants' -import Parser from 'tree-sitter' +import { InternalError } from '../../errors' export class CollectDefaultValues { private codeGenerator: GenerateCode @@ -9,18 +9,43 @@ export class CollectDefaultValues { this.codeGenerator = codeGenerator } - perform = (node: Parser.SyntaxNode): string => `[${this.rec(node).join(',')}]` + perform = (node: AST.Pattern | AST.Parameters): string => + `[${this.traverse(node).join(',')}]` - private rec = (node: Parser.SyntaxNode): (string | undefined)[] => { - // prettier-ignore - if (NODE_TYPES_WITH_DEFAULT_VALUES.includes(node.type)) - // eslint-disable-next-line @typescript-eslint/ban-ts-ignore - // @ts-ignore - if (node.defaultNode) return [this.codeGenerator.traverse(node.defaultNode)] + // eslint-disable-next-line max-lines-per-function + private traverse = ( + node: AST.Pattern | AST.Parameters, + ): (string | undefined)[] => { + if ( + node instanceof AST.IdentifierPattern || + node instanceof AST.ShorthandPairIdentifierPattern + ) + if (node.default) return [this.codeGenerator.traverse(node.default)] else return [undefined] - else - return node.namedChildren - .map(this.rec) - .reduce((acc, value) => acc.concat(value), []) + else if (node instanceof AST.ListPattern) + return this.handleWrapper(node.elements) + else if (node instanceof AST.MapPattern) + return this.handleWrapper(node.elements) + else if (node instanceof AST.Parameters) + return this.handleWrapper(node.parameters) + else if (node instanceof AST.TuplePattern) + return this.handleWrapper(node.elements) + else if ( + node instanceof AST.Boolean || + node instanceof AST.Number || + node instanceof AST.Regex || + node instanceof AST.StringPattern || + node instanceof AST.ParametricType + ) + return [] + + throw new InternalError( + 'Could not find generator for AST node ' + `'${node.constructor.name}'.`, + ) } + + private handleWrapper = ( + nodes: (AST.Pattern | AST.Parameters)[], + ): (string | undefined)[] => + nodes.map(this.traverse).reduce((acc, value) => acc.concat(value), []) } diff --git a/.yalc/tony-lang/src/code_generation/services/GenerateBlock.ts b/.yalc/tony-lang/src/code_generation/services/GenerateBlock.ts index 1449029..d87c46b 100644 --- a/.yalc/tony-lang/src/code_generation/services/GenerateBlock.ts +++ b/.yalc/tony-lang/src/code_generation/services/GenerateBlock.ts @@ -30,7 +30,9 @@ export class GenerateBlock { } private generateDeclarations = (bindings: Binding[]): string => { - const declarations = bindings.map((binding) => binding.transformedName) + const declarations = [ + ...new Set(bindings.map((binding) => binding.transformedName)), + ] return declarations.length > 0 ? `let ${declarations.join(',')}` : '' } diff --git a/.yalc/tony-lang/src/code_generation/services/GenerateImport.ts b/.yalc/tony-lang/src/code_generation/services/GenerateImport.ts index b59cc7b..8ace335 100644 --- a/.yalc/tony-lang/src/code_generation/services/GenerateImport.ts +++ b/.yalc/tony-lang/src/code_generation/services/GenerateImport.ts @@ -4,13 +4,13 @@ import { JAVASCRIPT_FILE_EXTENSION_REGEX, TARGET_FILE_EXTENSION, } from '../../constants' -import { ImportBinding } from '../../symbol_table/models' +import { Binding } from '../../symbol_table' import { InternalError } from '../../errors' const EXTERNAL_IMPORT_SUFFIX = Object.freeze('_EXT') export class GenerateImport { - perform = (sourcePath: string, bindings: ImportBinding[]): string => { + perform = (sourcePath: string, bindings: Binding[]): string => { if (FILE_EXTENSION_REGEX.test(sourcePath)) return this.handleImport(sourcePath, bindings) else if (JAVASCRIPT_FILE_EXTENSION_REGEX.test(sourcePath)) @@ -23,19 +23,16 @@ export class GenerateImport { private handleImport = ( sourcePath: string, - bindings: ImportBinding[], + bindings: Binding[], suffix?: string, - transformOriginalName = true, ): string => { const compiledSourcePath = GenerateImport.getCompiledSourcePath(sourcePath) const aliases = bindings .map((binding) => { - const originalName = transformOriginalName - ? binding.transformedOriginalName - : binding.originalName - const name = binding.transformedName + const name = binding.transformedImportName + const alias = binding.transformedName - return `${originalName} as ${name}${suffix ? suffix : ''}` + return `${name} as ${alias}${suffix ? suffix : ''}` }) .join(',') @@ -45,13 +42,12 @@ export class GenerateImport { // eslint-disable-next-line max-lines-per-function private handleJavaScript = ( sourcePath: string, - bindings: ImportBinding[], + bindings: Binding[], ): string => { const importStatement = this.handleImport( sourcePath, bindings, EXTERNAL_IMPORT_SUFFIX, - false, ) const currying = bindings .map((binding) => { diff --git a/.yalc/tony-lang/src/code_generation/services/GenerateProgram.ts b/.yalc/tony-lang/src/code_generation/services/GenerateProgram.ts index 49dd066..631a1af 100644 --- a/.yalc/tony-lang/src/code_generation/services/GenerateProgram.ts +++ b/.yalc/tony-lang/src/code_generation/services/GenerateProgram.ts @@ -1,5 +1,5 @@ -import { FileModuleScope, ImportBinding } from '../../symbol_table' import { DEFAULT_IMPORTS } from '../../constants' +import { FileModuleScope } from '../../symbol_table' import { GenerateImport } from './GenerateImport' export class GenerateProgram { @@ -17,9 +17,13 @@ export class GenerateProgram { )};${this.generateExports()}` private generateDeclarations = (): string => { - const declarations = this._scope.bindings - .filter((binding) => !binding.isImplicit) - .map((binding) => binding.transformedName) + const declarations = [ + ...new Set( + this._scope.bindings + .filter((binding) => !binding.isImplicit) + .map((binding) => binding.transformedName), + ), + ] return declarations.length > 0 ? `let ${declarations.join(',')}` : '' } @@ -27,7 +31,7 @@ export class GenerateProgram { private generateImports = (): string => { const importBindings = this._scope.bindings.filter( (binding) => binding.isImported, - ) as ImportBinding[] + ) return this._scope.dependencies .map((sourcePath) => { diff --git a/.yalc/tony-lang/src/code_generation/services/ParseStringContent.ts b/.yalc/tony-lang/src/code_generation/services/ParseStringContent.ts index 57e5568..774bce9 100644 --- a/.yalc/tony-lang/src/code_generation/services/ParseStringContent.ts +++ b/.yalc/tony-lang/src/code_generation/services/ParseStringContent.ts @@ -1,11 +1,10 @@ export class ParseStringContent { static perform = (text: string, qt = '`'): string => { - // removes on escape when there are an even number of escapes before a qt + // removes one escape when there are an even number of escapes before a qt // inside a string const regex = new RegExp(`(? s.substring(1)) } diff --git a/.yalc/tony-lang/src/compile.ts b/.yalc/tony-lang/src/compile.ts index f2efaba..53b0c0f 100644 --- a/.yalc/tony-lang/src/compile.ts +++ b/.yalc/tony-lang/src/compile.ts @@ -41,7 +41,7 @@ const inferTypes = (fileScopes: FileModuleScope[], verbose: boolean): void => if (verbose) console.log(`Running type inference on ${fileScope.filePath}...`) - new InferTypes(fileScope).perform() + fileScope.annotatedTree = new InferTypes(fileScope).perform() }) const generateCode = async ( diff --git a/.yalc/tony-lang/src/constants.ts b/.yalc/tony-lang/src/constants.ts index 4e13a1d..f0cee39 100644 --- a/.yalc/tony-lang/src/constants.ts +++ b/.yalc/tony-lang/src/constants.ts @@ -15,10 +15,6 @@ export const IMPORT_FILE_EXTENSIONS = Object.freeze([ export const OPERATOR_REGEX = Object.freeze( /(==|[!@$%^&*|<>~*\\\-+/.])[!@$%^&*|<>~*\\\-+/.=]*/, ) -export const NODE_TYPES_WITH_DEFAULT_VALUES = Object.freeze([ - 'identifier_pattern', - 'shorthand_pair_identifier_pattern', -]) // standard library const STDLIB_PATH = Object.freeze(path.join(__dirname, 'stdlib')) diff --git a/.yalc/tony-lang/src/errors/CompileError.ts b/.yalc/tony-lang/src/errors/CompileError.ts index a352d69..0c227ce 100644 --- a/.yalc/tony-lang/src/errors/CompileError.ts +++ b/.yalc/tony-lang/src/errors/CompileError.ts @@ -30,10 +30,25 @@ export abstract class CompileError extends Error { this._filePath = value } - addContext = (node: Parser.SyntaxNode): void => { + private addContext = (node: Parser.SyntaxNode): void => { this._context = { start: node.startPosition, end: node.endPosition, } } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + static addContext = , U>( + fn: (node: Parser.SyntaxNode, ...args: T) => U, + node: Parser.SyntaxNode, + ...args: T + ): U => { + try { + return fn(node, ...args) + } catch (error) { + if (error instanceof CompileError && error.context === undefined) + error.addContext(node) + throw error + } + } } diff --git a/.yalc/tony-lang/src/errors/IndeterminateTypeError.ts b/.yalc/tony-lang/src/errors/IndeterminateTypeError.ts new file mode 100644 index 0000000..3ac3647 --- /dev/null +++ b/.yalc/tony-lang/src/errors/IndeterminateTypeError.ts @@ -0,0 +1,16 @@ +import { CompileError } from './CompileError' + +export class IndeterminateTypeError extends CompileError { + private _types: string[] + + constructor(types: string[]) { + super(undefined) + this.name = this.constructor.name + + this._types = types + } + + get types(): string[] { + return this._types + } +} diff --git a/.yalc/tony-lang/src/errors/InternalTypeError.ts b/.yalc/tony-lang/src/errors/InternalTypeError.ts new file mode 100644 index 0000000..c63cd88 --- /dev/null +++ b/.yalc/tony-lang/src/errors/InternalTypeError.ts @@ -0,0 +1,28 @@ +import { InternalError } from './InternalError' +import { Type } from '../types' + +export type TypeMismatch = [string, string | undefined] + +export class InternalTypeError extends InternalError { + private _trace: TypeMismatch[] + + constructor(expected: Type, actual: Type | undefined, message: string) { + super(message) + this.name = this.constructor.name + + this._trace = [ + [expected.toString(), actual ? actual.toString() : undefined], + ] + } + + get trace(): TypeMismatch[] { + return this._trace + } + + addTypeMismatch = (expected: Type, actual?: Type): void => { + this._trace = [ + ...this.trace, + [expected.toString(), actual ? actual.toString() : undefined], + ] + } +} diff --git a/.yalc/tony-lang/src/errors/InvalidExternalTypeImportError.ts b/.yalc/tony-lang/src/errors/InvalidExternalTypeImportError.ts new file mode 100644 index 0000000..ae4bb3d --- /dev/null +++ b/.yalc/tony-lang/src/errors/InvalidExternalTypeImportError.ts @@ -0,0 +1,16 @@ +import { CompileError } from './CompileError' + +export class InvalidExternalTypeImportError extends CompileError { + private _type: string + + constructor(type: string) { + super(undefined) + this.name = this.constructor.name + + this._type = type + } + + get type(): string { + return this._type + } +} diff --git a/.yalc/tony-lang/src/errors/InvalidModuleAccessError.ts b/.yalc/tony-lang/src/errors/InvalidModuleAccessError.ts new file mode 100644 index 0000000..f22f17b --- /dev/null +++ b/.yalc/tony-lang/src/errors/InvalidModuleAccessError.ts @@ -0,0 +1,22 @@ +import { CompileError } from './CompileError' + +export class InvalidModuleAccessError extends CompileError { + private _binding: string | undefined + private _type: string + + constructor(type: string, binding?: string) { + super(undefined) + this.name = this.constructor.name + + this._binding = binding + this._type = type + } + + get binding(): string | undefined { + return this._binding + } + + get type(): string { + return this._type + } +} diff --git a/.yalc/tony-lang/src/errors/InvalidPropertyAccessError.ts b/.yalc/tony-lang/src/errors/InvalidPropertyAccessError.ts deleted file mode 100644 index a8998de..0000000 --- a/.yalc/tony-lang/src/errors/InvalidPropertyAccessError.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { CompileError } from './CompileError' - -export class InvalidPropertyAccessError extends CompileError { - private _property: string - private _representation: string - private _type: string - - constructor(property: string, type: string, representation: string) { - super(undefined) - this.name = this.constructor.name - - this._property = property - this._type = type - this._representation = representation - } - - get property(): string { - return this._property - } - - get representation(): string { - return this._representation - } - - get type(): string { - return this._type - } -} diff --git a/.yalc/tony-lang/src/errors/InvalidUseOfTypeAsValueError.ts b/.yalc/tony-lang/src/errors/InvalidUseOfTypeAsValueError.ts new file mode 100644 index 0000000..5b43e36 --- /dev/null +++ b/.yalc/tony-lang/src/errors/InvalidUseOfTypeAsValueError.ts @@ -0,0 +1,16 @@ +import { CompileError } from './CompileError' + +export class InvalidUseOfTypeAsValueError extends CompileError { + private _type: string + + constructor(type: string) { + super(undefined) + this.name = this.constructor.name + + this._type = type + } + + get type(): string { + return this._type + } +} diff --git a/.yalc/tony-lang/src/errors/MissingExternalImportTypeHintError.ts b/.yalc/tony-lang/src/errors/MissingExternalImportTypeHintError.ts new file mode 100644 index 0000000..71df275 --- /dev/null +++ b/.yalc/tony-lang/src/errors/MissingExternalImportTypeHintError.ts @@ -0,0 +1,16 @@ +import { CompileError } from './CompileError' + +export class MissingExternalImportTypeHintError extends CompileError { + private _binding: string + + constructor(binding: string) { + super(undefined) + this.name = this.constructor.name + + this._binding = binding + } + + get binding(): string { + return this._binding + } +} diff --git a/.yalc/tony-lang/src/errors/TypeError.ts b/.yalc/tony-lang/src/errors/TypeError.ts index 1801f3b..1549bdc 100644 --- a/.yalc/tony-lang/src/errors/TypeError.ts +++ b/.yalc/tony-lang/src/errors/TypeError.ts @@ -1,28 +1,41 @@ import { CompileError } from './CompileError' -import { Type } from '../types' - -export type TypeMismatch = [string, string | undefined] +import { InternalTypeError } from './InternalTypeError' export class TypeError extends CompileError { - private _typeTrace: TypeMismatch[] + private _trace: InternalTypeError[] - constructor(expected: Type, actual: Type | undefined, message: string) { - super(message) + constructor() { + super(undefined) this.name = this.constructor.name - this._typeTrace = [ - [expected.toString(), actual ? actual.toString() : undefined], - ] + this._trace = TypeError.trace + } + + get trace(): InternalTypeError[] { + return this._trace + } + + static _trace: InternalTypeError[] = [] + + static get trace(): InternalTypeError[] { + return TypeError._trace } - get typeTrace(): TypeMismatch[] { - return this._typeTrace + static addError(error: InternalTypeError): void { + TypeError._trace = [...TypeError.trace, error] } - addTypeMismatch = (expected: Type, actual?: Type): void => { - this._typeTrace = [ - ...this.typeTrace, - [expected.toString(), actual ? actual.toString() : undefined], - ] + static safe = (fn: () => T): T | undefined => { + try { + return fn() + } catch (error) { + if (error instanceof InternalTypeError) { + TypeError.addError(error) + + return + } + + throw error + } } } diff --git a/.yalc/tony-lang/src/errors/index.ts b/.yalc/tony-lang/src/errors/index.ts index 062a54f..11906cf 100644 --- a/.yalc/tony-lang/src/errors/index.ts +++ b/.yalc/tony-lang/src/errors/index.ts @@ -3,9 +3,14 @@ export { CyclicDependenciesError } from './CyclicDependenciesError' export { DuplicateBindingError } from './DuplicateBindingError' export { ExportOutsideModuleScopeError } from './ExportOutsideModuleScopeError' export { ImportOutsideFileModuleScopeError } from './ImportOutsideFileModuleScopeError' +export { IndeterminateTypeError } from './IndeterminateTypeError' export { assert, InternalError } from './InternalError' -export { InvalidPropertyAccessError } from './InvalidPropertyAccessError' +export { InternalTypeError } from './InternalTypeError' +export { InvalidExternalTypeImportError } from './InvalidExternalTypeImportError' +export { InvalidModuleAccessError } from './InvalidModuleAccessError' +export { InvalidUseOfTypeAsValueError } from './InvalidUseOfTypeAsValueError' export { MissingBindingError } from './MissingBindingError' +export { MissingExternalImportTypeHintError } from './MissingExternalImportTypeHintError' export { SyntaxError } from './SyntaxError' export { TypeError } from './TypeError' export { UnknownImportError } from './UnknownImportError' diff --git a/.yalc/tony-lang/src/index.ts b/.yalc/tony-lang/src/index.ts index fa65eaa..c4c0d9c 100644 --- a/.yalc/tony-lang/src/index.ts +++ b/.yalc/tony-lang/src/index.ts @@ -9,9 +9,14 @@ export { DuplicateBindingError, ExportOutsideModuleScopeError, ImportOutsideFileModuleScopeError, + IndeterminateTypeError, InternalError, - InvalidPropertyAccessError, + InternalTypeError, + InvalidExternalTypeImportError, + InvalidModuleAccessError, + InvalidUseOfTypeAsValueError, MissingBindingError, + MissingExternalImportTypeHintError, SyntaxError, TypeError, UnknownImportError, diff --git a/.yalc/tony-lang/src/symbol_table/BuildFileModuleScope.ts b/.yalc/tony-lang/src/symbol_table/BuildFileModuleScope.ts index 14c2214..8574850 100644 --- a/.yalc/tony-lang/src/symbol_table/BuildFileModuleScope.ts +++ b/.yalc/tony-lang/src/symbol_table/BuildFileModuleScope.ts @@ -1,33 +1,24 @@ import { - Binding, - FileModuleScope, - GlobalScope, - IdentifierBinding, - ModuleScope, - NestedScope, - TypeBinding, -} from './models' -import { - BuildImportBindings, + BuildImports, BuildPatternBindings, UnifyPatternBindings, } from './services' -import { - BuildRepresentation, - BuildType, - LIST_TYPE, - ParametricType, - RepresentationKind, - TypeVariable, -} from '../types' +import { BuildType, ModuleType } from '../types' import { CompileError, - DuplicateBindingError, ExportOutsideModuleScopeError, ImportOutsideFileModuleScopeError, MissingBindingError, assert, } from '../errors' +import { + FileModuleScope, + GlobalScope, + IdentifierBindingTemplate, + ModuleBinding, + ModuleScope, + NestedScope, +} from './models' import { IMPORT_FILE_EXTENSIONS } from '../constants' import Parser from 'tree-sitter' import { UnknownImportError } from '../errors/UnknownImportError' @@ -58,7 +49,7 @@ export class BuildFileModuleScope { verbose: this._verbose, }) try { - this.traverse(this._fileScope.tree.rootNode) + CompileError.addContext(this.traverse, this._fileScope.tree.rootNode) } catch (error) { if (error instanceof CompileError) error.filePath = this._filePath throw error @@ -69,47 +60,43 @@ export class BuildFileModuleScope { // eslint-disable-next-line max-lines-per-function private traverse = (node: Parser.SyntaxNode): void => { - try { - switch (node.type) { - case 'abstraction_branch': - return this.handleAbstractionBranch(node) - case 'assignment': - return this.handleAssignment(node) - case 'block': - return this.handleBlock(node) - case 'export': - return this.handleExport(node) - case 'generator': - return this.handleGenerator(node) - case 'identifier': - return this.handleIdentifier(node) - case 'import': - return this.handleImport(node) - case 'list_comprehension': - return this.handleListComprehension(node) - case 'module': - return this.handleModule(node) - case 'parameters': - return this.handleParameters(node) - case 'pattern_list': - return this.handlePatternList(node) - case 'when_clause': - return this.handleWhenClause(node) - default: - node.namedChildren.forEach((child) => this.traverse(child)) - } - } catch (error) { - if (error instanceof CompileError && error.context === undefined) - error.addContext(node) - throw error + switch (node.type) { + case 'abstraction_branch': + return this.handleAbstractionBranch(node) + case 'assignment': + return this.handleAssignment(node) + case 'block': + return this.handleBlock(node) + case 'export': + return this.handleExport(node) + case 'generator': + return this.handleGenerator(node) + case 'identifier': + return this.handleIdentifier(node) + case 'import': + return this.handleImport(node) + case 'list_comprehension': + return this.handleListComprehension(node) + case 'module': + return this.handleModule(node) + case 'parameters': + return this.handleParameters(node) + case 'pattern_list': + return this.handlePatternList(node) + case 'when_clause': + return this.handleWhenClause(node) + default: + node.namedChildren.forEach((child) => + CompileError.addContext(this.traverse, child), + ) } } private handleAbstractionBranch = (node: Parser.SyntaxNode): void => { this.enterBlock() - this.traverse(node.namedChild(0)!) - this.traverse(node.namedChild(1)!) + CompileError.addContext(this.traverse, node.namedChild(0)!) + CompileError.addContext(this.traverse, node.namedChild(1)!) this._scope.reduce() this.leaveBlock() @@ -118,18 +105,22 @@ export class BuildFileModuleScope { private handleAssignment = (node: Parser.SyntaxNode): void => { const isExported = this.disableExports() - this.traverse(node.namedChild(0)!) + CompileError.addContext(this.traverse, node.namedChild(0)!) new BuildPatternBindings({ isExported, isImplicit: false }) .perform(node.namedChild(0)!) - .forEach((binding) => this.addBinding(binding)) + .forEach((bindingTemplate) => + this._scope.addBindingTemplate(bindingTemplate), + ) - this.traverse(node.namedChild(1)!) + CompileError.addContext(this.traverse, node.namedChild(1)!) } private handleBlock = (node: Parser.SyntaxNode): void => { this.enterBlock() - node.namedChildren.forEach((child) => this.traverse(child)) + node.namedChildren.forEach((child) => + CompileError.addContext(this.traverse, child), + ) this.leaveBlock() } @@ -139,28 +130,26 @@ export class BuildFileModuleScope { throw new ExportOutsideModuleScopeError() this.enableExports() - this.traverse(node.namedChild(0)!) + CompileError.addContext(this.traverse, node.namedChild(0)!) } private handleGenerator = (node: Parser.SyntaxNode): void => { - this.traverse(node.namedChild(1)!) + CompileError.addContext(this.traverse, node.namedChild(1)!) const name = node.namedChild(0)!.text - const binding = new IdentifierBinding( - name, - new ParametricType(LIST_TYPE, [new TypeVariable()]), - { isImplicit: true }, - ) - this.addBinding(binding) + const bindingTemplate = new IdentifierBindingTemplate(node, name, { + isImplicit: true, + }) + this._scope.addBindingTemplate(bindingTemplate) - if (node.namedChildCount == 3) this.traverse(node.namedChild(2)!) + if (node.namedChildCount == 3) + CompileError.addContext(this.traverse, node.namedChild(2)!) } private handleIdentifier = (node: Parser.SyntaxNode): void => { const name = node.text - if (this._scope.resolveBinding(name) === undefined) - throw new MissingBindingError(name) + if (!this._scope.isBound(name)) throw new MissingBindingError(name) } private handleImport = (node: Parser.SyntaxNode): void => { @@ -173,16 +162,16 @@ export class BuildFileModuleScope { throw new UnknownImportError(sourcePath) this._fileScope.addDependency(sourcePath) - new BuildImportBindings(sourcePath) + new BuildImports(sourcePath) .perform(node) - .forEach((binding) => this.addBinding(binding)) + .forEach((imp) => this._fileScope.addImport(imp)) } private handleListComprehension = (node: Parser.SyntaxNode): void => { this.enterBlock() - this.traverse(node.namedChild(1)!) - this.traverse(node.namedChild(0)!) + CompileError.addContext(this.traverse, node.namedChild(1)!) + CompileError.addContext(this.traverse, node.namedChild(0)!) this._scope.reduce() this.leaveBlock() @@ -192,26 +181,21 @@ export class BuildFileModuleScope { this.enableDeclaration() const isExported = this.disableExports() - this.traverse(node.namedChild(1)!) + CompileError.addContext(this.traverse, node.namedChild(1)!) - const moduleScope = this._scope.lastNestedScope - const exportedBindings = moduleScope.bindings.filter( - (binding) => binding.isExported, - ) - const representation = new BuildRepresentation().perform( - RepresentationKind.Instance, - exportedBindings, - ) - - const type = new BuildType().handleType(node.namedChild(0)!) - const binding = new TypeBinding(type, representation, { isExported }) - this.addBinding(binding) + const moduleScope = this._scope.lastNestedScope! + const name = new BuildType().perform(node.namedChild(0)!) + const type = new ModuleType(moduleScope) + const binding = new ModuleBinding(node, name, type, { isExported }) + this._scope.addBinding(binding) } private handleParameters = (node: Parser.SyntaxNode): void => { new BuildPatternBindings({ isExported: false, isImplicit: true }) .perform(node) - .forEach((binding) => this.addBinding(binding)) + .forEach((bindingTemplate) => + this._scope.addBindingTemplate(bindingTemplate), + ) } private handlePatternList = (node: Parser.SyntaxNode): void => { @@ -224,14 +208,16 @@ export class BuildFileModuleScope { new UnifyPatternBindings() .perform(bindings) - .forEach((binding) => this.addBinding(binding)) + .forEach((bindingTemplate) => + this._scope.addBindingTemplate(bindingTemplate), + ) } private handleWhenClause = (node: Parser.SyntaxNode): void => { this.enterBlock() - this.traverse(node.namedChild(0)!) - this.traverse(node.namedChild(1)!) + CompileError.addContext(this.traverse, node.namedChild(0)!) + CompileError.addContext(this.traverse, node.namedChild(1)!) this._scope.reduce() this.leaveBlock() @@ -273,11 +259,4 @@ export class BuildFileModuleScope { this._exportBindings = false return value } - - private addBinding = (binding: Binding): void => { - const matchingBinding = this._scope.resolveBinding(binding.name, 0) - if (matchingBinding) throw new DuplicateBindingError(matchingBinding.name) - - this._scope.addBinding(binding) - } } diff --git a/.yalc/tony-lang/src/symbol_table/WalkFileModuleScope.ts b/.yalc/tony-lang/src/symbol_table/WalkFileModuleScope.ts index 2d6e885..8d9775e 100644 --- a/.yalc/tony-lang/src/symbol_table/WalkFileModuleScope.ts +++ b/.yalc/tony-lang/src/symbol_table/WalkFileModuleScope.ts @@ -4,7 +4,7 @@ import { assert } from '../errors' export class WalkFileModuleScope { private _scope: NestedScope - // tracks the progression through nested scopes within symbol table + // track the progression through nested scopes within symbol table private _currentNestedScopeIndex = -1 private _nestedScopesIndexStack: number[] = [] @@ -18,14 +18,20 @@ export class WalkFileModuleScope { enterBlock = (): void => { this._nestedScopesIndexStack.push(this._currentNestedScopeIndex + 1) - this._scope = this.scope.nestedScope(this._currentNestedScopeIndex + 1) + const scope = this.scope.nestedScope(this._currentNestedScopeIndex + 1) this._currentNestedScopeIndex = -1 + + assert(scope !== undefined, 'Entering a scope that does not exist.') + this._scope = scope } peekBlock = (): void => { this._nestedScopesIndexStack.push(this._currentNestedScopeIndex) - this._scope = this.scope.nestedScope(this._currentNestedScopeIndex + 1) + const scope = this.scope.nestedScope(this._currentNestedScopeIndex + 1) this._currentNestedScopeIndex = -1 + + assert(scope !== undefined, 'Entering a scope that does not exist.') + this._scope = scope } leaveBlock = (): void => { diff --git a/.yalc/tony-lang/src/symbol_table/index.ts b/.yalc/tony-lang/src/symbol_table/index.ts index d830cd8..706ad0a 100644 --- a/.yalc/tony-lang/src/symbol_table/index.ts +++ b/.yalc/tony-lang/src/symbol_table/index.ts @@ -1,11 +1,14 @@ export { Binding, + BindingTemplate, FileModuleScope, IdentifierBinding, - ImportBinding, + IdentifierBindingTemplate, + IdentifierImport, ModuleScope, NestedScope, - TypeBinding, + ModuleBinding, + ModuleImport, } from './models' export { BuildSymbolTable } from './BuildSymbolTable' export { WalkFileModuleScope } from './WalkFileModuleScope' diff --git a/.yalc/tony-lang/src/symbol_table/models/BasicTypeBinding.ts b/.yalc/tony-lang/src/symbol_table/models/BasicTypeBinding.ts index 4f1fa29..7336f19 100644 --- a/.yalc/tony-lang/src/symbol_table/models/BasicTypeBinding.ts +++ b/.yalc/tony-lang/src/symbol_table/models/BasicTypeBinding.ts @@ -1,11 +1,16 @@ import { Binding } from './Binding' import { ParametricType } from '../../types' +import Parser from 'tree-sitter' export class BasicTypeBinding implements Binding { - private _type: ParametricType + private _name: ParametricType - constructor(type: ParametricType) { - this._type = type + constructor(name: ParametricType) { + this._name = name + } + + get filePath(): string | undefined { + return undefined } get isExported(): boolean { @@ -21,10 +26,18 @@ export class BasicTypeBinding implements Binding { } get name(): string { - return this._type.name + return this._name.name + } + + get node(): Parser.SyntaxNode | undefined { + return undefined } get transformedName(): string { return this.name } + + get transformedImportName(): string | undefined { + return undefined + } } diff --git a/.yalc/tony-lang/src/symbol_table/models/Binding.ts b/.yalc/tony-lang/src/symbol_table/models/Binding.ts index b490c6f..892ba38 100644 --- a/.yalc/tony-lang/src/symbol_table/models/Binding.ts +++ b/.yalc/tony-lang/src/symbol_table/models/Binding.ts @@ -1,7 +1,12 @@ +import Parser from 'tree-sitter' + export interface Binding { + filePath: string | undefined isExported: boolean isImplicit: boolean isImported: boolean name: string + node: Parser.SyntaxNode | undefined + transformedImportName: string | undefined transformedName: string } diff --git a/.yalc/tony-lang/src/symbol_table/models/BindingTemplate.ts b/.yalc/tony-lang/src/symbol_table/models/BindingTemplate.ts new file mode 100644 index 0000000..0503922 --- /dev/null +++ b/.yalc/tony-lang/src/symbol_table/models/BindingTemplate.ts @@ -0,0 +1,13 @@ +import { Type, TypeConstraint } from '../../types' +import { Binding } from './Binding' +import Parser from 'tree-sitter' + +export interface BindingTemplate { + isExported: boolean + isImplicit: boolean + name: string + node: Parser.SyntaxNode | undefined + transformedName: string + + buildBinding: (typeConstraint: TypeConstraint) => Binding | undefined +} diff --git a/.yalc/tony-lang/src/symbol_table/models/FileModuleScope.ts b/.yalc/tony-lang/src/symbol_table/models/FileModuleScope.ts index 8221d50..518e124 100644 --- a/.yalc/tony-lang/src/symbol_table/models/FileModuleScope.ts +++ b/.yalc/tony-lang/src/symbol_table/models/FileModuleScope.ts @@ -1,11 +1,17 @@ +import * as AST from '../../ast' +import { GlobalScope } from './GlobalScope' +import { Import } from './Import' import { ModuleScope } from './ModuleScope' import Parser from 'tree-sitter' import { Scope } from './Scope' +import { assert } from '../../errors' export class FileModuleScope extends ModuleScope { private _dependencies: string[] = [] private _filePath: string + private _imports: Import[] = [] private _tree: Parser.Tree | undefined + private _annotatedTree: AST.Program | undefined constructor(parentScope: Scope, filePath: string) { super(parentScope) @@ -21,6 +27,20 @@ export class FileModuleScope extends ModuleScope { return this._filePath } + get imports(): Import[] { + return this._imports + } + + get parentScope(): GlobalScope { + const parentScope = super.parentScope + assert( + parentScope instanceof GlobalScope, + 'Parent scope of file-level module scope should be the global scope.', + ) + + return parentScope + } + get tree(): Parser.Tree | undefined { return this._tree } @@ -29,7 +49,23 @@ export class FileModuleScope extends ModuleScope { this._tree = value } + get annotatedTree(): AST.Program | undefined { + return this._annotatedTree + } + + set annotatedTree(value: AST.Program | undefined) { + this._annotatedTree = value + } + addDependency = (filePath: string): void => { this._dependencies = [...this.dependencies, filePath] } + + isBound = (name: string, depth?: number): boolean => + this.resolveBindings(name, depth).length > 0 || + this.imports.filter((imp) => imp.alias === name).length > 0 + + addImport = (imp: Import): void => { + this._imports = [imp, ...this.imports] + } } diff --git a/.yalc/tony-lang/src/symbol_table/models/IdentifierBinding.ts b/.yalc/tony-lang/src/symbol_table/models/IdentifierBinding.ts index d818c4b..6529c93 100644 --- a/.yalc/tony-lang/src/symbol_table/models/IdentifierBinding.ts +++ b/.yalc/tony-lang/src/symbol_table/models/IdentifierBinding.ts @@ -1,34 +1,53 @@ +import { Type, TypeConstraint } from '../../types' import { Binding } from './Binding' -import { Type } from '../../types' - -const INTERNAL_IDENTIFIER_PREFIX = Object.freeze('tony_internal_') +import Parser from 'tree-sitter' export class IdentifierBinding implements Binding { - private static count = 0 - - private _id: number + private _filePath: string | undefined private _isExported: boolean private _isImplicit: boolean + private _isImported: boolean private _name: string - protected _type: Type + private _node: Parser.SyntaxNode + private _transformedName: string + private _transformedImportName: string | undefined + protected _typeConstraint: TypeConstraint + // eslint-disable-next-line max-lines-per-function constructor( + node: Parser.SyntaxNode, name: string, - type: Type, + transformedName: string, + typeConstraint: TypeConstraint, { isExported = false, isImplicit = false, - }: { isExported?: boolean; isImplicit?: boolean } = { + importInformation, + }: { + isExported?: boolean + isImplicit?: boolean + importInformation?: { + filePath: string + transformedImportName: string + } + } = { isExported: false, isImplicit: false, }, ) { + this._filePath = importInformation?.filePath this._isExported = isExported this._isImplicit = isImplicit + this._isImported = importInformation !== undefined this._name = name - this._type = type + this._node = node + this._transformedName = transformedName + this._transformedImportName = importInformation?.transformedImportName + this._typeConstraint = typeConstraint + } - this._id = IdentifierBinding.count += 1 + get filePath(): string | undefined { + return this._filePath } get isExported(): boolean { @@ -40,22 +59,30 @@ export class IdentifierBinding implements Binding { } get isImported(): boolean { - return false + return this._isImported } get name(): string { return this._name } - get type(): Type { - return this._type + get node(): Parser.SyntaxNode { + return this._node } - set type(value: Type) { - this._type = value + get typeConstraint(): TypeConstraint { + return this._typeConstraint + } + + set typeConstraint(value: TypeConstraint) { + this._typeConstraint = value } get transformedName(): string { - return `${INTERNAL_IDENTIFIER_PREFIX}${this._id}` + return this._transformedName + } + + get transformedImportName(): string | undefined { + return this._transformedImportName } } diff --git a/.yalc/tony-lang/src/symbol_table/models/IdentifierBindingTemplate.ts b/.yalc/tony-lang/src/symbol_table/models/IdentifierBindingTemplate.ts new file mode 100644 index 0000000..73e872f --- /dev/null +++ b/.yalc/tony-lang/src/symbol_table/models/IdentifierBindingTemplate.ts @@ -0,0 +1,89 @@ +import { Type, TypeConstraint, TypeVariable } from '../../types' +import { BindingTemplate } from './BindingTemplate' +import { IdentifierBinding } from './IdentifierBinding' +import Parser from 'tree-sitter' + +const INTERNAL_IDENTIFIER_PREFIX = Object.freeze('tony_internal_') + +export class IdentifierBindingTemplate implements BindingTemplate { + private static count = 0 + + private _isExported: boolean + private _isImplicit: boolean + private _name: string + private _node: Parser.SyntaxNode + private _transformedName: string + protected _type: TypeVariable + + // eslint-disable-next-line max-lines-per-function + constructor( + node: Parser.SyntaxNode, + name: string, + { + isExported = false, + isImplicit = false, + }: { + isExported?: boolean + isImplicit?: boolean + } = { + isExported: false, + isImplicit: false, + }, + ) { + this._isExported = isExported + this._isImplicit = isImplicit + this._name = name + this._node = node + this._type = new TypeVariable() + + this._transformedName = IdentifierBindingTemplate.getTransformedName() + } + + get isExported(): boolean { + return this._isExported + } + + get isImplicit(): boolean { + return this._isImplicit + } + + get name(): string { + return this._name + } + + get node(): Parser.SyntaxNode { + return this._node + } + + get type(): TypeVariable { + return this._type + } + + get typeConstraint(): TypeConstraint { + return new TypeConstraint(this._type) + } + + get transformedName(): string { + return this._transformedName + } + + buildBinding = ( + typeConstraint: TypeConstraint, + ): IdentifierBinding | undefined => { + const unifiedTypeConstraint = new TypeConstraint(this.type).unify( + typeConstraint, + ) + if (unifiedTypeConstraint === undefined) return + + return new IdentifierBinding( + this.node, + this.name, + this.transformedName, + unifiedTypeConstraint, + { isExported: this.isExported, isImplicit: this.isImplicit }, + ) + } + + static getTransformedName = (): string => + `${INTERNAL_IDENTIFIER_PREFIX}${(IdentifierBindingTemplate.count += 1)}` +} diff --git a/.yalc/tony-lang/src/symbol_table/models/IdentifierImport.ts b/.yalc/tony-lang/src/symbol_table/models/IdentifierImport.ts new file mode 100644 index 0000000..b364f1c --- /dev/null +++ b/.yalc/tony-lang/src/symbol_table/models/IdentifierImport.ts @@ -0,0 +1,45 @@ +import { Import } from './Import' +import { ParametricType } from '../../types' +import Parser from 'tree-sitter' + +export class IdentifierImport implements Import { + private _alias: string | undefined + private _filePath: string + private _name: string + private _node: Parser.SyntaxNode + private _type: ParametricType + + constructor( + node: Parser.SyntaxNode, + filePath: string, + type: ParametricType, + name: string, + alias?: string, + ) { + this._alias = alias + this._filePath = filePath + this._name = name + this._node = node + this._type = type + } + + get alias(): string { + return this._alias || this.name + } + + get filePath(): string { + return this._filePath + } + + get name(): string { + return this._name + } + + get node(): Parser.SyntaxNode { + return this._node + } + + get type(): ParametricType { + return this._type + } +} diff --git a/.yalc/tony-lang/src/symbol_table/models/Import.ts b/.yalc/tony-lang/src/symbol_table/models/Import.ts new file mode 100644 index 0000000..afaea42 --- /dev/null +++ b/.yalc/tony-lang/src/symbol_table/models/Import.ts @@ -0,0 +1,8 @@ +import Parser from 'tree-sitter' + +export interface Import { + alias: string + filePath: string + name: string + node: Parser.SyntaxNode +} diff --git a/.yalc/tony-lang/src/symbol_table/models/ImportBinding.ts b/.yalc/tony-lang/src/symbol_table/models/ImportBinding.ts deleted file mode 100644 index 8cd4eef..0000000 --- a/.yalc/tony-lang/src/symbol_table/models/ImportBinding.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Binding } from './Binding' - -export interface ImportBinding extends Binding { - filePath: string - originalName: string - transformedOriginalName: string -} diff --git a/.yalc/tony-lang/src/symbol_table/models/ImportIdentifierBinding.ts b/.yalc/tony-lang/src/symbol_table/models/ImportIdentifierBinding.ts deleted file mode 100644 index e1b372c..0000000 --- a/.yalc/tony-lang/src/symbol_table/models/ImportIdentifierBinding.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { IdentifierBinding } from './IdentifierBinding' -import { ImportBinding } from './ImportBinding' -import { Type } from '../../types' - -export class ImportIdentifierBinding extends IdentifierBinding - implements ImportBinding { - private _filePath: string - private _originalName: string - - constructor( - filePath: string, - name: string, - originalName: string, - type: Type, - ) { - super(name, type, { isImplicit: true }) - - this._filePath = filePath - this._originalName = originalName - } - - get filePath(): string { - return this._filePath - } - - get isImported(): boolean { - return true - } - - get originalName(): string { - return this._originalName - } - - set originalName(value: string) { - this._originalName = value - } - - get transformedOriginalName(): string { - return this.originalName - } -} diff --git a/.yalc/tony-lang/src/symbol_table/models/ImportTypeBinding.ts b/.yalc/tony-lang/src/symbol_table/models/ImportTypeBinding.ts deleted file mode 100644 index a86ceae..0000000 --- a/.yalc/tony-lang/src/symbol_table/models/ImportTypeBinding.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Representation, RepresentationKind } from '../../types/models' -import { ImportBinding } from './ImportBinding' -import { ParametricType } from '../../types' -import { TypeBinding } from './TypeBinding' - -export class ImportTypeBinding extends TypeBinding implements ImportBinding { - private _filePath: string - private _originalType: ParametricType - - constructor( - filePath: string, - type: ParametricType, - originalType: ParametricType, - ) { - super(type, new Representation(RepresentationKind.Unknown, [])) - - this._filePath = filePath - this._originalType = originalType - } - - get filePath(): string { - return this._filePath - } - - get isImplicit(): boolean { - return true - } - - get isImported(): boolean { - return true - } - - get originalName(): string { - return this._originalType.name - } - - set originalType(value: ParametricType) { - this._originalType = value - } - - get transformedOriginalName(): string { - return this.originalName - } -} diff --git a/.yalc/tony-lang/src/symbol_table/models/ModuleBinding.ts b/.yalc/tony-lang/src/symbol_table/models/ModuleBinding.ts new file mode 100644 index 0000000..3747585 --- /dev/null +++ b/.yalc/tony-lang/src/symbol_table/models/ModuleBinding.ts @@ -0,0 +1,76 @@ +import { ModuleType, ParametricType } from '../../types' +import { Binding } from './Binding' +import Parser from 'tree-sitter' + +export class ModuleBinding implements Binding { + private _filePath: string | undefined + private _isExported: boolean + private _isImported: boolean + private _name: ParametricType + private _node: Parser.SyntaxNode + private _transformedImportName: string | undefined + private _type: ModuleType + + // eslint-disable-next-line max-lines-per-function + constructor( + node: Parser.SyntaxNode, + name: ParametricType, + type: ModuleType, + { + isExported = false, + importInformation, + }: { + isExported?: boolean + importInformation?: { + filePath: string + transformedImportName: string + } + } = { + isExported: false, + }, + ) { + this._filePath = importInformation?.filePath + this._isExported = isExported + this._isImported = importInformation !== undefined + this._name = name + this._node = node + this._transformedImportName = importInformation?.transformedImportName + this._type = type + } + + get filePath(): string | undefined { + return this._filePath + } + + get isExported(): boolean { + return this._isExported + } + + get isImplicit(): boolean { + return this.isImported + } + + get isImported(): boolean { + return this._isImported + } + + get name(): string { + return this._name.name + } + + get node(): Parser.SyntaxNode { + return this._node + } + + get transformedName(): string { + return this.name + } + + get transformedImportName(): string | undefined { + return this._transformedImportName + } + + get type(): ModuleType { + return this._type + } +} diff --git a/.yalc/tony-lang/src/symbol_table/models/ModuleImport.ts b/.yalc/tony-lang/src/symbol_table/models/ModuleImport.ts new file mode 100644 index 0000000..e33b97c --- /dev/null +++ b/.yalc/tony-lang/src/symbol_table/models/ModuleImport.ts @@ -0,0 +1,44 @@ +import { Import } from './Import' +import { ParametricType } from '../../types' +import Parser from 'tree-sitter' + +export class ModuleImport implements Import { + private _alias: ParametricType | undefined + private _filePath: string + private _name: ParametricType + private _node: Parser.SyntaxNode + + constructor( + node: Parser.SyntaxNode, + filePath: string, + name: ParametricType, + alias?: ParametricType, + ) { + this._alias = alias + this._filePath = filePath + this._name = name + this._node = node + } + + get alias(): string { + if (this._alias) return this._alias.name + else return this.name + } + + get aliasType(): ParametricType { + if (this._alias) return this._alias + else return this._name + } + + get filePath(): string { + return this._filePath + } + + get name(): string { + return this._name.name + } + + get node(): Parser.SyntaxNode { + return this._node + } +} diff --git a/.yalc/tony-lang/src/symbol_table/models/NestedScope.ts b/.yalc/tony-lang/src/symbol_table/models/NestedScope.ts index 64b78a8..94ee1bc 100644 --- a/.yalc/tony-lang/src/symbol_table/models/NestedScope.ts +++ b/.yalc/tony-lang/src/symbol_table/models/NestedScope.ts @@ -1,11 +1,16 @@ import { BASIC_TYPES, ParametricType } from '../../types' +import { DuplicateBindingError, assert } from '../../errors' import { BasicTypeBinding } from './BasicTypeBinding' import { Binding } from './Binding' +import { BindingTemplate } from './BindingTemplate' +import { IdentifierBinding } from './IdentifierBinding' import { Scope } from './Scope' -import { assert } from '../../errors' + +const OVERLOADED_BINDINGS = Object.freeze([IdentifierBinding]) export class NestedScope extends Scope { private _bindings: Binding[] = [] + private _bindingTemplates: BindingTemplate[] = [] private _parentScope: Scope constructor(parentScope: Scope) { @@ -18,6 +23,10 @@ export class NestedScope extends Scope { return this._bindings } + get bindingTemplates(): BindingTemplate[] { + return this._bindingTemplates + } + get parentScope(): Scope { return this._parentScope } @@ -29,7 +38,10 @@ export class NestedScope extends Scope { return scope } - resolveBinding = (name: string, depth?: number): Binding | undefined => { + resolveBinding = ( + name: string, + depth?: number, + ): Binding | BindingTemplate | undefined => { // TODO: remove this when basic types are implemented in Tony const matchingBasicType = BASIC_TYPES.find((type) => type === name) if (matchingBasicType) @@ -38,16 +50,88 @@ export class NestedScope extends Scope { const binding = this.bindings.find((binding) => binding.name === name) if (binding) return binding + const bindingTemplate = this.bindingTemplates.find( + (bindingTemplate) => bindingTemplate.name === name, + ) + if (bindingTemplate) return bindingTemplate + if (this.parentScope instanceof NestedScope) if (depth === undefined) return this.parentScope.resolveBinding(name) else if (depth > 0) return this.parentScope.resolveBinding(name, depth - 1) } + resolveBindings = ( + name: string, + depth?: number, + ): (Binding | BindingTemplate)[] => { + const bindings = [ + ...this.bindings.filter((binding) => binding.name === name), + ...this.bindingTemplates.filter( + (bindingTemplate) => bindingTemplate.name === name, + ), + ] + + if (this.parentScope instanceof NestedScope) + if (depth === undefined) + return this.filterTemplateBindings([ + ...bindings, + ...this.parentScope.resolveBindings(name), + ]) + else if (depth > 0) + return this.filterTemplateBindings([ + ...bindings, + ...this.parentScope.resolveBindings(name, depth - 1), + ]) + + return this.filterTemplateBindings(bindings) + } + + // removes templates for which there are already actual bindings, depends on bindings being in front of templates + private filterTemplateBindings = ( + bindings: (Binding | BindingTemplate)[], + ): (Binding | BindingTemplate)[] => + bindings.reduce((newBindings: (Binding | BindingTemplate)[], binding) => { + if ( + newBindings.find((otherBinding) => binding.node === otherBinding.node) + ) + return newBindings + else return [...newBindings, binding] + }, []) + + isBound = (name: string, depth?: number): boolean => { + if (this.resolveBinding(name, 0)) return true + + if (this.parentScope instanceof NestedScope) + if (depth === undefined) return this.parentScope.isBound(name) + else if (depth > 0) return this.parentScope.isBound(name, depth - 1) + + return false + } + addBinding = (binding: Binding): void => { + if (!OVERLOADED_BINDINGS.find((type) => binding instanceof type)) { + const matchingBinding = this.resolveBinding(binding.name, 0) + if (matchingBinding) throw new DuplicateBindingError(binding.name) + } + this._bindings = [binding, ...this.bindings] } + removeBinding = (binding: Binding): void => { + const index = this.bindings.indexOf(binding) + if (index < 0) return + + this._bindings = [ + ...this.bindings.slice(0, index), + ...this.bindings.slice(index + 1), + ] + } + + addBindingTemplate = (bindingTemplate: BindingTemplate): void => { + this._bindingTemplates = [bindingTemplate, ...this.bindingTemplates] + } + // takes the last nested scope and merges it with the current scope reduce = (): void => { assert( @@ -60,14 +144,18 @@ export class NestedScope extends Scope { scope._scopes.map((nestedScope) => (nestedScope._parentScope = this)) this._bindings = [...this.bindings, ...scope.bindings] + this._bindingTemplates = [ + ...this.bindingTemplates, + ...scope.bindingTemplates, + ] this._scopes = scope._scopes } - nestedScope(index: number): NestedScope { + nestedScope(index: number): NestedScope | undefined { return this._scopes[index] } - get lastNestedScope(): NestedScope { + get lastNestedScope(): NestedScope | undefined { return this._scopes[this._scopes.length - 1] } } diff --git a/.yalc/tony-lang/src/symbol_table/models/TypeBinding.ts b/.yalc/tony-lang/src/symbol_table/models/TypeBinding.ts deleted file mode 100644 index ade794c..0000000 --- a/.yalc/tony-lang/src/symbol_table/models/TypeBinding.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Binding } from './Binding' -import { ParametricType } from '../../types/models/ParametricType' -import { Representation } from '../../types/models/Representation' - -export class TypeBinding implements Binding { - private _isExported: boolean - private _representation: Representation - private _type: ParametricType - - constructor( - type: ParametricType, - representation: Representation, - { isExported = false }: { isExported?: boolean } = { isExported: false }, - ) { - this._isExported = isExported - this._type = type - this._representation = representation - } - - get isExported(): boolean { - return this._isExported - } - - get isImplicit(): boolean { - return false - } - - get isImported(): boolean { - return false - } - - get name(): string { - return this.type.name - } - - get representation(): Representation { - return this._representation - } - - get type(): ParametricType { - return this._type - } - - get transformedName(): string { - return this.name - } -} diff --git a/.yalc/tony-lang/src/symbol_table/models/index.ts b/.yalc/tony-lang/src/symbol_table/models/index.ts index c1ad11c..a1aa6da 100644 --- a/.yalc/tony-lang/src/symbol_table/models/index.ts +++ b/.yalc/tony-lang/src/symbol_table/models/index.ts @@ -1,10 +1,12 @@ export { Binding } from './Binding' +export { BindingTemplate } from './BindingTemplate' export { FileModuleScope } from './FileModuleScope' export { GlobalScope } from './GlobalScope' export { IdentifierBinding } from './IdentifierBinding' -export { ImportBinding } from './ImportBinding' -export { ImportIdentifierBinding } from './ImportIdentifierBinding' -export { ImportTypeBinding } from './ImportTypeBinding' +export { IdentifierBindingTemplate } from './IdentifierBindingTemplate' +export { IdentifierImport } from './IdentifierImport' +export { Import } from './Import' +export { ModuleBinding } from './ModuleBinding' +export { ModuleImport } from './ModuleImport' export { ModuleScope } from './ModuleScope' export { NestedScope } from './NestedScope' -export { TypeBinding } from './TypeBinding' diff --git a/.yalc/tony-lang/src/symbol_table/services/BuildImportBindings.ts b/.yalc/tony-lang/src/symbol_table/services/BuildImportBindings.ts deleted file mode 100644 index a87d32a..0000000 --- a/.yalc/tony-lang/src/symbol_table/services/BuildImportBindings.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { BuildType, TypeVariable } from '../../types' -import { DuplicateBindingError, InternalError } from '../../errors' -import { - ImportBinding, - ImportIdentifierBinding, - ImportTypeBinding, -} from '../models' -import Parser from 'tree-sitter' -import { isNotUndefined } from '../../utilities' - -export class BuildImportBindings { - private _filePath: string - - constructor(filePath: string) { - this._filePath = filePath - } - - perform = (node: Parser.SyntaxNode): ImportBinding[] => { - const importClauseNode = node.namedChild(0)! - const bindings = importClauseNode.namedChildren.reduce( - (bindings: ImportBinding[], child) => - [...bindings, this.handleImportClauseEntry(child)].filter( - isNotUndefined, - ), - [], - ) - - this.checkDuplicateIdentifiers(bindings) - - return bindings - } - - private handleImportClauseEntry = ( - node: Parser.SyntaxNode, - ): ImportBinding | undefined => { - switch (node.type) { - case 'identifier_pattern': - return this.handleIdentifierPattern(node) - case 'import_clause_identifier_pair': - return this.handleImportClauseIdentifierPair(node) - case 'import_clause_type_pair': - return this.handleImportClauseTypePair(node) - case 'type': - return this.handleType(node) - default: - throw new InternalError( - `ResolveImportBindings: Could not find generator for AST node '${node.type}'.`, - ) - } - } - - // prettier-ignore - private handleIdentifierPattern = ( - node: Parser.SyntaxNode, - ): ImportIdentifierBinding => { - const name = node.namedChild(0)!.text - // eslint-disable-next-line @typescript-eslint/ban-ts-ignore - // @ts-ignore - const type = node.typeNode ? new BuildType().handleTypeConstructor(node.typeNode) : new TypeVariable - - return new ImportIdentifierBinding(this._filePath, name, name, type) - } - - private handleImportClauseIdentifierPair = ( - node: Parser.SyntaxNode, - ): ImportIdentifierBinding => { - const originalName = node.namedChild(0)!.text - const binding = this.handleIdentifierPattern(node.namedChild(1)!) - - binding.originalName = originalName - - return binding - } - - private handleImportClauseTypePair = ( - node: Parser.SyntaxNode, - ): ImportTypeBinding => { - const originalType = this.handleType(node.namedChild(0)!) - const binding = this.handleType(node.namedChild(1)!) - - binding.originalType = originalType.type - - return binding - } - - private handleType = (node: Parser.SyntaxNode): ImportTypeBinding => { - const type = new BuildType().handleType(node) - - return new ImportTypeBinding(this._filePath, type, type) - } - - private checkDuplicateIdentifiers = (bindings: ImportBinding[]): void => { - const identifiers = bindings.map((binding) => binding.name) - const duplicateIdentifier = identifiers.find((identifier, i) => - identifiers.slice(i, i).includes(identifier), - ) - if (!duplicateIdentifier) return - - throw new DuplicateBindingError(duplicateIdentifier) - } -} diff --git a/.yalc/tony-lang/src/symbol_table/services/BuildImports.ts b/.yalc/tony-lang/src/symbol_table/services/BuildImports.ts new file mode 100644 index 0000000..6000a6b --- /dev/null +++ b/.yalc/tony-lang/src/symbol_table/services/BuildImports.ts @@ -0,0 +1,90 @@ +import { IdentifierImport, Import, ModuleImport } from '../models' +import { + InternalError, + MissingExternalImportTypeHintError, + assert, +} from '../../errors' +import { BuildType } from '../../types' +import Parser from 'tree-sitter' + +export class BuildImports { + private _filePath: string + + constructor(filePath: string) { + this._filePath = filePath + } + + perform = (node: Parser.SyntaxNode): Import[] => { + assert(node.type === 'import', 'Should be import node.') + + const importClauseNode = node.namedChild(0)! + return importClauseNode.namedChildren.reduce( + (bindings: Import[], child) => [ + ...bindings, + this.handleImportClauseEntry(child), + ], + [], + ) + } + + private handleImportClauseEntry = (node: Parser.SyntaxNode): Import => { + switch (node.type) { + case 'identifier_pattern': + return this.handleIdentifierPattern(node) + case 'import_clause_identifier_pair': + return this.handleImportClauseIdentifierPair(node) + case 'import_clause_type_pair': + return this.handleImportClauseTypePair(node) + case 'type': + return this.handleType(node) + default: + throw new InternalError( + `Could not find generator for AST node '${node.type}'.`, + ) + } + } + + private handleIdentifierPattern = ( + node: Parser.SyntaxNode, + ): IdentifierImport => { + const name = node.namedChild(0)!.text + // eslint-disable-next-line @typescript-eslint/ban-ts-ignore + // @ts-ignore + const typeNode: Parser.SyntaxNode | undefined = node.typeNode + if (typeNode === undefined) + throw new MissingExternalImportTypeHintError(name) + + const type = new BuildType().perform(typeNode) + return new IdentifierImport(node, this._filePath, type, name) + } + + private handleImportClauseIdentifierPair = ( + node: Parser.SyntaxNode, + ): IdentifierImport => { + const name = node.namedChild(0)!.text + const alias = this.handleIdentifierPattern(node.namedChild(1)!) + + return new IdentifierImport( + node, + this._filePath, + alias.type, + name, + alias.name, + ) + } + + private handleImportClauseTypePair = ( + node: Parser.SyntaxNode, + ): ModuleImport => { + const name = new BuildType().perform(node.namedChild(0)!) + const alias = new BuildType().perform(node.namedChild(1)!) + + return new ModuleImport(node, this._filePath, name, alias) + } + + private handleType = (node: Parser.SyntaxNode): ModuleImport => { + const name = new BuildType().perform(node) + + return new ModuleImport(node, this._filePath, name) + } +} diff --git a/.yalc/tony-lang/src/symbol_table/services/BuildPatternBindings.ts b/.yalc/tony-lang/src/symbol_table/services/BuildPatternBindings.ts index 0141e2c..be48221 100644 --- a/.yalc/tony-lang/src/symbol_table/services/BuildPatternBindings.ts +++ b/.yalc/tony-lang/src/symbol_table/services/BuildPatternBindings.ts @@ -1,5 +1,4 @@ -import { BuildType, TypeVariable } from '../../types' -import { IdentifierBinding } from '../models' +import { IdentifierBindingTemplate } from '../models' import Parser from 'tree-sitter' export class BuildPatternBindings { @@ -17,7 +16,7 @@ export class BuildPatternBindings { this._isImplicit = isImplicit } - perform = (node: Parser.SyntaxNode): IdentifierBinding[] => { + perform = (node: Parser.SyntaxNode): IdentifierBindingTemplate[] => { if ( node.type === 'identifier_pattern' || node.type === 'shorthand_pair_identifier_pattern' @@ -25,22 +24,19 @@ export class BuildPatternBindings { return this.handleIdentifierPattern(node) return node.namedChildren.reduce( - (names: IdentifierBinding[], child) => names.concat(this.perform(child)), + (names: IdentifierBindingTemplate[], child) => + names.concat(this.perform(child)), [], ) } - // prettier-ignore private handleIdentifierPattern = ( node: Parser.SyntaxNode, - ): IdentifierBinding[] => { + ): IdentifierBindingTemplate[] => { const name = node.namedChild(0)!.text - // eslint-disable-next-line @typescript-eslint/ban-ts-ignore - // @ts-ignore - const type = node.typeNode ? new BuildType().handleTypeConstructor(node.typeNode) : new TypeVariable() return [ - new IdentifierBinding(name, type, { + new IdentifierBindingTemplate(node, name, { isExported: this._isExported, isImplicit: this._isImplicit, }), diff --git a/.yalc/tony-lang/src/symbol_table/services/UnifyPatternBindings.ts b/.yalc/tony-lang/src/symbol_table/services/UnifyPatternBindings.ts index fe1ff65..1fdf099 100644 --- a/.yalc/tony-lang/src/symbol_table/services/UnifyPatternBindings.ts +++ b/.yalc/tony-lang/src/symbol_table/services/UnifyPatternBindings.ts @@ -1,8 +1,8 @@ -import { Binding } from '../models' +import { BindingTemplate } from '../models' import { MissingBindingError } from '../../errors' export class UnifyPatternBindings { - perform = (bindings: Binding[][]): Binding[] => { + perform = (bindings: BindingTemplate[][]): BindingTemplate[] => { return bindings.reduce((acc, bindings) => { this.checkBindingMissing(acc, bindings) @@ -10,7 +10,7 @@ export class UnifyPatternBindings { }) } - checkBindingMissing = (a: Binding[], b: Binding[]): void => { + checkBindingMissing = (a: BindingTemplate[], b: BindingTemplate[]): void => { const missingBinding = a.find((binding) => !b.includes(binding)) || b.find((binding) => !a.includes(binding)) diff --git a/.yalc/tony-lang/src/symbol_table/services/index.ts b/.yalc/tony-lang/src/symbol_table/services/index.ts index 0fc3181..7f79fce 100644 --- a/.yalc/tony-lang/src/symbol_table/services/index.ts +++ b/.yalc/tony-lang/src/symbol_table/services/index.ts @@ -1,3 +1,3 @@ -export { BuildImportBindings } from './BuildImportBindings' +export { BuildImports } from './BuildImports' export { BuildPatternBindings } from './BuildPatternBindings' export { UnifyPatternBindings } from './UnifyPatternBindings' diff --git a/.yalc/tony-lang/src/type_inference/InferTypes.ts b/.yalc/tony-lang/src/type_inference/InferTypes.ts index 90f63bc..7051d37 100644 --- a/.yalc/tony-lang/src/type_inference/InferTypes.ts +++ b/.yalc/tony-lang/src/type_inference/InferTypes.ts @@ -1,8 +1,36 @@ +import * as AST from '../ast' +import { + AccumulateTypeDisjunction, + DistributeTypeDisjunction, + GetModuleTypeRepresentation, + InferAbstractionBranchType, + InferAccessType, + InferApplicationType, + InferAssignmentType, + InferBlockType, + InferBranchType, + InferExpressionPairType, + InferGeneratorType, + InferImportBindingTypes, + InferListType, + InferMapType, + InferPatternBindingTypes, + InferStringType, + InferTupleType, + MergeAccumulatedTypeDisjunction, + MergeTypeDisjunction, +} from './services' +import { + AccumulatedAnswer, + AccumulatedDisjunction, + Answer, + Disjunction, + GeneralizedDisjunction, +} from './models' import { BOOLEAN_TYPE, BuildType, - CurriedType, - INTERNAL_PARTIAL_APPLICATION_TYPE_NAME, + FUNCTION_TYPE, LIST_TYPE, MAP_TYPE, NUMBER_TYPE, @@ -10,57 +38,46 @@ import { REGULAR_EXPRESSION_TYPE, STRING_TYPE, TUPLE_TYPE, - Type, - TypeConstraints, - TypeVariable, + TypeConstraint, + TypeEqualityGraph, VOID_TYPE, } from '../types' import { CompileError, + IndeterminateTypeError, InternalError, - MissingBindingError, + TypeError, assert, } from '../errors' import { FileModuleScope, IdentifierBinding, - TypeBinding, + IdentifierBindingTemplate, WalkFileModuleScope, } from '../symbol_table' -import { - InferAccessType, - InferApplicationType, - InferBranchType, - InferListType, - InferMapType, - InferPatternBindingTypes, - InferTupleType, -} from './services' import Parser from 'tree-sitter' import { isNotUndefined } from '../utilities' export class InferTypes { private _fileScope: FileModuleScope - private _typeConstraints: TypeConstraints private _walkFileModuleScope: WalkFileModuleScope - // used to communicate type of provided value to when branches - private caseValueType: Type | undefined - constructor(fileScope: FileModuleScope) { this._fileScope = fileScope - this._typeConstraints = new TypeConstraints() this._walkFileModuleScope = new WalkFileModuleScope(fileScope) } - perform = (): void => { + perform = (): AST.Program => { assert( this._fileScope.tree !== undefined, 'Syntax tree of file scope should be present.', ) try { - this.handleProgram(this._fileScope.tree.rootNode) + return CompileError.addContext( + this.chooseAnswer, + this._fileScope.tree.rootNode, + ) } catch (error) { if (error instanceof CompileError) error.filePath = this._fileScope.filePath @@ -68,471 +85,883 @@ export class InferTypes { } } + chooseAnswer = (node: Parser.SyntaxNode): AST.Program => { + const answers = CompileError.addContext(this.handleProgram, node) + console.log( + this._walkFileModuleScope.scope + .resolveBinding('quicksort') + .typeConstraint.toString(), + ) + + return answers.answers.reduce((acc, answer) => { + const unifiedTypeConstraint = acc.typeConstraint.unify( + answer.typeConstraint, + ) + if (unifiedTypeConstraint === undefined) + throw new IndeterminateTypeError( + answers.answers.map((answer) => answer.typeConstraint.toString()), + ) + + return new Answer(acc.node, unifiedTypeConstraint) + }).node + } + // eslint-disable-next-line max-lines-per-function - traverse = (node: Parser.SyntaxNode): Type | undefined => { - try { - switch (node.type) { - case 'abstraction': - return this.handleAbstraction(node) - case 'abstraction_branch': - return this.handleAbstractionBranch(node) - case 'access': - return this.handleAccess(node) - case 'application': - return this.handleApplication(node) - case 'argument': - return this.handleArgument(node) - case 'arguments': - return this.handleArguments(node) - case 'assignment': - return this.handleAssignment(node) - case 'block': - return this.handleBlock(node) - case 'boolean': - return new ParametricType(BOOLEAN_TYPE) - case 'case': - return this.handleCase(node) - case 'comment': - return - case 'escape_sequence': - return new ParametricType(STRING_TYPE) - case 'else_if_clause': - return this.handleElseIfClause(node) - case 'else_if_clauses': - return this.handleElseIfClauses(node) - case 'export': - return this.handleExport(node) - case 'expression_pair': - return this.handleExpressionPair(node) - case 'generator': - return this.handleGenerator(node) - case 'generators': - return this.handleGenerators(node) - case 'identifier': - return this.handleIdentifier(node) - case 'if': - return this.handleIf(node) - case 'import': - return new ParametricType(VOID_TYPE) - case 'infix_application': - return this.handleInfixApplication(node) - case 'interpolation': - return this.handleInterpolation(node) - case 'list': - return this.handleList(node) - case 'list_comprehension': - return this.handleListComprehension(node) - case 'map': - return this.handleMap(node) - case 'module': - return this.handleModule(node) - case 'number': - return new ParametricType(NUMBER_TYPE) - case 'parameters': - return this.handleParameters(node) - case 'pattern_list': - return this.handlePatternList(node) - case 'pipeline': - return this.handlePipeline(node) - case 'prefix_application': - return this.handlePrefixApplication(node) - case 'regex': - return new ParametricType(REGULAR_EXPRESSION_TYPE) - case 'return': - return this.handleReturn(node) - case 'shorthand_access_identifier': - return new ParametricType(STRING_TYPE) - case 'shorthand_pair_identifier': - return this.handleShorthandPairIdentifier(node) - case 'spread_list': - return this.handleSpreadList(node) - case 'spread_map': - return this.handleSpreadMap(node) - case 'spread_tuple': - return this.handleSpreadTuple(node) - case 'string': - return this.handleString(node) - case 'tuple': - return this.handleTuple(node) - case 'type': - return this.handleType(node) - case 'when_clause': - return this.handleWhenClause(node) - case 'when_clauses': - return this.handleWhenClauses(node) - default: - throw new InternalError( - `InferTypes: Could not find generator for AST node '${node.type}'.`, - ) - } - } catch (error) { - if (error instanceof CompileError && error.context === undefined) - error.addContext(node) - throw error + traverse = (node: Parser.SyntaxNode): Disjunction => { + switch (node.type) { + case 'abstraction': + return this.handleAbstraction(node) + case 'abstraction_branch': + return this.handleAbstractionBranch(node) + case 'access': + return this.handleAccess(node) + case 'application': + return this.handleApplication(node) + case 'argument': + return this.handleArgument(node) + case 'assignment': + return this.handleAssignment(node) + case 'block': + return this.handleBlock(node) + case 'boolean': + return this.handleBoolean(node) + case 'case': + return this.handleCase(node) + case 'else_if': + return this.handleElseIf(node) + case 'export': + return this.handleExport(node) + case 'expression_pair': + return this.handleExpressionPair(node) + case 'generator': + return this.handleGenerator(node) + case 'identifier': + return this.handleIdentifier(node) + case 'if': + return this.handleIf(node) + case 'import': + return this.handleImport(node) + case 'infix_application': + return this.handleInfixApplication(node) + case 'list': + return this.handleList(node) + case 'list_comprehension': + return this.handleListComprehension(node) + case 'map': + return this.handleMap(node) + case 'module': + return this.handleModule(node) + case 'number': + return this.handleNumber(node) + case 'parameters': + return this.handleParameters(node) + case 'pipeline': + return this.handlePipeline(node) + case 'prefix_application': + return this.handlePrefixApplication(node) + case 'regex': + return this.handleRegex(node) + case 'return': + return this.handleReturn(node) + case 'shorthand_access_identifier': + return this.handleShorthandAccessIdentifier(node) + case 'shorthand_pair_identifier': + return this.handleShorthandPairIdentifier(node) + case 'spread_list': + return this.handleSpreadList(node) + case 'spread_map': + return this.handleSpreadMap(node) + case 'spread_tuple': + return this.handleSpreadTuple(node) + case 'string': + return this.handleString(node) + case 'tuple': + return this.handleTuple(node) + case 'type': + return this.handleType(node) + default: + throw new InternalError( + `Could not find generator for AST node '${node.type}'.`, + ) } } - private handleAbstraction = (node: Parser.SyntaxNode): Type => { - const abstractionBranchTypes = node.namedChildren - .map((child) => this.traverse(child)) - .filter(isNotUndefined) - - return new InferBranchType(this._typeConstraints).perform( - abstractionBranchTypes, + private handleAbstraction = ( + node: Parser.SyntaxNode, + ): Disjunction => { + const abstractionBranches = node.namedChildren.map((child) => + CompileError.addContext(this.handleAbstractionBranch, child), ) + + return new InferBranchType( + (branches) => new AST.Abstraction(branches), + ).perform(abstractionBranches) } - private handleAbstractionBranch = (node: Parser.SyntaxNode): CurriedType => { + private handleAbstractionBranch = ( + node: Parser.SyntaxNode, + ): Disjunction => { this._walkFileModuleScope.peekBlock() - const parameterTypes = this.handleParameters(node.namedChild(0)!) + const parameters = CompileError.addContext( + this.handleParameters, + node.namedChild(0)!, + ) this._walkFileModuleScope.leaveBlock() - const bodyType = this.handleBlock(node.namedChild(1)!) + const body = CompileError.addContext(this.handleBlock, node.namedChild(1)!) - return parameterTypes.concat(bodyType) + return new InferAbstractionBranchType().perform(parameters, body) } - private handleAccess = (node: Parser.SyntaxNode): Type => { - const valueType = this.traverse(node.namedChild(0)!)! - const accessorType = this.traverse(node.namedChild(1)!)! + private handleAccess = (node: Parser.SyntaxNode): Disjunction => { + const value = CompileError.addContext(this.traverse, node.namedChild(0)!)! + const accessor = CompileError.addContext( + this.traverse, + node.namedChild(1)!, + )! return new InferAccessType( - node, - this._walkFileModuleScope.scope, - this._typeConstraints, - ).perform(valueType, accessorType) + new GetModuleTypeRepresentation(this._walkFileModuleScope.scope), + ).perform(value, accessor) } - private handleApplication = (node: Parser.SyntaxNode): Type => { - const valueType = this.traverse(node.namedChild(0)!)! - const argumentTypes = this.handleArguments(node.namedChild(1)!) - - return new InferApplicationType(this._typeConstraints).perform( - valueType, - argumentTypes, + private handleApplication = ( + node: Parser.SyntaxNode, + ): Disjunction => { + const value = CompileError.addContext(this.traverse, node.namedChild(0)!)! + const args = CompileError.addContext( + this.handleArguments, + node.namedChild(1)!, ) + + return new InferApplicationType< + AST.Expression, + AST.Argument, + AST.Application + >( + (valueNode, argumentNodes, typeConstraint) => + new Answer( + new AST.Application(valueNode, argumentNodes), + typeConstraint, + ), + ).perform(value, args) } - private handleArgument = (node: Parser.SyntaxNode): Type => { + private handleArgument = ( + node: Parser.SyntaxNode, + ): Disjunction => { if (node.namedChildCount == 0) - return new TypeVariable(INTERNAL_PARTIAL_APPLICATION_TYPE_NAME) - - return this.traverse(node.namedChild(0)!)! + return new Disjunction([ + new Answer( + new AST.Argument(), + new TypeConstraint(new ParametricType(VOID_TYPE)), + ), + ]) + + const value = CompileError.addContext(this.traverse, node.namedChild(0)!)! + return new Disjunction( + value.answers.map( + (answer) => + new Answer(new AST.Argument(answer.node), answer.typeConstraint), + ), + ) } - private handleArguments = (node: Parser.SyntaxNode): CurriedType => { - if (node.namedChildCount == 0) - return new CurriedType([new ParametricType(VOID_TYPE)]) + private handleArguments = ( + node: Parser.SyntaxNode, + ): AccumulatedDisjunction => { + if (node.namedChildCount == 0) return [new AccumulatedAnswer()] - const argumentTypes = node.namedChildren - .map((child) => this.traverse(child)) - .filter(isNotUndefined) - return new CurriedType(argumentTypes) + const args = node.namedChildren.map((child) => + CompileError.addContext(this.handleArgument, child), + ) + return new DistributeTypeDisjunction().perform(args) } - private handleAssignment = (node: Parser.SyntaxNode): Type => { + private handleAssignment = ( + node: Parser.SyntaxNode, + ): Disjunction => { const patternNode = node.namedChild(0)! - const valueType = this.traverse(node.namedChild(1)!)! + const value = CompileError.addContext(this.traverse, node.namedChild(1)!)! - return new InferPatternBindingTypes( - this, - this._walkFileModuleScope.scope, - this._typeConstraints, - ).perform(patternNode, valueType) + return new InferAssignmentType( + new InferPatternBindingTypes(this, this._walkFileModuleScope.scope), + ).perform(patternNode, value) } - private handleBlock = (node: Parser.SyntaxNode): Type => { + private handleBlock = (node: Parser.SyntaxNode): Disjunction => { this._walkFileModuleScope.enterBlock() - const expressionTypes = node.namedChildren - .map((child) => this.traverse(child)) - .filter(isNotUndefined) + const expressions = node.namedChildren.map((child) => + CompileError.addContext(this.traverse, child), + ) this._walkFileModuleScope.leaveBlock() - return expressionTypes[expressionTypes.length - 1] + return new InferBlockType( + (expressions) => new AST.Block(expressions.nodes), + ).perform(expressions) } - private handleCase = (node: Parser.SyntaxNode): Type => { - this.caseValueType = this.traverse(node.namedChild(0)!) - const branchTypes = node.namedChildren - .slice(1) - .map((child) => this.traverse(child)) - .filter(isNotUndefined) + private handleBoolean = (node: Parser.SyntaxNode): Disjunction => + new Disjunction([ + new Answer( + new AST.Boolean(node.text), + new TypeConstraint(new ParametricType(BOOLEAN_TYPE)), + ), + ]) - return new InferBranchType(this._typeConstraints).perform(branchTypes) + // eslint-disable-next-line max-lines-per-function + private handleCase = (node: Parser.SyntaxNode): Disjunction => { + const value = CompileError.addContext(this.traverse, node.namedChild(0)!) + let branches: GeneralizedDisjunction[] = [] + if (node.namedChildCount == 3) + branches = [ + ...branches, + CompileError.addContext(this.handleBlock, node.namedChild(2)!), + ] + + return new Disjunction( + value.answers + .map((valueAnswer) => { + branches = [ + ...branches, + CompileError.addContext( + this.handleWhenClauses, + node.namedChild(1)!, + valueAnswer, + ), + ] + + return new InferBranchType((branches) => { + const whens = branches.filter( + (node) => node instanceof AST.When, + ) as AST.When[] + const [els] = branches.filter( + (node) => node instanceof AST.Block, + ) as AST.Block[] + + return new AST.Case(valueAnswer.node, whens, els) + }).perform(branches).answers + }) + .flat(1), + ) } - private handleElseIfClause = (node: Parser.SyntaxNode): Type => { - const conditionType = this.traverse(node.namedChild(0)!)! - const type = this.traverse(node.namedChild(1)!)! - - new ParametricType(BOOLEAN_TYPE).unify(conditionType, this._typeConstraints) + // eslint-disable-next-line max-lines-per-function + private handleElseIf = (node: Parser.SyntaxNode): Disjunction => { + const condition = CompileError.addContext( + this.traverse, + node.namedChild(0)!, + ) + const body = CompileError.addContext(this.handleBlock, node.namedChild(1)!) + + return new MergeTypeDisjunction( + (condition, body) => { + const unifiedConditionTypeConstraint = new TypeConstraint( + new ParametricType(BOOLEAN_TYPE), + ).unify(condition.typeConstraint) + if (unifiedConditionTypeConstraint === undefined) return + + return new Answer( + new AST.ElseIf(condition.node, body.node), + new TypeConstraint( + body.typeConstraint.type, + TypeEqualityGraph.build( + unifiedConditionTypeConstraint.typeEqualityGraph, + body.typeConstraint.typeEqualityGraph, + ), + ), + ) + }, + ).perform(condition, body) + } + + private handleElseIfClauses = ( + node: Parser.SyntaxNode, + ): AccumulatedDisjunction => { + const branches = node.namedChildren.map((child) => + CompileError.addContext(this.handleElseIf, child), + ) - return type + return new DistributeTypeDisjunction().perform(branches) } - private handleElseIfClauses = (node: Parser.SyntaxNode): Type => { - const branchTypes = node.namedChildren - .map((child) => this.traverse(child)) - .filter(isNotUndefined) - - return new InferBranchType(this._typeConstraints).perform(branchTypes) - } + private handleExport = (node: Parser.SyntaxNode): Disjunction => { + const declaration = CompileError.addContext( + this.traverse, + node.namedChild(0)!, + )! - private handleExport = (node: Parser.SyntaxNode): Type => { - return this.traverse(node.namedChild(0)!)! + return new Disjunction( + declaration.answers.map( + (answer) => + new Answer( + new AST.Export(answer.node as AST.Declaration), + answer.typeConstraint, + ), + ), + ) } - private handleExpressionPair = (node: Parser.SyntaxNode): ParametricType => { - const keyType = this.traverse(node.namedChild(0)!)! - const valueType = this.traverse(node.namedChild(1)!)! + private handleExpressionPair = ( + node: Parser.SyntaxNode, + ): Disjunction => { + const key = CompileError.addContext(this.traverse, node.namedChild(0)!)! + const value = CompileError.addContext(this.traverse, node.namedChild(1)!)! - return new ParametricType(MAP_TYPE, [keyType, valueType]) + return new InferExpressionPairType().perform(key, value) } - // eslint-disable-next-line max-lines-per-function - private handleGenerator = (node: Parser.SyntaxNode): undefined => { - const name = node.namedChild(0)!.text - const valueType = this.traverse(node.namedChild(1)!)! - const binding = this._walkFileModuleScope.scope.resolveBinding(name, 0) + private handleGenerator = ( + node: Parser.SyntaxNode, + ): Disjunction => { + const value = CompileError.addContext(this.traverse, node.namedChild(1)!) + const condition = + node.namedChildCount == 3 + ? CompileError.addContext(this.traverse, node.namedChild(2)!) + : undefined + const bindingTemplate = this._walkFileModuleScope.scope.bindingTemplates.find( + (bindingTemplate) => bindingTemplate.node === node, + ) assert( - binding instanceof IdentifierBinding, + bindingTemplate instanceof IdentifierBindingTemplate, 'Generator binding should be found in current scope.', ) - const type = new ParametricType(LIST_TYPE, [new TypeVariable()]).unify( - valueType, - this._typeConstraints, - ).parameters[0] - binding.type = type - - if (node.namedChildCount == 3) { - const conditionType = this.traverse(node.namedChild(2)!)! - - new ParametricType(BOOLEAN_TYPE).unify( - conditionType, - this._typeConstraints, - ) - } - - return + return new InferGeneratorType(this._walkFileModuleScope.scope).perform( + bindingTemplate, + value, + condition, + ) } - private handleGenerators = (node: Parser.SyntaxNode): undefined => { - node.namedChildren.forEach((child) => this.traverse(child)) + private handleGenerators = ( + node: Parser.SyntaxNode, + ): AccumulatedDisjunction => { + const generators = node.namedChildren.map((child) => + CompileError.addContext(this.handleGenerator, child), + ) - return + return new DistributeTypeDisjunction().perform(generators) } - private handleIdentifier = (node: Parser.SyntaxNode): Type => { + private handleIdentifier = ( + node: Parser.SyntaxNode, + ): Disjunction => { const name = node.text - const binding = this._walkFileModuleScope.scope.resolveBinding(name) - - assert( - binding instanceof IdentifierBinding, - 'Identifier binding should be found in scope.', + const bindings = this._walkFileModuleScope.scope.resolveBindings(name) + + return new Disjunction( + bindings.map((binding) => { + assert( + binding instanceof IdentifierBinding || + binding instanceof IdentifierBindingTemplate, + 'Binding should be an identifier binding.', + ) + + return new Answer( + new AST.Identifier( + binding.typeConstraint.type, + binding.name, + binding.transformedName, + ), + binding.typeConstraint, + ) + }), ) + } - return binding.type + // eslint-disable-next-line max-lines-per-function + private handleIf = (node: Parser.SyntaxNode): Disjunction => { + const condition = CompileError.addContext( + this.traverse, + node.namedChild(0)!, + ) + let branches: GeneralizedDisjunction[] = [ + CompileError.addContext(this.handleBlock, node.namedChild(1)!), + ] + if (node.namedChildCount >= 3) + if (node.namedChild(2)!.type === 'block') + branches = [ + ...branches, + CompileError.addContext(this.handleBlock, node.namedChild(2)!), + ] + else + branches = [ + ...branches, + CompileError.addContext( + this.handleElseIfClauses, + node.namedChild(2)!, + ), + ] + if (node.namedChildCount >= 4) + branches = [ + ...branches, + CompileError.addContext(this.handleBlock, node.namedChild(3)!), + ] + + return new Disjunction( + condition.answers + .map((conditionAnswer) => { + return new InferBranchType((branches) => { + const [body, els] = branches.filter( + (node) => node instanceof AST.Block, + ) as (AST.Block | undefined)[] + const elseIfs = branches.filter( + (node) => node instanceof AST.ElseIf, + ) as AST.ElseIf[] + + assert(body !== undefined, 'Body node should be given.') + + return new AST.If(conditionAnswer.node, body, elseIfs, els) + }).perform(branches).answers + }) + .flat(1), + ) } - private handleIf = (node: Parser.SyntaxNode): Type => { - const [conditionType, ...branchTypes] = node.namedChildren - .map((child) => this.traverse(child)) - .filter(isNotUndefined) + private handleImport = (node: Parser.SyntaxNode): Disjunction => { + const typeEqualityGraph = new TypeEqualityGraph() - new ParametricType(BOOLEAN_TYPE).unify(conditionType, this._typeConstraints) + new InferImportBindingTypes( + this._fileScope, + this._fileScope.parentScope.scopes, + typeEqualityGraph, + ).perform(node) - return new InferBranchType(this._typeConstraints).perform(branchTypes) + return new Disjunction([ + new Answer( + new AST.Import(), + new TypeConstraint(new ParametricType(VOID_TYPE), typeEqualityGraph), + ), + ]) } - private handleInfixApplication = (node: Parser.SyntaxNode): Type => { - const leftType = this.traverse(node.namedChild(0)!)! - const valueType = this.traverse(node.namedChild(1)!)! - const rightType = this.traverse(node.namedChild(2)!)! - const argumentTypes = new CurriedType([leftType, rightType]) - - return new InferApplicationType(this._typeConstraints).perform( - valueType, - argumentTypes, + // eslint-disable-next-line max-lines-per-function + private handleInfixApplication = ( + node: Parser.SyntaxNode, + ): Disjunction => { + const left = CompileError.addContext(this.traverse, node.namedChild(0)!)! + const value = CompileError.addContext( + this.handleIdentifier, + node.namedChild(1)!, + )! + const right = CompileError.addContext(this.traverse, node.namedChild(2)!)! + const args = new AccumulateTypeDisjunction( + (left, right) => new AccumulatedAnswer([left, right]), + ).perform(left, right) + + return new InferApplicationType< + AST.Identifier, + AST.Expression, + AST.InfixApplication + >( + (valueNode, argumentNodes, typeConstraint) => + new Answer( + new AST.InfixApplication( + argumentNodes[0], + valueNode, + argumentNodes[1], + ), + typeConstraint, + ), + ).perform(value, args) + } + + private handleList = (node: Parser.SyntaxNode): Disjunction => { + const elements = node.namedChildren.map((child) => + CompileError.addContext(this.traverse, child), ) - } - - private handleInterpolation = (node: Parser.SyntaxNode): Type => { - const type = this.traverse(node.namedChild(0)!)! - - return new ParametricType(STRING_TYPE).unify(type, this._typeConstraints) - } - private handleList = (node: Parser.SyntaxNode): ParametricType => { - return new InferListType(this, this._typeConstraints).perform( - node.namedChildren, + return new InferListType((elements) => new AST.List(elements)).perform( + elements, ) } + // eslint-disable-next-line max-lines-per-function private handleListComprehension = ( node: Parser.SyntaxNode, - ): ParametricType => { + ): Disjunction => { this._walkFileModuleScope.peekBlock() - this.handleGenerators(node.namedChild(1)!) + const generators = CompileError.addContext( + this.handleGenerators, + node.namedChild(1)!, + ) this._walkFileModuleScope.leaveBlock() - const bodyType = this.traverse(node.namedChild(0)!)! + const body = this.handleBlock(node.namedChild(0)!)! + + return new MergeAccumulatedTypeDisjunction< + AST.Generator, + AST.Block, + AST.ListComprehension + >( + (generators, body) => + new Answer( + new AST.ListComprehension(body.node, generators.nodes), + new TypeConstraint( + new ParametricType(LIST_TYPE, [body.typeConstraint.type]), + TypeEqualityGraph.build( + body.typeConstraint.typeEqualityGraph, + ...generators.typeConstraints.map( + (typeConstraint) => typeConstraint.typeEqualityGraph, + ), + ), + ), + ), + ).perform(generators, body) + } + + private handleMap = (node: Parser.SyntaxNode): Disjunction => { + const elements = node.namedChildren.map((child) => + CompileError.addContext(this.traverse, child), + ) - return new ParametricType(LIST_TYPE, [bodyType]) + return new InferMapType( + (elements) => new AST.Map(elements as AST.MapElement[]), + ).perform(elements) } - private handleMap = (node: Parser.SyntaxNode): ParametricType => { - const mapTypes = node.namedChildren - .map((child) => this.traverse(child)) - .filter(isNotUndefined) - - return new InferMapType(this._typeConstraints).perform(mapTypes) + private handleModule = (node: Parser.SyntaxNode): Disjunction => { + const type = new BuildType().perform(node.namedChild(0)!) + const representation = new GetModuleTypeRepresentation( + this._walkFileModuleScope.scope, + ).perform(type) + const body = CompileError.addContext(this.handleBlock, node.namedChild(1)!) + + return new Disjunction( + body.answers.map( + (answer) => + new Answer( + new AST.Module(type.name, answer.node), + new TypeConstraint( + representation, + answer.typeConstraint.typeEqualityGraph, + ), + ), + ), + ) } - private handleModule = (node: Parser.SyntaxNode): ParametricType => { - const type = new BuildType().handleType(node.namedChild(0)!) - - this.traverse(node.namedChild(1)!) - - return type - } + private handleNumber = (node: Parser.SyntaxNode): Disjunction => + new Disjunction([ + new Answer( + new AST.Number(node.text), + new TypeConstraint(new ParametricType(NUMBER_TYPE)), + ), + ]) - private handleParameters = (node: Parser.SyntaxNode): CurriedType => { + // eslint-disable-next-line max-lines-per-function + private handleParameters = ( + node: Parser.SyntaxNode, + ): Disjunction => { if (node.namedChildCount == 0) - return new CurriedType([new ParametricType(VOID_TYPE)]) - - const parameterTypes = new InferPatternBindingTypes( + return new Disjunction([ + new Answer( + new AST.Parameters(), + new TypeConstraint( + new ParametricType(FUNCTION_TYPE, [new ParametricType(VOID_TYPE)]), + ), + ), + ]) + + const parameters = new InferPatternBindingTypes( this, this._walkFileModuleScope.scope, - this._typeConstraints, - ).perform(node) - assert(parameterTypes instanceof ParametricType, 'Should be tuple type.') + ) + .perform(node) + ?.answers.map((answer) => { + assert( + answer.node instanceof AST.TuplePattern, + 'Should be a tuple pattern.', + ) + + return new Answer( + new AST.Parameters(answer.node.elements), + answer.typeConstraint, + ) + }) + if (parameters === undefined) throw new TypeError() - return new CurriedType(parameterTypes.parameters) + return new Disjunction(parameters) } - private handlePatternList = (node: Parser.SyntaxNode): undefined => { - node.namedChildren.forEach((patternNode) => { - new InferPatternBindingTypes( - this, - this._walkFileModuleScope.scope, - this._typeConstraints, - ).perform(patternNode, this.caseValueType!) - }) + private handlePatternList = ( + node: Parser.SyntaxNode, + value: Answer, + ): AccumulatedDisjunction => { + const patterns = node.namedChildren + .map((patternNode) => + new InferPatternBindingTypes( + this, + this._walkFileModuleScope.scope, + ).perform(patternNode, value.typeConstraint), + ) + .filter(isNotUndefined) - return + return new DistributeTypeDisjunction().perform(patterns) } - private handlePipeline = (node: Parser.SyntaxNode): Type => { - const argumentType = this.traverse(node.namedChild(0)!)! - const valueType = this.traverse(node.namedChild(1)!)! - const argumentTypes = new CurriedType([argumentType]) - - return new InferApplicationType(this._typeConstraints).perform( - valueType, - argumentTypes, + // eslint-disable-next-line max-lines-per-function + private handlePipeline = ( + node: Parser.SyntaxNode, + ): Disjunction => { + const argument = CompileError.addContext( + this.traverse, + node.namedChild(0)!, + )! + const value = CompileError.addContext(this.traverse, node.namedChild(1)!)! + const args = argument.answers.map( + (answer) => new AccumulatedAnswer([answer]), ) - } - private handlePrefixApplication = (node: Parser.SyntaxNode): Type => { - const valueType = this.traverse(node.namedChild(0)!)! - const argumentType = this.traverse(node.namedChild(1)!)! - const argumentTypes = new CurriedType([argumentType]) + return new InferApplicationType< + AST.Expression, + AST.Expression, + AST.Pipeline + >( + (valueNode, argumentNodes, typeConstraint) => + new Answer( + new AST.Pipeline(valueNode, argumentNodes[0]), + typeConstraint, + ), + ).perform(value, args) + } - return new InferApplicationType(this._typeConstraints).perform( - valueType, - argumentTypes, + // eslint-disable-next-line max-lines-per-function + private handlePrefixApplication = ( + node: Parser.SyntaxNode, + ): Disjunction => { + const value = CompileError.addContext( + this.handleIdentifier, + node.namedChild(0)!, + )! + const argument = CompileError.addContext( + this.traverse, + node.namedChild(1)!, + )! + const args = argument.answers.map( + (answer) => new AccumulatedAnswer([answer]), ) + + return new InferApplicationType< + AST.Identifier, + AST.Expression, + AST.PrefixApplication + >( + (valueNode, argumentNodes, typeConstraint) => + new Answer( + new AST.PrefixApplication(valueNode, argumentNodes[0]), + typeConstraint, + ), + ).perform(value, args) } - private handleProgram = (node: Parser.SyntaxNode): void => { - node.namedChildren.forEach((child) => this.traverse(child)) + private handleProgram = ( + node: Parser.SyntaxNode, + ): Disjunction => { + const expressions = node.namedChildren.map((child) => + CompileError.addContext(this.traverse, child), + ) + + return new InferBlockType( + (expressions) => new AST.Program(expressions.nodes), + ).perform(expressions) } - private handleReturn = (node: Parser.SyntaxNode): Type => - this.traverse(node.namedChild(0)!)! + private handleRegex = (node: Parser.SyntaxNode): Disjunction => + new Disjunction([ + new Answer( + new AST.Regex(node.text), + new TypeConstraint(new ParametricType(REGULAR_EXPRESSION_TYPE)), + ), + ]) - private handleShorthandPairIdentifier = ( - node: Parser.SyntaxNode, - ): ParametricType => { - const name = node.text - const binding = this._walkFileModuleScope.scope.resolveBinding(name) + private handleReturn = (node: Parser.SyntaxNode): Disjunction => { + const value = CompileError.addContext(this.traverse, node.namedChild(0)!)! - assert( - binding instanceof IdentifierBinding, - 'Identifier binding should be found in scope.', + return new Disjunction( + value.answers.map( + (answer) => + new Answer(new AST.Return(answer.node), answer.typeConstraint), + ), ) + } - const keyType = new ParametricType(STRING_TYPE) - const valueType = binding.type + private handleShorthandAccessIdentifier = ( + node: Parser.SyntaxNode, + ): Disjunction => { + const name = node.text + const shorthandAccessIdentifier = new AST.ShorthandAccessIdentifier(name) - return new ParametricType(MAP_TYPE, [keyType, valueType]) + return new Disjunction([ + new Answer( + shorthandAccessIdentifier, + new TypeConstraint(new ParametricType(STRING_TYPE)), + ), + ]) } - private handleSpreadList = (node: Parser.SyntaxNode): Type => { - const valueType = this.traverse(node.namedChild(0)!)! + // eslint-disable-next-line max-lines-per-function + private handleShorthandPairIdentifier = ( + node: Parser.SyntaxNode, + ): Disjunction => { + const name = node.text + const bindings = this._walkFileModuleScope.scope.resolveBindings(name) + const keyType = new ParametricType(STRING_TYPE) - return new ParametricType(LIST_TYPE).unify( - valueType, - this._typeConstraints, - true, + return new Disjunction( + bindings.map((binding) => { + assert( + binding instanceof IdentifierBinding || + binding instanceof IdentifierBindingTemplate, + 'Binding should be an identifier binding.', + ) + + return new Answer( + new AST.ShorthandPairIdentifier( + binding.name, + binding.transformedName, + ), + new TypeConstraint( + new ParametricType(MAP_TYPE, [ + keyType, + binding.typeConstraint.type, + ]), + binding.typeConstraint.typeEqualityGraph, + ), + ) + }), ) } - private handleSpreadMap = (node: Parser.SyntaxNode): Type => { - const valueType = this.traverse(node.namedChild(0)!)! - - return new ParametricType(MAP_TYPE, []).unify( - valueType, - this._typeConstraints, - true, + private handleSpreadList = ( + node: Parser.SyntaxNode, + ): Disjunction => { + const value = CompileError.addContext(this.traverse, node.namedChild(0)!)! + + return new Disjunction( + value.answers.map((answer) => { + const typeConstraint = new TypeConstraint( + new ParametricType(LIST_TYPE, []), + ).unify(answer.typeConstraint, true) + if (typeConstraint === undefined) return + + return new Answer(new AST.SpreadList(answer.node), typeConstraint) + }), ) } - private handleSpreadTuple = (node: Parser.SyntaxNode): Type => { - const valueType = this.traverse(node.namedChild(0)!)! + private handleSpreadMap = ( + node: Parser.SyntaxNode, + ): Disjunction => { + const value = CompileError.addContext(this.traverse, node.namedChild(0)!)! + + return new Disjunction( + value.answers.map((answer) => { + const typeConstraint = new TypeConstraint( + new ParametricType(MAP_TYPE, []), + ).unify(answer.typeConstraint, true) + if (typeConstraint === undefined) return + + return new Answer(new AST.SpreadMap(answer.node), typeConstraint) + }), + ) + } - return new ParametricType(TUPLE_TYPE, []).unify( - valueType, - this._typeConstraints, - true, + private handleSpreadTuple = ( + node: Parser.SyntaxNode, + ): Disjunction => { + const value = CompileError.addContext(this.traverse, node.namedChild(0)!)! + + return new Disjunction( + value.answers.map((answer) => { + const typeConstraint = new TypeConstraint( + new ParametricType(TUPLE_TYPE, []), + ).unify(answer.typeConstraint, true) + if (typeConstraint === undefined) return + + return new Answer(new AST.SpreadTuple(answer.node), typeConstraint) + }), ) } - private handleString = (node: Parser.SyntaxNode): ParametricType => { - node.namedChildren.forEach((child) => this.traverse(child)) + private handleString = (node: Parser.SyntaxNode): Disjunction => { + const content = node.text.slice(1, -1) + const interpolationValues = node.namedChildren.map((child) => + CompileError.addContext(this.traverse, child.namedChild(0)!), + ) - return new ParametricType(STRING_TYPE) + return new InferStringType().perform(content, interpolationValues) } - private handleTuple = (node: Parser.SyntaxNode): ParametricType => { - return new InferTupleType(this, this._typeConstraints).perform( - node.namedChildren, + private handleTuple = (node: Parser.SyntaxNode): Disjunction => { + const elements = node.namedChildren.map((child) => + CompileError.addContext(this.traverse, child), + ) + + return new InferTupleType((elements) => new AST.Tuple(elements)).perform( + elements, ) } - private handleType = (node: Parser.SyntaxNode): Type => { - const name = node.text - const binding = this._walkFileModuleScope.scope.resolveBinding(name) + private handleType = ( + node: Parser.SyntaxNode, + ): Disjunction => { + const type = new BuildType().perform(node) - if (binding instanceof TypeBinding) return binding.type - else throw new MissingBindingError(name) + return new Disjunction([ + new Answer( + new AST.ParametricType(type.name), + new TypeConstraint( + new GetModuleTypeRepresentation( + this._walkFileModuleScope.scope, + ).perform(type), + ), + ), + ]) } - private handleWhenClause = (node: Parser.SyntaxNode): Type => { + // eslint-disable-next-line max-lines-per-function + private handleWhen = ( + node: Parser.SyntaxNode, + value: Answer, + ): Disjunction => { this._walkFileModuleScope.peekBlock() - this.handlePatternList(node.namedChild(0)!) + const patterns = this.handlePatternList(node.namedChild(0)!, value) this._walkFileModuleScope.leaveBlock() - return this.traverse(node.namedChild(1)!)! - } - - private handleWhenClauses = (node: Parser.SyntaxNode): Type => { - const branchTypes = node.namedChildren - .map((child) => this.traverse(child)) - .filter(isNotUndefined) + const body = this.handleBlock(node.namedChild(1)!)! + + return new MergeAccumulatedTypeDisjunction< + AST.Pattern, + AST.Block, + AST.When + >( + (patterns, body) => + new Answer( + new AST.When(patterns.nodes, body.node), + new TypeConstraint( + body.typeConstraint.type, + TypeEqualityGraph.build( + ...patterns.typeConstraints.map( + (typeConstraint) => typeConstraint.typeEqualityGraph, + ), + body.typeConstraint.typeEqualityGraph, + ), + ), + ), + ).perform(patterns, body) + } + + private handleWhenClauses = ( + node: Parser.SyntaxNode, + value: Answer, + ): AccumulatedDisjunction => { + const branches = node.namedChildren.map((child) => + CompileError.addContext(this.handleWhen, child, value), + ) - return new InferBranchType(this._typeConstraints).perform(branchTypes) + return new DistributeTypeDisjunction().perform(branches) } } diff --git a/.yalc/tony-lang/src/type_inference/models/AccumulatedAnswer.ts b/.yalc/tony-lang/src/type_inference/models/AccumulatedAnswer.ts new file mode 100644 index 0000000..803380b --- /dev/null +++ b/.yalc/tony-lang/src/type_inference/models/AccumulatedAnswer.ts @@ -0,0 +1,23 @@ +import * as AST from '../../ast' +import { Type, TypeConstraint } from '../../types' +import { Answer } from './Answer' + +export class AccumulatedAnswer { + private _answers: Answer[] + + constructor(answers: Answer[] = []) { + this._answers = answers + } + + get answers(): Answer[] { + return this._answers + } + + get nodes(): T[] { + return this._answers.map((answer) => answer.node) + } + + get typeConstraints(): TypeConstraint[] { + return this._answers.map((answer) => answer.typeConstraint) + } +} diff --git a/.yalc/tony-lang/src/type_inference/models/AccumulatedDisjunction.ts b/.yalc/tony-lang/src/type_inference/models/AccumulatedDisjunction.ts new file mode 100644 index 0000000..5588225 --- /dev/null +++ b/.yalc/tony-lang/src/type_inference/models/AccumulatedDisjunction.ts @@ -0,0 +1,6 @@ +import * as AST from '../../ast' +import { AccumulatedAnswer } from './AccumulatedAnswer' + +export type AccumulatedDisjunction< + T extends AST.SyntaxNode +> = AccumulatedAnswer[] diff --git a/.yalc/tony-lang/src/type_inference/models/Answer.ts b/.yalc/tony-lang/src/type_inference/models/Answer.ts new file mode 100644 index 0000000..984ed8f --- /dev/null +++ b/.yalc/tony-lang/src/type_inference/models/Answer.ts @@ -0,0 +1,29 @@ +import * as AST from '../../ast' +import { Type, TypeConstraint } from '../../types' + +export class Answer { + private _node: T + private _typeConstraint: TypeConstraint + + constructor(node: T, typeConstraint: TypeConstraint) { + this._node = node + this._typeConstraint = typeConstraint + } + + get node(): T { + return this._node + } + + get typeConstraint(): TypeConstraint { + return this._typeConstraint + } + + static build = ( + node: T, + typeConstraint?: TypeConstraint, + ): Answer | undefined => { + if (typeConstraint === undefined) return + + return new Answer(node, typeConstraint) + } +} diff --git a/.yalc/tony-lang/src/type_inference/models/Disjunction.ts b/.yalc/tony-lang/src/type_inference/models/Disjunction.ts new file mode 100644 index 0000000..9a10723 --- /dev/null +++ b/.yalc/tony-lang/src/type_inference/models/Disjunction.ts @@ -0,0 +1,19 @@ +import * as AST from '../../ast' +import { Answer } from './Answer' +import { TypeError } from '../../errors' +import { isNotUndefined } from '../../utilities' + +export class Disjunction { + private _answers: Answer[] + + constructor(answers: (Answer | undefined)[]) { + const filteredAnswers = answers.filter(isNotUndefined) + if (filteredAnswers.length == 0) throw new TypeError() + + this._answers = filteredAnswers + } + + get answers(): Answer[] { + return this._answers + } +} diff --git a/.yalc/tony-lang/src/type_inference/models/GeneralizedDisjunction.ts b/.yalc/tony-lang/src/type_inference/models/GeneralizedDisjunction.ts new file mode 100644 index 0000000..fb18abe --- /dev/null +++ b/.yalc/tony-lang/src/type_inference/models/GeneralizedDisjunction.ts @@ -0,0 +1,16 @@ +import * as AST from '../../ast' +import { AccumulatedAnswer } from './AccumulatedAnswer' +import { AccumulatedDisjunction } from './AccumulatedDisjunction' +import { Answer } from './Answer' +import { Disjunction } from './Disjunction' + +export type GeneralizedDisjunction = + | Disjunction + | AccumulatedDisjunction + +export const getAnswers = ( + disjunction: GeneralizedDisjunction, +): (Answer | AccumulatedAnswer)[] => { + if (disjunction instanceof Disjunction) return disjunction.answers + else return disjunction +} diff --git a/.yalc/tony-lang/src/type_inference/models/index.ts b/.yalc/tony-lang/src/type_inference/models/index.ts new file mode 100644 index 0000000..ceb18cf --- /dev/null +++ b/.yalc/tony-lang/src/type_inference/models/index.ts @@ -0,0 +1,5 @@ +export { AccumulatedAnswer } from './AccumulatedAnswer' +export { AccumulatedDisjunction } from './AccumulatedDisjunction' +export { Answer } from './Answer' +export { Disjunction } from './Disjunction' +export { GeneralizedDisjunction, getAnswers } from './GeneralizedDisjunction' diff --git a/.yalc/tony-lang/src/type_inference/services/AccumulateTypeDisjunction.ts b/.yalc/tony-lang/src/type_inference/services/AccumulateTypeDisjunction.ts new file mode 100644 index 0000000..3a634a4 --- /dev/null +++ b/.yalc/tony-lang/src/type_inference/services/AccumulateTypeDisjunction.ts @@ -0,0 +1,35 @@ +import * as AST from '../../ast' +import { + AccumulatedAnswer, + AccumulatedDisjunction, + Answer, + Disjunction, +} from '../models' + +type Factory = (fst: Answer, snd: Answer) => AccumulatedAnswer + +export class AccumulateTypeDisjunction { + private _factory: Factory + + constructor(factory: Factory) { + this._factory = factory + } + + perform = ( + fstDisj: Disjunction, + sndDisj: Disjunction, + ): AccumulatedDisjunction => + fstDisj.answers.reduce( + (answers: AccumulatedDisjunction, fst) => [ + ...answers, + ...sndDisj.answers.reduce( + (answers: AccumulatedDisjunction, snd) => [ + ...answers, + this._factory(fst, snd), + ], + [], + ), + ], + [], + ) +} diff --git a/.yalc/tony-lang/src/type_inference/services/DistributeTypeDisjunction.ts b/.yalc/tony-lang/src/type_inference/services/DistributeTypeDisjunction.ts new file mode 100644 index 0000000..b6382c3 --- /dev/null +++ b/.yalc/tony-lang/src/type_inference/services/DistributeTypeDisjunction.ts @@ -0,0 +1,52 @@ +import * as AST from '../../ast' +import { + AccumulatedAnswer, + AccumulatedDisjunction, + Answer, + GeneralizedDisjunction, + getAnswers, +} from '../models' + +export class DistributeTypeDisjunction { + perform = ([disjunction, ...otherDisjunctions]: GeneralizedDisjunction< + T + >[]): AccumulatedDisjunction => { + if (disjunction === undefined) return [] + + const distributedAnswers = this.perform(otherDisjunctions) + if (distributedAnswers.length == 0) + return getAnswers(disjunction).map((answer) => + this.buildAccumulatedAnswer(answer), + ) + + return getAnswers(disjunction).reduce( + (answers: AccumulatedAnswer[], answer) => [ + ...answers, + ...distributedAnswers.map((accumulatedAnswer) => + this.mergeAccumulatedAnswers(accumulatedAnswer, answer), + ), + ], + [], + ) + } + + private buildAccumulatedAnswer = ( + answer: Answer | AccumulatedAnswer, + ): AccumulatedAnswer => { + if (answer instanceof Answer) return new AccumulatedAnswer([answer]) + else return answer + } + + private mergeAccumulatedAnswers = ( + accumulatedAnswer: AccumulatedAnswer, + answer: Answer | AccumulatedAnswer, + ): AccumulatedAnswer => { + if (answer instanceof Answer) + return new AccumulatedAnswer([answer, ...accumulatedAnswer.answers]) + else + return new AccumulatedAnswer([ + ...answer.answers, + ...accumulatedAnswer.answers, + ]) + } +} diff --git a/.yalc/tony-lang/src/type_inference/services/GetModuleTypeRepresentation.ts b/.yalc/tony-lang/src/type_inference/services/GetModuleTypeRepresentation.ts new file mode 100644 index 0000000..2eeb954 --- /dev/null +++ b/.yalc/tony-lang/src/type_inference/services/GetModuleTypeRepresentation.ts @@ -0,0 +1,19 @@ +import { InvalidUseOfTypeAsValueError, MissingBindingError } from '../../errors' +import { ModuleBinding, NestedScope } from '../../symbol_table' +import { ModuleType, ParametricType } from '../../types' + +export class GetModuleTypeRepresentation { + private _scope: NestedScope + + constructor(scope: NestedScope) { + this._scope = scope + } + + perform = (type: ParametricType): ModuleType => { + const binding = this._scope.resolveBinding(type.name) + + if (binding === undefined) throw new MissingBindingError(type.name) + else if (binding instanceof ModuleBinding) return binding.type + else throw new InvalidUseOfTypeAsValueError(binding.name) + } +} diff --git a/.yalc/tony-lang/src/type_inference/services/InferAbstractionBranchType.ts b/.yalc/tony-lang/src/type_inference/services/InferAbstractionBranchType.ts new file mode 100644 index 0000000..1dfd44e --- /dev/null +++ b/.yalc/tony-lang/src/type_inference/services/InferAbstractionBranchType.ts @@ -0,0 +1,50 @@ +import * as AST from '../../ast' +import { Answer, Disjunction } from '../models' +import { + FUNCTION_TYPE, + ParametricType, + TypeConstraint, + TypeEqualityGraph, +} from '../../types' +import { MergeTypeDisjunction } from './MergeTypeDisjunction' +import { assert } from '../../errors' + +export class InferAbstractionBranchType { + perform = ( + parameters: Disjunction, + body: Disjunction, + ): Disjunction => + new MergeTypeDisjunction( + (parametersAnswer, bodyAnswer) => { + const typeEqualityGraph = TypeEqualityGraph.build( + parametersAnswer.typeConstraint.typeEqualityGraph, + bodyAnswer.typeConstraint.typeEqualityGraph, + ) + if (typeEqualityGraph === undefined) return + + return this.buildAnswer(parametersAnswer, bodyAnswer, typeEqualityGraph) + }, + ).perform(parameters, body) + + private buildAnswer = ( + parametersAnswer: Answer, + bodyAnswer: Answer, + typeEqualityGraph: TypeEqualityGraph, + ): Answer => { + assert( + parametersAnswer.typeConstraint.type instanceof ParametricType, + 'Parameter types should be a parametric function type.', + ) + + return new Answer( + new AST.AbstractionBranch(parametersAnswer.node, bodyAnswer.node), + new TypeConstraint( + new ParametricType(FUNCTION_TYPE, [ + ...parametersAnswer.typeConstraint.type.parameters, + bodyAnswer.typeConstraint.type, + ]), + typeEqualityGraph, + ), + ) + } +} diff --git a/.yalc/tony-lang/src/type_inference/services/InferAccessType.ts b/.yalc/tony-lang/src/type_inference/services/InferAccessType.ts index 7fd99a2..d233259 100644 --- a/.yalc/tony-lang/src/type_inference/services/InferAccessType.ts +++ b/.yalc/tony-lang/src/type_inference/services/InferAccessType.ts @@ -1,9 +1,12 @@ +import * as AST from '../../ast' +import { Answer, Disjunction } from '../models' import { - InternalError, - InvalidPropertyAccessError, - TypeError, - assert, -} from '../../errors' + Binding, + BindingTemplate, + IdentifierBinding, + IdentifierBindingTemplate, +} from '../../symbol_table' +import { InternalError, InvalidModuleAccessError, assert } from '../../errors' import { LIST_TYPE, MAP_TYPE, @@ -12,110 +15,235 @@ import { STRING_TYPE, TUPLE_TYPE, Type, - TypeConstraints, + TypeConstraint, + TypeEqualityGraph, + TypeVariable, } from '../../types' -import { NestedScope, TypeBinding } from '../../symbol_table' -import Parser from 'tree-sitter' +import { GetModuleTypeRepresentation } from './GetModuleTypeRepresentation' +import { MergeTypeDisjunction } from './MergeTypeDisjunction' export class InferAccessType { - private _node: Parser.SyntaxNode - private _scope: NestedScope - private _typeConstraints: TypeConstraints - - constructor( - node: Parser.SyntaxNode, - scope: NestedScope, - typeConstraints: TypeConstraints, - ) { - this._node = node - this._scope = scope - this._typeConstraints = typeConstraints + private _getModuleTypeRepresentation: GetModuleTypeRepresentation + + constructor(getModuleTypeRepresentation: GetModuleTypeRepresentation) { + this._getModuleTypeRepresentation = getModuleTypeRepresentation } - perform = (valueType: Type, accessorType: Type): Type => { - if (valueType instanceof ParametricType) { - if (valueType.name === LIST_TYPE) - return this.accessList(valueType, accessorType) - else if (valueType.name === TUPLE_TYPE) - return this.accessTuple(valueType, accessorType) - else if (valueType.name === MAP_TYPE) - return this.accessMap(valueType, accessorType) - else return this.accessRepresentation(valueType, accessorType) - } - - throw new TypeError( - valueType, - undefined, - 'The access operator may only be used on objects or values of a list, ' + - 'tuple or map type.', + perform = ( + value: Disjunction, + accessor: Disjunction, + ): Disjunction => + new MergeTypeDisjunction( + this.buildAnswers, + ).perform(value, accessor) + + private buildAnswers = ( + value: Answer, + accessor: Answer, + ): Disjunction | undefined => { + const typeEqualityGraph = TypeEqualityGraph.build( + value.typeConstraint.typeEqualityGraph, + accessor.typeConstraint.typeEqualityGraph, ) + if (typeEqualityGraph === undefined) return + + return this.inferType(value, accessor, typeEqualityGraph) + } + + // eslint-disable-next-line max-lines-per-function + private inferType = ( + value: Answer, + accessor: Answer, + typeEqualityGraph: TypeEqualityGraph, + ): Disjunction | undefined => { + const valueType = value.typeConstraint.type + const accessorType = accessor.typeConstraint.type + + return new Disjunction([ + this.buildAnswer( + value, + accessor, + this.inferListAccessType(valueType, accessorType, typeEqualityGraph), + typeEqualityGraph, + ), + this.buildAnswer( + value, + accessor, + this.inferMapAccessType(valueType, accessorType, typeEqualityGraph), + typeEqualityGraph, + ), + this.buildAnswer( + value, + accessor, + this.inferTupleAccessType( + valueType, + accessorType, + accessor.node, + typeEqualityGraph, + ), + typeEqualityGraph, + ), + ...this.inferModuleAccessType( + valueType, + value.node, + accessorType, + accessor.node, + typeEqualityGraph, + ), + ]) } - private accessList = ( - valueType: ParametricType, + private buildAnswer = ( + value: Answer, + accessor: Answer, + type: Type | undefined, + typeEqualityGraph: TypeEqualityGraph, + ): Answer | undefined => + Answer.build( + new AST.Access(value.node, accessor.node), + TypeConstraint.build(type, typeEqualityGraph), + ) + + private inferListAccessType = ( + valueType: Type, accessorType: Type, - ): Type => { - new ParametricType(NUMBER_TYPE).unify(accessorType, this._typeConstraints) + typeEqualityGraph: TypeEqualityGraph, + ): Type | undefined => { + const unifiedValueType = new ParametricType(LIST_TYPE, [ + new TypeVariable(), + ]).unify(valueType, typeEqualityGraph) + if (unifiedValueType === undefined) return + const unifiedAccessorType = new ParametricType(NUMBER_TYPE).unify( + accessorType, + typeEqualityGraph, + ) + if (unifiedAccessorType === undefined) return - return valueType.parameters[0] + assert( + unifiedValueType instanceof ParametricType, + 'Should be parametric type.', + ) + return unifiedValueType.parameters[0] } - private accessMap = (valueType: ParametricType, accessorType: Type): Type => { - valueType.parameters[0].unify(accessorType, this._typeConstraints) + private inferMapAccessType = ( + valueType: Type, + accessorType: Type, + typeEqualityGraph: TypeEqualityGraph, + ): Type | undefined => { + const unifiedValueType = new ParametricType(MAP_TYPE, [ + new TypeVariable(), + new TypeVariable(), + ]).unify(valueType, typeEqualityGraph) + if (unifiedValueType === undefined) return + + assert( + unifiedValueType instanceof ParametricType, + 'Should be parametric type.', + ) + const unifiedAccessorType = unifiedValueType.parameters[0].unify( + accessorType, + typeEqualityGraph, + ) + if (unifiedAccessorType === undefined) return - return valueType.parameters[1] + return unifiedValueType.parameters[1] } - private accessTuple = ( - valueType: ParametricType, + private inferTupleAccessType = ( + valueType: Type, accessorType: Type, - ): Type => { - new ParametricType(NUMBER_TYPE).unify(accessorType, this._typeConstraints) + accessorNode: AST.Expression, + typeEqualityGraph: TypeEqualityGraph, + ): Type | undefined => { + if (!(valueType instanceof ParametricType) || valueType.name !== TUPLE_TYPE) + return + const unifiedAccessorType = new ParametricType(NUMBER_TYPE).unify( + accessorType, + typeEqualityGraph, + ) + if (unifiedAccessorType === undefined) return // TODO: implement dynamic access with union types - if (this._node.namedChild(1)!.type === 'number') { - const numberNode = this._node.namedChild(1)! - const index = parseInt(numberNode.text) - - return valueType.parameters[index] - } else + if (accessorNode instanceof AST.Number) + return valueType.parameters[accessorNode.value] + else throw new InternalError( 'Dynamic tuple access has not been implemented yet.', ) } - private accessRepresentation = ( - valueType: ParametricType, + // eslint-disable-next-line max-lines-per-function + private inferModuleAccessType = ( + valueType: Type, + valueNode: AST.Expression, accessorType: Type, - ): Type => { - new ParametricType(STRING_TYPE).unify(accessorType, this._typeConstraints) - - // TODO: implement dynamic access with union types - if (this._node.namedChild(1)!.type === 'shorthand_access_identifier') { - const propertyName = this._node.namedChild(1)!.text - - const binding = this._scope.resolveBinding(valueType.name) - assert(binding instanceof TypeBinding, 'Should be a type binding.') - - return this.accessRepresentationProperty(binding, propertyName) - } else - throw new InternalError( - 'Dynamic object access has not been implemented yet.', - ) + accessorNode: AST.Expression, + typeEqualityGraph: TypeEqualityGraph, + ): Answer[] => { + if (!(valueType instanceof ParametricType)) return [] + const unifiedAccessorType = new ParametricType(STRING_TYPE).unify( + accessorType, + typeEqualityGraph, + ) + if (unifiedAccessorType === undefined) return [] + + const moduleType = this._getModuleTypeRepresentation.perform(valueType) + + if (accessorNode instanceof AST.ShorthandAccessIdentifier) { + const bindings = moduleType.resolveProperty(accessorNode.name) + + if (bindings.length > 0) + return this.inferModuleBindingType( + valueNode, + bindings, + typeEqualityGraph, + ) + else + throw new InvalidModuleAccessError( + accessorNode.name, + moduleType.toString(), + ) + } else throw new InvalidModuleAccessError(moduleType.toString()) } - private accessRepresentationProperty = ( - binding: TypeBinding, - propertyName: string, - ): Type => { - const property = binding.representation.findProperty(propertyName) - - if (property) return property.type - else - throw new InvalidPropertyAccessError( - propertyName, - binding.type.toString(), - binding.representation.toString(), + private inferModuleBindingType = ( + valueNode: AST.Expression, + bindings: (Binding | BindingTemplate)[], + typeEqualityGraph: TypeEqualityGraph, + ): Answer[] => + bindings.reduce((answers: Answer[], binding) => { + assert( + binding instanceof IdentifierBinding || + binding instanceof IdentifierBindingTemplate, + 'Binding should be an identifier binding.', ) - } + + return [ + ...answers, + this.buildModuleAccessAnswer(valueNode, binding, typeEqualityGraph), + ] + }, []) + + private buildModuleAccessAnswer = ( + valueNode: AST.Expression, + binding: IdentifierBinding | IdentifierBindingTemplate, + typeEqualityGraph: TypeEqualityGraph, + ): Answer => + new Answer( + new AST.Access( + valueNode, + new AST.ShorthandAccessIdentifier( + binding.name, + binding.transformedName, + ), + ), + new TypeConstraint( + binding.typeConstraint.type, + TypeEqualityGraph.build( + typeEqualityGraph, + binding.typeConstraint.typeEqualityGraph, + ), + ), + ) } diff --git a/.yalc/tony-lang/src/type_inference/services/InferApplicationType.ts b/.yalc/tony-lang/src/type_inference/services/InferApplicationType.ts index 1eb5971..25af60d 100644 --- a/.yalc/tony-lang/src/type_inference/services/InferApplicationType.ts +++ b/.yalc/tony-lang/src/type_inference/services/InferApplicationType.ts @@ -1,120 +1,259 @@ +import * as AST from '../../ast' import { - CurriedType, - INTERNAL_PARTIAL_APPLICATION_TYPE_NAME, + AccumulatedAnswer, + AccumulatedDisjunction, + Answer, + Disjunction, +} from '../models' +import { + FUNCTION_TYPE, ParametricType, Type, - TypeConstraints, + TypeConstraint, + TypeEqualityGraph, TypeVariable, VOID_TYPE, } from '../../types' -import { TypeError } from '../../errors' +import { InternalTypeError, TypeError } from '../../errors' -export class InferApplicationType { - private _typeConstraints: TypeConstraints +type Factory = ( + valueNode: T, + argumentNodes: U[], + typeConstraint: TypeConstraint, +) => Answer - constructor(typeConstraints: TypeConstraints) { - this._typeConstraints = typeConstraints - } +export class InferApplicationType< + T extends AST.SyntaxNode, + U extends AST.SyntaxNode, + V extends AST.SyntaxNode +> { + private _factory: Factory - perform = (valueType: Type, argumentTypes: CurriedType): Type => { - if (valueType instanceof TypeVariable) { - valueType.unify( - argumentTypes.concat(new TypeVariable()), - this._typeConstraints, - ) + constructor(factory: Factory) { + this._factory = factory + } - return new TypeVariable() - } + perform = ( + value: Disjunction, + args: AccumulatedDisjunction, + ): Disjunction => + new Disjunction( + value.answers.reduce( + (answers: (Answer | undefined)[], valueAnswer) => [ + ...answers, + ...args.map((argumentsAnswer) => + this.buildAnswers(valueAnswer, argumentsAnswer), + ), + ], + [], + ), + ) - this.checkAppliedToNonCurriedType(valueType, argumentTypes) - this.handleVoidParameterType(valueType) - this.checkAppliedTooManyArguments(valueType, argumentTypes) + private buildAnswers = ( + valueAnswer: Answer, + argumentsAnswer: AccumulatedAnswer, + ): Answer | undefined => { + const typeConstraint = this.inferType(valueAnswer, argumentsAnswer) + if (typeConstraint === undefined) return - return this.inferType(valueType, argumentTypes) + return this._factory( + valueAnswer.node, + argumentsAnswer.nodes, + typeConstraint, + ) } + // eslint-disable-next-line max-lines-per-function private inferType = ( - valueType: CurriedType, - argumentTypes: CurriedType, - ): Type => { - const typeConstraints = new TypeConstraints() - const inferParameterType = this.inferParameterTypeFactory( - typeConstraints, - valueType, - argumentTypes, - ) - const parameterTypes = valueType.parameters.reduce(inferParameterType, []) - - if (parameterTypes.length == 1) - return parameterTypes[0]._reduce(typeConstraints) - else return new CurriedType(parameterTypes)._reduce(typeConstraints) - } + value: Answer, + args: AccumulatedAnswer, + ): TypeConstraint | undefined => { + let valueTypeConstraint = value.typeConstraint + const argumentsTypeEqualityGraph = TypeEqualityGraph.build( + ...args.typeConstraints.map( + (typeConstraint) => typeConstraint.typeEqualityGraph, + ), + ) + if (argumentsTypeEqualityGraph === undefined) return - private inferParameterTypeFactory = ( - typeConstraints: TypeConstraints, - valueType: CurriedType, - argumentTypes: CurriedType, - ) => (parameterTypes: Type[], parameterType: Type, i: number): Type[] => { - const argumentType = argumentTypes.parameters[i] - if (argumentType === undefined || this.isPlaceholderArgument(argumentType)) - return parameterTypes.concat([parameterType]) + const argumentsTypeConstraint = new TypeConstraint( + new ParametricType( + FUNCTION_TYPE, + args.typeConstraints.map((typeConstraint) => typeConstraint.type), + ), + argumentsTypeEqualityGraph, + ) + console.log( + valueTypeConstraint.toString(), + argumentsTypeConstraint.toString(), + ) - try { - parameterType.unify(argumentType, typeConstraints) - } catch (error) { - if (error instanceof TypeError) - this.handleArgumentTypeMismatch(error, valueType, argumentTypes) - throw error - } + if (valueTypeConstraint.type instanceof TypeVariable) + return this.inferValueType(value, argumentsTypeConstraint) - return parameterTypes - } + if ( + !this.checkAppliedToNonCurriedType( + valueTypeConstraint.type, + argumentsTypeConstraint.type, + ) + ) + return + valueTypeConstraint = this.handleVoidParameterType( + valueTypeConstraint as TypeConstraint, + ) + if ( + !this.checkAppliedTooManyArguments( + valueTypeConstraint.type as ParametricType, + argumentsTypeConstraint.type, + ) + ) + return - private handleArgumentTypeMismatch = ( - error: TypeError, - valueType: CurriedType, - argumentTypes: CurriedType, - ): void => - error.addTypeMismatch( - new CurriedType(valueType.parameters.slice(0, -1)), - argumentTypes, + return this.matchArgumentsAndParameters( + valueTypeConstraint as TypeConstraint, + argumentsTypeConstraint, ) + } - private handleVoidParameterType = (valueType: CurriedType): void => { - if (!(valueType instanceof ParametricType && valueType.name === VOID_TYPE)) - return + private inferValueType = ( + value: Answer, + argumentsType: TypeConstraint, + ): TypeConstraint | undefined => { + const returnType = new TypeVariable() + const functionType = new TypeConstraint( + argumentsType.type.concat(returnType), + argumentsType.typeEqualityGraph, + ) + const applicationTypeConstraint = value.typeConstraint.unify(functionType) + if (applicationTypeConstraint === undefined) return - valueType.parameters.pop() + return new TypeConstraint( + returnType, + applicationTypeConstraint.typeEqualityGraph, + ) } private checkAppliedToNonCurriedType( valueType: Type, - argumentTypes: CurriedType, - ): asserts valueType is CurriedType { - if (valueType instanceof CurriedType) return + argumentsType: ParametricType, + ): valueType is ParametricType { + if (valueType instanceof ParametricType && valueType.name === FUNCTION_TYPE) + return true - throw new TypeError( - valueType, - argumentTypes, - 'Cannot apply to a non-curried type.', + TypeError.addError( + new InternalTypeError( + valueType, + argumentsType, + 'Cannot apply to a non-curried type.', + ), + ) + return false + } + + private handleVoidParameterType = ( + valueTypeConstraint: TypeConstraint, + ): TypeConstraint => { + const firstParameter = valueTypeConstraint.type.parameters[0] + if ( + !( + firstParameter instanceof ParametricType && + firstParameter.name === VOID_TYPE + ) + ) + return valueTypeConstraint + + return new TypeConstraint( + new ParametricType( + FUNCTION_TYPE, + valueTypeConstraint.type.parameters.slice(1), + ), + valueTypeConstraint.typeEqualityGraph, ) } private checkAppliedTooManyArguments = ( - valueType: CurriedType, - argumentTypes: CurriedType, - ): void => { - if (valueType.parameters.length > argumentTypes.parameters.length) return + valueType: ParametricType, + argumentsType: ParametricType, + ): boolean => { + if (valueType.parameters.length > argumentsType.parameters.length) + return true - throw new TypeError( - new CurriedType(valueType.parameters.slice(0, -1)), - argumentTypes, - `Applied ${argumentTypes.parameters.length} arguments to a curried ` + - `type accepting at most ${valueType.parameters.length - 1} arguments.`, + TypeError.addError( + new InternalTypeError( + new ParametricType(FUNCTION_TYPE, valueType.parameters.slice(0, -1)), + argumentsType, + `Applied ${argumentsType.parameters.length} arguments to a curried ` + + `type accepting at most ${ + valueType.parameters.length - 1 + } arguments.`, + ), ) + return false } + // eslint-disable-next-line max-lines-per-function + private matchArgumentsAndParameters = ( + valueTypeConstraint: TypeConstraint, + argumentsTypeConstraint: TypeConstraint, + ): TypeConstraint | undefined => { + const typeEqualityGraph = TypeEqualityGraph.build( + valueTypeConstraint.typeEqualityGraph, + argumentsTypeConstraint.typeEqualityGraph, + ) + const returnTypeEqualityGraph = TypeEqualityGraph.build( + valueTypeConstraint.typeEqualityGraph, + argumentsTypeConstraint.typeEqualityGraph, + ) + if ( + typeEqualityGraph === undefined || + returnTypeEqualityGraph === undefined + ) + return + + const parameterTypes = valueTypeConstraint.type.parameters.reduce( + this.inferParameterType(typeEqualityGraph, argumentsTypeConstraint.type), + [], + ) + if (parameterTypes === undefined) return + + return this.buildReturnTypeConstraint( + parameterTypes, + returnTypeEqualityGraph, + ) + } + + private inferParameterType = ( + typeEqualityGraph: TypeEqualityGraph, + argumentTypes: ParametricType, + ) => ( + parameterTypes: Type[] | undefined, + parameterType: Type, + i: number, + ): Type[] | undefined => { + if (parameterTypes === undefined) return + + const argumentType = argumentTypes.parameters[i] + if (argumentType === undefined || this.isPlaceholderArgument(argumentType)) + return [...parameterTypes, parameterType] + if (parameterType.unify(argumentType, typeEqualityGraph) === undefined) + return + + return parameterTypes + } + + private buildReturnTypeConstraint = ( + parameterTypes: Type[], + typeEqualityGraph: TypeEqualityGraph, + ): TypeConstraint => + new TypeConstraint( + parameterTypes.length == 1 + ? parameterTypes[0].reduce(typeEqualityGraph) + : new ParametricType(FUNCTION_TYPE, parameterTypes).reduce( + typeEqualityGraph, + ), + typeEqualityGraph, + ) + private isPlaceholderArgument = (argumentType: Type): boolean => - argumentType instanceof TypeVariable && - argumentType.name === INTERNAL_PARTIAL_APPLICATION_TYPE_NAME + argumentType instanceof ParametricType && argumentType.name === VOID_TYPE } diff --git a/.yalc/tony-lang/src/type_inference/services/InferAssignmentType.ts b/.yalc/tony-lang/src/type_inference/services/InferAssignmentType.ts new file mode 100644 index 0000000..4006971 --- /dev/null +++ b/.yalc/tony-lang/src/type_inference/services/InferAssignmentType.ts @@ -0,0 +1,41 @@ +import * as AST from '../../ast' +import { Answer, Disjunction } from '../models' +import { InferPatternBindingTypes } from './InferPatternBindingTypes' +import Parser from 'tree-sitter' + +export class InferAssignmentType { + private _inferPatternBindingTypes: InferPatternBindingTypes + + constructor(inferPatternBindingTypes: InferPatternBindingTypes) { + this._inferPatternBindingTypes = inferPatternBindingTypes + } + + perform = ( + patternNode: Parser.SyntaxNode, + value: Disjunction, + ): Disjunction => + new Disjunction( + value.answers.reduce((answers: Answer[], valueAnswer) => { + const patternAnswers = this._inferPatternBindingTypes + .perform(patternNode, valueAnswer.typeConstraint) + ?.answers.map((patternAnswer) => + this.buildAnswer(patternAnswer, valueAnswer), + ) + + if (patternAnswers !== undefined) return [...answers, ...patternAnswers] + else return answers + }, []), + ) + + private buildAnswer = ( + patternAnswer: Answer, + valueAnswer: Answer, + ): Answer => + new Answer( + new AST.Assignment( + patternAnswer.node as AST.AssignablePattern, + valueAnswer.node, + ), + patternAnswer.typeConstraint, + ) +} diff --git a/.yalc/tony-lang/src/type_inference/services/InferBlockType.ts b/.yalc/tony-lang/src/type_inference/services/InferBlockType.ts new file mode 100644 index 0000000..fe905f4 --- /dev/null +++ b/.yalc/tony-lang/src/type_inference/services/InferBlockType.ts @@ -0,0 +1,40 @@ +import * as AST from '../../ast' +import { AccumulatedAnswer, Answer, Disjunction } from '../models' +import { TypeConstraint, TypeEqualityGraph } from '../../types' +import { DistributeTypeDisjunction } from './DistributeTypeDisjunction' + +type Factory = (expressions: AccumulatedAnswer) => T + +export class InferBlockType { + private _factory: Factory + + constructor(factory: Factory) { + this._factory = factory + } + + perform = (expressions: Disjunction[]): Disjunction => + new Disjunction( + new DistributeTypeDisjunction() + .perform(expressions) + .map(this.buildBlock), + ) + + private buildBlock = ( + expressions: AccumulatedAnswer, + ): Answer | undefined => { + const typeEqualityGraph = TypeEqualityGraph.build( + ...expressions.typeConstraints.map( + (typeConstraint) => typeConstraint.typeEqualityGraph, + ), + ) + if (typeEqualityGraph === undefined) return + + return new Answer( + this._factory(expressions), + new TypeConstraint( + expressions.answers[expressions.answers.length - 1].typeConstraint.type, + typeEqualityGraph, + ), + ) + } +} diff --git a/.yalc/tony-lang/src/type_inference/services/InferBranchType.ts b/.yalc/tony-lang/src/type_inference/services/InferBranchType.ts index ae7e7e1..b005ae6 100644 --- a/.yalc/tony-lang/src/type_inference/services/InferBranchType.ts +++ b/.yalc/tony-lang/src/type_inference/services/InferBranchType.ts @@ -1,16 +1,34 @@ -import { Type, TypeConstraints } from '../../types' +import * as AST from '../../ast' +import { Answer, Disjunction, GeneralizedDisjunction } from '../models' +import { Type, TypeConstraint, TypeVariable } from '../../types' +import { DistributeTypeDisjunction } from './DistributeTypeDisjunction' -export class InferBranchType { - private _typeConstraints: TypeConstraints +type Factory = (branches: T[]) => U - constructor(typeConstraints: TypeConstraints) { - this._typeConstraints = typeConstraints +export class InferBranchType< + T extends AST.SyntaxNode, + U extends AST.SyntaxNode +> { + private _factory: Factory + + constructor(factory: Factory) { + this._factory = factory } - perform = (branchTypes: Type[]): Type => - branchTypes - .reduce((type, branchType) => - type.unify(branchType, this._typeConstraints), - ) - ._reduce(this._typeConstraints) + perform = (branches: GeneralizedDisjunction[]): Disjunction => + new Disjunction( + new DistributeTypeDisjunction().perform(branches).map((branches) => { + const typeConstraint = branches.typeConstraints.reduce( + (acc: TypeConstraint | undefined, typeConstraint) => { + if (acc === undefined) return + + return acc.unify(typeConstraint) + }, + new TypeConstraint(new TypeVariable()), + ) + if (typeConstraint === undefined) return + + return new Answer(this._factory(branches.nodes), typeConstraint) + }), + ) } diff --git a/.yalc/tony-lang/src/type_inference/services/InferExpressionPairType.ts b/.yalc/tony-lang/src/type_inference/services/InferExpressionPairType.ts new file mode 100644 index 0000000..47590be --- /dev/null +++ b/.yalc/tony-lang/src/type_inference/services/InferExpressionPairType.ts @@ -0,0 +1,43 @@ +import * as AST from '../../ast' +import { Answer, Disjunction } from '../models' +import { + MAP_TYPE, + ParametricType, + TypeConstraint, + TypeEqualityGraph, +} from '../../types' +import { MergeTypeDisjunction } from './MergeTypeDisjunction' + +export class InferExpressionPairType { + perform = ( + key: Disjunction, + value: Disjunction, + ): Disjunction => + new MergeTypeDisjunction< + AST.Expression, + AST.Expression, + AST.ExpressionPair + >(this.buildAnswer).perform(key, value) + + private buildAnswer = ( + key: Answer, + value: Answer, + ): Answer | undefined => { + const typeEqualityGraph = TypeEqualityGraph.build( + key.typeConstraint.typeEqualityGraph, + value.typeConstraint.typeEqualityGraph, + ) + if (typeEqualityGraph === undefined) return + + return new Answer( + new AST.ExpressionPair(key.node, value.node), + new TypeConstraint( + new ParametricType(MAP_TYPE, [ + key.typeConstraint.type, + value.typeConstraint.type, + ]), + typeEqualityGraph, + ), + ) + } +} diff --git a/.yalc/tony-lang/src/type_inference/services/InferGeneratorType.ts b/.yalc/tony-lang/src/type_inference/services/InferGeneratorType.ts new file mode 100644 index 0000000..1f91f54 --- /dev/null +++ b/.yalc/tony-lang/src/type_inference/services/InferGeneratorType.ts @@ -0,0 +1,152 @@ +import * as AST from '../../ast' +import { Answer, Disjunction } from '../models' +import { + BOOLEAN_TYPE, + LIST_TYPE, + ParametricType, + Type, + TypeConstraint, + TypeEqualityGraph, + VOID_TYPE, +} from '../../types' +import { IdentifierBindingTemplate, NestedScope } from '../../symbol_table' + +export class InferGeneratorType { + private _scope: NestedScope + + constructor(scope: NestedScope) { + this._scope = scope + } + + perform = ( + bindingTemplate: IdentifierBindingTemplate, + value: Disjunction, + condition?: Disjunction, + ): Disjunction => + new Disjunction( + value.answers.reduce( + this.handleValueAnswer(bindingTemplate, condition), + [], + ), + ) + + // eslint-disable-next-line max-lines-per-function + private handleValueAnswer = ( + bindingTemplate: IdentifierBindingTemplate, + condition: Disjunction | undefined, + // eslint-disable-next-line max-lines-per-function + ) => ( + answers: (Answer | undefined)[], + valueAnswer: Answer, + ): (Answer | undefined)[] => { + const valueTypeConstraint = this.unifyValueTypeConstraint( + bindingTemplate, + valueAnswer, + ) + if (valueTypeConstraint === undefined) return answers + + if (condition) { + return [ + ...answers, + ...this.buildAnswersWithCondition( + bindingTemplate, + valueTypeConstraint, + valueAnswer, + condition, + ), + ] + } else + return [ + ...answers, + this.buildAnswer(bindingTemplate, valueTypeConstraint, valueAnswer), + ] + } + + private unifyValueTypeConstraint = ( + bindingTemplate: IdentifierBindingTemplate, + value: Answer, + ): TypeConstraint | undefined => { + const unifiedTypeConstraint = new TypeConstraint( + new ParametricType(LIST_TYPE, [bindingTemplate.type]), + ).unify(value.typeConstraint) as TypeConstraint + if (unifiedTypeConstraint === undefined) return unifiedTypeConstraint + + const binding = bindingTemplate.buildBinding( + new TypeConstraint( + unifiedTypeConstraint.type.parameters[0], + unifiedTypeConstraint.typeEqualityGraph, + ), + ) + if (binding === undefined) return + + this._scope.addBinding(binding) + return unifiedTypeConstraint + } + + private buildAnswer = ( + bindingTemplate: IdentifierBindingTemplate, + typeConstraint: TypeConstraint, + value: Answer, + ): Answer => + new Answer( + new AST.Generator( + bindingTemplate.name, + bindingTemplate.transformedName, + value.node, + ), + new TypeConstraint( + new ParametricType(VOID_TYPE), + typeConstraint.typeEqualityGraph, + ), + ) + + private buildAnswersWithCondition = ( + bindingTemplate: IdentifierBindingTemplate, + valueTypeConstraint: TypeConstraint, + valueAnswer: Answer, + condition: Disjunction, + ): (Answer | undefined)[] => + condition.answers.map( + this.handleConditionAnswer( + bindingTemplate, + valueTypeConstraint, + valueAnswer, + ), + ) + + // eslint-disable-next-line max-lines-per-function + private handleConditionAnswer = ( + bindingTemplate: IdentifierBindingTemplate, + valueTypeConstraint: TypeConstraint, + value: Answer, + // eslint-disable-next-line max-lines-per-function + ) => ( + condition: Answer, + ): Answer | undefined => { + const conditionTypeConstraint = this.unifyConditionTypeConstraint(condition) + if (conditionTypeConstraint === undefined) return + + return new Answer( + new AST.Generator( + bindingTemplate.name, + bindingTemplate.transformedName, + value.node, + condition.node, + ), + new TypeConstraint( + new ParametricType(VOID_TYPE), + TypeEqualityGraph.build( + valueTypeConstraint.typeEqualityGraph, + conditionTypeConstraint.typeEqualityGraph, + ), + ), + ) + } + + private unifyConditionTypeConstraint = ( + condition: Answer, + ): TypeConstraint | undefined => + new TypeConstraint(new ParametricType(BOOLEAN_TYPE)).unify( + condition.typeConstraint, + ) +} diff --git a/.yalc/tony-lang/src/type_inference/services/InferIdentifierPatternType.ts b/.yalc/tony-lang/src/type_inference/services/InferIdentifierPatternType.ts new file mode 100644 index 0000000..49a9911 --- /dev/null +++ b/.yalc/tony-lang/src/type_inference/services/InferIdentifierPatternType.ts @@ -0,0 +1,205 @@ +import * as AST from '../../ast' +import { Answer, Disjunction } from '../models' +import { DuplicateBindingError, assert } from '../../errors' +import { + IdentifierBinding, + IdentifierBindingTemplate, + NestedScope, +} from '../../symbol_table' +import { Type, TypeConstraint, TypeVariable } from '../../types' +import { InferTypes } from '../InferTypes' +import Parser from 'tree-sitter' + +type Factory = ( + name: string, + transformedName: string, + def?: AST.Expression, +) => T +type TypeFactory = (type: Type) => Type +type TypeConstraintFactory = ( + type: TypeConstraint, +) => TypeConstraint + +export class InferIdentifierPatternType { + private _factory: Factory + private _inferTypes: InferTypes + private _scope: NestedScope + private _typeFactory: TypeFactory + private _typeConstraintFactory: TypeConstraintFactory + + constructor( + inferTypes: InferTypes, + scope: NestedScope, + factory: Factory, + typeFactory = (type: Type): Type => type, + typeConstraintFactory = ( + type: TypeConstraint, + ): TypeConstraint => type, + ) { + this._factory = factory + this._inferTypes = inferTypes + this._scope = scope + this._typeFactory = typeFactory + this._typeConstraintFactory = typeConstraintFactory + } + + // eslint-disable-next-line max-lines-per-function + perform = ( + patternNode: Parser.SyntaxNode, + typeConstraint: TypeConstraint, + ): Disjunction => { + const bindingTemplate = this.findBindingTemplate(patternNode) + // prettier-ignore + // eslint-disable-next-line @typescript-eslint/ban-ts-ignore + // @ts-ignore + const type = patternNode.typeNode ? new BuildType().perform(patternNode.typeNode) : new TypeVariable() + + const unifiedTypeConstraint = new TypeConstraint( + this._typeFactory(type).unsafeUnify( + typeConstraint.type, + typeConstraint.typeEqualityGraph, + ), + typeConstraint.typeEqualityGraph, + ) + + // eslint-disable-next-line @typescript-eslint/ban-ts-ignore + // @ts-ignore + const defaultNode: Parser.SyntaxNode | undefined = patternNode.defaultNode + if (defaultNode) + return this.handleDefaultNode( + bindingTemplate, + unifiedTypeConstraint, + this._inferTypes.traverse(defaultNode), + ) + + if ( + !this.buildIdentifierBinding( + bindingTemplate, + this._typeConstraintFactory(unifiedTypeConstraint), + ) + ) + return new Disjunction([]) + + return new Disjunction([ + new Answer( + this._factory(bindingTemplate.name, bindingTemplate.transformedName), + unifiedTypeConstraint, + ), + ]) + } + + private findBindingTemplate = ( + patternNode: Parser.SyntaxNode, + ): IdentifierBindingTemplate => { + const bindingTemplate = this._scope.bindingTemplates.find( + (bindingTemplate) => bindingTemplate.node === patternNode, + ) + + assert( + bindingTemplate instanceof IdentifierBindingTemplate, + 'Pattern identifier binding should be found in current scope.', + ) + + return bindingTemplate + } + + // eslint-disable-next-line max-lines-per-function + private handleDefaultNode = ( + bindingTemplate: IdentifierBindingTemplate, + typeConstraint: TypeConstraint, + def: Disjunction, + ): Disjunction => + new Disjunction( + def.answers.map((answer) => { + const unifiedTypeConstraint = typeConstraint.unify( + new TypeConstraint( + this._typeFactory(answer.typeConstraint.type), + answer.typeConstraint.typeEqualityGraph, + ), + ) + if (unifiedTypeConstraint === undefined) return + + if ( + !this.buildIdentifierBinding( + bindingTemplate, + this._typeConstraintFactory(unifiedTypeConstraint), + ) + ) + return + + return new Answer( + this._factory( + bindingTemplate.name, + bindingTemplate.transformedName, + answer.node, + ), + unifiedTypeConstraint, + ) + }), + ) + + private buildIdentifierBinding = ( + bindingTemplate: IdentifierBindingTemplate, + typeConstraint: TypeConstraint, + ): boolean => { + this.checkDuplicateBinding(bindingTemplate, typeConstraint) + + const binding = this.findBindingForTemplate(bindingTemplate) + if (binding) { + const unifiedTypeConstraint = binding.typeConstraint.unify(typeConstraint) + if (unifiedTypeConstraint === undefined) return false + + this._scope.removeBinding(binding) + return this.addBinding(bindingTemplate, unifiedTypeConstraint) + } + + return this.addBinding(bindingTemplate, typeConstraint) + } + + private checkDuplicateBinding = ( + bindingTemplate: IdentifierBindingTemplate, + typeConstraint: TypeConstraint, + ): void => { + const duplicateBinding = this._scope + .resolveBindings(bindingTemplate.name) + .find( + (binding) => + binding instanceof IdentifierBinding && + binding.node !== bindingTemplate.node && + typeConstraint.unify(binding.typeConstraint), + ) + if (duplicateBinding === undefined) return + + throw new DuplicateBindingError(bindingTemplate.name) + } + + private findBindingForTemplate = ( + bindingTemplate: IdentifierBindingTemplate, + ): IdentifierBinding | undefined => { + const binding = this._scope + .resolveBindings(bindingTemplate.name) + .find( + (binding) => + binding.node === bindingTemplate.node && + binding instanceof IdentifierBinding, + ) + if (binding === undefined) return + + assert( + binding instanceof IdentifierBinding, + 'Should be identifier binding.', + ) + return binding + } + + private addBinding = ( + bindingTemplate: IdentifierBindingTemplate, + typeConstraint: TypeConstraint, + ): boolean => { + const binding = bindingTemplate.buildBinding(typeConstraint) + if (binding === undefined) return false + + this._scope.addBinding(binding) + return true + } +} diff --git a/.yalc/tony-lang/src/type_inference/services/InferImportBindingTypes.ts b/.yalc/tony-lang/src/type_inference/services/InferImportBindingTypes.ts new file mode 100644 index 0000000..26b0feb --- /dev/null +++ b/.yalc/tony-lang/src/type_inference/services/InferImportBindingTypes.ts @@ -0,0 +1,163 @@ +import { BuildType, TypeConstraint, TypeEqualityGraph } from '../../types' +import { + FileModuleScope, + IdentifierBinding, + IdentifierBindingTemplate, +} from '../../symbol_table' +import { + IdentifierImport, + ModuleBinding, + ModuleImport, +} from '../../symbol_table' +import { + InvalidExternalTypeImportError, + MissingBindingError, + assert, +} from '../../errors' +import Parser from 'tree-sitter' +import { isNotUndefined } from '../../utilities' + +export class InferImportBindingTypes { + private _typeEqualityGraph: TypeEqualityGraph + private _fileScopes: FileModuleScope[] + private _scope: FileModuleScope + + constructor( + scope: FileModuleScope, + fileScopes: FileModuleScope[], + typeEqualityGraph: TypeEqualityGraph, + ) { + this._typeEqualityGraph = typeEqualityGraph + this._fileScopes = fileScopes + this._scope = scope + } + + perform = (node: Parser.SyntaxNode): void => { + assert(node.type === 'import', 'Should be import node.') + + this.traverse(node) + } + + private traverse = (node: Parser.SyntaxNode): void => { + switch (node.type) { + case 'identifier_pattern': + return this.handleIdentifier(node) + case 'import_clause_identifier_pair': + return this.handleIdentifier(node) + case 'import_clause_type_pair': + return this.handleType(node) + case 'type': + return this.handleType(node) + default: + return node.namedChildren.forEach(this.traverse) + } + } + + private handleIdentifier = (node: Parser.SyntaxNode): void => { + const imp = this._scope.imports.find((imp) => imp.node === node) + assert(imp instanceof IdentifierImport, 'Should be an identifier import.') + + const fileScope = this._fileScopes.find( + (fileScope) => fileScope.filePath === imp.filePath, + ) + + if (fileScope) this.handleInternalIdentifierImport(node, imp, fileScope) + else this.handleExternalIdentifierImport(node, imp) + } + + // eslint-disable-next-line max-lines-per-function + private handleInternalIdentifierImport = ( + node: Parser.SyntaxNode, + imp: IdentifierImport, + fileScope: FileModuleScope, + ): void => { + const bindings = fileScope + .resolveBindings(imp.name) + .map((binding) => { + assert( + binding instanceof IdentifierBinding, + 'Should be an identifier binding.', + ) + + const typeConstraint = new TypeConstraint( + imp.type, + this._typeEqualityGraph, + ).unify(binding.typeConstraint) + if (typeConstraint === undefined) return + + return new IdentifierBinding( + node, + imp.alias, + IdentifierBindingTemplate.getTransformedName(), + typeConstraint, + { + isImplicit: true, + importInformation: { + filePath: imp.filePath, + transformedImportName: binding.transformedName, + }, + }, + ) + }) + .filter(isNotUndefined) + + if (bindings.length > 0) + bindings.forEach((binding) => this._scope.addBinding(binding)) + else throw new MissingBindingError(name) + } + + private handleExternalIdentifierImport = ( + node: Parser.SyntaxNode, + imp: IdentifierImport, + ): void => { + this._scope.addBinding( + new IdentifierBinding( + node, + imp.alias, + IdentifierBindingTemplate.getTransformedName(), + new TypeConstraint(imp.type), + { + isImplicit: true, + importInformation: { + filePath: imp.filePath, + transformedImportName: imp.name, + }, + }, + ), + ) + } + + private handleType = (node: Parser.SyntaxNode): void => { + const name = new BuildType().perform(node).name + const imp = this._scope.imports.find((imp) => imp.node === node) + assert(imp instanceof ModuleImport, 'Should be a module import.') + + const fileScope = this._fileScopes.find( + (fileScope) => fileScope.filePath === imp.filePath, + ) + + if (fileScope) this.handleInternalTypeImport(node, imp, fileScope) + else throw new InvalidExternalTypeImportError(name) + } + + private handleInternalTypeImport = ( + node: Parser.SyntaxNode, + imp: ModuleImport, + fileScope: FileModuleScope, + ): void => { + const bindings = fileScope.resolveBindings(imp.name).map((binding) => { + assert(binding instanceof ModuleBinding, 'Should be a module binding.') + + return new ModuleBinding(node, imp.aliasType, binding.type, { + importInformation: { + filePath: imp.filePath, + transformedImportName: binding.transformedName, + }, + }) + }) + + if (bindings.length > 0) + bindings.forEach((binding) => this._scope.addBinding(binding)) + else throw new MissingBindingError(name) + } +} diff --git a/.yalc/tony-lang/src/type_inference/services/InferListType.ts b/.yalc/tony-lang/src/type_inference/services/InferListType.ts index 3d9cede..861101e 100644 --- a/.yalc/tony-lang/src/type_inference/services/InferListType.ts +++ b/.yalc/tony-lang/src/type_inference/services/InferListType.ts @@ -1,39 +1,87 @@ +import * as AST from '../../ast' +import { AccumulatedAnswer, Answer, Disjunction } from '../models' import { LIST_TYPE, ParametricType, Type, - TypeConstraints, + TypeConstraint, + TypeEqualityGraph, TypeVariable, } from '../../types' -import { InferTypes } from '../InferTypes' -import Parser from 'tree-sitter' +import { DistributeTypeDisjunction } from './DistributeTypeDisjunction' -export class InferListType { - private _inferTypes: InferTypes - private _typeConstraints: TypeConstraints +type Factory = (elements: AST.SyntaxNode[]) => T - constructor(inferTypes: InferTypes, typeConstraints: TypeConstraints) { - this._inferTypes = inferTypes - this._typeConstraints = typeConstraints +export class InferListType { + private _factory: Factory + + constructor(factory: Factory) { + this._factory = factory + } + + perform = (elements: Disjunction[]): Disjunction => { + const answers = this.inferAnswers(elements) + if (answers.length == 0) + return new Disjunction([ + new Answer( + this._factory([]), + new TypeConstraint( + new ParametricType(LIST_TYPE, [new TypeVariable()]), + ), + ), + ]) + + return new Disjunction(answers) } - perform = (valueNodes: Parser.SyntaxNode[]): ParametricType => { - const valueType = valueNodes.reduce((valueType: Type, valueNode) => { - const otherValueType = this._inferTypes.traverse(valueNode)! + private inferAnswers = ( + elements: Disjunction[], + ): (Answer | undefined)[] => + new DistributeTypeDisjunction().perform(elements).map((elements) => { + const typeEqualityGraph = TypeEqualityGraph.build( + ...elements.typeConstraints.map( + (typeConstraint) => typeConstraint.typeEqualityGraph, + ), + ) + if (typeEqualityGraph === undefined) return + const type = this.inferType(elements, typeEqualityGraph) + if (type === undefined) return + + return new Answer( + this._factory(elements.nodes), + new TypeConstraint(type, typeEqualityGraph), + ) + }) + + private inferType = ( + elements: AccumulatedAnswer, + typeEqualityGraph: TypeEqualityGraph, + ): Type | undefined => + elements.answers.reduce((type: Type | undefined, answer) => { + if (type === undefined) return - if (valueNode.type === 'spread_list') { - const restType = new ParametricType(LIST_TYPE).unify( - otherValueType, - this._typeConstraints, - true, - ) + if ( + answer.node instanceof AST.RestList || + answer.node instanceof AST.SpreadList + ) + this.handleSpread(type, answer, typeEqualityGraph) - return valueType.unify(restType.parameters[0], this._typeConstraints) - } + return type.unify( + new ParametricType(LIST_TYPE, [answer.typeConstraint.type]), + typeEqualityGraph, + ) + }, new ParametricType(LIST_TYPE, [new TypeVariable()])) - return valueType.unify(otherValueType, this._typeConstraints) - }, new TypeVariable()) + private handleSpread = ( + type: Type, + answer: Answer, + typeEqualityGraph: TypeEqualityGraph, + ): Type | undefined => { + const spreadType = new ParametricType(LIST_TYPE, [ + new TypeVariable(), + ]).unify(answer.typeConstraint.type, typeEqualityGraph) + if (spreadType === undefined) return - return new ParametricType(LIST_TYPE, [valueType]) + return type.unify(spreadType, typeEqualityGraph) } } diff --git a/.yalc/tony-lang/src/type_inference/services/InferMapType.ts b/.yalc/tony-lang/src/type_inference/services/InferMapType.ts index 934857c..e2066fa 100644 --- a/.yalc/tony-lang/src/type_inference/services/InferMapType.ts +++ b/.yalc/tony-lang/src/type_inference/services/InferMapType.ts @@ -1,20 +1,68 @@ +import * as AST from '../../ast' +import { AccumulatedAnswer, Answer, Disjunction } from '../models' import { MAP_TYPE, ParametricType, Type, - TypeConstraints, + TypeConstraint, + TypeEqualityGraph, TypeVariable, } from '../../types' +import { DistributeTypeDisjunction } from './DistributeTypeDisjunction' -export class InferMapType { - private _typeConstraints: TypeConstraints +type Factory = (elements: AST.SyntaxNode[]) => T - constructor(typeConstraints: TypeConstraints) { - this._typeConstraints = typeConstraints +export class InferMapType { + private _factory: Factory + + constructor(factory: Factory) { + this._factory = factory + } + + perform = (elements: Disjunction[]): Disjunction => { + const answers = this.inferAnswers(elements) + if (answers.length == 0) + return new Disjunction([ + new Answer( + this._factory([]), + new TypeConstraint( + new ParametricType(MAP_TYPE, [ + new TypeVariable(), + new TypeVariable(), + ]), + ), + ), + ]) + + return new Disjunction(answers) } - perform = (mapTypes: Type[]): ParametricType => - mapTypes.reduce((mapType: ParametricType, otherMapType) => { - return mapType.unify(otherMapType, this._typeConstraints) + private inferAnswers = ( + elements: Disjunction[], + ): (Answer | undefined)[] => + new DistributeTypeDisjunction().perform(elements).map((elements) => { + const typeEqualityGraph = TypeEqualityGraph.build( + ...elements.typeConstraints.map( + (typeConstraint) => typeConstraint.typeEqualityGraph, + ), + ) + if (typeEqualityGraph === undefined) return + const type = this.inferType(elements, typeEqualityGraph) + if (type === undefined) return + + return new Answer( + this._factory(elements.nodes), + new TypeConstraint(type, typeEqualityGraph), + ) + }) + + private inferType = ( + elements: AccumulatedAnswer, + typeEqualityGraph: TypeEqualityGraph, + ): Type | undefined => + elements.answers.reduce((type: Type | undefined, answer) => { + if (type === undefined) return + + return type.unify(answer.typeConstraint.type, typeEqualityGraph) }, new ParametricType(MAP_TYPE, [new TypeVariable(), new TypeVariable()])) } diff --git a/.yalc/tony-lang/src/type_inference/services/InferPatternBindingTypes.ts b/.yalc/tony-lang/src/type_inference/services/InferPatternBindingTypes.ts index e667c6c..3393987 100644 --- a/.yalc/tony-lang/src/type_inference/services/InferPatternBindingTypes.ts +++ b/.yalc/tony-lang/src/type_inference/services/InferPatternBindingTypes.ts @@ -1,3 +1,5 @@ +import * as AST from '../../ast' +import { Answer, Disjunction } from '../models' import { BOOLEAN_TYPE, BuildType, @@ -9,274 +11,311 @@ import { STRING_TYPE, TUPLE_TYPE, Type, - TypeConstraints, + TypeConstraint, TypeVariable, } from '../../types' -import { CompileError, InternalError, assert } from '../../errors' -import { IdentifierBinding, NestedScope } from '../../symbol_table' +import { CompileError, InternalError, TypeError } from '../../errors' +import { InferIdentifierPatternType } from './InferIdentifierPatternType' +import { InferListType } from './InferListType' import { InferMapType } from './InferMapType' +import { InferPatternPairType } from './InferPatternPairType' +import { InferTupleType } from './InferTupleType' import { InferTypes } from '../InferTypes' +import { NestedScope } from '../../symbol_table' import Parser from 'tree-sitter' export class InferPatternBindingTypes { private _inferTypes: InferTypes private _scope: NestedScope - private _typeConstraints: TypeConstraints - constructor( - inferTypes: InferTypes, - scope: NestedScope, - typeConstraints: TypeConstraints, - ) { + constructor(inferTypes: InferTypes, scope: NestedScope) { this._inferTypes = inferTypes this._scope = scope - this._typeConstraints = typeConstraints } perform = ( patternNode: Parser.SyntaxNode, - type: Type = new TypeVariable(), - ): Type => { - const patternType = this.traverse(patternNode, type) - - assert(patternType !== undefined, 'Should not be undefined.') - - return patternType - } + typeConstraint: TypeConstraint = new TypeConstraint( + new TypeVariable(), + ), + ): Disjunction | undefined => + TypeError.safe(() => + CompileError.addContext(this.traverse, patternNode, typeConstraint), + ) // eslint-disable-next-line max-lines-per-function private traverse = ( patternNode: Parser.SyntaxNode, - type: Type, - ): Type | undefined => { - try { - switch (patternNode.type) { - case 'boolean': - return this.handleBoolean(patternNode, type) - case 'comment': - return - case 'identifier_pattern': - return this.handleIdentifierPattern(patternNode, type) - case 'list_pattern': - return this.handleListPattern(patternNode, type) - case 'map_pattern': - return this.handleMapPattern(patternNode, type) - case 'number': - return this.handleNumber(patternNode, type) - case 'pattern_pair': - return this.handlePatternPair(patternNode, type) - case 'regex': - return this.handleRegex(patternNode, type) - case 'rest_list': - return this.handleRestList(patternNode, type) - case 'rest_map': - return this.handleRestMap(patternNode, type) - case 'rest_tuple': - return this.handleRestTuple(patternNode, type) - case 'shorthand_pair_identifier_pattern': - return this.handleShorthandPairIdentifierPattern(patternNode, type) - case 'string_pattern': - return this.handleStringPattern(patternNode, type) - case 'parameters': - case 'tuple_pattern': - return this.handleTuplePattern(patternNode, type) - case 'type': - return this.handleType(patternNode, type) - default: - throw new InternalError( - 'InferPatternBindingTypes: Could not find matcher for AST pattern ' + - `node '${patternNode.type}'.`, - ) - } - } catch (error) { - if (error instanceof CompileError && error.context === undefined) - error.addContext(patternNode) - throw error + typeConstraint: TypeConstraint, + ): Disjunction => { + switch (patternNode.type) { + case 'boolean': + return this.handleBoolean(patternNode, typeConstraint) + case 'identifier_pattern': + return this.handleIdentifierPattern(patternNode, typeConstraint) + case 'list_pattern': + return this.handleListPattern(patternNode, typeConstraint) + case 'map_pattern': + return this.handleMapPattern(patternNode, typeConstraint) + case 'number': + return this.handleNumber(patternNode, typeConstraint) + case 'pattern_pair': + return this.handlePatternPair(patternNode, typeConstraint) + case 'regex': + return this.handleRegex(patternNode, typeConstraint) + case 'rest_list': + return this.handleRestList(patternNode, typeConstraint) + case 'rest_map': + return this.handleRestMap(patternNode, typeConstraint) + case 'rest_tuple': + return this.handleRestTuple(patternNode, typeConstraint) + case 'shorthand_pair_identifier_pattern': + return this.handleShorthandPairIdentifierPattern( + patternNode, + typeConstraint, + ) + case 'string_pattern': + return this.handleStringPattern(patternNode, typeConstraint) + case 'parameters': + case 'tuple_pattern': + return this.handleTuplePattern(patternNode, typeConstraint) + case 'type': + return this.handleType(patternNode, typeConstraint) + default: + throw new InternalError( + 'Could not find matcher for AST pattern ' + + `node '${patternNode.type}'.`, + ) } } private handleBoolean = ( patternNode: Parser.SyntaxNode, - type: Type, - ): ParametricType => - new ParametricType(BOOLEAN_TYPE).unify(type, this._typeConstraints) + typeConstraint: TypeConstraint, + ): Disjunction => { + const unifiedTypeConstraint = new TypeConstraint( + new ParametricType(BOOLEAN_TYPE), + ).unify(typeConstraint) + if (unifiedTypeConstraint === undefined) return new Disjunction([]) + + return new Disjunction([ + new Answer(new AST.Boolean(patternNode.text), unifiedTypeConstraint), + ]) + } - // prettier-ignore private handleIdentifierPattern = ( patternNode: Parser.SyntaxNode, - type: Type, - ): Type => { - const name = patternNode.namedChild(0)!.text - // eslint-disable-next-line @typescript-eslint/ban-ts-ignore - // @ts-ignore - const typeHint = patternNode.typeNode ? new BuildType().perform(patternNode.typeNode) : new TypeVariable() - // eslint-disable-next-line @typescript-eslint/ban-ts-ignore - // @ts-ignore - const defaultType = patternNode.defaultNode ? this._inferTypes.traverse(patternNode.defaultNode)! : new TypeVariable() - const binding = this._scope.resolveBinding(name, 0) - - assert( - binding instanceof IdentifierBinding, - 'Pattern identifier binding should be found in current scope.', - ) - - type = typeHint.unify(type, this._typeConstraints) - type = defaultType.unify(type, this._typeConstraints) - binding.type = binding.type.unify(type, this._typeConstraints) - - return binding.type - } + typeConstraint: TypeConstraint, + ): Disjunction => + new InferIdentifierPatternType( + this._inferTypes, + this._scope, + (name, transformedName, def) => + new AST.IdentifierPattern(name, transformedName, def), + ).perform(patternNode, typeConstraint) private handleListPattern = ( patternNode: Parser.SyntaxNode, - type: Type, - ): ParametricType => { - const unifiedType = new ParametricType(LIST_TYPE, [ + typeConstraint: TypeConstraint, + ): Disjunction => { + const listType = new ParametricType(LIST_TYPE, [ new TypeVariable(), - ]).unify(type, this._typeConstraints) - const valueTypes = patternNode.namedChildren.map((child) => - this.perform(child, unifiedType.parameters[0]), + ]).unsafeUnify(typeConstraint.type, typeConstraint.typeEqualityGraph) + const elements = patternNode.namedChildren.map((elementNode) => + CompileError.addContext( + this.traverse, + elementNode, + new TypeConstraint( + listType.parameters[0], + typeConstraint.typeEqualityGraph, + ), + ), ) - return valueTypes.reduce((valueType: ParametricType, otherValueType) => { - return valueType.unify( - new ParametricType(LIST_TYPE, [otherValueType]), - this._typeConstraints, - ) - }, new ParametricType(LIST_TYPE, [new TypeVariable()])) + return new InferListType( + (elements) => new AST.ListPattern(elements as AST.Pattern[]), + ).perform(elements) } private handleMapPattern = ( patternNode: Parser.SyntaxNode, - type: Type, - ): ParametricType => { - const mapTypes = patternNode.namedChildren.map((child) => - this.perform(child, type), + typeConstraint: TypeConstraint, + ): Disjunction => { + const elements = patternNode.namedChildren.map((elementNode) => + CompileError.addContext(this.traverse, elementNode, typeConstraint), ) - return new InferMapType(this._typeConstraints).perform(mapTypes) + return new InferMapType( + (elements) => new AST.MapPattern(elements as AST.MapPatternElement[]), + ).perform(elements) } private handleNumber = ( patternNode: Parser.SyntaxNode, - type: Type, - ): ParametricType => - new ParametricType(NUMBER_TYPE).unify(type, this._typeConstraints) + typeConstraint: TypeConstraint, + ): Disjunction => { + const unifiedTypeConstraint = new TypeConstraint( + new ParametricType(NUMBER_TYPE), + ).unify(typeConstraint) + if (unifiedTypeConstraint === undefined) return new Disjunction([]) + + return new Disjunction([ + new Answer(new AST.Number(patternNode.text), unifiedTypeConstraint), + ]) + } private handlePatternPair = ( patternNode: Parser.SyntaxNode, - type: Type, - ): Type => { - const keyType = this._inferTypes.traverse(patternNode.namedChild(0)!)! - const unifiedType = new ParametricType(MAP_TYPE, [ - keyType, - new TypeVariable(), - ]).unify(type, this._typeConstraints) - const valueType = this.perform( + typeConstraint: TypeConstraint, + ): Disjunction => { + const key = this._inferTypes.traverse(patternNode.namedChild(0)!) + const value = CompileError.addContext( + this.traverse, patternNode.namedChild(1)!, - unifiedType.parameters[1], + new TypeConstraint(new TypeVariable(), typeConstraint.typeEqualityGraph), ) - return new ParametricType(MAP_TYPE, [keyType, valueType]) + return new InferPatternPairType().perform(key, value, typeConstraint) } private handleRegex = ( patternNode: Parser.SyntaxNode, - type: Type, - ): ParametricType => - new ParametricType(REGULAR_EXPRESSION_TYPE).unify( - type, - this._typeConstraints, - ) + typeConstraint: TypeConstraint, + ): Disjunction => { + const unifiedTypeConstraint = new TypeConstraint( + new ParametricType(REGULAR_EXPRESSION_TYPE), + ).unify(typeConstraint) + if (unifiedTypeConstraint === undefined) return new Disjunction([]) + + return new Disjunction([ + new Answer(new AST.Regex(patternNode.text), unifiedTypeConstraint), + ]) + } private handleRestList = ( patternNode: Parser.SyntaxNode, - type: Type, - ): Type => { - const valueType = this.perform( + typeConstraint: TypeConstraint, + ): Disjunction => { + const value = CompileError.addContext( + this.handleIdentifierPattern, patternNode.namedChild(0)!, - new ParametricType(LIST_TYPE, [type]), + new TypeConstraint( + new ParametricType(LIST_TYPE, [typeConstraint.type]), + typeConstraint.typeEqualityGraph, + ), ) - assert(valueType instanceof ParametricType, 'Should be parametric type.') - - return valueType.parameters[0] + return new Disjunction( + value.answers.map( + (answer) => + new Answer(new AST.RestList(answer.node), answer.typeConstraint), + ), + ) } - private handleRestMap = (patternNode: Parser.SyntaxNode, type: Type): Type => - this.perform(patternNode.namedChild(0)!, type) - - private handleRestTuple = ( + private handleRestMap = ( patternNode: Parser.SyntaxNode, - type: Type, - ): ParametricType => { - const valueType = this.perform(patternNode.namedChild(0)!, type) - - assert(valueType instanceof ParametricType, 'Should be parametric type.') + typeConstraint: TypeConstraint, + ): Disjunction => { + const value = CompileError.addContext( + this.handleIdentifierPattern, + patternNode.namedChild(0)!, + typeConstraint, + ) - return valueType + return new Disjunction( + value.answers.map( + (answer) => + new Answer(new AST.RestMap(answer.node), answer.typeConstraint), + ), + ) } - // prettier-ignore - private handleShorthandPairIdentifierPattern = ( + private handleRestTuple = ( patternNode: Parser.SyntaxNode, - type: Type, - ): ParametricType => { - // eslint-disable-next-line @typescript-eslint/ban-ts-ignore - // @ts-ignore - const typeHint = patternNode.typeNode ? new BuildType().perform(patternNode.typeNode) : new TypeVariable() - // eslint-disable-next-line @typescript-eslint/ban-ts-ignore - // @ts-ignore - const defaultType = patternNode.defaultNode ? this._inferTypes.traverse(patternNode.defaultNode)! : new TypeVariable() - const valueType = typeHint.unify(defaultType, this._typeConstraints) - const keyType = new ParametricType(STRING_TYPE) + typeConstraint: TypeConstraint, + ): Disjunction => { + const value = CompileError.addContext( + this.handleIdentifierPattern, + patternNode.namedChild(0)!, + typeConstraint, + ) - return new ParametricType(MAP_TYPE, [keyType, valueType]).unify( - type, - this._typeConstraints, + return new Disjunction( + value.answers.map( + (answer) => + new Answer(new AST.RestTuple(answer.node), answer.typeConstraint), + ), ) } + private handleShorthandPairIdentifierPattern = ( + patternNode: Parser.SyntaxNode, + typeConstraint: TypeConstraint, + ): Disjunction => + new InferIdentifierPatternType( + this._inferTypes, + this._scope, + (name, transformedName, def) => + new AST.ShorthandPairIdentifierPattern(name, transformedName, def), + (type) => + new ParametricType(MAP_TYPE, [new ParametricType(STRING_TYPE), type]), + (typeConstraint) => + new TypeConstraint( + (typeConstraint.type as ParametricType).parameters[1], + typeConstraint.typeEqualityGraph, + ), + ).perform(patternNode, typeConstraint) + private handleStringPattern = ( patternNode: Parser.SyntaxNode, - type: Type, - ): ParametricType => - new ParametricType(STRING_TYPE).unify(type, this._typeConstraints) + typeConstraint: TypeConstraint, + ): Disjunction => { + const content = patternNode.text.slice(1, -1) + const unifiedTypeConstraint = new TypeConstraint( + new ParametricType(STRING_TYPE), + ).unify(typeConstraint) + if (unifiedTypeConstraint === undefined) return new Disjunction([]) + + return new Disjunction([ + new Answer(new AST.StringPattern(content), unifiedTypeConstraint), + ]) + } private handleTuplePattern = ( patternNode: Parser.SyntaxNode, - type: Type, - ): ParametricType => { - const tupleType = new ParametricType(TUPLE_TYPE, []).unify( - type, - this._typeConstraints, - true, - ) - const parameters = patternNode.namedChildren.reduce( - this.handleTuplePatternValues(tupleType), - [], + typeConstraint: TypeConstraint, + ): Disjunction => { + const tupleType = new ParametricType( + TUPLE_TYPE, + [...Array(patternNode.namedChildCount)].map(() => new TypeVariable()), + ).unsafeUnify(typeConstraint.type, typeConstraint.typeEqualityGraph) + const elements = patternNode.namedChildren.map((elementNode, i) => + CompileError.addContext( + this.traverse, + elementNode, + new TypeConstraint( + tupleType.parameters[i], + typeConstraint.typeEqualityGraph, + ), + ), ) - return new ParametricType(TUPLE_TYPE, parameters) + return new InferTupleType( + (elements) => new AST.TuplePattern(elements as AST.Pattern[]), + ).perform(elements) } - private handleTuplePatternValues = (type: ParametricType) => ( - parameters: Type[], + private handleType = ( patternNode: Parser.SyntaxNode, - i: number, - ): Type[] => { - if (patternNode.type === 'rest_tuple') - return [ - ...parameters, - ...this.handleRestTuple( - patternNode, - new ParametricType(TUPLE_TYPE, type.parameters.slice(i)), - ).parameters, - ] - - return [...parameters, this.perform(patternNode, type.parameters[i])] + typeConstraint: TypeConstraint, + ): Disjunction => { + const type = new BuildType().perform(patternNode) + const unifiedTypeConstraint = new TypeConstraint(type).unify(typeConstraint) + if (unifiedTypeConstraint === undefined) return new Disjunction([]) + + return new Disjunction([ + new Answer(new AST.ParametricType(type.name), unifiedTypeConstraint), + ]) } - - private handleType = (patternNode: Parser.SyntaxNode, type: Type): Type => - new BuildType().handleType(patternNode).unify(type, this._typeConstraints) } diff --git a/.yalc/tony-lang/src/type_inference/services/InferPatternPairType.ts b/.yalc/tony-lang/src/type_inference/services/InferPatternPairType.ts new file mode 100644 index 0000000..d106ea0 --- /dev/null +++ b/.yalc/tony-lang/src/type_inference/services/InferPatternPairType.ts @@ -0,0 +1,61 @@ +import * as AST from '../../ast' +import { Answer, Disjunction } from '../models' +import { + MAP_TYPE, + ParametricType, + Type, + TypeConstraint, + TypeEqualityGraph, +} from '../../types' + +export class InferPatternPairType { + perform = ( + key: Disjunction, + value: Disjunction, + typeConstraint: TypeConstraint, + ): Disjunction => + new Disjunction( + key.answers + .map((keyAnswer) => + value.answers.map((valueAnswer) => + this.buildAnswer(keyAnswer, valueAnswer, typeConstraint), + ), + ) + .flat(1), + ) + + private buildAnswer = ( + key: Answer, + value: Answer, + typeConstraint: TypeConstraint, + ): Answer | undefined => { + const unifiedTypeConstraint = this.unifyTypeConstraints( + key.typeConstraint, + value.typeConstraint, + typeConstraint, + ) + if (unifiedTypeConstraint === undefined) return + + return new Answer( + new AST.PatternPair(key.node, value.node), + unifiedTypeConstraint, + ) + } + + private unifyTypeConstraints = ( + key: TypeConstraint, + value: TypeConstraint, + typeConstraint: TypeConstraint, + ): TypeConstraint | undefined => { + const typeEqualityGraph = TypeEqualityGraph.build( + key.typeEqualityGraph, + value.typeEqualityGraph, + ) + if (typeEqualityGraph === undefined) return + + return new TypeConstraint( + new ParametricType(MAP_TYPE, [key.type, value.type]), + typeEqualityGraph, + ).unify(typeConstraint) + } +} diff --git a/.yalc/tony-lang/src/type_inference/services/InferStringType.ts b/.yalc/tony-lang/src/type_inference/services/InferStringType.ts new file mode 100644 index 0000000..063fc74 --- /dev/null +++ b/.yalc/tony-lang/src/type_inference/services/InferStringType.ts @@ -0,0 +1,53 @@ +import * as AST from '../../ast' +import { Answer, Disjunction } from '../models' +import { ParametricType, STRING_TYPE, TypeConstraint } from '../../types' + +export class InferStringType { + perform = ( + content: string, + interpolationValues: Disjunction[], + ): Disjunction => { + const interpolations = this.handleInterpolations(interpolationValues) + const string = new AST.String( + content, + interpolations.map((answer) => answer.node), + ) + const typeConstraint = interpolations.reduce( + (typeConstraint: TypeConstraint, answer) => + typeConstraint.unify(answer.typeConstraint) as TypeConstraint< + ParametricType + >, + new TypeConstraint(new ParametricType(STRING_TYPE)), + ) + + return new Disjunction([new Answer(string, typeConstraint)]) + } + + private handleInterpolations = ( + values: Disjunction[], + ): Answer[] => + values.reduce( + (interpolations: Answer[], value) => [ + ...interpolations, + this.handleInterpolation(value).answers[0], + ], + [], + ) + + private handleInterpolation = ( + value: Disjunction, + ): Disjunction => + new Disjunction( + value.answers.reduce((answers: Answer[], answer) => { + const typeConstraint = new TypeConstraint( + new ParametricType(STRING_TYPE), + ).unify(answer.typeConstraint) + if (typeConstraint === undefined) return answers + + return [ + ...answers, + new Answer(new AST.Interpolation(answer.node), typeConstraint), + ] + }, []), + ) +} diff --git a/.yalc/tony-lang/src/type_inference/services/InferTupleType.ts b/.yalc/tony-lang/src/type_inference/services/InferTupleType.ts index c4cffc8..32c3abc 100644 --- a/.yalc/tony-lang/src/type_inference/services/InferTupleType.ts +++ b/.yalc/tony-lang/src/type_inference/services/InferTupleType.ts @@ -1,33 +1,76 @@ -import { ParametricType, TUPLE_TYPE, Type, TypeConstraints } from '../../types' -import { InferTypes } from '../InferTypes' -import Parser from 'tree-sitter' +import * as AST from '../../ast' +import { AccumulatedAnswer, Answer, Disjunction } from '../models' +import { + ParametricType, + TUPLE_TYPE, + Type, + TypeConstraint, + TypeEqualityGraph, +} from '../../types' +import { DistributeTypeDisjunction } from './DistributeTypeDisjunction' -export class InferTupleType { - private _inferTypes: InferTypes - private _typeConstraints: TypeConstraints +type Factory = (elements: AST.SyntaxNode[]) => T - constructor(inferTypes: InferTypes, typeConstraints: TypeConstraints) { - this._inferTypes = inferTypes - this._typeConstraints = typeConstraints +export class InferTupleType { + private _factory: Factory + + constructor(factory: Factory) { + this._factory = factory } - perform = (valueNodes: Parser.SyntaxNode[]): ParametricType => { - const valueTypes = valueNodes.reduce((valueTypes: Type[], valueNode) => { - const valueType = this._inferTypes.traverse(valueNode)! + perform = (elements: Disjunction[]): Disjunction => + new Disjunction( + new DistributeTypeDisjunction().perform(elements).map((elements) => { + const typeEqualityGraph = TypeEqualityGraph.build( + ...elements.typeConstraints.map( + (typeConstraint) => typeConstraint.typeEqualityGraph, + ), + ) + if (typeEqualityGraph === undefined) return + const type = this.inferType(elements, typeEqualityGraph) + if (type === undefined) return + + return new Answer( + this._factory(elements.nodes), + new TypeConstraint(type, typeEqualityGraph), + ) + }), + ) - if (valueNode.type === 'spread_tuple') { - const restType = new ParametricType(TUPLE_TYPE).unify( - valueType, - this._typeConstraints, - true, + private inferType = ( + elements: AccumulatedAnswer, + typeEqualityGraph: TypeEqualityGraph, + ): ParametricType | undefined => { + const parameterTypes = elements.answers.reduce( + (parameters: Type[] | undefined, answer) => { + if (parameters === undefined) return + if ( + answer.node instanceof AST.RestTuple || + answer.node instanceof AST.SpreadTuple ) + return this.handleSpread(parameters, answer, typeEqualityGraph) + + return [...parameters, answer.typeConstraint.type] + }, + [], + ) + if (parameterTypes === undefined) return - return [...valueTypes, ...restType.parameters] - } + return new ParametricType(TUPLE_TYPE, parameterTypes) + } - return [...valueTypes, valueType] - }, []) + private handleSpread = ( + parameters: Type[], + answer: Answer, + typeEqualityGraph: TypeEqualityGraph, + ): Type[] | undefined => { + const spreadType = new ParametricType(TUPLE_TYPE, []).unify( + answer.typeConstraint.type, + typeEqualityGraph, + true, + ) + if (!(spreadType instanceof ParametricType)) return - return new ParametricType(TUPLE_TYPE, valueTypes) + return [...parameters, ...spreadType.parameters] } } diff --git a/.yalc/tony-lang/src/type_inference/services/MergeAccumulatedTypeDisjunction.ts b/.yalc/tony-lang/src/type_inference/services/MergeAccumulatedTypeDisjunction.ts new file mode 100644 index 0000000..b201165 --- /dev/null +++ b/.yalc/tony-lang/src/type_inference/services/MergeAccumulatedTypeDisjunction.ts @@ -0,0 +1,48 @@ +import * as AST from '../../ast' +import { + AccumulatedAnswer, + AccumulatedDisjunction, + Answer, + Disjunction, +} from '../models' + +type Factory = ( + fst: AccumulatedAnswer, + snd: Answer, +) => Disjunction | Answer | undefined + +export class MergeAccumulatedTypeDisjunction< + T extends AST.SyntaxNode, + U extends AST.SyntaxNode, + V extends AST.SyntaxNode +> { + private _factory: Factory + + constructor(factory: Factory) { + this._factory = factory + } + + perform = ( + fstDisj: AccumulatedDisjunction, + sndDisj: Disjunction, + ): Disjunction => + new Disjunction( + fstDisj.reduce( + (answers: (Answer | undefined)[], fst) => [ + ...answers, + ...sndDisj.answers.reduce(this.merge(fst), []), + ], + [], + ), + ) + + private merge = (fst: AccumulatedAnswer) => ( + answers: (Answer | undefined)[], + snd: Answer, + ): (Answer | undefined)[] => { + const merged = this._factory(fst, snd) + + if (merged instanceof Disjunction) return [...answers, ...merged.answers] + else return [...answers, merged] + } +} diff --git a/.yalc/tony-lang/src/type_inference/services/MergeTypeDisjunction.ts b/.yalc/tony-lang/src/type_inference/services/MergeTypeDisjunction.ts new file mode 100644 index 0000000..d269923 --- /dev/null +++ b/.yalc/tony-lang/src/type_inference/services/MergeTypeDisjunction.ts @@ -0,0 +1,43 @@ +import * as AST from '../../ast' +import { Answer, Disjunction } from '../models' + +type Factory = ( + fst: Answer, + snd: Answer, +) => Disjunction | Answer | undefined + +export class MergeTypeDisjunction< + T extends AST.SyntaxNode, + U extends AST.SyntaxNode, + V extends AST.SyntaxNode +> { + private _factory: Factory + + constructor(factory: Factory) { + this._factory = factory + } + + perform = ( + fstDisj: Disjunction, + sndDisj: Disjunction, + ): Disjunction => + new Disjunction( + fstDisj.answers.reduce( + (answers: (Answer | undefined)[], fst) => [ + ...answers, + ...sndDisj.answers.reduce(this.merge(fst), []), + ], + [], + ), + ) + + private merge = (fst: Answer) => ( + answers: (Answer | undefined)[], + snd: Answer, + ): (Answer | undefined)[] => { + const merged = this._factory(fst, snd) + + if (merged instanceof Disjunction) return [...answers, ...merged.answers] + else return [...answers, merged] + } +} diff --git a/.yalc/tony-lang/src/type_inference/services/index.ts b/.yalc/tony-lang/src/type_inference/services/index.ts index f4ef2be..9cc0c84 100644 --- a/.yalc/tony-lang/src/type_inference/services/index.ts +++ b/.yalc/tony-lang/src/type_inference/services/index.ts @@ -1,7 +1,21 @@ +export { AccumulateTypeDisjunction } from './AccumulateTypeDisjunction' +export { DistributeTypeDisjunction } from './DistributeTypeDisjunction' +export { GetModuleTypeRepresentation } from './GetModuleTypeRepresentation' +export { InferAbstractionBranchType } from './InferAbstractionBranchType' export { InferAccessType } from './InferAccessType' export { InferApplicationType } from './InferApplicationType' +export { InferAssignmentType } from './InferAssignmentType' +export { InferBlockType } from './InferBlockType' export { InferBranchType } from './InferBranchType' +export { InferExpressionPairType } from './InferExpressionPairType' +export { InferGeneratorType } from './InferGeneratorType' +export { InferIdentifierPatternType } from './InferIdentifierPatternType' +export { InferImportBindingTypes } from './InferImportBindingTypes' export { InferListType } from './InferListType' export { InferMapType } from './InferMapType' export { InferPatternBindingTypes } from './InferPatternBindingTypes' +export { InferPatternPairType } from './InferPatternPairType' +export { InferStringType } from './InferStringType' export { InferTupleType } from './InferTupleType' +export { MergeAccumulatedTypeDisjunction } from './MergeAccumulatedTypeDisjunction' +export { MergeTypeDisjunction } from './MergeTypeDisjunction' diff --git a/.yalc/tony-lang/src/types/index.ts b/.yalc/tony-lang/src/types/index.ts index 7273d5f..54c9b97 100644 --- a/.yalc/tony-lang/src/types/index.ts +++ b/.yalc/tony-lang/src/types/index.ts @@ -1,17 +1,14 @@ export { - CurriedType, - IdentifierProperty, + ModuleType, ParametricType, - RepresentationKind, Type, - TypeConstraints, - TypeProperty, + TypeConstraint, + TypeEqualityGraph, TypeVariable, } from './models' -export { BuildRepresentation, BuildType } from './services' - -export const INTERNAL_PARTIAL_APPLICATION_TYPE_NAME = Object.freeze('?') +export { BuildType } from './services' +export const FUNCTION_TYPE = Object.freeze('->') export const VOID_TYPE = Object.freeze('Void') export const BOOLEAN_TYPE = Object.freeze('Boolean') export const NUMBER_TYPE = Object.freeze('Number') @@ -22,6 +19,7 @@ export const MAP_TYPE = Object.freeze('Map') export const TUPLE_TYPE = Object.freeze('Tuple') export const BASIC_TYPES = Object.freeze([ + FUNCTION_TYPE, VOID_TYPE, BOOLEAN_TYPE, NUMBER_TYPE, diff --git a/.yalc/tony-lang/src/types/models/CurriedType.ts b/.yalc/tony-lang/src/types/models/CurriedType.ts deleted file mode 100644 index 9d6bc09..0000000 --- a/.yalc/tony-lang/src/types/models/CurriedType.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { TypeError, assert } from '../../errors' -import { Type } from './Type' -import { TypeConstraints } from './TypeConstraints' -import { TypeVariable } from './TypeVariable' - -export class CurriedType extends Type { - private _parameters: Type[] - - constructor(parameters: Type[]) { - super() - - this._parameters = parameters - } - - get parameters(): Type[] { - return this._parameters - } - - concat = (type: Type): CurriedType => - new CurriedType(this.parameters.concat(type)) - - unify = (actual: Type, constraints: TypeConstraints): CurriedType => - this._unify(actual, constraints)._reduce(constraints) - - _unify = (actual: Type, constraints: TypeConstraints): CurriedType => { - if (actual instanceof TypeVariable) { - constraints.add(actual, this) - return this - } else if ( - actual instanceof CurriedType && - this.parameters.length == actual.parameters.length - ) { - return new CurriedType(this.unifyParameters(actual, constraints)) - } - - throw new TypeError(this, actual, 'Non-variable types do not match') - } - - private unifyParameters = ( - actual: CurriedType, - constraints: TypeConstraints, - ): Type[] => - this.parameters.map((parameter, i) => { - try { - return parameter._unify(actual.parameters[i], constraints) - } catch (error) { - assert(error instanceof TypeError, 'Should be TypeError.') - - error.addTypeMismatch(this, actual) - throw error - } - }) - - _reduce = (constraints: TypeConstraints): CurriedType => { - const parameters = this.parameters.map((parameter) => - parameter._reduce(constraints), - ) - - return new CurriedType(parameters) - } - - toString = (): string => { - const parameters = this.parameters - .map((parameter) => parameter.toString()) - .join(' -> ') - - return `(${parameters})` - } -} diff --git a/.yalc/tony-lang/src/types/models/IdentifierProperty.ts b/.yalc/tony-lang/src/types/models/IdentifierProperty.ts deleted file mode 100644 index 8474b0e..0000000 --- a/.yalc/tony-lang/src/types/models/IdentifierProperty.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Property } from './Property' -import { Type } from './Type' - -export class IdentifierProperty implements Property { - private _name: string - protected _type: Type - - constructor(name: string, type: Type) { - this._name = name - this._type = type - } - - get name(): string { - return this._name - } - - get type(): Type { - return this._type - } - - set type(value: Type) { - this._type = value - } - - toString = (): string => { - if (this.type === undefined) return this.name - - const type = this.type.toString() - return `${this.name}: ${type}` - } -} diff --git a/.yalc/tony-lang/src/types/models/ModuleType.ts b/.yalc/tony-lang/src/types/models/ModuleType.ts new file mode 100644 index 0000000..ed0edef --- /dev/null +++ b/.yalc/tony-lang/src/types/models/ModuleType.ts @@ -0,0 +1,44 @@ +import { Binding, BindingTemplate, ModuleScope } from '../../symbol_table' +import { InternalError } from '../../errors' +import { Type } from './Type' + +export class ModuleType extends Type { + private _scope: ModuleScope + + constructor(scope: ModuleScope) { + super() + + this._scope = scope + } + + get scope(): ModuleScope { + return this._scope + } + + get properties(): Binding[] { + return this.scope.bindings.filter((binding) => binding.isExported) + } + + resolveProperty = (name: string): (Binding | BindingTemplate)[] => + this.scope.resolveBindings(name, 0).filter((binding) => binding.isExported) + + unsafeUnify = (): Type => { + throw new InternalError('Module types may not be unified.') + } + + reduce = (): Type => { + throw new InternalError('Module types may not be reduced.') + } + + equals = (type: Type): boolean => this === type + + toString = (): string => { + if (this.properties.length == 0) return '{}' + + const properties = this.properties + .map((binding) => binding.toString()) + .join(', ') + + return `{ ${properties} }` + } +} diff --git a/.yalc/tony-lang/src/types/models/ParametricType.ts b/.yalc/tony-lang/src/types/models/ParametricType.ts index 5215f08..eb11d1f 100644 --- a/.yalc/tony-lang/src/types/models/ParametricType.ts +++ b/.yalc/tony-lang/src/types/models/ParametricType.ts @@ -1,7 +1,6 @@ -import { TypeError, assert } from '../../errors' -import { CurriedType } from './CurriedType' +import { InternalTypeError } from '../../errors' import { Type } from './Type' -import { TypeConstraints } from './TypeConstraints' +import { TypeEqualityGraph } from './TypeEqualityGraph' import { TypeVariable } from './TypeVariable' export class ParametricType extends Type { @@ -23,73 +22,67 @@ export class ParametricType extends Type { return this._parameters } - concat = (type: Type): CurriedType => { - if (type instanceof CurriedType) return type.concat(this) + concat = (type: Type): ParametricType => + new ParametricType(this.name, [...this.parameters, type]) - return new CurriedType([this, type]) - } - - unify = ( - actual: Type, - constraints: TypeConstraints, - useActualParameters = false, - ): ParametricType => - this._unify(actual, constraints, useActualParameters)._reduce(constraints) - - _unify = ( + // eslint-disable-next-line max-lines-per-function + unsafeUnify = ( actual: Type, - constraints: TypeConstraints, - useActualParameters = false, + typeEqualityGraph?: TypeEqualityGraph, + ignoreExpectedParameters = false, ): ParametricType => { if (actual instanceof TypeVariable) { - constraints.add(actual, this) + if (typeEqualityGraph) typeEqualityGraph.add(actual, this) + return this } else if ( actual instanceof ParametricType && this.name === actual.name && - (useActualParameters || + (ignoreExpectedParameters || this.parameters.length == actual.parameters.length) - ) - return this.buildUnifiedType(actual, constraints, useActualParameters) - - throw new TypeError(this, actual, 'Non-variable types do not match') - } - - private buildUnifiedType = ( - actual: ParametricType, - constraints: TypeConstraints, - useActualParameters = false, - ): ParametricType => - new ParametricType( - this.name, - useActualParameters + ) { + const parameters = ignoreExpectedParameters ? actual.parameters - : this.unifyParameters(actual, constraints), + : this.parameters.map((parameter, i) => { + try { + return parameter.unsafeUnify( + actual.parameters[i], + typeEqualityGraph, + ) + } catch (error) { + if (error instanceof InternalTypeError) + error.addTypeMismatch(this, actual) + throw error + } + }) + + return new ParametricType(this.name, parameters) + } + + throw new InternalTypeError( + this, + actual, + 'Non-variable types do not match.', ) + } - private unifyParameters = ( - actual: ParametricType, - constraints: TypeConstraints, - ): Type[] => - this.parameters.map((parameter, i) => { - try { - return parameter._unify(actual.parameters[i], constraints) - } catch (error) { - assert(error instanceof TypeError, 'Should be TypeError.') - - error.addTypeMismatch(this, actual) - throw error - } - }) - - _reduce = (constraints: TypeConstraints): ParametricType => { + reduce = (typeEqualityGraph: TypeEqualityGraph): Type => { const parameters = this.parameters.map((parameter) => - parameter._reduce(constraints), + parameter.reduce(typeEqualityGraph), ) return new ParametricType(this.name, parameters) } + equals = (type: Type): boolean => { + if (!(type instanceof ParametricType)) return false + if (this.parameters.length != type.parameters.length) return false + + return this.name === type.name && this.parameters.every((parameter, i) => + parameter.equals(type.parameters[i]), + ) + } + toString = (): string => { const parameters = this.parameters.map((parameter) => parameter.toString()) const combinedParameters = diff --git a/.yalc/tony-lang/src/types/models/Property.ts b/.yalc/tony-lang/src/types/models/Property.ts deleted file mode 100644 index 502bc6d..0000000 --- a/.yalc/tony-lang/src/types/models/Property.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Type } from './Type' - -export interface Property { - name: string - type: Type - - toString(): string -} diff --git a/.yalc/tony-lang/src/types/models/Representation.ts b/.yalc/tony-lang/src/types/models/Representation.ts deleted file mode 100644 index bc1c96b..0000000 --- a/.yalc/tony-lang/src/types/models/Representation.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Property } from './Property' - -export enum RepresentationKind { - Instance, - Interface, - Unknown, -} - -export class Representation { - private _kind: RepresentationKind - private _properties: Property[] - - constructor(kind: RepresentationKind, properties: Property[]) { - this._kind = kind - this._properties = properties - } - - get properties(): Property[] { - return this._properties - } - - findProperty = (name: string): Property | undefined => - this.properties.find((property) => property.name === name) - - toString = (): string => { - if (this.properties.length == 0) return '{}' - - const properties = this.properties - .map((property) => property.toString()) - .join(', ') - - return `{ ${properties} }` - } -} diff --git a/.yalc/tony-lang/src/types/models/Type.ts b/.yalc/tony-lang/src/types/models/Type.ts index 542f110..35db0c1 100644 --- a/.yalc/tony-lang/src/types/models/Type.ts +++ b/.yalc/tony-lang/src/types/models/Type.ts @@ -1,12 +1,25 @@ -import { CurriedType } from './CurriedType' -import { TypeConstraints } from './TypeConstraints' +import { TypeEqualityGraph } from './TypeEqualityGraph' +import { TypeError } from '../../errors' export abstract class Type { - abstract concat: (type: Type) => CurriedType + abstract unsafeUnify: ( + actual: Type, + typeEqualityGraph?: TypeEqualityGraph, + ignoreExpectedParameters?: boolean, + ) => Type - abstract unify: (actual: Type, constraints: TypeConstraints) => Type - abstract _unify: (actual: Type, constraints: TypeConstraints) => Type - abstract _reduce: (constraints: TypeConstraints) => Type + unify = ( + actual: Type, + typeEqualityGraph?: TypeEqualityGraph, + ignoreExpectedParameters?: boolean, + ): Type | undefined => + TypeError.safe(() => + this.unsafeUnify(actual, typeEqualityGraph, ignoreExpectedParameters), + ) + + abstract reduce: (typeEqualityGraph: TypeEqualityGraph) => Type + + abstract equals: (type: Type) => boolean abstract toString: () => string } diff --git a/.yalc/tony-lang/src/types/models/TypeConstraint.ts b/.yalc/tony-lang/src/types/models/TypeConstraint.ts new file mode 100644 index 0000000..c99ee4e --- /dev/null +++ b/.yalc/tony-lang/src/types/models/TypeConstraint.ts @@ -0,0 +1,49 @@ +import { Type } from './Type' +import { TypeEqualityGraph } from './TypeEqualityGraph' + +export class TypeConstraint { + private _type: T + private _typeEqualityGraph: TypeEqualityGraph + + constructor(type: T, typeEqualityGraph = new TypeEqualityGraph()) { + this._type = type + this._typeEqualityGraph = typeEqualityGraph + } + + get type(): T { + return this._type + } + + get typeEqualityGraph(): TypeEqualityGraph { + return this._typeEqualityGraph + } + + unify = ( + actual: TypeConstraint, + ignoreExpectedParameters = false, + ): TypeConstraint | undefined => { + const typeEqualityGraph = TypeEqualityGraph.build( + this.typeEqualityGraph, + actual.typeEqualityGraph, + ) + if (typeEqualityGraph === undefined) return + + const type = this.type + .unify(actual.type, typeEqualityGraph, ignoreExpectedParameters) + ?.reduce(typeEqualityGraph) + + if (type) return new TypeConstraint(type, typeEqualityGraph) + } + + toString = (): string => + `${this.type.toString()}${this.typeEqualityGraph.toString()}` + + static build = ( + type?: T, + typeEqualityGraph = new TypeEqualityGraph(), + ): TypeConstraint | undefined => { + if (type === undefined) return + + return new TypeConstraint(type, typeEqualityGraph) + } +} diff --git a/.yalc/tony-lang/src/types/models/TypeConstraints.ts b/.yalc/tony-lang/src/types/models/TypeConstraints.ts deleted file mode 100644 index 5d65a30..0000000 --- a/.yalc/tony-lang/src/types/models/TypeConstraints.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Type } from './Type' -import { TypeVariable } from './TypeVariable' - -export class TypeConstraints { - private _map = new Map() - - has = (variable: TypeVariable): boolean => this._map.has(variable.name) - - resolve = (variable: TypeVariable): Type | undefined => { - const type = this._map.get(variable.name) - - if (type instanceof TypeVariable && this.has(type)) - return this.resolve(type) - - return type - } - - add = (variable: TypeVariable, type: Type): void => { - if (this.has(variable)) { - this.resolve(variable)!.unify(type, this) - return - } - if (type instanceof TypeVariable && this.has(type)) { - this.resolve(type)!.unify(variable, this) - return - } - - this._map.set(variable.name, type) - } -} diff --git a/.yalc/tony-lang/src/types/models/TypeEqualityGraph.ts b/.yalc/tony-lang/src/types/models/TypeEqualityGraph.ts new file mode 100644 index 0000000..0fdbae4 --- /dev/null +++ b/.yalc/tony-lang/src/types/models/TypeEqualityGraph.ts @@ -0,0 +1,148 @@ +import { ParametricType } from './ParametricType' +import { Type } from './Type' +import { TypeEquivalenceClass } from './TypeEquivalenceClass' +import { TypeError } from '../../errors' +import { TypeVariable } from './TypeVariable' + +export class TypeEqualityGraph { + private _equivalenceClasses: TypeEquivalenceClass[] + + constructor(equivalenceClasses: TypeEquivalenceClass[] = []) { + this._equivalenceClasses = equivalenceClasses + } + + add = (typeVariable: TypeVariable, type: Type): void => + this.addEquivalenceClass(new TypeEquivalenceClass(this, [typeVariable, type])) + + private addEquivalenceClass = (equivalence: TypeEquivalenceClass): void => { + const [ + unifiedEquivalenceClass, + equivalenceClasses, + ] = this._equivalenceClasses.reduce( + ( + [unifiedEquivalenceClass, equivalenceClasses]: [ + TypeEquivalenceClass, + TypeEquivalenceClass[], + ], + equivalenceClass, + ) => { + if ( + unifiedEquivalenceClass.types.some((type) => + equivalenceClass.includes(type), + ) + ) + return [ + TypeEquivalenceClass.unify(this, [ + unifiedEquivalenceClass, + equivalenceClass, + ]), + equivalenceClasses, + ] + + return [ + unifiedEquivalenceClass, + [...equivalenceClasses, equivalenceClass], + ] + }, + [equivalence, []], + ) + + this._equivalenceClasses = [...equivalenceClasses, unifiedEquivalenceClass] + } + + reduce = (type: Type): Type => + this.equivalenceClass(type)?.representative(this) || type + + private equivalenceClass = (type: Type): TypeEquivalenceClass | undefined => + this._equivalenceClasses.find((equivalenceClass) => + equivalenceClass.includes(type), + ) + + toString = (): string => { + if (this._equivalenceClasses.length == 0) return '' + + const equivalenceClasses = this._equivalenceClasses + .map((equivalenceClass) => equivalenceClass.toString()) + .join(', ') + return `{${equivalenceClasses}}` + } + + // private connectedComponent = (start: Type): Set => + // new Set(this.buildConnectedComponent([start])) + + // private buildConnectedComponent = ( + // frontier: Type[], + // explored: TypeEquality[] = [], + // ): TypeEquality[] => { + // if (frontier.length == 0) return explored + + // const [type, ...remainingFrontier] = frontier + // return Array.from(this._internal).reduce((newExplored, typeEquality) => { + // if (!explored.includes(typeEquality) && typeEquality.has(type)) + // return this.buildConnectedComponent(remainingFrontier, [ + // ...newExplored, + // typeEquality, + // ]) + // return newExplored + // }, explored) + // } + + static build = ( + ...typeEqualityGraphs: TypeEqualityGraph[] + ): TypeEqualityGraph | undefined => + TypeError.safe(() => + typeEqualityGraphs.reduce( + (unifiedTypeEqualityGraph, typeEqualityGraph) => { + typeEqualityGraph._equivalenceClasses.forEach((equivalenceClass) => + unifiedTypeEqualityGraph.addEquivalenceClass(equivalenceClass), + ) + return unifiedTypeEqualityGraph + }, + ), + ) + + // private static reduce = ( + // frontier: TypeEquality[], + // explored: Set = new Set(), + // ): Set => { + // if (frontier.length == 0) return explored + + // const [typeEquality, ...remainingFrontier] = frontier + // const newFrontier = Array.from(explored).reduce( + // (newFrontier, otherTypeEquality) => { + // if (typeEquality.types.some((type) => otherTypeEquality.has(type))) + // return [ + // ...newFrontier, + // ...TypeEqualityGraph.unify(otherTypeEquality, typeEquality), + // ] + + // return newFrontier + // }, + // remainingFrontier, + // ) + + // if (newFrontier.length == remainingFrontier.length) + // return TypeEqualityGraph.reduce( + // remainingFrontier, + // new Set([...explored, typeEquality]), + // ) + // else return TypeEqualityGraph.reduce(newFrontier, explored) + // } + + // private static unify = ( + // expected: TypeEquality, + // actual: TypeEquality, + // ): Set => { + // const typeEqualityGraph = new TypeEqualityGraph() + + // expected.types.every((expectedType) => + // actual.types.every( + // (actualType) => + // actualType instanceof ParametricType && + // expectedType.unsafeUnify(actualType, typeEqualityGraph), + // ), + // ) + + // return typeEqualityGraph._internal + // } +} diff --git a/.yalc/tony-lang/src/types/models/TypeEquivalenceClass.ts b/.yalc/tony-lang/src/types/models/TypeEquivalenceClass.ts new file mode 100644 index 0000000..960b445 --- /dev/null +++ b/.yalc/tony-lang/src/types/models/TypeEquivalenceClass.ts @@ -0,0 +1,72 @@ +import { Type } from './Type' +import { TypeEqualityGraph } from './TypeEqualityGraph' +import { TypeVariable } from './TypeVariable' + +export class TypeEquivalenceClass { + private _types: Type[] + + constructor( + typeEqualityGraph: TypeEqualityGraph, + types: Type[] + ) { + this._types = TypeEquivalenceClass.reduce(typeEqualityGraph, types) + } + + get size(): number { + return this._types.length + } + + get types(): Type[] { + return this._types + } + + includes = (type: Type): boolean => this._types.some(otherType => type.equals(otherType)) + + representative = ( + typeEqualityGraph: TypeEqualityGraph + ): Type | undefined => + TypeEquivalenceClass.buildRepresentative(typeEqualityGraph, this._types) + + toString = (): string => + `[${this._types.map((type) => type.toString()).join(', ')}]` + + static unify = ( + typeEqualityGraph: TypeEqualityGraph, + equivalenceClasses: TypeEquivalenceClass[], + ): TypeEquivalenceClass => + new TypeEquivalenceClass( + typeEqualityGraph, + equivalenceClasses.reduce( + (types: Type[], equivalenceClass) => [ + ...types, + ...equivalenceClass._types, + ], + [], + ), + ) + + private static reduce = ( + typeEqualityGraph: TypeEqualityGraph, + types: Type[], + ): Type[] => + types.reduce((types: Type[], type) => { + if (types.some(otherType => type.equals(otherType))) return types + + TypeEquivalenceClass.buildRepresentative(typeEqualityGraph, types) + return [...types, type] + }, []) + + private static buildRepresentative = ( + typeEqualityGraph: TypeEqualityGraph, + types: Type[], + ): Type | undefined => { + const immediateTypes = types.filter(type => !(type instanceof TypeVariable)) + if (immediateTypes.length == 0) return + + console.log(typeEqualityGraph.toString(), immediateTypes.map(t => t.toString())) + return immediateTypes.reduce((representative: Type, type) => + representative.unsafeUnify(type, typeEqualityGraph), + // representative.unsafeUnify(type), + ).reduce(typeEqualityGraph) + } +} diff --git a/.yalc/tony-lang/src/types/models/TypeProperty.ts b/.yalc/tony-lang/src/types/models/TypeProperty.ts deleted file mode 100644 index 78a92cf..0000000 --- a/.yalc/tony-lang/src/types/models/TypeProperty.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { ParametricType } from './ParametricType' -import { Property } from './Property' -import { Representation } from './Representation' - -export class TypeProperty implements Property { - private _representation: Representation | undefined - private _type: ParametricType - - constructor(type: ParametricType, representation?: Representation) { - this._type = type - this._representation = representation - } - - get name(): string { - return this.type.name - } - - get representation(): Representation | undefined { - return this._representation - } - - get type(): ParametricType { - return this._type - } - - toString = (): string => { - const type = this.type.toString() - if (this.representation === undefined) return type - - const representation = this.representation.toString() - return `${type}: ${representation}` - } -} diff --git a/.yalc/tony-lang/src/types/models/TypeVariable.ts b/.yalc/tony-lang/src/types/models/TypeVariable.ts index 17364e5..a580540 100644 --- a/.yalc/tony-lang/src/types/models/TypeVariable.ts +++ b/.yalc/tony-lang/src/types/models/TypeVariable.ts @@ -1,6 +1,5 @@ -import { CurriedType } from './CurriedType' import { Type } from './Type' -import { TypeConstraints } from './TypeConstraints' +import { TypeEqualityGraph } from './TypeEqualityGraph' export class TypeVariable extends Type { private static unnamedVariableCount = 0 @@ -17,37 +16,29 @@ export class TypeVariable extends Type { return this._name } - concat = (type: Type): CurriedType => { - if (type instanceof CurriedType) return type.concat(this) - - return new CurriedType([this, type]) - } - - unify = (actual: Type, constraints: TypeConstraints): Type => - this._unify(actual, constraints)._reduce(constraints) - - _unify = (actual: Type, constraints: TypeConstraints): Type => { + unsafeUnify = (actual: Type, typeEqualityGraph?: TypeEqualityGraph): Type => { if (!(actual instanceof TypeVariable)) { - constraints.add(this, actual) + if (typeEqualityGraph) typeEqualityGraph.add(this, actual) + return actual } - if (this.name !== actual.name) constraints.add(actual, this) + if (this.name !== actual.name && typeEqualityGraph) + typeEqualityGraph.add(actual, this) return this } - _reduce = (constraints: TypeConstraints): Type => { - if (constraints.has(this)) return constraints.resolve(this)! + reduce = (typeEqualityGraph: TypeEqualityGraph): Type => + typeEqualityGraph.reduce(this) - return this + equals = (type: Type): boolean => { + if (!(type instanceof TypeVariable)) return false + + return this.name === type.name } toString = (): string => this.name - private static getUnusedVariableName = (): string => { - const name = `t${TypeVariable.unnamedVariableCount}` - TypeVariable.unnamedVariableCount += 1 - - return name - } + private static getUnusedVariableName = (): string => + `t${(TypeVariable.unnamedVariableCount += 1)}` } diff --git a/.yalc/tony-lang/src/types/models/index.ts b/.yalc/tony-lang/src/types/models/index.ts index 7bd0b07..400e913 100644 --- a/.yalc/tony-lang/src/types/models/index.ts +++ b/.yalc/tony-lang/src/types/models/index.ts @@ -1,8 +1,6 @@ -export { CurriedType } from './CurriedType' -export { IdentifierProperty } from './IdentifierProperty' +export { ModuleType } from './ModuleType' export { ParametricType } from './ParametricType' -export { Representation, RepresentationKind } from './Representation' export { Type } from './Type' -export { TypeConstraints } from './TypeConstraints' -export { TypeProperty } from './TypeProperty' +export { TypeConstraint } from './TypeConstraint' +export { TypeEqualityGraph } from './TypeEqualityGraph' export { TypeVariable } from './TypeVariable' diff --git a/.yalc/tony-lang/src/types/services/BuildRepresentation.ts b/.yalc/tony-lang/src/types/services/BuildRepresentation.ts deleted file mode 100644 index ab12e59..0000000 --- a/.yalc/tony-lang/src/types/services/BuildRepresentation.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Binding, IdentifierBinding, TypeBinding } from '../../symbol_table' -import { - IdentifierProperty, - Representation, - RepresentationKind, - TypeProperty, -} from '../models' -import { isNotUndefined } from '../../utilities' - -export class BuildRepresentation { - perform = (kind: RepresentationKind, bindings: Binding[]): Representation => { - const properties = bindings - .map((binding) => { - if (binding instanceof IdentifierBinding) - return new IdentifierProperty(binding.name, binding.type) - else if (binding instanceof TypeBinding) - return new TypeProperty(binding.type, binding.representation) - }) - .filter(isNotUndefined) - - return new Representation(kind, properties) - } -} diff --git a/.yalc/tony-lang/src/types/services/BuildType.ts b/.yalc/tony-lang/src/types/services/BuildType.ts index 9ca7475..2010be3 100644 --- a/.yalc/tony-lang/src/types/services/BuildType.ts +++ b/.yalc/tony-lang/src/types/services/BuildType.ts @@ -1,10 +1,10 @@ -import { CurriedType, ParametricType, Type } from '../models' -import { InternalError, assert } from '../../errors' -import { LIST_TYPE, MAP_TYPE, TUPLE_TYPE } from '..' +import { FUNCTION_TYPE, LIST_TYPE, MAP_TYPE, TUPLE_TYPE } from '..' +import { InternalError } from '../../errors' +import { ParametricType } from '../models' import Parser from 'tree-sitter' export class BuildType { - perform = (node: Parser.SyntaxNode): Type => { + perform = (node: Parser.SyntaxNode): ParametricType => { switch (node.type) { case 'list_type': return this.handleListType(node) @@ -18,7 +18,7 @@ export class BuildType { return this.handleType(node) default: throw new InternalError( - `ParseType: Could not find generator for AST node '${node.type}'.`, + `Could not find generator for AST node '${node.type}'.`, ) } } @@ -42,12 +42,7 @@ export class BuildType { return new ParametricType(TUPLE_TYPE, valueTypes) } - handleTypeConstructor = (node: Parser.SyntaxNode): Type => { - assert( - node.type === 'type_constructor', - 'Should be `type_constructor` node.', - ) - + private handleTypeConstructor = (node: Parser.SyntaxNode): ParametricType => { if (node.namedChildCount == 1) { const type = this.perform(node.namedChild(0)!) @@ -55,12 +50,10 @@ export class BuildType { } const types = node.namedChildren.map((childNode) => this.perform(childNode)) - return new CurriedType(types) + return new ParametricType(FUNCTION_TYPE, types) } - handleType = (node: Parser.SyntaxNode): ParametricType => { - assert(node.type === 'type', 'Should be `type` node.') - + private handleType = (node: Parser.SyntaxNode): ParametricType => { const name = node.text return new ParametricType(name) diff --git a/.yalc/tony-lang/src/types/services/index.ts b/.yalc/tony-lang/src/types/services/index.ts index ac967b6..02638e9 100644 --- a/.yalc/tony-lang/src/types/services/index.ts +++ b/.yalc/tony-lang/src/types/services/index.ts @@ -1,2 +1 @@ -export { BuildRepresentation } from './BuildRepresentation' export { BuildType } from './BuildType' diff --git a/.yalc/tony-lang/test/abstraction/quicksort.tn b/.yalc/tony-lang/test/abstraction/quicksort.tn index a5ce368..629b17d 100644 --- a/.yalc/tony-lang/test/abstraction/quicksort.tn +++ b/.yalc/tony-lang/test/abstraction/quicksort.tn @@ -3,7 +3,7 @@ quicksort := ([pivot, ...rest]) => l := quicksort([n | n in rest if n <= pivot]) r := quicksort([n | n in rest if n > pivot]) - l ++ [pivot] ++ r + l + [pivot] + r list := quicksort([5, 7, 2, 0, 4, 3]) print(list[0].to_str) diff --git a/.yalc/tony-lang/test/application/index.tn b/.yalc/tony-lang/test/application/index.tn index c88528d..64af1aa 100644 --- a/.yalc/tony-lang/test/application/index.tn +++ b/.yalc/tony-lang/test/application/index.tn @@ -1,3 +1,2 @@ print(((a, b) => [a, b])(1, 2)[0].to_str) -# print(((a, b = 1) => [a, b])(1)[1].to_str) print('hello world') diff --git a/.yalc/tony-lang/test/assignment/duplicate_binding.tn b/.yalc/tony-lang/test/assignment/duplicate_binding.tn new file mode 100644 index 0000000..841ae63 --- /dev/null +++ b/.yalc/tony-lang/test/assignment/duplicate_binding.tn @@ -0,0 +1,2 @@ +x := 1 +x := 2 diff --git a/.yalc/tony-lang/test/assignment/duplicate_binding.txt b/.yalc/tony-lang/test/assignment/duplicate_binding.txt new file mode 100644 index 0000000..f4c2755 --- /dev/null +++ b/.yalc/tony-lang/test/assignment/duplicate_binding.txt @@ -0,0 +1 @@ +DuplicateBindingError diff --git a/.yalc/tony-lang/test/assignment/overloading.tn b/.yalc/tony-lang/test/assignment/overloading.tn new file mode 100644 index 0000000..0b6afb7 --- /dev/null +++ b/.yalc/tony-lang/test/assignment/overloading.tn @@ -0,0 +1,5 @@ +x := 1 +x := 'string' + +print(x) +print(x.to_str) diff --git a/.yalc/tony-lang/test/assignment/overloading.txt b/.yalc/tony-lang/test/assignment/overloading.txt new file mode 100644 index 0000000..c5119f1 --- /dev/null +++ b/.yalc/tony-lang/test/assignment/overloading.txt @@ -0,0 +1,2 @@ +string +1 diff --git a/.yalc/tony-lang/test/assignment/pattern_matching.tn b/.yalc/tony-lang/test/assignment/pattern_matching.tn index b228326..c644d9f 100644 --- a/.yalc/tony-lang/test/assignment/pattern_matching.tn +++ b/.yalc/tony-lang/test/assignment/pattern_matching.tn @@ -12,9 +12,6 @@ print(z.to_str) [(1, h), i] := [(1, 'hello'), (2, 'Tony')] print(h) -# Dynamic tuple access -# print(i[0].to_str) -# print(i[1]) [j, 1, k = 200] := [0, 1] print(j.to_str) diff --git a/.yalc/tony-lang/test/stdlib.tn b/.yalc/tony-lang/test/stdlib.tn index 5aa79fa..89712b1 100644 --- a/.yalc/tony-lang/test/stdlib.tn +++ b/.yalc/tony-lang/test/stdlib.tn @@ -5,7 +5,7 @@ import { add: + :: Number -> Number -> Number, sub: - :: Number -> Number -> Number, mod: % :: Number -> Number -> Number, - concat: ++ :: [Number] -> [Number] -> [Number], + concat: + :: [Number] -> [Number] -> [Number], leq: <= :: Number -> Number -> Boolean, lt: < :: Number -> Number -> Boolean, geq: >= :: Number -> Number -> Boolean, diff --git a/.yalc/tony-lang/tony-lang.d.ts b/.yalc/tony-lang/tony-lang.d.ts index a7c6583..1c8ca76 100644 --- a/.yalc/tony-lang/tony-lang.d.ts +++ b/.yalc/tony-lang/tony-lang.d.ts @@ -3,24 +3,30 @@ import Parser from 'tree-sitter' declare module 'tony-lang' { export function compile( file: string, - { outFile, emit, webpack, webpackMode, verbose }: { - outFile?: string; - emit?: boolean; - webpack?: boolean; - webpackMode?: string; - verbose?: boolean; - } + { + outFile, + emit, + webpack, + webpackMode, + verbose, + }: { + outFile?: string + emit?: boolean + webpack?: boolean + webpackMode?: string + verbose?: boolean + }, ): Promise export function exec( file: string, args: string[], - { verbose }: { verbose?: boolean } + { verbose }: { verbose?: boolean }, ): Promise export function parse( file: string, - { verbose }: { verbose?: boolean } + { verbose }: { verbose?: boolean }, ): Promise export const VERSION: string @@ -44,11 +50,26 @@ declare module 'tony-lang' { export class ImportOutsideFileModuleScopeError extends CompileError {} + export class IndeterminateTypeError extends CompileError { + get types(): string[] + } + export class InternalError extends Error {} - export class InvalidPropertyAccessError extends CompileError { - get property(): string - get representation(): string + export class InternalTypeError extends InternalError { + get trace(): [string, string][] + } + + export class InvalidExternalTypeImportError extends CompileError { + get type(): string + } + + export class InvalidModuleAccessError extends CompileError { + get binding(): string | undefined + get type(): string + } + + export class InvalidUseOfTypeAsValueError extends CompileError { get type(): string } @@ -56,13 +77,17 @@ declare module 'tony-lang' { get binding(): string } + export class MissingExternalImportTypeHintError extends CompileError { + get binding(): string + } + export class SyntaxError extends Error { get filePath(): string get tree(): Parser.Tree } export class TypeError extends CompileError { - get typeTrace(): [string, string][] + get trace(): InternalTypeError[] } export class UnknownImportError extends CompileError { diff --git a/.yalc/tony-lang/yalc.sig b/.yalc/tony-lang/yalc.sig index 5848715..11fdd1f 100644 --- a/.yalc/tony-lang/yalc.sig +++ b/.yalc/tony-lang/yalc.sig @@ -1 +1 @@ -afbd7cb804326aa5fded581a46697147 \ No newline at end of file +dae48e8c032e9be3346d19eba294b036 \ No newline at end of file diff --git a/src/CLI.ts b/src/CLI.ts index 3b07f3a..a8f6310 100644 --- a/src/CLI.ts +++ b/src/CLI.ts @@ -40,7 +40,7 @@ export default class CLI { return Tony.exec(entryPath, args, { verbose: this.options.debug }) }) - .catch((error) => new ErrorHandler().perform(error)) + .catch((error) => new ErrorHandler(this.options.debug).perform(error)) } compile = async ( @@ -70,18 +70,18 @@ export default class CLI { if (emit) console.log(`Your compiled file can be found here: ${entryPath}`) }) - .catch((error) => new ErrorHandler().perform(error)) + .catch((error) => new ErrorHandler(this.options.debug).perform(error)) } exec = async (file: string, args: string[]): Promise => { await Tony.exec(path.join(process.cwd(), file), args, { verbose: this.options.debug, - }).catch((error) => new ErrorHandler().perform(error)) + }).catch((error) => new ErrorHandler(this.options.debug).perform(error)) } parse = async (file: string): Promise => { await Tony.parse(file, { verbose: this.options.debug }) .then((tree) => console.log(new TreeFormatter().perform(tree.rootNode))) - .catch((error) => new ErrorHandler().perform(error)) + .catch((error) => new ErrorHandler(this.options.debug).perform(error)) } } diff --git a/src/ErrorHandler.ts b/src/ErrorHandler.ts index 4a6848e..1e9ffa5 100644 --- a/src/ErrorHandler.ts +++ b/src/ErrorHandler.ts @@ -5,7 +5,7 @@ import { ExportOutsideModuleScopeErrorFormatter, ImportOutsideFileModuleScopeErrorFormatter, InternalErrorFormatter, - InvalidPropertyAccessErrorFormatter, + InvalidModuleAccessErrorFormatter, MissingBindingErrorFormatter, SyntaxErrorFormatter, TypeErrorFormatter, @@ -14,6 +14,12 @@ import { import chalk from 'chalk' export class ErrorHandler { + private _verbose: boolean + + constructor(verbose: boolean) { + this._verbose = verbose + } + perform = async (error: Error): Promise => { console.error( `${chalk.gray(error.stack)}\n\n${chalk.bold.underline.redBright( @@ -33,14 +39,16 @@ export class ErrorHandler { await new ExportOutsideModuleScopeErrorFormatter().perform(error) else if (error instanceof Tony.ImportOutsideFileModuleScopeError) await new ImportOutsideFileModuleScopeErrorFormatter().perform(error) - else if (error instanceof Tony.InvalidPropertyAccessError) - await new InvalidPropertyAccessErrorFormatter().perform(error) + else if (error instanceof Tony.InvalidModuleAccessError) + await new InvalidModuleAccessErrorFormatter().perform(error) else if (error instanceof Tony.MissingBindingError) - return new MissingBindingErrorFormatter().perform(error) + await new MissingBindingErrorFormatter().perform(error) else if (error instanceof Tony.TypeError) - return new TypeErrorFormatter().perform(error) + await new TypeErrorFormatter().perform(error) else if (error instanceof Tony.UnknownImportError) await new UnknownImportErrorFormatter().perform(error) else console.error(error.message) + + if (this._verbose) console.error(error) } } diff --git a/src/formatting/InvalidPropertyAccessErrorFormatter.ts b/src/formatting/InvalidModuleAccessErrorFormatter.ts similarity index 55% rename from src/formatting/InvalidPropertyAccessErrorFormatter.ts rename to src/formatting/InvalidModuleAccessErrorFormatter.ts index 8de4a2b..42c0725 100644 --- a/src/formatting/InvalidPropertyAccessErrorFormatter.ts +++ b/src/formatting/InvalidModuleAccessErrorFormatter.ts @@ -1,15 +1,14 @@ -import { InvalidPropertyAccessError } from 'tony-lang' +import { InvalidModuleAccessError } from 'tony-lang' import { SourceFormatter } from './SourceFormatter' import chalk from 'chalk' -export class InvalidPropertyAccessErrorFormatter { - perform = async (error: InvalidPropertyAccessError): Promise => { +export class InvalidModuleAccessErrorFormatter { + perform = async (error: InvalidModuleAccessError): Promise => { console.error( `The name ${chalk.bold.whiteBright( - error.property, + error.binding, )} is not a property of ` + - `the type ${chalk.whiteBright(error.type)} with the representation ` + - `${chalk.whiteBright(error.representation)}.`, + `the module ${chalk.whiteBright(error.type)}.`, ) if (error.filePath && error.context) { diff --git a/src/formatting/TypeErrorFormatter.ts b/src/formatting/TypeErrorFormatter.ts index 32cbb5c..6daaa51 100644 --- a/src/formatting/TypeErrorFormatter.ts +++ b/src/formatting/TypeErrorFormatter.ts @@ -5,21 +5,24 @@ import chalk from 'chalk' export class TypeErrorFormatter { perform = async (error: TypeError): Promise => { console.error( - 'Types could not be unified.\n\n' + - `${chalk.bold.whiteBright(error.message)}\n\nUnification trace:`, + 'Type unification resulted in an empty type disjunction.\n\nUnification trace:', ) - error.typeTrace.forEach(([expected, actual]) => { - if (actual) - console.error( - `${chalk.gray('- expected')} ${chalk.whiteBright( - expected, - )}\n ` + `${chalk.gray('got')} ${chalk.whiteBright(actual)}`, - ) - else - console.error( - `${chalk.gray('-')} ${chalk.whiteBright(expected)}`, - ) + error.trace.forEach((internalTypeError) => { + console.error(chalk.bold.whiteBright(internalTypeError.message)) + + internalTypeError.trace.forEach(([expected, actual]) => { + if (actual) + console.error( + `${chalk.gray('- expected')} ${chalk.whiteBright( + expected, + )}\n ` + `${chalk.gray('got')} ${chalk.whiteBright(actual)}`, + ) + else + console.error( + `${chalk.gray('-')} ${chalk.whiteBright(expected)}`, + ) + }) }) if (error.filePath && error.context) { diff --git a/src/formatting/index.ts b/src/formatting/index.ts index 5cb558d..e5febdc 100644 --- a/src/formatting/index.ts +++ b/src/formatting/index.ts @@ -3,7 +3,7 @@ export { DuplicateBindingErrorFormatter } from './DuplicateBindingErrorFormatter export { ExportOutsideModuleScopeErrorFormatter } from './ExportOutsideModuleScopeErrorFormatter' export { ImportOutsideFileModuleScopeErrorFormatter } from './ImportOutsideFileModuleScopeErrorFormatter' export { InternalErrorFormatter } from './InternalErrorFormatter' -export { InvalidPropertyAccessErrorFormatter } from './InvalidPropertyAccessErrorFormatter' +export { InvalidModuleAccessErrorFormatter } from './InvalidModuleAccessErrorFormatter' export { MissingBindingErrorFormatter } from './MissingBindingErrorFormatter' export { SyntaxErrorFormatter } from './SyntaxErrorFormatter' export { TreeFormatter } from './TreeFormatter' diff --git a/test.js b/test.js new file mode 100644 index 0000000..70775e5 --- /dev/null +++ b/test.js @@ -0,0 +1 @@ +!function(t){var e={};function r(n){if(e[n])return e[n].exports;var o=e[n]={i:n,l:!1,exports:{}};return t[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)r.d(n,o,function(e){return t[e]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=26)}([function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"Curry",{enumerable:!0,get:function(){return n.Curry}}),Object.defineProperty(e,"PatternMatch",{enumerable:!0,get:function(){return o.PatternMatch}}),Object.defineProperty(e,"ResolveAbstractionBranch",{enumerable:!0,get:function(){return i.ResolveAbstractionBranch}});var n=r(27),o=r(12),i=r(57)},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.eq=e.gt=e.geq=e.lt=e.leq=e.concat=e.mod=e.sub=e.add=e.neg=e.to_str=e.print=void 0;const n=console.log;e.print=n;e.to_str=t=>String(t);e.neg=t=>-t;e.add=(t,e)=>t+e;e.sub=(t,e)=>t-e;e.mod=(t,e)=>Math.abs(t%e);e.concat=(t,e)=>t.concat(e);e.leq=(t,e)=>t<=e;e.lt=(t,e)=>tt>=e;e.gt=(t,e)=>t>e;e.eq=(t,e)=>t==e},function(t,e,r){"use strict";var n=TypeError,o=Object.getOwnPropertyDescriptor;if(o)try{o({},"")}catch(t){o=null}var i=function(){throw new n},a=o?function(){try{return arguments.callee,i}catch(t){try{return o(arguments,"callee").get}catch(t){return i}}}():i,u=r(4)(),c=Object.getPrototypeOf||function(t){return t.__proto__},f=void 0,p="undefined"==typeof Uint8Array?void 0:c(Uint8Array),l={"%Array%":Array,"%ArrayBuffer%":"undefined"==typeof ArrayBuffer?void 0:ArrayBuffer,"%ArrayBufferPrototype%":"undefined"==typeof ArrayBuffer?void 0:ArrayBuffer.prototype,"%ArrayIteratorPrototype%":u?c([][Symbol.iterator]()):void 0,"%ArrayPrototype%":Array.prototype,"%ArrayProto_entries%":Array.prototype.entries,"%ArrayProto_forEach%":Array.prototype.forEach,"%ArrayProto_keys%":Array.prototype.keys,"%ArrayProto_values%":Array.prototype.values,"%AsyncFromSyncIteratorPrototype%":void 0,"%AsyncFunction%":void 0,"%AsyncFunctionPrototype%":void 0,"%AsyncGenerator%":void 0,"%AsyncGeneratorFunction%":void 0,"%AsyncGeneratorPrototype%":void 0,"%AsyncIteratorPrototype%":f&&u&&Symbol.asyncIterator?f[Symbol.asyncIterator]():void 0,"%Atomics%":"undefined"==typeof Atomics?void 0:Atomics,"%Boolean%":Boolean,"%BooleanPrototype%":Boolean.prototype,"%DataView%":"undefined"==typeof DataView?void 0:DataView,"%DataViewPrototype%":"undefined"==typeof DataView?void 0:DataView.prototype,"%Date%":Date,"%DatePrototype%":Date.prototype,"%decodeURI%":decodeURI,"%decodeURIComponent%":decodeURIComponent,"%encodeURI%":encodeURI,"%encodeURIComponent%":encodeURIComponent,"%Error%":Error,"%ErrorPrototype%":Error.prototype,"%eval%":eval,"%EvalError%":EvalError,"%EvalErrorPrototype%":EvalError.prototype,"%Float32Array%":"undefined"==typeof Float32Array?void 0:Float32Array,"%Float32ArrayPrototype%":"undefined"==typeof Float32Array?void 0:Float32Array.prototype,"%Float64Array%":"undefined"==typeof Float64Array?void 0:Float64Array,"%Float64ArrayPrototype%":"undefined"==typeof Float64Array?void 0:Float64Array.prototype,"%Function%":Function,"%FunctionPrototype%":Function.prototype,"%Generator%":void 0,"%GeneratorFunction%":void 0,"%GeneratorPrototype%":void 0,"%Int8Array%":"undefined"==typeof Int8Array?void 0:Int8Array,"%Int8ArrayPrototype%":"undefined"==typeof Int8Array?void 0:Int8Array.prototype,"%Int16Array%":"undefined"==typeof Int16Array?void 0:Int16Array,"%Int16ArrayPrototype%":"undefined"==typeof Int16Array?void 0:Int8Array.prototype,"%Int32Array%":"undefined"==typeof Int32Array?void 0:Int32Array,"%Int32ArrayPrototype%":"undefined"==typeof Int32Array?void 0:Int32Array.prototype,"%isFinite%":isFinite,"%isNaN%":isNaN,"%IteratorPrototype%":u?c(c([][Symbol.iterator]())):void 0,"%JSON%":"object"==typeof JSON?JSON:void 0,"%JSONParse%":"object"==typeof JSON?JSON.parse:void 0,"%Map%":"undefined"==typeof Map?void 0:Map,"%MapIteratorPrototype%":"undefined"!=typeof Map&&u?c((new Map)[Symbol.iterator]()):void 0,"%MapPrototype%":"undefined"==typeof Map?void 0:Map.prototype,"%Math%":Math,"%Number%":Number,"%NumberPrototype%":Number.prototype,"%Object%":Object,"%ObjectPrototype%":Object.prototype,"%ObjProto_toString%":Object.prototype.toString,"%ObjProto_valueOf%":Object.prototype.valueOf,"%parseFloat%":parseFloat,"%parseInt%":parseInt,"%Promise%":"undefined"==typeof Promise?void 0:Promise,"%PromisePrototype%":"undefined"==typeof Promise?void 0:Promise.prototype,"%PromiseProto_then%":"undefined"==typeof Promise?void 0:Promise.prototype.then,"%Promise_all%":"undefined"==typeof Promise?void 0:Promise.all,"%Promise_reject%":"undefined"==typeof Promise?void 0:Promise.reject,"%Promise_resolve%":"undefined"==typeof Promise?void 0:Promise.resolve,"%Proxy%":"undefined"==typeof Proxy?void 0:Proxy,"%RangeError%":RangeError,"%RangeErrorPrototype%":RangeError.prototype,"%ReferenceError%":ReferenceError,"%ReferenceErrorPrototype%":ReferenceError.prototype,"%Reflect%":"undefined"==typeof Reflect?void 0:Reflect,"%RegExp%":RegExp,"%RegExpPrototype%":RegExp.prototype,"%Set%":"undefined"==typeof Set?void 0:Set,"%SetIteratorPrototype%":"undefined"!=typeof Set&&u?c((new Set)[Symbol.iterator]()):void 0,"%SetPrototype%":"undefined"==typeof Set?void 0:Set.prototype,"%SharedArrayBuffer%":"undefined"==typeof SharedArrayBuffer?void 0:SharedArrayBuffer,"%SharedArrayBufferPrototype%":"undefined"==typeof SharedArrayBuffer?void 0:SharedArrayBuffer.prototype,"%String%":String,"%StringIteratorPrototype%":u?c(""[Symbol.iterator]()):void 0,"%StringPrototype%":String.prototype,"%Symbol%":u?Symbol:void 0,"%SymbolPrototype%":u?Symbol.prototype:void 0,"%SyntaxError%":SyntaxError,"%SyntaxErrorPrototype%":SyntaxError.prototype,"%ThrowTypeError%":a,"%TypedArray%":p,"%TypedArrayPrototype%":p?p.prototype:void 0,"%TypeError%":n,"%TypeErrorPrototype%":n.prototype,"%Uint8Array%":"undefined"==typeof Uint8Array?void 0:Uint8Array,"%Uint8ArrayPrototype%":"undefined"==typeof Uint8Array?void 0:Uint8Array.prototype,"%Uint8ClampedArray%":"undefined"==typeof Uint8ClampedArray?void 0:Uint8ClampedArray,"%Uint8ClampedArrayPrototype%":"undefined"==typeof Uint8ClampedArray?void 0:Uint8ClampedArray.prototype,"%Uint16Array%":"undefined"==typeof Uint16Array?void 0:Uint16Array,"%Uint16ArrayPrototype%":"undefined"==typeof Uint16Array?void 0:Uint16Array.prototype,"%Uint32Array%":"undefined"==typeof Uint32Array?void 0:Uint32Array,"%Uint32ArrayPrototype%":"undefined"==typeof Uint32Array?void 0:Uint32Array.prototype,"%URIError%":URIError,"%URIErrorPrototype%":URIError.prototype,"%WeakMap%":"undefined"==typeof WeakMap?void 0:WeakMap,"%WeakMapPrototype%":"undefined"==typeof WeakMap?void 0:WeakMap.prototype,"%WeakSet%":"undefined"==typeof WeakSet?void 0:WeakSet,"%WeakSetPrototype%":"undefined"==typeof WeakSet?void 0:WeakSet.prototype},s=r(6).call(Function.call,String.prototype.replace),y=/[^%.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|%$))/g,d=/\\(\\)?/g,b=function(t){var e=[];return s(t,y,(function(t,r,n,o){e[e.length]=n?s(o,d,"$1"):r||t})),e},v=function(t,e){if(!(t in l))throw new SyntaxError("intrinsic "+t+" does not exist!");if(void 0===l[t]&&!e)throw new n("intrinsic "+t+" exists, but is not available. Please file an issue!");return l[t]};t.exports=function(t,e){if("string"!=typeof t||0===t.length)throw new TypeError("intrinsic name must be a non-empty string");if(arguments.length>1&&"boolean"!=typeof e)throw new TypeError('"allowMissing" argument must be a boolean');for(var r=b(t),i=v("%"+(r.length>0?r[0]:"")+"%",e),a=1;a=r.length){var u=o(i,r[a]);if(!e&&!(r[a]in i))throw new n("base intrinsic for "+t+" exists, but the property is not available.");i=u?u.get||u.value:i[r[a]]}else i=i[r[a]];return i}},function(t,e,r){"use strict";var n=r(8),o="function"==typeof Symbol&&"symbol"==typeof Symbol("foo"),i=Object.prototype.toString,a=Array.prototype.concat,u=Object.defineProperty,c=u&&function(){var t={};try{for(var e in u(t,"x",{enumerable:!1,value:t}),t)return!1;return t.x===t}catch(t){return!1}}(),f=function(t,e,r,n){var o;(!(e in t)||"function"==typeof(o=n)&&"[object Function]"===i.call(o)&&n())&&(c?u(t,e,{configurable:!0,enumerable:!1,value:r,writable:!0}):t[e]=r)},p=function(t,e){var r=arguments.length>2?arguments[2]:{},i=n(e);o&&(i=a.call(i,Object.getOwnPropertySymbols(e)));for(var u=0;u~*\\\-+/.])[!@$%^&*|<>~*\\\-+/.=]*/);e.OPERATOR_REGEX=p;const l=Object.freeze(o.default.join(t,"stdlib")),s=Object.freeze(`import * as stdlib from '${l}'`);e.DEFAULT_IMPORTS=s;const y=Object.freeze("#internal/TRANSFORM_PLACEHOLDER_ARGUMENT");e.TRANSFORM_PLACEHOLDER_ARGUMENT=y;const d=Object.freeze("#internal/TRANSFORM_IDENTIFIER_PATTERN");e.TRANSFORM_IDENTIFIER_PATTERN=d;const b=Object.freeze("#internal/TRANSFORM_REST_PATTERN");e.TRANSFORM_REST_PATTERN=b}).call(this,"/")},function(t,e){var r,n,o=t.exports={};function i(){throw new Error("setTimeout has not been defined")}function a(){throw new Error("clearTimeout has not been defined")}function u(t){if(r===setTimeout)return setTimeout(t,0);if((r===i||!r)&&setTimeout)return r=setTimeout,setTimeout(t,0);try{return r(t,0)}catch(e){try{return r.call(null,t,0)}catch(e){return r.call(this,t,0)}}}!function(){try{r="function"==typeof setTimeout?setTimeout:i}catch(t){r=i}try{n="function"==typeof clearTimeout?clearTimeout:a}catch(t){n=a}}();var c,f=[],p=!1,l=-1;function s(){p&&c&&(p=!1,c.length?f=c.concat(f):l=-1,f.length&&y())}function y(){if(!p){var t=u(s);p=!0;for(var e=f.length;e;){for(c=f,f=[];++l1)for(var r=1;r{if(f.matchesIdentifier(t))return this.patternMatchIdentifier(e,r);if(f.matchesLiteral(t,e))return[];if(f.matchesArray(t,e))return this.patternMatchArray(t,e,r);if(f.matchesObject(t,e))return this.patternMatchObject(t,e,r);if(this.omitsPattern(t,e))return this.patternMatchOmittedPattern(t);throw new u("Pattern does not match")}),a(this,"patternMatchIdentifier",(t,e)=>{const r=this.defaults.shift();if(void 0===t){if(void 0===r)throw this.partialMatching&&1==e?new c("Pattern does only partially match"):new u("Pattern does not match");return[t||r]}return[t]}),a(this,"patternMatchArray",(t,e,r)=>{if(!this.overmatching&&f.isOvermatchingArray(t,e))throw new u("Pattern does not match");return t.slice(0).reduce((t,n,i,a)=>n===o.TRANSFORM_REST_PATTERN?(a.splice(i-1),t.concat([e.slice(i)])):t.concat(this.perform(n,e[i],r+1)),[])}),a(this,"patternMatchObject",(t,e,r)=>{if(!this.overmatching&&f.isOvermatchingObject(t,e))throw new u("Pattern does not match");const n={...e};return Object.entries(t).reduce((t,[i,a],u,c)=>i===o.TRANSFORM_REST_PATTERN?(c.splice(u-1),t.concat([n])):(delete n[i],t.concat(this.perform(a,e[i],r+1))),[])}),a(this,"omitsPattern",(t,e)=>void 0===e&&this.isOmittable(t)),a(this,"isOmittable",t=>{const e=f.identifierPatternsCount(t);return this.defaults.length>=e&&this.defaults.slice(e-1).every(t=>void 0!==t)}),a(this,"patternMatchOmittedPattern",t=>this.defaults.splice(0,f.identifierPatternsCount(t))),t&&(this.defaults=t),e&&(this.partialMatching=e),r&&(this.overmatching=r)}}e.PatternMatch=f,a(f,"matchesIdentifier",t=>t===o.TRANSFORM_IDENTIFIER_PATTERN),a(f,"matchesLiteral",(t,e)=>"object"!=typeof t&&(0,i.default)(t,e)),a(f,"matchesArray",(t,e)=>Array.isArray(t)&&Array.isArray(e)),a(f,"matchesObject",(t,e)=>"object"==typeof t&&!Array.isArray(t)&&"object"==typeof e&&!Array.isArray(e)),a(f,"identifierPatternsCount",t=>t===o.TRANSFORM_IDENTIFIER_PATTERN?1:Array.isArray(t)?t.reduce((t,e)=>t+f.identifierPatternsCount(e),0):"object"==typeof t?Object.entries(t).reduce((t,[,e])=>t+f.identifierPatternsCount(e),0):0),a(f,"isOvermatchingArray",(t,e)=>!t.includes(o.TRANSFORM_REST_PATTERN)&&t.length!Object.keys(t).includes(o.TRANSFORM_REST_PATTERN)&&Object.entries(t).length=0&&"[object Function]"===n.call(t.callee)),r}},function(t,e,r){"use strict";var n="function"==typeof Symbol&&"symbol"==typeof Symbol.toStringTag,o=Object.prototype.toString,i=function(t){return!(n&&t&&"object"==typeof t&&Symbol.toStringTag in t)&&"[object Arguments]"===o.call(t)},a=function(t){return!!i(t)||null!==t&&"object"==typeof t&&"number"==typeof t.length&&t.length>=0&&"[object Array]"!==o.call(t)&&"[object Function]"===o.call(t.callee)},u=function(){return i(arguments)}();i.isLegacyArguments=a,t.exports=u?i:a},function(t,e,r){"use strict";var n=r(6),o=r(2)("%Function%"),i=o.apply,a=o.call;t.exports=function(){return n.apply(a,arguments)},t.exports.apply=function(){return n.apply(i,arguments)}},function(t,e,r){"use strict";var n=Object,o=TypeError;t.exports=function(){if(null!=this&&this!==n(this))throw new o("RegExp.prototype.flags getter called on non-object");var t="";return this.global&&(t+="g"),this.ignoreCase&&(t+="i"),this.multiline&&(t+="m"),this.dotAll&&(t+="s"),this.unicode&&(t+="u"),this.sticky&&(t+="y"),t}},function(t,e,r){"use strict";var n=r(16),o=r(3).supportsDescriptors,i=Object.getOwnPropertyDescriptor,a=TypeError;t.exports=function(){if(!o)throw new a("RegExp.prototype.flags requires a true ES5 environment that supports property descriptors");if("gim"===/a/gim.flags){var t=i(RegExp.prototype,"flags");if(t&&"function"==typeof t.get&&"boolean"==typeof/a/.dotAll)return t.get}return n}},function(t,e,r){"use strict";var n=String.prototype.valueOf,o=Object.prototype.toString,i="function"==typeof Symbol&&"symbol"==typeof Symbol.toStringTag;t.exports=function(t){return"string"==typeof t||"object"==typeof t&&(i?function(t){try{return n.call(t),!0}catch(t){return!1}}(t):"[object String]"===o.call(t))}},function(t,e,r){"use strict";var n,o="function"==typeof Map&&Map.prototype?Map:null,i="function"==typeof Set&&Set.prototype?Set:null;o||(n=function(t){return!1});var a=o?Map.prototype.has:null,u=i?Set.prototype.has:null;n||a||(n=function(t){return!1}),t.exports=n||function(t){if(!t||"object"!=typeof t)return!1;try{if(a.call(t),u)try{u.call(t)}catch(t){return!0}return t instanceof o}catch(t){}return!1}},function(t,e,r){"use strict";var n,o="function"==typeof Map&&Map.prototype?Map:null,i="function"==typeof Set&&Set.prototype?Set:null;i||(n=function(t){return!1});var a=o?Map.prototype.has:null,u=i?Set.prototype.has:null;n||u||(n=function(t){return!1}),t.exports=n||function(t){if(!t||"object"!=typeof t)return!1;try{if(u.call(t),a)try{a.call(t)}catch(t){return!0}return t instanceof i}catch(t){}return!1}},function(t,e){var r=Object.prototype.hasOwnProperty,n=Object.prototype.toString;t.exports=function(t,e,o){if("[object Function]"!==n.call(e))throw new TypeError("iterator must be a function");var i=t.length;if(i===+i)for(var a=0;a{const t=n.Curry.perform((...t)=>n.ResolveAbstractionBranch.perform(t,[[[0],[],t=>{const[]=t;return 0}],[[1],[],t=>{const[]=t;return 1}],[["#internal/TRANSFORM_IDENTIFIER_PATTERN"],[],t=>{const[e]=t;return a(f(i(e,1)),f(i(e,2)))}]]));[f]=new n.PatternMatch({defaults:[],overmatching:!0}).perform("#internal/TRANSFORM_IDENTIFIER_PATTERN",t)})(),c(u(f(0))),c(u(f(1))),c(u(f(2))),c(u(f(3))),c(u(f(4))),c(u(f(5)))},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.Curry=void 0;var n=r(10);function o(t,e,r){return e in t?Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}):t[e]=r,t}class i{}e.Curry=i,o(i,"perform",(t,...e)=>(...r)=>{const n=i.addNewArgs(e,r),o=i.getActualArgs(n),a=t(...o);return void 0===a?i.perform(t,...n):a}),o(i,"getActualArgs",t=>t.filter(t=>t!==n.TRANSFORM_PLACEHOLDER_ARGUMENT)),o(i,"addNewArgs",(t,e)=>t.map(t=>t===n.TRANSFORM_PLACEHOLDER_ARGUMENT?e.pop():t).concat(e)),o(i,"external",(t,...e)=>(...r)=>{const n=e.concat(r);return n.length>=t.length?t(...n):i.external(t,...n)})},function(t,e,r){(function(t){function r(t,e){for(var r=0,n=t.length-1;n>=0;n--){var o=t[n];"."===o?t.splice(n,1):".."===o?(t.splice(n,1),r++):r&&(t.splice(n,1),r--)}if(e)for(;r--;r)t.unshift("..");return t}function n(t,e){if(t.filter)return t.filter(e);for(var r=[],n=0;n=-1&&!o;i--){var a=i>=0?arguments[i]:t.cwd();if("string"!=typeof a)throw new TypeError("Arguments to path.resolve must be strings");a&&(e=a+"/"+e,o="/"===a.charAt(0))}return(o?"/":"")+(e=r(n(e.split("/"),(function(t){return!!t})),!o).join("/"))||"."},e.normalize=function(t){var i=e.isAbsolute(t),a="/"===o(t,-1);return(t=r(n(t.split("/"),(function(t){return!!t})),!i).join("/"))||i||(t="."),t&&a&&(t+="/"),(i?"/":"")+t},e.isAbsolute=function(t){return"/"===t.charAt(0)},e.join=function(){var t=Array.prototype.slice.call(arguments,0);return e.normalize(n(t,(function(t,e){if("string"!=typeof t)throw new TypeError("Arguments to path.join must be strings");return t})).join("/"))},e.relative=function(t,r){function n(t){for(var e=0;e=0&&""===t[r];r--);return e>r?[]:t.slice(e,r-e+1)}t=e.resolve(t).substr(1),r=e.resolve(r).substr(1);for(var o=n(t.split("/")),i=n(r.split("/")),a=Math.min(o.length,i.length),u=a,c=0;c=1;--i)if(47===(e=t.charCodeAt(i))){if(!o){n=i;break}}else o=!1;return-1===n?r?"/":".":r&&1===n?"/":t.slice(0,n)},e.basename=function(t,e){var r=function(t){"string"!=typeof t&&(t+="");var e,r=0,n=-1,o=!0;for(e=t.length-1;e>=0;--e)if(47===t.charCodeAt(e)){if(!o){r=e+1;break}}else-1===n&&(o=!1,n=e+1);return-1===n?"":t.slice(r,n)}(t);return e&&r.substr(-1*e.length)===e&&(r=r.substr(0,r.length-e.length)),r},e.extname=function(t){"string"!=typeof t&&(t+="");for(var e=-1,r=0,n=-1,o=!0,i=0,a=t.length-1;a>=0;--a){var u=t.charCodeAt(a);if(47!==u)-1===n&&(o=!1,n=a+1),46===u?-1===e?e=a:1!==i&&(i=1):-1!==e&&(i=-1);else if(!o){r=a+1;break}}return-1===e||-1===n||0===i||1===i&&e===n-1&&e===r+1?"":t.slice(e,n)};var o="b"==="ab".substr(-1)?function(t,e,r){return t.substr(e,r)}:function(t,e,r){return e<0&&(e=t.length+e),t.substr(e,r)}}).call(this,r(11))},function(t,e,r){"use strict";var n=r(8),o=r(14),i=r(31),a=r(32),u=r(35),c=r(37),f=r(38),p=r(39),l=r(2),s=r(5),y=r(44),d=r(47),b=r(49),v=r(52),h=r(55),g=s("Date.prototype.getTime"),m=Object.getPrototypeOf,S=s("Object.prototype.toString"),O=l("%Set%",!0),A=s("Map.prototype.has",!0),j=s("Map.prototype.get",!0),E=s("Map.prototype.size",!0),P=s("Set.prototype.add",!0),T=s("Set.prototype.delete",!0),w=s("Set.prototype.has",!0),x=s("Set.prototype.size",!0);function R(t,e,r,n){for(var o,i=d(t);(o=i.next())&&!o.done;)if(F(e,o.value,r,n))return T(t,o.value),!0;return!1}function M(t){return void 0===t?null:"object"!=typeof t?"symbol"!=typeof t&&("string"!=typeof t&&"number"!=typeof t||+t==+t):void 0}function I(t,e,r,n,o,i){var a=M(r);if(null!=a)return a;var u=j(e,a),c=h({},o,{strict:!1});return!(void 0===u&&!A(e,a)||!F(n,u,c,i))&&(!A(t,a)&&F(n,u,c,i))}function _(t,e,r){var n=M(r);return null!=n?n:w(e,n)&&!w(t,n)}function N(t,e,r,n,o,i){for(var a,u,c=d(t);(a=c.next())&&!a.done;)if(F(r,u=a.value,o,i)&&F(n,j(e,u),o,i))return T(t,u),!0;return!1}function F(t,e,r,l){var s=r||{};if(s.strict?i(t,e):t===e)return!0;if(p(t)!==p(e))return!1;if(!t||!e||"object"!=typeof t&&"object"!=typeof e)return s.strict?i(t,e):t==e;var b,T=l.has(t),M=l.has(e);if(T&&M){if(l.get(t)===l.get(e))return!0}else b={};return T||l.set(t,b),M||l.set(e,b),function(t,e,r,i){var p,l;if(typeof t!=typeof e)return!1;if(null==t||null==e)return!1;if(S(t)!==S(e))return!1;if(o(t)!==o(e))return!1;var s=c(t),b=c(e);if(s!==b)return!1;var T=t instanceof Error,M=e instanceof Error;if(T!==M)return!1;if((T||M)&&(t.name!==e.name||t.message!==e.message))return!1;var C=a(t),U=a(e);if(C!==U)return!1;if((C||U)&&(t.source!==e.source||u(t)!==u(e)))return!1;var D=f(t),W=f(e);if(D!==W)return!1;if((D||W)&&g(t)!==g(e))return!1;if(r.strict&&m&&m(t)!==m(e))return!1;if(v(t)!==v(e))return!1;var B=k(t),$=k(e);if(B!==$)return!1;if(B||$){if(t.length!==e.length)return!1;for(p=0;p=0;p--)if(L[p]!=z[p])return!1;for(p=L.length-1;p>=0;p--)if(l=L[p],!F(t[l],e[l],r,i))return!1;var G=y(t),q=y(e);if(G!==q)return!1;if("Set"===G||"Set"===q)return function(t,e,r,n){if(x(t)!==x(e))return!1;var o,i,a,u=d(t),c=d(e);for(;(o=u.next())&&!o.done;)if(o.value&&"object"==typeof o.value)a||(a=new O),P(a,o.value);else if(!w(e,o.value)){if(r.strict)return!1;if(!_(t,e,o.value))return!1;a||(a=new O),P(a,o.value)}if(a){for(;(i=c.next())&&!i.done;)if(i.value&&"object"==typeof i.value){if(!R(a,i.value,r.strict,n))return!1}else if(!r.strict&&!w(t,i.value)&&!R(a,i.value,r.strict,n))return!1;return 0===x(a)}return!0}(t,e,r,i);if("Map"===G)return function(t,e,r,n){if(E(t)!==E(e))return!1;var o,i,a,u,c,f,p=d(t),l=d(e);for(;(o=p.next())&&!o.done;)if(u=o.value[0],c=o.value[1],u&&"object"==typeof u)a||(a=new O),P(a,u);else if(void 0===(f=j(e,u))&&!A(e,u)||!F(c,f,r,n)){if(r.strict)return!1;if(!I(t,e,u,c,r,n))return!1;a||(a=new O),P(a,u)}if(a){for(;(i=l.next())&&!i.done;)if(u=i.value[0],f=i.value[1],u&&"object"==typeof u){if(!N(a,t,u,f,r,n))return!1}else if(!(r.strict||t.has(u)&&F(j(t,u),f,r,n)||N(a,t,u,f,h({},r,{strict:!1}),n)))return!1;return 0===x(a)}return!0}(t,e,r,i);return!0}(t,e,s,l)}function k(t){return!(!t||"object"!=typeof t||"number"!=typeof t.length)&&("function"==typeof t.copy&&"function"==typeof t.slice&&(!(t.length>0&&"number"!=typeof t[0])&&!!(t.constructor&&t.constructor.isBuffer&&t.constructor.isBuffer(t))))}t.exports=function(t,e,r){return F(t,e,r,b())}},function(t,e,r){"use strict";var n;if(!Object.keys){var o=Object.prototype.hasOwnProperty,i=Object.prototype.toString,a=r(13),u=Object.prototype.propertyIsEnumerable,c=!u.call({toString:null},"toString"),f=u.call((function(){}),"prototype"),p=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],l=function(t){var e=t.constructor;return e&&e.prototype===t},s={$applicationCache:!0,$console:!0,$external:!0,$frame:!0,$frameElement:!0,$frames:!0,$innerHeight:!0,$innerWidth:!0,$onmozfullscreenchange:!0,$onmozfullscreenerror:!0,$outerHeight:!0,$outerWidth:!0,$pageXOffset:!0,$pageYOffset:!0,$parent:!0,$scrollLeft:!0,$scrollTop:!0,$scrollX:!0,$scrollY:!0,$self:!0,$webkitIndexedDB:!0,$webkitStorageInfo:!0,$window:!0},y=function(){if("undefined"==typeof window)return!1;for(var t in window)try{if(!s["$"+t]&&o.call(window,t)&&null!==window[t]&&"object"==typeof window[t])try{l(window[t])}catch(t){return!0}}catch(t){return!0}return!1}();n=function(t){var e=null!==t&&"object"==typeof t,r="[object Function]"===i.call(t),n=a(t),u=e&&"[object String]"===i.call(t),s=[];if(!e&&!r&&!n)throw new TypeError("Object.keys called on a non-object");var d=f&&r;if(u&&t.length>0&&!o.call(t,0))for(var b=0;b0)for(var v=0;v=t.length;return n||(r=t[e],e+=1),{done:n,value:r}}}},b=function(t){if(i(t)||n(t))return d(t);if(a(t)){var e=0;return{next:function(){var r=function(t,e){if(e+1>=t.length)return e+1;var r=s(t,e);if(r<55296||r>56319)return e+1;var n=s(t,e+1);return n<56320||n>57343?e+1:e+2}(t,e),n=y(t,e,r);return e=r,{done:r>t.length,value:n}}}}};if(c||f){var v=r(19),h=r(20),g=p("Map.prototype.forEach",!0),m=p("Set.prototype.forEach",!0);if(void 0===e||!e.versions||!e.versions.node)var S=p("Map.prototype.iterator",!0),O=p("Set.prototype.iterator",!0),A=function(t){var e=!1;return{next:function(){try{return{done:e,value:e?void 0:t.next()}}catch(t){return e=!0,{done:!0,value:void 0}}}}};var j=p("Map.prototype.@@iterator",!0)||p("Map.prototype._es6-shim iterator_",!0),E=p("Set.prototype.@@iterator",!0)||p("Set.prototype._es6-shim iterator_",!0);t.exports=function(t){return function(t){if(v(t)){if(S)return A(S(t));if(j)return j(t);if(g){var e=[];return g(t,(function(t,r){l(e,[r,t])})),d(e)}}if(h(t)){if(O)return A(O(t));if(E)return E(t);if(m){var r=[];return m(t,(function(t){l(r,t)})),d(r)}}}(t)||b(t)}}else t.exports=b}}).call(this,r(11))},function(t,e){var r={}.toString;t.exports=Array.isArray||function(t){return"[object Array]"==r.call(t)}},function(t,e,r){"use strict";var n=r(2),o=r(5),i=r(50),a=n("%TypeError%"),u=n("%WeakMap%",!0),c=n("%Map%",!0),f=o("Array.prototype.push"),p=o("WeakMap.prototype.get",!0),l=o("WeakMap.prototype.set",!0),s=o("WeakMap.prototype.has",!0),y=o("Map.prototype.get",!0),d=o("Map.prototype.set",!0),b=o("Map.prototype.has",!0);t.exports=function(){var t,e,r,n={assert:function(t){if(!n.has(t))throw new a("Side channel does not contain "+i(t))},get:function(n){if(u&&n&&("object"==typeof n||"function"==typeof n)){if(t)return p(t,n)}else if(c){if(e)return y(e,n)}else if(r)return function(t,e){for(var r=0;r0?"0":"-0":String(e);if("bigint"==typeof e)return String(e)+"n";var c=void 0===u.depth?5:u.depth;if(void 0===n&&(n=0),n>=c&&c>0&&"object"==typeof e)return"[Object]";if(void 0===o)o=[];else if(function(t,e){if(t.indexOf)return t.indexOf(e);for(var r=0,n=t.length;r=0)return"[Circular]";function d(e,r){return r&&(o=o.slice()).push(r),t(e,u,n+1,o)}if("function"==typeof e){var h=function(t){if(t.name)return t.name;var e=b.call(t,/^function\s*([\w$]+)/);if(e)return e[1];return null}(e);return"[Function"+(h?": "+h:"")+"]"}if(A(e)){var j=Symbol.prototype.toString.call(e);return"object"==typeof e?w(j):j}if(function(t){if(!t||"object"!=typeof t)return!1;if("undefined"!=typeof HTMLElement&&t instanceof HTMLElement)return!0;return"string"==typeof t.nodeName&&"function"==typeof t.getAttribute}(e)){for(var I="<"+String(e.nodeName).toLowerCase(),_=e.attributes||[],N=0;N<_.length;N++)I+=" "+_[N].name+"="+m(S(_[N].value),"double",u);return I+=">",e.childNodes&&e.childNodes.length&&(I+="..."),I+=""}if(O(e))return 0===e.length?"[]":"[ "+M(e,d).join(", ")+" ]";if(function(t){return"[object Error]"===P(t)}(e)){var F=M(e,d);return 0===F.length?"["+String(e)+"]":"{ ["+String(e)+"] "+F.join(", ")+" }"}if("object"==typeof e){if(g&&"function"==typeof e[g])return e[g]();if("function"==typeof e.inspect)return e.inspect()}if(function(t){if(!i||!t||"object"!=typeof t)return!1;try{i.call(t);try{f.call(t)}catch(t){return!0}return t instanceof Map}catch(t){}return!1}(e)){var k=[];return a.call(e,(function(t,r){k.push(d(r,e)+" => "+d(t,e))})),R("Map",i.call(e),k)}if(function(t){if(!f||!t||"object"!=typeof t)return!1;try{f.call(t);try{i.call(t)}catch(t){return!0}return t instanceof Set}catch(t){}return!1}(e)){var C=[];return p.call(e,(function(t){C.push(d(t,e))})),R("Set",f.call(e),C)}if(function(t){if(!l||!t||"object"!=typeof t)return!1;try{l.call(t,l);try{s.call(t,s)}catch(t){return!0}return t instanceof WeakMap}catch(t){}return!1}(e))return x("WeakMap");if(function(t){if(!s||!t||"object"!=typeof t)return!1;try{s.call(t,s);try{l.call(t,l)}catch(t){return!0}return t instanceof WeakSet}catch(t){}return!1}(e))return x("WeakSet");if(function(t){return"[object Number]"===P(t)}(e))return w(d(Number(e)));if(function(t){return"[object BigInt]"===P(t)}(e))return w(d(v.call(e)));if(function(t){return"[object Boolean]"===P(t)}(e))return w(y.call(e));if(function(t){return"[object String]"===P(t)}(e))return w(d(String(e)));if(!function(t){return"[object Date]"===P(t)}(e)&&!function(t){return"[object RegExp]"===P(t)}(e)){var U=M(e,d);return 0===U.length?"{}":"{ "+U.join(", ")+" }"}return String(e)};var j=Object.prototype.hasOwnProperty||function(t){return t in this};function E(t,e){return j.call(t,e)}function P(t){return d.call(t)}function T(t){var e=t.charCodeAt(0),r={8:"b",9:"t",10:"n",12:"f",13:"r"}[e];return r?"\\"+r:"\\x"+(e<16?"0":"")+e.toString(16)}function w(t){return"Object("+t+")"}function x(t){return t+" { ? }"}function R(t,e,r){return t+" ("+e+") {"+r.join(", ")+"}"}function M(t,e){var r=O(t),n=[];if(r){n.length=t.length;for(var o=0;o-1}return!!s&&function(t){var e=!1;return n(l,(function(r,n){if(!e)try{e=r.call(t)===n}catch(t){}})),e}(t)}}).call(this,r(7))},function(t,e,r){"use strict";var n=r(3),o=r(24),i=r(25),a=r(56),u=i();n(u,{getPolyfill:i,implementation:o,shim:a}),t.exports=u},function(t,e,r){"use strict";var n=r(3),o=r(25);t.exports=function(){var t=o();return n(Object,{assign:t},{assign:function(){return Object.assign!==t}}),t}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.ResolveAbstractionBranch=e.NonExhaustivePatterns=void 0;var n,o,i,a=r(12);class u extends Error{}e.NonExhaustivePatterns=u;class c{}e.ResolveAbstractionBranch=c,i=(t,e,r,n=!0)=>{let o;for(const[r,i,u]of e){try{o=new a.PatternMatch({defaults:i,partialMatching:n}).perform(r,t)}catch(t){if(t instanceof a.PatternNotMatching)continue;t instanceof a.PatternPartiallyMatching&&(o=void 0)}return void 0===o?void 0:u(o)}if(r)return r();throw new u("Non-exhaustive patterns")},(o="perform")in(n=c)?Object.defineProperty(n,o,{value:i,enumerable:!0,configurable:!0,writable:!0}):n[o]=i}]); \ No newline at end of file diff --git a/test.tn b/test.tn new file mode 100644 index 0000000..f92112e --- /dev/null +++ b/test.tn @@ -0,0 +1,30 @@ +import { + print :: String -> Void, + to_str :: Number -> String, + neg: ! :: Number -> Number, + add: + :: Number -> Number -> Number, + sub: - :: Number -> Number -> Number, + mod: % :: Number -> Number -> Number, + concat: + :: [Number] -> [Number] -> [Number], + leq: <= :: Number -> Number -> Boolean, + lt: < :: Number -> Number -> Boolean, + geq: >= :: Number -> Number -> Boolean, + gt: > :: Number -> Number -> Boolean, + eq: == :: Number -> Number -> Boolean +} from '../tony/dist/test/stdlib.js' + + +quicksort := + ([]) => [] + ([pivot, ...rest]) => + l := quicksort([n | n in rest if n <= pivot]) + r := quicksort([n | n in rest if n > pivot]) + l + [pivot] + r + +list := quicksort([5, 7, 2, 0, 4, 3]) +print(list[0].to_str) +print(list[1].to_str) +print(list[2].to_str) +print(list[3].to_str) +print(list[4].to_str) +print(list[5].to_str) diff --git a/yalc.lock b/yalc.lock index dbbcf98..6f0a724 100644 --- a/yalc.lock +++ b/yalc.lock @@ -2,7 +2,7 @@ "version": "v1", "packages": { "tony-lang": { - "signature": "afbd7cb804326aa5fded581a46697147", + "signature": "dae48e8c032e9be3346d19eba294b036", "file": true } } diff --git a/yarn.lock b/yarn.lock index de8577d..8c14089 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1785,7 +1785,7 @@ core-js-compat@^3.6.2: browserslist "^4.8.3" semver "7.0.0" -core-js@^3.6.5: +core-js@^3.6.4: version "3.6.5" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a" integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA== @@ -5064,9 +5064,9 @@ to-regex@^3.0.1, to-regex@^3.0.2: safe-regex "^1.1.0" "tony-lang@file:.yalc/tony-lang": - version "0.1.4-afbd7cb8" + version "0.1.4-698841e7" dependencies: - core-js "^3.6.5" + core-js "^3.6.4" deep-equal "^2.0.1" inquirer "^7.1.0" mkdirp "^1.0.3"