@aster-cloud/aster-lang-ts 1.0.37 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/README.md +13 -7
- package/dist/scripts/aster.js +10 -5
- package/dist/scripts/aster.js.map +1 -1
- package/dist/scripts/core-ir-json.js +8 -3
- package/dist/scripts/core-ir-json.js.map +1 -1
- package/dist/scripts/emit-classfiles.js +3 -2
- package/dist/scripts/emit-classfiles.js.map +1 -1
- package/dist/scripts/emit-core.js +8 -5
- package/dist/scripts/emit-core.js.map +1 -1
- package/dist/scripts/generate-casefold.d.ts +2 -0
- package/dist/scripts/generate-casefold.d.ts.map +1 -0
- package/dist/scripts/generate-casefold.js +34 -0
- package/dist/scripts/generate-casefold.js.map +1 -0
- package/dist/scripts/golden.js +16 -16
- package/dist/scripts/golden.js.map +1 -1
- package/dist/scripts/perf-benchmark.js +1 -0
- package/dist/scripts/perf-benchmark.js.map +1 -1
- package/dist/scripts/profile-medium-project.js +1 -1
- package/dist/scripts/profile-medium-project.js.map +1 -1
- package/dist/scripts/repl.js +3 -8
- package/dist/scripts/repl.js.map +1 -1
- package/dist/scripts/typecheck-cli.js +10 -9
- package/dist/scripts/typecheck-cli.js.map +1 -1
- package/dist/scripts/update-eff-violation-expected.js +6 -6
- package/dist/scripts/update-eff-violation-expected.js.map +1 -1
- package/dist/scripts/verify-capabilities.js +3 -6
- package/dist/scripts/verify-capabilities.js.map +1 -1
- package/dist/src/ai/validator.d.ts +1 -1
- package/dist/src/ai/validator.d.ts.map +1 -1
- package/dist/src/ai/validator.js +12 -12
- package/dist/src/ai/validator.js.map +1 -1
- package/dist/src/ast/ast.d.ts +1 -0
- package/dist/src/ast/ast.d.ts.map +1 -1
- package/dist/src/ast/ast.js.map +1 -1
- package/dist/src/browser.d.ts +27 -10
- package/dist/src/browser.d.ts.map +1 -1
- package/dist/src/browser.js +42 -54
- package/dist/src/browser.js.map +1 -1
- package/dist/src/cli/policy-converter.js +5 -9
- package/dist/src/cli/policy-converter.js.map +1 -1
- package/dist/src/compile-pipeline.d.ts +48 -0
- package/dist/src/compile-pipeline.d.ts.map +1 -0
- package/dist/src/compile-pipeline.js +89 -0
- package/dist/src/compile-pipeline.js.map +1 -0
- package/dist/src/config/lexicons/diagnostic-messages.d.ts.map +1 -1
- package/dist/src/config/lexicons/diagnostic-messages.js +20 -0
- package/dist/src/config/lexicons/diagnostic-messages.js.map +1 -1
- package/dist/src/config/lexicons/identifiers/casefold-table.generated.d.ts +9 -0
- package/dist/src/config/lexicons/identifiers/casefold-table.generated.d.ts.map +1 -0
- package/dist/src/config/lexicons/identifiers/casefold-table.generated.js +1467 -0
- package/dist/src/config/lexicons/identifiers/casefold-table.generated.js.map +1 -0
- package/dist/src/config/lexicons/identifiers/casefold.d.ts +7 -0
- package/dist/src/config/lexicons/identifiers/casefold.d.ts.map +1 -0
- package/dist/src/config/lexicons/identifiers/casefold.js +37 -0
- package/dist/src/config/lexicons/identifiers/casefold.js.map +1 -0
- package/dist/src/config/lexicons/identifiers/domains/finance.loan.de-DE.d.ts.map +1 -1
- package/dist/src/config/lexicons/identifiers/domains/finance.loan.de-DE.js +8 -0
- package/dist/src/config/lexicons/identifiers/domains/finance.loan.de-DE.js.map +1 -1
- package/dist/src/config/lexicons/identifiers/domains/finance.loan.en-US.d.ts.map +1 -1
- package/dist/src/config/lexicons/identifiers/domains/finance.loan.en-US.js +8 -0
- package/dist/src/config/lexicons/identifiers/domains/finance.loan.en-US.js.map +1 -1
- package/dist/src/config/lexicons/identifiers/domains/finance.loan.zh-CN.d.ts.map +1 -1
- package/dist/src/config/lexicons/identifiers/domains/finance.loan.zh-CN.js +8 -0
- package/dist/src/config/lexicons/identifiers/domains/finance.loan.zh-CN.js.map +1 -1
- package/dist/src/config/lexicons/identifiers/domains/insurance.auto.de-DE.d.ts.map +1 -1
- package/dist/src/config/lexicons/identifiers/domains/insurance.auto.de-DE.js +6 -0
- package/dist/src/config/lexicons/identifiers/domains/insurance.auto.de-DE.js.map +1 -1
- package/dist/src/config/lexicons/identifiers/domains/insurance.auto.en-US.d.ts.map +1 -1
- package/dist/src/config/lexicons/identifiers/domains/insurance.auto.en-US.js +6 -0
- package/dist/src/config/lexicons/identifiers/domains/insurance.auto.en-US.js.map +1 -1
- package/dist/src/config/lexicons/identifiers/domains/insurance.auto.zh-CN.d.ts.map +1 -1
- package/dist/src/config/lexicons/identifiers/domains/insurance.auto.zh-CN.js +6 -0
- package/dist/src/config/lexicons/identifiers/domains/insurance.auto.zh-CN.js.map +1 -1
- package/dist/src/config/lexicons/identifiers/index.d.ts +7 -9
- package/dist/src/config/lexicons/identifiers/index.d.ts.map +1 -1
- package/dist/src/config/lexicons/identifiers/index.js +8 -9
- package/dist/src/config/lexicons/identifiers/index.js.map +1 -1
- package/dist/src/config/lexicons/identifiers/registry.d.ts +11 -5
- package/dist/src/config/lexicons/identifiers/registry.d.ts.map +1 -1
- package/dist/src/config/lexicons/identifiers/registry.js +20 -14
- package/dist/src/config/lexicons/identifiers/registry.js.map +1 -1
- package/dist/src/config/lexicons/identifiers/scopes.d.ts +52 -0
- package/dist/src/config/lexicons/identifiers/scopes.d.ts.map +1 -0
- package/dist/src/config/lexicons/identifiers/scopes.js +166 -0
- package/dist/src/config/lexicons/identifiers/scopes.js.map +1 -0
- package/dist/src/config/lexicons/identifiers/types.d.ts +15 -29
- package/dist/src/config/lexicons/identifiers/types.d.ts.map +1 -1
- package/dist/src/config/lexicons/identifiers/types.js +91 -101
- package/dist/src/config/lexicons/identifiers/types.js.map +1 -1
- package/dist/src/config/lexicons/lsp-ui-texts.d.ts +7 -0
- package/dist/src/config/lexicons/lsp-ui-texts.d.ts.map +1 -1
- package/dist/src/config/lexicons/lsp-ui-texts.js +12 -0
- package/dist/src/config/lexicons/lsp-ui-texts.js.map +1 -1
- package/dist/src/config/semantic.d.ts +5 -0
- package/dist/src/config/semantic.d.ts.map +1 -1
- package/dist/src/config/semantic.js +12 -0
- package/dist/src/config/semantic.js.map +1 -1
- package/dist/src/core/inferred_fields.d.ts +10 -0
- package/dist/src/core/inferred_fields.d.ts.map +1 -1
- package/dist/src/core/inferred_fields.js +9 -0
- package/dist/src/core/inferred_fields.js.map +1 -1
- package/dist/src/core/interpreter.d.ts.map +1 -1
- package/dist/src/core/interpreter.js +12 -10
- package/dist/src/core/interpreter.js.map +1 -1
- package/dist/src/core/resolution-mark.d.ts +28 -0
- package/dist/src/core/resolution-mark.d.ts.map +1 -0
- package/dist/src/core/resolution-mark.js +19 -0
- package/dist/src/core/resolution-mark.js.map +1 -0
- package/dist/src/core/resolve-typed-references.d.ts +36 -0
- package/dist/src/core/resolve-typed-references.d.ts.map +1 -0
- package/dist/src/core/resolve-typed-references.js +703 -0
- package/dist/src/core/resolve-typed-references.js.map +1 -0
- package/dist/src/diagnostics/error_codes.d.ts +11 -1
- package/dist/src/diagnostics/error_codes.d.ts.map +1 -1
- package/dist/src/diagnostics/error_codes.js +80 -0
- package/dist/src/diagnostics/error_codes.js.map +1 -1
- package/dist/src/frontend/canonicalizer.d.ts +8 -13
- package/dist/src/frontend/canonicalizer.d.ts.map +1 -1
- package/dist/src/frontend/canonicalizer.js +19 -48
- package/dist/src/frontend/canonicalizer.js.map +1 -1
- package/dist/src/frontend/resolution-choice.d.ts +34 -0
- package/dist/src/frontend/resolution-choice.d.ts.map +1 -0
- package/dist/src/frontend/resolution-choice.js +69 -0
- package/dist/src/frontend/resolution-choice.js.map +1 -0
- package/dist/src/frontend/resolution-context.d.ts +28 -0
- package/dist/src/frontend/resolution-context.d.ts.map +1 -0
- package/dist/src/frontend/resolution-context.js +7 -0
- package/dist/src/frontend/resolution-context.js.map +1 -0
- package/dist/src/frontend/resolve-declarations.d.ts +13 -0
- package/dist/src/frontend/resolve-declarations.d.ts.map +1 -0
- package/dist/src/frontend/resolve-declarations.js +611 -0
- package/dist/src/frontend/resolve-declarations.js.map +1 -0
- package/dist/src/frontend/resolve-policy-types.d.ts +54 -0
- package/dist/src/frontend/resolve-policy-types.d.ts.map +1 -0
- package/dist/src/frontend/resolve-policy-types.js +71 -0
- package/dist/src/frontend/resolve-policy-types.js.map +1 -0
- package/dist/src/index.d.ts +14 -7
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +14 -7
- package/dist/src/index.js.map +1 -1
- package/dist/src/lower_to_core.d.ts +2 -16
- package/dist/src/lower_to_core.d.ts.map +1 -1
- package/dist/src/lower_to_core.js +101 -44
- package/dist/src/lower_to_core.js.map +1 -1
- package/dist/src/lsp/canonicalize-options.d.ts +8 -11
- package/dist/src/lsp/canonicalize-options.d.ts.map +1 -1
- package/dist/src/lsp/canonicalize-options.js +8 -8
- package/dist/src/lsp/canonicalize-options.js.map +1 -1
- package/dist/src/lsp/completion.d.ts +32 -5
- package/dist/src/lsp/completion.d.ts.map +1 -1
- package/dist/src/lsp/completion.js +59 -0
- package/dist/src/lsp/completion.js.map +1 -1
- package/dist/src/lsp/diagnostics.d.ts +2 -0
- package/dist/src/lsp/diagnostics.d.ts.map +1 -1
- package/dist/src/lsp/diagnostics.js +77 -73
- package/dist/src/lsp/diagnostics.js.map +1 -1
- package/dist/src/lsp/navigation/hover.d.ts +12 -5
- package/dist/src/lsp/navigation/hover.d.ts.map +1 -1
- package/dist/src/lsp/navigation/hover.js +136 -90
- package/dist/src/lsp/navigation/hover.js.map +1 -1
- package/dist/src/lsp/parse-document.d.ts +3 -0
- package/dist/src/lsp/parse-document.d.ts.map +1 -1
- package/dist/src/lsp/parse-document.js +5 -17
- package/dist/src/lsp/parse-document.js.map +1 -1
- package/dist/src/lsp/resolution.d.ts +40 -0
- package/dist/src/lsp/resolution.d.ts.map +1 -0
- package/dist/src/lsp/resolution.js +179 -0
- package/dist/src/lsp/resolution.js.map +1 -0
- package/dist/src/lsp/server.js +3 -1
- package/dist/src/lsp/server.js.map +1 -1
- package/dist/src/lsp/source-positions.d.ts +2 -8
- package/dist/src/lsp/source-positions.d.ts.map +1 -1
- package/dist/src/lsp/source-positions.js +4 -16
- package/dist/src/lsp/source-positions.js.map +1 -1
- package/dist/src/mapping/pipeline.js +3 -2
- package/dist/src/mapping/pipeline.js.map +1 -1
- package/dist/src/mapping/review-queue.d.ts.map +1 -1
- package/dist/src/mapping/review-queue.js +3 -2
- package/dist/src/mapping/review-queue.js.map +1 -1
- package/dist/src/parser/context.d.ts +2 -0
- package/dist/src/parser/context.d.ts.map +1 -1
- package/dist/src/parser/context.js +1 -0
- package/dist/src/parser/context.js.map +1 -1
- package/dist/src/parser/decl-parser.d.ts.map +1 -1
- package/dist/src/parser/decl-parser.js +1 -0
- package/dist/src/parser/decl-parser.js.map +1 -1
- package/dist/src/parser/expr-stmt-parser.d.ts.map +1 -1
- package/dist/src/parser/expr-stmt-parser.js +2 -16
- package/dist/src/parser/expr-stmt-parser.js.map +1 -1
- package/dist/src/parser.d.ts.map +1 -1
- package/dist/src/parser.js +3 -1
- package/dist/src/parser.js.map +1 -1
- package/dist/src/typecheck/expression.d.ts.map +1 -1
- package/dist/src/typecheck/expression.js +13 -0
- package/dist/src/typecheck/expression.js.map +1 -1
- package/dist/src/typecheck/generics.d.ts +5 -0
- package/dist/src/typecheck/generics.d.ts.map +1 -1
- package/dist/src/typecheck/generics.js +15 -8
- package/dist/src/typecheck/generics.js.map +1 -1
- package/dist/src/types.d.ts +45 -13
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/dist/src/version.d.ts +17 -0
- package/dist/src/version.d.ts.map +1 -0
- package/dist/src/version.js +17 -0
- package/dist/src/version.js.map +1 -0
- package/dist/test/integration/builtin-enum-vocabulary.test.d.ts +2 -0
- package/dist/test/integration/builtin-enum-vocabulary.test.d.ts.map +1 -0
- package/dist/test/integration/builtin-enum-vocabulary.test.js +51 -0
- package/dist/test/integration/builtin-enum-vocabulary.test.js.map +1 -0
- package/dist/test/integration/lsp/lsp-rename-safety.test.js +0 -13
- package/dist/test/integration/lsp/lsp-rename-safety.test.js.map +1 -1
- package/dist/test/integration/name-resolution-entrypoints.test.d.ts +2 -0
- package/dist/test/integration/name-resolution-entrypoints.test.d.ts.map +1 -0
- package/dist/test/integration/name-resolution-entrypoints.test.js +109 -0
- package/dist/test/integration/name-resolution-entrypoints.test.js.map +1 -0
- package/dist/test/integration/vocabulary-resolution.test.d.ts +2 -0
- package/dist/test/integration/vocabulary-resolution.test.d.ts.map +1 -0
- package/dist/test/integration/vocabulary-resolution.test.js +291 -0
- package/dist/test/integration/vocabulary-resolution.test.js.map +1 -0
- package/dist/test/property/property.test.js +2 -1
- package/dist/test/property/property.test.js.map +1 -1
- package/dist/test/unit/canonicalizer/canonicalizer.test.js +7 -79
- package/dist/test/unit/canonicalizer/canonicalizer.test.js.map +1 -1
- package/dist/test/unit/config/lexicons/identifiers/identifier-mapping.test.js +134 -31
- package/dist/test/unit/config/lexicons/identifiers/identifier-mapping.test.js.map +1 -1
- package/dist/test/unit/config/lexicons/identifiers/literal-macro.test.js +16 -18
- package/dist/test/unit/config/lexicons/identifiers/literal-macro.test.js.map +1 -1
- package/dist/test/unit/core/resolve-typed-references.test.d.ts +2 -0
- package/dist/test/unit/core/resolve-typed-references.test.d.ts.map +1 -0
- package/dist/test/unit/core/resolve-typed-references.test.js +600 -0
- package/dist/test/unit/core/resolve-typed-references.test.js.map +1 -0
- package/dist/test/unit/diagnostics/resolution-codes.test.d.ts +6 -0
- package/dist/test/unit/diagnostics/resolution-codes.test.d.ts.map +1 -0
- package/dist/test/unit/diagnostics/resolution-codes.test.js +83 -0
- package/dist/test/unit/diagnostics/resolution-codes.test.js.map +1 -0
- package/dist/test/unit/formatter-corpus-roundtrip.test.js +9 -0
- package/dist/test/unit/formatter-corpus-roundtrip.test.js.map +1 -1
- package/dist/test/unit/frontend/resolve-declarations.test.d.ts +2 -0
- package/dist/test/unit/frontend/resolve-declarations.test.d.ts.map +1 -0
- package/dist/test/unit/frontend/resolve-declarations.test.js +400 -0
- package/dist/test/unit/frontend/resolve-declarations.test.js.map +1 -0
- package/dist/test/unit/identifiers/casefold.test.d.ts +2 -0
- package/dist/test/unit/identifiers/casefold.test.d.ts.map +1 -0
- package/dist/test/unit/identifiers/casefold.test.js +77 -0
- package/dist/test/unit/identifiers/casefold.test.js.map +1 -0
- package/dist/test/unit/identifiers/scopes.test.d.ts +2 -0
- package/dist/test/unit/identifiers/scopes.test.d.ts.map +1 -0
- package/dist/test/unit/identifiers/scopes.test.js +109 -0
- package/dist/test/unit/identifiers/scopes.test.js.map +1 -0
- package/dist/test/unit/lambda-capture-parity.test.js +12 -5
- package/dist/test/unit/lambda-capture-parity.test.js.map +1 -1
- package/dist/test/unit/lsp/lsp-locale-fixes.test.js +10 -11
- package/dist/test/unit/lsp/lsp-locale-fixes.test.js.map +1 -1
- package/dist/test/unit/lsp/resolution-completion.test.d.ts +2 -0
- package/dist/test/unit/lsp/resolution-completion.test.d.ts.map +1 -0
- package/dist/test/unit/lsp/resolution-completion.test.js +79 -0
- package/dist/test/unit/lsp/resolution-completion.test.js.map +1 -0
- package/dist/test/unit/lsp/resolution-diagnostics.test.d.ts +2 -0
- package/dist/test/unit/lsp/resolution-diagnostics.test.d.ts.map +1 -0
- package/dist/test/unit/lsp/resolution-diagnostics.test.js +88 -0
- package/dist/test/unit/lsp/resolution-diagnostics.test.js.map +1 -0
- package/dist/test/unit/lsp/resolution-fixture.d.ts +32 -0
- package/dist/test/unit/lsp/resolution-fixture.d.ts.map +1 -0
- package/dist/test/unit/lsp/resolution-fixture.js +69 -0
- package/dist/test/unit/lsp/resolution-fixture.js.map +1 -0
- package/dist/test/unit/lsp/resolution-hover.test.d.ts +2 -0
- package/dist/test/unit/lsp/resolution-hover.test.d.ts.map +1 -0
- package/dist/test/unit/lsp/resolution-hover.test.js +107 -0
- package/dist/test/unit/lsp/resolution-hover.test.js.map +1 -0
- package/dist/test/unit/lsp/tenant-vocabulary.test.js +21 -47
- package/dist/test/unit/lsp/tenant-vocabulary.test.js.map +1 -1
- package/dist/test/unit/parser/parser.test.js +22 -0
- package/dist/test/unit/parser/parser.test.js.map +1 -1
- package/dist/test/unit/public-api-checked-entries.test.d.ts +2 -0
- package/dist/test/unit/public-api-checked-entries.test.d.ts.map +1 -0
- package/dist/test/unit/public-api-checked-entries.test.js +28 -0
- package/dist/test/unit/public-api-checked-entries.test.js.map +1 -0
- package/dist/test/unit/typecheck/qualified-enum-member.test.d.ts +2 -0
- package/dist/test/unit/typecheck/qualified-enum-member.test.d.ts.map +1 -0
- package/dist/test/unit/typecheck/qualified-enum-member.test.js +73 -0
- package/dist/test/unit/typecheck/qualified-enum-member.test.js.map +1 -0
- package/dist/test/unit/version.test.d.ts +9 -0
- package/dist/test/unit/version.test.d.ts.map +1 -0
- package/dist/test/unit/version.test.js +34 -0
- package/dist/test/unit/version.test.js.map +1 -0
- package/package.json +1 -1
- package/dist/src/config/lexicons/identifiers/examples/canonicalizer-integration.d.ts +0 -47
- package/dist/src/config/lexicons/identifiers/examples/canonicalizer-integration.d.ts.map +0 -1
- package/dist/src/config/lexicons/identifiers/examples/canonicalizer-integration.js +0 -142
- package/dist/src/config/lexicons/identifiers/examples/canonicalizer-integration.js.map +0 -1
|
@@ -0,0 +1,600 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module test/unit/core/resolve-typed-references.test
|
|
3
|
+
*
|
|
4
|
+
* R2 类型驱动解析(ADR 0051 §4 三态表、§6.2 期望枚举跨层裁决)。
|
|
5
|
+
* 除特别注明外均为 解析器 → R1 → lower → R2 端到端。
|
|
6
|
+
*/
|
|
7
|
+
import { describe, it } from 'node:test';
|
|
8
|
+
import assert from 'node:assert/strict';
|
|
9
|
+
import { canonicalize } from '../../../src/frontend/canonicalizer.js';
|
|
10
|
+
import { lex } from '../../../src/frontend/lexer.js';
|
|
11
|
+
import { parseWithLexicon } from '../../../src/parser.js';
|
|
12
|
+
import { EN_US } from '../../../src/config/lexicons/en-US.js';
|
|
13
|
+
import { DE_DE } from '../../../src/config/lexicons/de-DE.js';
|
|
14
|
+
import { buildVocabularyScopes } from '../../../src/config/lexicons/identifiers/scopes.js';
|
|
15
|
+
import { IdentifierKind } from '../../../src/config/lexicons/identifiers/types.js';
|
|
16
|
+
import { resolveDeclarations } from '../../../src/frontend/resolve-declarations.js';
|
|
17
|
+
import { NO_IMPORTS } from '../../../src/frontend/resolution-context.js';
|
|
18
|
+
import { lowerModule } from '../../../src/lower_to_core.js';
|
|
19
|
+
import { resolveTypedReferences } from '../../../src/core/resolve-typed-references.js';
|
|
20
|
+
import { resolutionMarkOf } from '../../../src/core/resolution-mark.js';
|
|
21
|
+
function parse(source, lexicon = DE_DE) {
|
|
22
|
+
const tokens = lex(canonicalize(source, lexicon), lexicon);
|
|
23
|
+
const result = parseWithLexicon(tokens, lexicon);
|
|
24
|
+
const errors = result.diagnostics.filter((d) => d.severity === 'error');
|
|
25
|
+
assert.equal(errors.length, 0, JSON.stringify(errors));
|
|
26
|
+
return result.ast;
|
|
27
|
+
}
|
|
28
|
+
const struct = (canonical, localized) => ({
|
|
29
|
+
canonical, localized, kind: IdentifierKind.STRUCT,
|
|
30
|
+
});
|
|
31
|
+
const field = (parent, canonical, localized) => ({
|
|
32
|
+
canonical, localized, kind: IdentifierKind.FIELD, parent,
|
|
33
|
+
});
|
|
34
|
+
const member = (parent, canonical, localized) => ({
|
|
35
|
+
canonical, localized, kind: IdentifierKind.ENUM_VALUE, parent,
|
|
36
|
+
});
|
|
37
|
+
const fn = (canonical, localized) => ({
|
|
38
|
+
canonical, localized, kind: IdentifierKind.FUNCTION,
|
|
39
|
+
});
|
|
40
|
+
function ctxOf(mappings) {
|
|
41
|
+
const vocab = {
|
|
42
|
+
id: 't', name: 't', locale: 'de-DE', version: '1',
|
|
43
|
+
structs: mappings.filter((m) => m.kind === IdentifierKind.STRUCT),
|
|
44
|
+
fields: mappings.filter((m) => m.kind === IdentifierKind.FIELD),
|
|
45
|
+
functions: mappings.filter((m) => m.kind === IdentifierKind.FUNCTION),
|
|
46
|
+
enumValues: mappings.filter((m) => m.kind === IdentifierKind.ENUM_VALUE),
|
|
47
|
+
};
|
|
48
|
+
return { scopes: buildVocabularyScopes(vocab), imports: NO_IMPORTS };
|
|
49
|
+
}
|
|
50
|
+
/** 解析器 → R1 → lower → R2;edit 用于改写解析器无法直接产出的 AST 形状。 */
|
|
51
|
+
function run(source, mappings, lexicon = DE_DE, edit = (m) => m) {
|
|
52
|
+
const ctx = ctxOf(mappings);
|
|
53
|
+
const r1 = resolveDeclarations(edit(parse(source, lexicon)), ctx);
|
|
54
|
+
const r2 = resolveTypedReferences(lowerModule(r1.module), ctx);
|
|
55
|
+
return { module: r2.module, r1: r1.diagnostics, r2: r2.diagnostics };
|
|
56
|
+
}
|
|
57
|
+
const codes = (ds) => ds.map((d) => d.code);
|
|
58
|
+
function func(m, name) {
|
|
59
|
+
const f = m.decls.find((d) => d.kind === 'Func' && d.name === name);
|
|
60
|
+
assert.ok(f, `函数 ${name} 不存在`);
|
|
61
|
+
return f;
|
|
62
|
+
}
|
|
63
|
+
function stmt(f, i, kind) {
|
|
64
|
+
const s = f.body.statements[i];
|
|
65
|
+
assert.ok(s && s.kind === kind, `body[${i}] 应为 ${kind},实为 ${s?.kind}`);
|
|
66
|
+
return s;
|
|
67
|
+
}
|
|
68
|
+
function returned(f, i) {
|
|
69
|
+
return stmt(f, i, 'Return').expr;
|
|
70
|
+
}
|
|
71
|
+
function nameOf(e) {
|
|
72
|
+
assert.equal(e.kind, 'Name');
|
|
73
|
+
return e;
|
|
74
|
+
}
|
|
75
|
+
const MODULE = 'Modul m.\n\n';
|
|
76
|
+
const DRIVER_DECL = 'Definiere Fahrer hat alter als Ganzzahl.\n\n';
|
|
77
|
+
const LEVEL_DECL = 'Definiere Stufe als eines von Hoch, Niedrig.\n\n';
|
|
78
|
+
const DRIVER = [struct('Driver', 'Fahrer'), field('Driver', 'age', 'alter')];
|
|
79
|
+
const LEVEL = [struct('Level', 'Stufe'), member('Level', 'High', 'Hoch'), member('Level', 'Low', 'Niedrig')];
|
|
80
|
+
describe('resolveTypedReferences(R2)', () => {
|
|
81
|
+
describe('FIELD_REF:点分后续段与 Set 目标', () => {
|
|
82
|
+
it('x 为 Fahrer 参数:x.alter → x.age,origin 记原写法与角色', () => {
|
|
83
|
+
const { module, r1, r2 } = run(MODULE + DRIVER_DECL + 'Regel f gegeben x als Fahrer, liefert Ganzzahl:\n gib zurueck x.alter.\n', DRIVER);
|
|
84
|
+
assert.deepEqual([...codes(r1), ...codes(r2)], []);
|
|
85
|
+
const n = nameOf(returned(func(module, 'f'), 0));
|
|
86
|
+
assert.equal(n.name, 'x.age');
|
|
87
|
+
assert.equal(n.origin?.written, 'x.alter');
|
|
88
|
+
assert.equal(n.origin?.role, 'FIELD_REF');
|
|
89
|
+
});
|
|
90
|
+
it('多段路径按每段的声明类型解析', () => {
|
|
91
|
+
const { module, r2 } = run(MODULE +
|
|
92
|
+
'Definiere Halter hat fahrer als Fahrer.\n\n' + DRIVER_DECL +
|
|
93
|
+
'Regel f gegeben h als Halter, liefert Ganzzahl:\n gib zurueck h.fahrer.alter.\n', [...DRIVER, struct('Owner', 'Halter'), field('Owner', 'driver', 'fahrer')]);
|
|
94
|
+
assert.deepEqual(codes(r2), []);
|
|
95
|
+
assert.equal(nameOf(returned(func(module, 'f'), 0)).name, 'h.driver.age');
|
|
96
|
+
});
|
|
97
|
+
it('Set x.alter → x.age(解析器只接受单段 Set 目标,AST 中改写为点分名)', () => {
|
|
98
|
+
const toDotted = (m) => ({
|
|
99
|
+
...m,
|
|
100
|
+
decls: m.decls.map((d) => d.kind === 'Func' && d.body
|
|
101
|
+
? {
|
|
102
|
+
...d,
|
|
103
|
+
body: {
|
|
104
|
+
...d.body,
|
|
105
|
+
statements: d.body.statements.map((s) => (s.kind === 'Set' ? { ...s, name: 'x.alter' } : s)),
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
: d),
|
|
109
|
+
});
|
|
110
|
+
const { module, r2 } = run(MODULE + DRIVER_DECL + 'Regel f gegeben x als Fahrer, liefert Ganzzahl:\n setze x auf 5.\n gib zurueck 1.\n', DRIVER, DE_DE, toDotted);
|
|
111
|
+
assert.deepEqual(codes(r2), []);
|
|
112
|
+
const set = stmt(func(module, 'f'), 0, 'Set');
|
|
113
|
+
assert.equal(set.name, 'x.age');
|
|
114
|
+
assert.equal(set.origin?.written, 'x.alter');
|
|
115
|
+
assert.equal(set.origin?.role, 'FIELD_REF');
|
|
116
|
+
});
|
|
117
|
+
it('类型已知但字段不存在 → E801', () => {
|
|
118
|
+
const { r2 } = run(MODULE + DRIVER_DECL + 'Regel f gegeben x als Fahrer, liefert Ganzzahl:\n gib zurueck x.gewicht.\n', DRIVER);
|
|
119
|
+
assert.deepEqual(codes(r2), ["E801" /* ErrorCode.UNRESOLVED_FIELD */]);
|
|
120
|
+
});
|
|
121
|
+
it('首段不是局部名(限定调用的命名空间)→ 原样保留,不报错', () => {
|
|
122
|
+
const { module, r2 } = run('Module m.\n\nRule f given t as Text, produce Int:\n Return Text.length(t).\n', [], EN_US);
|
|
123
|
+
assert.deepEqual(codes(r2), []);
|
|
124
|
+
const call = returned(func(module, 'f'), 0);
|
|
125
|
+
assert.equal(nameOf(call.target).name, 'Text.length');
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
describe('Let 无注解右侧:只用显式声明类型', () => {
|
|
129
|
+
const lets = [
|
|
130
|
+
['字面量', '1'],
|
|
131
|
+
['ListLiteral', '[x]'],
|
|
132
|
+
['Some', 'einige von x'],
|
|
133
|
+
['IfExpr', 'wenn wahr dann x sonst x'],
|
|
134
|
+
];
|
|
135
|
+
for (const [label, rhs] of lets) {
|
|
136
|
+
it(`右侧为${label} → 类型未知,全局唯一候选采用 + W801`, () => {
|
|
137
|
+
const { module, r2 } = run(MODULE + DRIVER_DECL +
|
|
138
|
+
`Regel f gegeben x als Fahrer, liefert Ganzzahl:\n sei y gleich ${rhs}.\n gib zurueck y.alter.\n`, DRIVER);
|
|
139
|
+
assert.deepEqual(codes(r2), ["W801" /* ErrorCode.RESOLVED_WITHOUT_TYPE */]);
|
|
140
|
+
assert.equal(r2[0].severity, 'warning');
|
|
141
|
+
assert.equal(nameOf(returned(func(module, 'f'), 1)).name, 'y.age');
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
it('右侧为构造 X with … → 类型为 X,无 W801', () => {
|
|
145
|
+
const { module, r2 } = run(MODULE + DRIVER_DECL +
|
|
146
|
+
'Regel f gegeben x als Ganzzahl, liefert Ganzzahl:\n sei y gleich Fahrer mit alter setze auf x.\n gib zurueck y.alter.\n', DRIVER);
|
|
147
|
+
assert.deepEqual(codes(r2), []);
|
|
148
|
+
assert.equal(nameOf(returned(func(module, 'f'), 1)).name, 'y.age');
|
|
149
|
+
});
|
|
150
|
+
it('右侧为对源码函数的直接调用 → 声明返回类型,无 W801', () => {
|
|
151
|
+
const { module, r2 } = run(MODULE + DRIVER_DECL +
|
|
152
|
+
'Regel neu gegeben x als Ganzzahl, liefert Fahrer:\n gib zurueck Fahrer mit alter setze auf x.\n\n' +
|
|
153
|
+
'Regel f gegeben x als Ganzzahl, liefert Ganzzahl:\n sei y gleich neu(x).\n gib zurueck y.alter.\n', DRIVER);
|
|
154
|
+
assert.deepEqual(codes(r2), []);
|
|
155
|
+
assert.equal(nameOf(returned(func(module, 'f'), 1)).name, 'y.age');
|
|
156
|
+
});
|
|
157
|
+
it('类型未知且多个结构体各有 alter → E805', () => {
|
|
158
|
+
const { r2 } = run(MODULE + DRIVER_DECL + 'Definiere Halter hat alter als Ganzzahl.\n\n' +
|
|
159
|
+
'Regel f gegeben x als Ganzzahl, liefert Ganzzahl:\n sei y gleich 1.\n gib zurueck y.alter.\n', [...DRIVER, struct('Owner', 'Halter'), field('Owner', 'years', 'alter')]);
|
|
160
|
+
assert.deepEqual(codes(r2), ["E805" /* ErrorCode.AMBIGUOUS_FIELD */]);
|
|
161
|
+
});
|
|
162
|
+
it('类型未知且零候选 → E801', () => {
|
|
163
|
+
const { r2 } = run(MODULE + DRIVER_DECL +
|
|
164
|
+
'Regel f gegeben x als Ganzzahl, liefert Ganzzahl:\n sei y gleich 1.\n gib zurueck y.gewicht.\n', DRIVER);
|
|
165
|
+
assert.deepEqual(codes(r2), ["E801" /* ErrorCode.UNRESOLVED_FIELD */]);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
describe('期望类型为枚举时的裸单段名', () => {
|
|
169
|
+
it('Return Hoch(返回类型 Stufe)→ High(裸名保持裸形态,R18)', () => {
|
|
170
|
+
const { module, r1, r2 } = run(MODULE + LEVEL_DECL + 'Regel f gegeben x als Ganzzahl, liefert Stufe:\n gib zurueck Hoch.\n', LEVEL);
|
|
171
|
+
assert.deepEqual([...codes(r1), ...codes(r2)], []);
|
|
172
|
+
const n = nameOf(returned(func(module, 'f'), 0));
|
|
173
|
+
assert.equal(n.name, 'High');
|
|
174
|
+
assert.equal(n.origin?.written, 'Hoch');
|
|
175
|
+
assert.equal(n.origin?.role, 'MEMBER_REF');
|
|
176
|
+
});
|
|
177
|
+
it('R18:本地化源码与规范名拼写的同一程序产出相同 Core(去 origin 比较);限定写法保持限定', () => {
|
|
178
|
+
const strip = (m) => JSON.stringify(m, (k, v) => (k === 'origin' ? undefined : v));
|
|
179
|
+
const de = run(MODULE + LEVEL_DECL + 'Regel f gegeben x als Stufe, liefert Stufe:\n gib zurueck Hoch.\n\n' +
|
|
180
|
+
'Regel g gegeben x als Stufe, liefert Stufe:\n gib zurueck Stufe.Niedrig.\n', LEVEL);
|
|
181
|
+
const en = run('Module m.\n\nDefine Level as one of High, Low.\n\n' +
|
|
182
|
+
'Rule f given x as Level, produce Level:\n Return High.\n\n' +
|
|
183
|
+
'Rule g given x as Level, produce Level:\n Return Level.Low.\n', [], EN_US);
|
|
184
|
+
assert.deepEqual([...codes(de.r1), ...codes(de.r2), ...codes(en.r1), ...codes(en.r2)], []);
|
|
185
|
+
assert.equal(strip(de.module), strip(en.module));
|
|
186
|
+
assert.equal(nameOf(returned(func(de.module, 'g'), 0)).name, 'Level.Low');
|
|
187
|
+
});
|
|
188
|
+
it('规则 hoch 同时存在 → E804(不静默择一)', () => {
|
|
189
|
+
const { r2 } = run(MODULE + LEVEL_DECL +
|
|
190
|
+
'Regel hoch gegeben x als Ganzzahl, liefert Ganzzahl:\n gib zurueck x.\n\n' +
|
|
191
|
+
'Regel f gegeben x als Ganzzahl, liefert Stufe:\n gib zurueck Hoch.\n', LEVEL);
|
|
192
|
+
assert.deepEqual(codes(r2), ["E804" /* ErrorCode.AMBIGUOUS_NAME */]);
|
|
193
|
+
assert.match(r2[0].message, /Level\.High|High/);
|
|
194
|
+
});
|
|
195
|
+
it('词汇函数同写法 → E804', () => {
|
|
196
|
+
const { r2 } = run(MODULE + LEVEL_DECL + 'Regel f gegeben x als Ganzzahl, liefert Stufe:\n gib zurueck Hoch.\n', [...LEVEL, fn('raise', 'hoch')]);
|
|
197
|
+
assert.deepEqual(codes(r2), ["E804" /* ErrorCode.AMBIGUOUS_NAME */]);
|
|
198
|
+
});
|
|
199
|
+
it('局部名命中 → 取局部名并报 W800', () => {
|
|
200
|
+
const { module, r2 } = run(MODULE + LEVEL_DECL + 'Regel f gegeben hoch als Stufe, liefert Stufe:\n gib zurueck Hoch.\n', LEVEL);
|
|
201
|
+
assert.deepEqual(codes(r2), ["W800" /* ErrorCode.SHADOWED_VOCABULARY */]);
|
|
202
|
+
assert.equal(nameOf(returned(func(module, 'f'), 0)).name, 'hoch');
|
|
203
|
+
});
|
|
204
|
+
it('调用实参按被调函数参数类型解析', () => {
|
|
205
|
+
const { module, r2 } = run(MODULE + LEVEL_DECL +
|
|
206
|
+
'Regel g gegeben s als Stufe, liefert Stufe:\n gib zurueck s.\n\n' +
|
|
207
|
+
'Regel f gegeben x als Ganzzahl, liefert Stufe:\n gib zurueck g(Niedrig).\n', LEVEL);
|
|
208
|
+
assert.deepEqual(codes(r2), []);
|
|
209
|
+
const call = returned(func(module, 'f'), 0);
|
|
210
|
+
assert.equal(nameOf(call.args[0]).name, 'Low');
|
|
211
|
+
});
|
|
212
|
+
it('ConstructField 按字段声明类型解析', () => {
|
|
213
|
+
const { module, r2 } = run(MODULE + LEVEL_DECL + 'Definiere Fahrer hat stufe als Stufe.\n\n' +
|
|
214
|
+
'Regel f gegeben x als Ganzzahl, liefert Fahrer:\n gib zurueck Fahrer mit stufe setze auf Hoch.\n', [...LEVEL, struct('Driver', 'Fahrer'), field('Driver', 'level', 'stufe')]);
|
|
215
|
+
assert.deepEqual(codes(r2), []);
|
|
216
|
+
const c = returned(func(module, 'f'), 0);
|
|
217
|
+
assert.equal(c.typeName, 'Driver');
|
|
218
|
+
assert.equal(c.fields[0].name, 'level');
|
|
219
|
+
assert.equal(nameOf(c.fields[0].expr).name, 'High');
|
|
220
|
+
});
|
|
221
|
+
describe('相等比较:一侧为已知枚举时另一侧裸名以其为期望类型(裁决 R49)', () => {
|
|
222
|
+
const CMP_DECLS = MODULE + LEVEL_DECL + 'Definiere Fahrer hat stufe als Stufe.\n\n';
|
|
223
|
+
const CMP_VOCAB = [...LEVEL, struct('Driver', 'Fahrer'), field('Driver', 'level', 'stufe')];
|
|
224
|
+
const ifCond = (m) => stmt(func(m, 'f'), 0, 'If').cond;
|
|
225
|
+
it('wenn d.stufe entspricht Hoch → Hoch 解析为 High(与规范名孪生 d.level equals to High 一致)', () => {
|
|
226
|
+
const { module, r1, r2 } = run(CMP_DECLS + 'Regel f gegeben d als Fahrer, liefert Ganzzahl:\n wenn d.stufe entspricht Hoch:\n gib zurueck 1.\n gib zurueck 0.\n', CMP_VOCAB);
|
|
227
|
+
assert.deepEqual([...codes(r1), ...codes(r2)], []);
|
|
228
|
+
const cond = ifCond(module);
|
|
229
|
+
assert.equal(nameOf(cond.target).name, '==');
|
|
230
|
+
assert.equal(nameOf(cond.args[0]).name, 'd.level');
|
|
231
|
+
const member = nameOf(cond.args[1]);
|
|
232
|
+
assert.equal(member.name, 'High');
|
|
233
|
+
assert.equal(member.origin?.written, 'Hoch');
|
|
234
|
+
assert.equal(member.origin?.role, 'MEMBER_REF');
|
|
235
|
+
});
|
|
236
|
+
it('枚举一侧在右、裸名在左同样解析;局部名参数为枚举类型时亦然', () => {
|
|
237
|
+
const { module, r1, r2 } = run(CMP_DECLS + 'Regel f gegeben s als Stufe, liefert Ganzzahl:\n wenn Niedrig entspricht s:\n gib zurueck 1.\n gib zurueck 0.\n', CMP_VOCAB);
|
|
238
|
+
assert.deepEqual([...codes(r1), ...codes(r2)], []);
|
|
239
|
+
const cond = ifCond(module);
|
|
240
|
+
assert.equal(nameOf(cond.args[0]).name, 'Low');
|
|
241
|
+
assert.equal(nameOf(cond.args[1]).name, 's');
|
|
242
|
+
});
|
|
243
|
+
it('不等比较(not equal to)同样提供期望类型', () => {
|
|
244
|
+
const { module, r1, r2 } = run('Module m.\n\nDefine Level as one of High, Low.\n\n' +
|
|
245
|
+
'Rule f given s as Level, produce Int:\n If s not equal to Low:\n Return 1.\n Return 0.\n', [], EN_US);
|
|
246
|
+
assert.deepEqual([...codes(r1), ...codes(r2)], []);
|
|
247
|
+
const cond = ifCond(module);
|
|
248
|
+
assert.equal(nameOf(cond.target).name, '!=');
|
|
249
|
+
assert.equal(nameOf(cond.args[1]).name, 'Low');
|
|
250
|
+
});
|
|
251
|
+
it('词汇函数 hoch 同时存在 → E804(不静默解析为函数 high),候选不重复且帮助提示 Stufe.Hoch', () => {
|
|
252
|
+
const { r2 } = run(CMP_DECLS + 'Regel f gegeben d als Fahrer, liefert Ganzzahl:\n wenn d.stufe entspricht Hoch:\n gib zurueck 1.\n gib zurueck 0.\n', [...CMP_VOCAB, fn('high', 'hoch')]);
|
|
253
|
+
// error 级诊断使调用方不发出 IR,因此 R1 的函数命中不会静默进入产物
|
|
254
|
+
assert.deepEqual(codes(r2), ["E804" /* ErrorCode.AMBIGUOUS_NAME */]);
|
|
255
|
+
assert.match(r2[0].message, /High \(Hoch\)/);
|
|
256
|
+
assert.match(r2[0].help ?? '', /Stufe\.Hoch/);
|
|
257
|
+
});
|
|
258
|
+
it('源码规则 hoch 与词汇函数 hoch→high 同时命中 → E804 候选去重', () => {
|
|
259
|
+
const { r2 } = run(CMP_DECLS +
|
|
260
|
+
'Regel hoch gegeben x als Ganzzahl, liefert Ganzzahl:\n gib zurueck x.\n\n' +
|
|
261
|
+
'Regel f gegeben s als Stufe, liefert Ganzzahl:\n wenn s entspricht Hoch:\n gib zurueck 1.\n gib zurueck 0.\n', [...CMP_VOCAB, fn('high', 'hoch')]);
|
|
262
|
+
assert.deepEqual(codes(r2), ["E804" /* ErrorCode.AMBIGUOUS_NAME */]);
|
|
263
|
+
const candidates = r2[0].message.split(': ').at(-1).split(', ');
|
|
264
|
+
assert.deepEqual(candidates, [...new Set(candidates)], r2[0].message);
|
|
265
|
+
});
|
|
266
|
+
it('另一侧类型不是枚举 → 无期望类型,裸成员名仍报 E802(角色 VALUE_REF)', () => {
|
|
267
|
+
const { r2 } = run(CMP_DECLS + 'Regel f gegeben x als Ganzzahl, liefert Ganzzahl:\n wenn x entspricht Hoch:\n gib zurueck 1.\n gib zurueck 0.\n', CMP_VOCAB);
|
|
268
|
+
assert.deepEqual(codes(r2), ["E802" /* ErrorCode.UNRESOLVED_NAME */]);
|
|
269
|
+
assert.match(r2[0].message, /VALUE_REF/);
|
|
270
|
+
assert.match(r2[0].help ?? '', /Stufe\.Hoch/);
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
it('无期望类型的裸成员名不按枚举解析 → E802', () => {
|
|
274
|
+
const { r2 } = run(MODULE + LEVEL_DECL +
|
|
275
|
+
'Regel f gegeben x als Ganzzahl, liefert Ganzzahl:\n sei y gleich Hoch.\n gib zurueck x.\n', LEVEL);
|
|
276
|
+
assert.deepEqual(codes(r2), ["E802" /* ErrorCode.UNRESOLVED_NAME */]);
|
|
277
|
+
});
|
|
278
|
+
it('无期望类型的未知裸名 → E802;运算符与 R1 已解析的名字不报', () => {
|
|
279
|
+
const { r2 } = run(MODULE + 'Regel f gegeben x als Ganzzahl, liefert Ganzzahl:\n gib zurueck x plus unbekannt.\n', []);
|
|
280
|
+
assert.deepEqual(codes(r2), ["E802" /* ErrorCode.UNRESOLVED_NAME */]);
|
|
281
|
+
assert.match(r2[0].message, /unbekannt/);
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
describe('模式匹配', () => {
|
|
285
|
+
it('主语为参数:bei Hoch(PatternName)按主语枚举解析为成员', () => {
|
|
286
|
+
const { module, r2 } = run(MODULE + LEVEL_DECL +
|
|
287
|
+
'Regel f gegeben s als Stufe, liefert Ganzzahl:\n pruefe s:\n bei Hoch, gib zurueck 1.\n bei andere, gib zurueck 2.\n', LEVEL);
|
|
288
|
+
assert.deepEqual(codes(r2), []);
|
|
289
|
+
const cases = stmt(func(module, 'f'), 0, 'Match').cases;
|
|
290
|
+
const first = cases[0].pattern;
|
|
291
|
+
assert.equal(first.name, 'High');
|
|
292
|
+
assert.equal(first.origin?.written, 'Hoch');
|
|
293
|
+
assert.equal(first.origin?.role, 'MEMBER_REF');
|
|
294
|
+
assert.equal(cases[1].pattern.name, 'andere');
|
|
295
|
+
});
|
|
296
|
+
it('主语为构造得到的 Let:bei Fahrer(a) 按主语类型解析', () => {
|
|
297
|
+
const { module, r2 } = run(MODULE + DRIVER_DECL +
|
|
298
|
+
'Regel f gegeben x als Ganzzahl, liefert Ganzzahl:\n sei d gleich Fahrer mit alter setze auf x.\n' +
|
|
299
|
+
' pruefe d:\n bei Fahrer(a), gib zurueck a.\n', DRIVER);
|
|
300
|
+
assert.deepEqual(codes(r2), []);
|
|
301
|
+
const pat = stmt(func(module, 'f'), 1, 'Match').cases[0].pattern;
|
|
302
|
+
assert.equal(pat.typeName, 'Driver');
|
|
303
|
+
assert.deepEqual(pat.names, ['a']);
|
|
304
|
+
});
|
|
305
|
+
it('主语未知、全部枚举成员与构造器中唯一 → 采用 + W801', () => {
|
|
306
|
+
const { module, r2 } = run(MODULE + LEVEL_DECL +
|
|
307
|
+
'Regel f gegeben x als Ganzzahl, liefert Ganzzahl:\n sei s gleich 1.\n' +
|
|
308
|
+
' pruefe s:\n bei Hoch(), gib zurueck 1.\n bei Niedrig, gib zurueck 2.\n', LEVEL);
|
|
309
|
+
assert.deepEqual(codes(r2), ["W801" /* ErrorCode.RESOLVED_WITHOUT_TYPE */, "W801" /* ErrorCode.RESOLVED_WITHOUT_TYPE */]);
|
|
310
|
+
const cases = stmt(func(module, 'f'), 1, 'Match').cases;
|
|
311
|
+
assert.equal(cases[0].pattern.typeName, 'High');
|
|
312
|
+
assert.equal(cases[1].pattern.name, 'Low');
|
|
313
|
+
});
|
|
314
|
+
it('主语未知、多个候选 → E806', () => {
|
|
315
|
+
const { r2 } = run(MODULE + LEVEL_DECL + 'Definiere Rang als eines von Hoch, Mittel.\n\n' +
|
|
316
|
+
'Regel f gegeben x als Ganzzahl, liefert Ganzzahl:\n sei s gleich 1.\n' +
|
|
317
|
+
' pruefe s:\n bei Hoch(), gib zurueck 1.\n bei Hoch, gib zurueck 2.\n', [...LEVEL, struct('Priority', 'Rang'), member('Priority', 'Top', 'Hoch'), member('Priority', 'Mid', 'Mittel')]);
|
|
318
|
+
assert.deepEqual(codes(r2), ["E806" /* ErrorCode.AMBIGUOUS_PATTERN */, "E806" /* ErrorCode.AMBIGUOUS_PATTERN */]);
|
|
319
|
+
});
|
|
320
|
+
it('主语未知、零候选 → 构造器 E803;小写模式名仍为绑定', () => {
|
|
321
|
+
const { module, r2 } = run(MODULE + 'Regel f gegeben x als Ganzzahl, liefert Ganzzahl:\n sei s gleich 1.\n' +
|
|
322
|
+
' pruefe s:\n bei Riesig(), gib zurueck 1.\n bei wert, gib zurueck wert.\n', []);
|
|
323
|
+
assert.deepEqual(codes(r2), ["E803" /* ErrorCode.UNRESOLVED_PATTERN */]);
|
|
324
|
+
const cases = stmt(func(module, 'f'), 1, 'Match').cases;
|
|
325
|
+
assert.equal(cases[1].pattern.name, 'wert');
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
describe('其它位置', () => {
|
|
329
|
+
it('UFCS:x.berechne() 的方法名按函数解析', () => {
|
|
330
|
+
const { module, r2 } = run(MODULE + DRIVER_DECL +
|
|
331
|
+
'Regel berechne gegeben d als Fahrer, liefert Ganzzahl:\n gib zurueck d.alter.\n\n' +
|
|
332
|
+
'Regel f gegeben x als Fahrer, liefert Ganzzahl:\n gib zurueck x.berechne().\n', [...DRIVER, fn('compute', 'berechne')]);
|
|
333
|
+
assert.deepEqual(codes(r2), []);
|
|
334
|
+
const call = returned(func(module, 'f'), 0);
|
|
335
|
+
assert.equal(nameOf(call.target).name, 'compute');
|
|
336
|
+
assert.equal(nameOf(call.args[0]).name, 'x');
|
|
337
|
+
});
|
|
338
|
+
it('限定调用:首段折叠命中局部名也不按字段路径解析;UFCS 接收者为命名空间时不报 E802', () => {
|
|
339
|
+
const { module, r2 } = run('Module m.\n\nRule f given action as Text, produce Bool. It performs io:\n' +
|
|
340
|
+
' Let x be io.get(action).\n Return Action.equals(action, "a").\n', [], EN_US);
|
|
341
|
+
assert.deepEqual(codes(r2), []);
|
|
342
|
+
const call = returned(func(module, 'f'), 1);
|
|
343
|
+
assert.equal(nameOf(call.target).name, 'Action.equals');
|
|
344
|
+
const ufcs = stmt(func(module, 'f'), 0, 'Let').expr;
|
|
345
|
+
assert.deepEqual([nameOf(ufcs.target).name, nameOf(ufcs.args[0]).name], ['get', 'io']);
|
|
346
|
+
});
|
|
347
|
+
it('lambda:期望类型取 lambda 声明返回类型,捕获列表随解析结果改写', () => {
|
|
348
|
+
const { module, r2 } = run('Module m.\n\nDefine Stufe as one of Hoch, Niedrig.\n\n' +
|
|
349
|
+
'Rule f given outer as Int, produce Int:\n' +
|
|
350
|
+
' Let g be function with x as Int, produce Stufe:\n' +
|
|
351
|
+
' Let unused be outer.\n' +
|
|
352
|
+
' Return Hoch.\n' +
|
|
353
|
+
' Return outer.\n', LEVEL, EN_US);
|
|
354
|
+
assert.deepEqual(codes(r2), []);
|
|
355
|
+
const lambda = stmt(func(module, 'f'), 0, 'Let').expr;
|
|
356
|
+
const ret = lambda.body.statements[1];
|
|
357
|
+
assert.equal(nameOf(ret.expr).name, 'High');
|
|
358
|
+
assert.deepEqual(lambda.captures, ['outer', 'High']);
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
describe('裁决 R19:未知调用目标交由类型检查', () => {
|
|
362
|
+
it('调用目标在声明、导入、内置、词汇中均无候选 → 保留原写法,不报 E802;值位置仍报', () => {
|
|
363
|
+
const { module, r2 } = run(MODULE + 'Regel f gegeben x als Ganzzahl, liefert Ganzzahl:\n gib zurueck unbekannt(x).\n\n' +
|
|
364
|
+
'Regel g gegeben x als Ganzzahl, liefert Ganzzahl:\n gib zurueck fehlend.\n', []);
|
|
365
|
+
assert.deepEqual(codes(r2), ["E802" /* ErrorCode.UNRESOLVED_NAME */]);
|
|
366
|
+
assert.match(r2[0].message, /fehlend/);
|
|
367
|
+
assert.equal(nameOf(returned(func(module, 'f'), 0).target).name, 'unbekannt');
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
describe('Core 不变量', () => {
|
|
371
|
+
const SOURCE = MODULE + LEVEL_DECL + 'Definiere Fahrer hat alter als Ganzzahl, stufe als Stufe.\n\n' +
|
|
372
|
+
'Regel bewerten gegeben fahrer als Fahrer, liefert Stufe:\n' +
|
|
373
|
+
' sei a gleich fahrer.alter.\n' +
|
|
374
|
+
' pruefe fahrer.stufe:\n bei Hoch, gib zurueck Niedrig.\n bei Niedrig, gib zurueck Hoch.\n';
|
|
375
|
+
const VOCAB = [...LEVEL, ...DRIVER, field('Driver', 'level', 'stufe'), fn('check', 'bewerten')];
|
|
376
|
+
const LOCALIZED = ['Stufe', 'Hoch', 'Niedrig', 'Fahrer', 'alter', 'stufe', 'bewerten'];
|
|
377
|
+
function collect(node, names, origins) {
|
|
378
|
+
if (Array.isArray(node))
|
|
379
|
+
return node.forEach((x) => collect(x, names, origins));
|
|
380
|
+
if (!node || typeof node !== 'object')
|
|
381
|
+
return;
|
|
382
|
+
for (const [k, v] of Object.entries(node)) {
|
|
383
|
+
if (k === 'origin')
|
|
384
|
+
origins.push(v);
|
|
385
|
+
else if (typeof v === 'string' && ['name', 'typeName', 'base'].includes(k))
|
|
386
|
+
names.push(...v.split('.'));
|
|
387
|
+
else if (k === 'variants' || k === 'names')
|
|
388
|
+
names.push(...v);
|
|
389
|
+
else
|
|
390
|
+
collect(v, names, origins);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
it('解析后 Core 中无本地化名;origin.written/role 仅在 written !== canonical 时出现', () => {
|
|
394
|
+
const { module, r1, r2 } = run(SOURCE, VOCAB);
|
|
395
|
+
assert.deepEqual([...codes(r1), ...codes(r2)], []);
|
|
396
|
+
const names = [];
|
|
397
|
+
const origins = [];
|
|
398
|
+
collect(module, names, origins);
|
|
399
|
+
assert.deepEqual(names.filter((n) => LOCALIZED.includes(n)), []);
|
|
400
|
+
for (const o of origins) {
|
|
401
|
+
if (o?.written === undefined && o?.role === undefined)
|
|
402
|
+
continue;
|
|
403
|
+
assert.ok(o.written !== undefined && o.role !== undefined, '两者同时出现');
|
|
404
|
+
}
|
|
405
|
+
const withWritten = origins.filter((o) => o?.written !== undefined).map((o) => o.written);
|
|
406
|
+
assert.ok(withWritten.includes('fahrer.alter'));
|
|
407
|
+
assert.ok(withWritten.includes('Hoch'));
|
|
408
|
+
const f = func(module, 'check');
|
|
409
|
+
assert.equal(f.origin?.written, 'bewerten');
|
|
410
|
+
assert.equal(f.origin?.role, 'FUNC_DECL');
|
|
411
|
+
// 局部名 fahrer 未改写:其节点 origin 不带 written
|
|
412
|
+
const let0 = stmt(f, 0, 'Let');
|
|
413
|
+
assert.equal(let0.origin?.written, undefined);
|
|
414
|
+
});
|
|
415
|
+
it('函数签名的参数 / 返回类型名记录 written/role(与 Java 对齐)', () => {
|
|
416
|
+
const { module, r1, r2 } = run(SOURCE, VOCAB);
|
|
417
|
+
assert.deepEqual([...codes(r1), ...codes(r2)], []);
|
|
418
|
+
const f = func(module, 'check');
|
|
419
|
+
const paramType = f.params[0].type;
|
|
420
|
+
assert.equal(paramType.name, 'Driver');
|
|
421
|
+
assert.equal(paramType.origin?.written, 'Fahrer');
|
|
422
|
+
assert.equal(paramType.origin?.role, 'TYPE');
|
|
423
|
+
const ret = f.ret;
|
|
424
|
+
assert.equal(ret.name, 'Level');
|
|
425
|
+
assert.equal(ret.origin?.written, 'Stufe');
|
|
426
|
+
assert.equal(ret.origin?.role, 'TYPE');
|
|
427
|
+
});
|
|
428
|
+
it('函数签名中未改写的类型名不新增 origin(无词汇 IR 不变)', () => {
|
|
429
|
+
const { module } = run('Module m.\n\nDefine Driver has age as Int.\n\nRule f given d as Driver, produce Int:\n Return d.age.\n', DRIVER, EN_US);
|
|
430
|
+
const f = func(module, 'f');
|
|
431
|
+
assert.equal(f.params[0].type.origin, undefined);
|
|
432
|
+
assert.equal(f.ret.origin, undefined);
|
|
433
|
+
});
|
|
434
|
+
it('规范名直写的源码:origin 不带 written/role', () => {
|
|
435
|
+
const { module, r2 } = run('Module m.\n\nDefine Driver has age as Int.\n\nRule f given d as Driver, produce Int:\n Return d.age.\n', DRIVER, EN_US);
|
|
436
|
+
assert.deepEqual(codes(r2), []);
|
|
437
|
+
const origins = [];
|
|
438
|
+
collect(module, [], origins);
|
|
439
|
+
assert.deepEqual(origins.filter((o) => o?.written !== undefined || o?.role !== undefined), []);
|
|
440
|
+
});
|
|
441
|
+
it('任一 E8xx 时 diagnostics 非空(调用方据此不发出 IR)', () => {
|
|
442
|
+
const { r2 } = run(MODULE + DRIVER_DECL + 'Regel f gegeben x als Fahrer, liefert Ganzzahl:\n gib zurueck x.gewicht.\n', DRIVER);
|
|
443
|
+
assert.ok(r2.some((d) => d.severity === 'error' && d.code.startsWith('E80')));
|
|
444
|
+
});
|
|
445
|
+
it('R2 只读标记:输出中不再携带 ResolutionMark,输入 Core 不被修改', () => {
|
|
446
|
+
const ctx = ctxOf(DRIVER);
|
|
447
|
+
const r1 = resolveDeclarations(parse(MODULE + DRIVER_DECL + 'Regel f gegeben x als Fahrer, liefert Ganzzahl:\n gib zurueck x.alter.\n'), ctx);
|
|
448
|
+
const core = lowerModule(r1.module);
|
|
449
|
+
const before = nameOf(returned(func(core, 'f'), 0));
|
|
450
|
+
assert.equal(before.name, 'x.alter');
|
|
451
|
+
assert.ok(resolutionMarkOf(before));
|
|
452
|
+
const out = resolveTypedReferences(core, ctx).module;
|
|
453
|
+
assert.equal(before.name, 'x.alter');
|
|
454
|
+
assert.equal(resolutionMarkOf(nameOf(returned(func(out, 'f'), 0))), undefined);
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
describe('裁决 R16:规范名直写不告警、动态字段透传', () => {
|
|
458
|
+
const TIER = 'Module m.\n\nDefine Tier as one of Gold, Silver.\n\n';
|
|
459
|
+
it('1. 接收者类型未知、字段写法即规范名 → 无 W801;模式写法即成员规范名 → 无 W801', () => {
|
|
460
|
+
const { module, r1, r2 } = run(TIER + 'Define Driver has age as Int.\n\n' +
|
|
461
|
+
'Rule f given x as Int, produce Int:\n Let y be 1.\n Let a be y.age.\n' +
|
|
462
|
+
' Match y:\n When Gold, Return 1.\n When Silver(), Return a.\n', [], EN_US);
|
|
463
|
+
assert.deepEqual([...codes(r1), ...codes(r2)], []);
|
|
464
|
+
assert.equal(nameOf(stmt(func(module, 'f'), 1, 'Let').expr).name, 'y.age');
|
|
465
|
+
const cases = stmt(func(module, 'f'), 2, 'Match').cases;
|
|
466
|
+
assert.equal(cases[0].pattern.name, 'Gold');
|
|
467
|
+
assert.equal(cases[1].pattern.typeName, 'Silver');
|
|
468
|
+
});
|
|
469
|
+
it('2. 无期望类型的裸成员规范名 → 原样接受;期望枚举下写法即规范名不改写', () => {
|
|
470
|
+
const { module, r1, r2 } = run(TIER + 'Rule f given t as Text:\n Return Gold.\n\n' +
|
|
471
|
+
'Rule g given t as Text, produce Bool:\n Return Text.equals(t, Silver).\n\n' +
|
|
472
|
+
'Rule h given t as Text, produce Tier:\n Return Gold.\n', [], EN_US);
|
|
473
|
+
assert.deepEqual([...codes(r1), ...codes(r2)], []);
|
|
474
|
+
assert.equal(nameOf(returned(func(module, 'f'), 0)).name, 'Gold');
|
|
475
|
+
assert.equal(nameOf(returned(func(module, 'g'), 0).args[1]).name, 'Silver');
|
|
476
|
+
const h = nameOf(returned(func(module, 'h'), 0));
|
|
477
|
+
assert.equal(h.name, 'Gold');
|
|
478
|
+
assert.equal(h.origin?.written, undefined);
|
|
479
|
+
});
|
|
480
|
+
it('2. 成员规范名属于多个枚举且无期望类型 → E804', () => {
|
|
481
|
+
const { r2 } = run(TIER + 'Define Medal as one of Gold, Bronze.\n\nRule f given t as Text:\n Return Gold.\n', [], EN_US);
|
|
482
|
+
assert.deepEqual(codes(r2), ["E804" /* ErrorCode.AMBIGUOUS_NAME */]);
|
|
483
|
+
});
|
|
484
|
+
it('3. 未声明类型的字段之后的段为动态键(ADR 0040):原样保留,不报错,经 Let 传递', () => {
|
|
485
|
+
const { module, r1, r2 } = run('Module m.\n\nDefine GuardRequest has action, context.\n\n' +
|
|
486
|
+
'Rule decide given req as GuardRequest, produce Int:\n' +
|
|
487
|
+
' Let large be req.context.amount.\n Let c be req.context.\n Let b be c.limit.\n' +
|
|
488
|
+
' Let n be req.action.size.\n Return 1.\n', [], EN_US);
|
|
489
|
+
assert.deepEqual([...codes(r1), ...codes(r2)], []);
|
|
490
|
+
const f = func(module, 'decide');
|
|
491
|
+
assert.equal(nameOf(stmt(f, 0, 'Let').expr).name, 'req.context.amount');
|
|
492
|
+
assert.equal(nameOf(stmt(f, 2, 'Let').expr).name, 'c.limit');
|
|
493
|
+
assert.equal(nameOf(stmt(f, 3, 'Let').expr).name, 'req.action.size');
|
|
494
|
+
});
|
|
495
|
+
it('3. 已知非 Data 类型(Text)之后的段原样保留', () => {
|
|
496
|
+
const { r2 } = run('Module m.\n\nRule f given t as Text, produce Int:\n Let n be t.size.\n Return 1.\n', [], EN_US);
|
|
497
|
+
assert.deepEqual(codes(r2), []);
|
|
498
|
+
});
|
|
499
|
+
it('3b. 类型参数(未声明类型名成为隐式类型参数)上的字段段 → E801,不透传', () => {
|
|
500
|
+
const { r2 } = run('Module m.\n\nRule f given x as Foo, produce Int:\n Return x.age.\n', [], EN_US);
|
|
501
|
+
assert.deepEqual(codes(r2), ["E801" /* ErrorCode.UNRESOLVED_FIELD */]);
|
|
502
|
+
});
|
|
503
|
+
it('4. lambda 绑定的局部名与内置运算符不报 E802', () => {
|
|
504
|
+
const { r1, r2 } = run('Module m.\n\nRule f given x as Int, produce Int:\n' +
|
|
505
|
+
' Let inc be function with v as Int, produce Int:\n Return v plus 1.\n' +
|
|
506
|
+
' Return inc(x) times 2.\n', [], EN_US);
|
|
507
|
+
assert.deepEqual([...codes(r1), ...codes(r2)], []);
|
|
508
|
+
});
|
|
509
|
+
});
|
|
510
|
+
describe('透传白名单、标记幂等与期望枚举裁决', () => {
|
|
511
|
+
const EN_DRIVER = 'Module m.\n\nDefine Fahrer has alter as Int.\n\n';
|
|
512
|
+
const receivers = [
|
|
513
|
+
['类型别名', 'Type Fz as Fahrer.\n\nRule f given x as Fz, produce Int:\n Return x.alter.\n'],
|
|
514
|
+
['Option of', 'Rule f given x as Option of Fahrer, produce Int:\n Return x.alter.\n'],
|
|
515
|
+
['可空 ?', 'Rule f given x as Fahrer?, produce Int:\n Return x.alter.\n'],
|
|
516
|
+
['未声明类型名(Data 字段)', 'Define Halter has fz as Unbekannt.\n\nRule f given x as Halter, produce Int:\n Return x.fz.alter.\n'],
|
|
517
|
+
];
|
|
518
|
+
for (const [label, body] of receivers) {
|
|
519
|
+
it(`接收者类型为${label} → 不透传,按全局候选采用 + W801`, () => {
|
|
520
|
+
const { module, r2 } = run(EN_DRIVER + body, DRIVER, EN_US);
|
|
521
|
+
assert.deepEqual(codes(r2), ["W801" /* ErrorCode.RESOLVED_WITHOUT_TYPE */]);
|
|
522
|
+
assert.match(nameOf(returned(func(module, 'f'), 0)).name, /\.age$/);
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
it('同一 Core 上连续两次 R2:模块与诊断相同', () => {
|
|
526
|
+
const ctx = ctxOf(DRIVER);
|
|
527
|
+
const r1 = resolveDeclarations(parse(MODULE + DRIVER_DECL + 'Regel f gegeben x als Ganzzahl, liefert Ganzzahl:\n sei y gleich 1.\n sei z gleich hoch.\n gib zurueck y.alter.\n'), ctx);
|
|
528
|
+
const core = lowerModule(r1.module);
|
|
529
|
+
const a = resolveTypedReferences(core, ctx);
|
|
530
|
+
const b = resolveTypedReferences(core, ctx);
|
|
531
|
+
assert.deepEqual(codes(a.diagnostics), ["E802" /* ErrorCode.UNRESOLVED_NAME */, "W801" /* ErrorCode.RESOLVED_WITHOUT_TYPE */]);
|
|
532
|
+
assert.deepEqual(b.diagnostics, a.diagnostics);
|
|
533
|
+
assert.equal(JSON.stringify(b.module), JSON.stringify(a.module));
|
|
534
|
+
assert.equal(nameOf(returned(func(b.module, 'f'), 2)).name, 'y.age');
|
|
535
|
+
});
|
|
536
|
+
it('期望枚举下其它枚举的同名成员不冲突', () => {
|
|
537
|
+
const { module, r1, r2 } = run('Module m.\n\nDefine Tier as one of Gold, Silver.\n\nDefine Medal as one of Gold, Bronze.\n\n' +
|
|
538
|
+
'Rule f given x as Int, produce Tier:\n Return Gold.\n', [], EN_US);
|
|
539
|
+
assert.deepEqual([...codes(r1), ...codes(r2)], []);
|
|
540
|
+
assert.equal(nameOf(returned(func(module, 'f'), 0)).name, 'Gold');
|
|
541
|
+
});
|
|
542
|
+
it('LOCAL 精确命中优先于成员的折叠歧义', () => {
|
|
543
|
+
const { module, r1, r2 } = run(MODULE + 'Definiere Stufe als eines von Hoch, HOCH.\n\n' +
|
|
544
|
+
'Regel f gegeben hoch als Stufe, liefert Stufe:\n gib zurueck hoch.\n', [struct('Level', 'Stufe'), member('Level', 'High', 'Hoch'), member('Level', 'Top', 'HOCH')]);
|
|
545
|
+
assert.deepEqual([...codes(r1), ...codes(r2)], []);
|
|
546
|
+
assert.equal(nameOf(returned(func(module, 'f'), 0)).name, 'hoch');
|
|
547
|
+
});
|
|
548
|
+
it('局部名遮蔽模块函数:调用不取模块函数的返回类型', () => {
|
|
549
|
+
const { r2 } = run(EN_DRIVER + 'Rule make given x as Int, produce Fahrer:\n Return Fahrer with alter set to x.\n\n' +
|
|
550
|
+
'Rule f given x as Int, produce Int:\n' +
|
|
551
|
+
' Let make be function with v as Int, produce Int:\n Return v.\n' +
|
|
552
|
+
' Let y be make(x).\n Return y.alter.\n', DRIVER, EN_US);
|
|
553
|
+
assert.deepEqual(codes(r2), ["W801" /* ErrorCode.RESOLVED_WITHOUT_TYPE */]);
|
|
554
|
+
});
|
|
555
|
+
it('R1 已在同一位置报 E804 时 R2 不重复报告', () => {
|
|
556
|
+
const { r1, r2 } = run(MODULE + LEVEL_DECL + 'Regel f gegeben x als Ganzzahl, liefert Stufe:\n gib zurueck hoch.\n', [...LEVEL, fn('raise', 'HoCh'), fn('lift', 'HOCH')]);
|
|
557
|
+
assert.deepEqual([...codes(r1), ...codes(r2)], ["E804" /* ErrorCode.AMBIGUOUS_NAME */]);
|
|
558
|
+
});
|
|
559
|
+
});
|
|
560
|
+
describe('裁决 R30:未注解参数类型未知', () => {
|
|
561
|
+
it('未注解参数上的路径 → 全局候选采用 + W801', () => {
|
|
562
|
+
const { module, r2 } = run(MODULE + DRIVER_DECL + 'Regel f gegeben fahrer, liefert Ganzzahl:\n gib zurueck fahrer.alter.\n', DRIVER);
|
|
563
|
+
assert.deepEqual(codes(r2), ["W801" /* ErrorCode.RESOLVED_WITHOUT_TYPE */]);
|
|
564
|
+
assert.equal(nameOf(returned(func(module, 'f'), 0)).name, 'fahrer.age');
|
|
565
|
+
});
|
|
566
|
+
it('未注解主语上的裸成员模式 → 采用 + W801', () => {
|
|
567
|
+
const { module, r2 } = run(MODULE + LEVEL_DECL + 'Regel f gegeben s, liefert Ganzzahl:\n pruefe s:\n bei Hoch, gib zurueck 1.\n bei andere, gib zurueck 2.\n', LEVEL);
|
|
568
|
+
assert.deepEqual(codes(r2), ["W801" /* ErrorCode.RESOLVED_WITHOUT_TYPE */]);
|
|
569
|
+
assert.equal(stmt(func(module, 'f'), 0, 'Match').cases[0].pattern.name, 'High');
|
|
570
|
+
});
|
|
571
|
+
it('未注解 lambda 参数上的路径 → 全局候选采用 + W801(裁决 R31)', () => {
|
|
572
|
+
const { module, r2 } = run('Module m.\n\nDefine Fahrer has alter as Int.\n\n' +
|
|
573
|
+
'Rule f given x as Int, produce Int:\n Let g be function with fahrer, produce Int:\n Return fahrer.alter.\n Return x.\n', DRIVER, EN_US);
|
|
574
|
+
assert.deepEqual(codes(r2), ["W801" /* ErrorCode.RESOLVED_WITHOUT_TYPE */]);
|
|
575
|
+
const lambda = stmt(func(module, 'f'), 0, 'Let').expr;
|
|
576
|
+
assert.equal(nameOf(lambda.body.statements[0].expr).name, 'fahrer.age');
|
|
577
|
+
});
|
|
578
|
+
it('未注解 lambda 参数上的未知字段 → E801(裁决 R31)', () => {
|
|
579
|
+
const { r2 } = run('Module m.\n\nDefine Fahrer has alter as Int.\n\n' +
|
|
580
|
+
'Rule f given x as Int, produce Int:\n Let g be function with y, produce Int:\n Return y.foo.\n Return x.\n', DRIVER, EN_US);
|
|
581
|
+
assert.deepEqual(codes(r2), ["E801" /* ErrorCode.UNRESOLVED_FIELD */]);
|
|
582
|
+
});
|
|
583
|
+
it('未注解参数上的未知字段 → E801', () => {
|
|
584
|
+
const { r2 } = run(MODULE + DRIVER_DECL + 'Regel f gegeben x, liefert Ganzzahl:\n gib zurueck x.foo.\n', DRIVER);
|
|
585
|
+
assert.deepEqual(codes(r2), ["E801" /* ErrorCode.UNRESOLVED_FIELD */]);
|
|
586
|
+
});
|
|
587
|
+
});
|
|
588
|
+
describe('lower 在未运行 R1 时行为不变', () => {
|
|
589
|
+
it('无 resolved 的 AST:名字保持原写法,origin 不带 written/role', () => {
|
|
590
|
+
const ast = parse(MODULE + DRIVER_DECL + 'Regel f gegeben x als Fahrer, liefert Ganzzahl:\n gib zurueck x.alter.\n');
|
|
591
|
+
const core = lowerModule(ast);
|
|
592
|
+
const json = JSON.stringify(core);
|
|
593
|
+
assert.ok(!json.includes('"written"'));
|
|
594
|
+
assert.ok(!json.includes('"role"'));
|
|
595
|
+
assert.equal(nameOf(returned(func(core, 'f'), 0)).name, 'x.alter');
|
|
596
|
+
assert.equal(core.decls[0].name, 'Fahrer');
|
|
597
|
+
});
|
|
598
|
+
});
|
|
599
|
+
});
|
|
600
|
+
//# sourceMappingURL=resolve-typed-references.test.js.map
|