semantifold 0.4.0 → 0.5.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/README.md +5 -2
- package/build/index.d.ts +12 -8
- package/build/index.d.ts.map +1 -1
- package/build/index.js +10 -7
- package/build/src/backends/android.d.ts +49 -0
- package/build/src/backends/android.d.ts.map +1 -0
- package/build/src/backends/android.js +1274 -0
- package/build/src/backends/identifiers.d.ts.map +1 -1
- package/build/src/backends/identifiers.js +7 -1
- package/build/src/backends/kotlin.d.ts +10 -0
- package/build/src/backends/kotlin.d.ts.map +1 -1
- package/build/src/backends/kotlin.js +11 -9
- package/build/src/backends/program.d.ts +6 -4
- package/build/src/backends/program.d.ts.map +1 -1
- package/build/src/backends/program.js +5 -4
- package/build/src/backends/shared.d.ts.map +1 -1
- package/build/src/backends/shared.js +17 -1
- package/build/src/backends/writer.d.ts +21 -1
- package/build/src/backends/writer.d.ts.map +1 -1
- package/build/src/backends/writer.js +25 -2
- package/build/src/language-registry.d.ts.map +1 -1
- package/build/src/language-registry.js +11 -1
- package/build/src/semantic/types.d.ts +182 -2
- package/build/src/semantic/types.d.ts.map +1 -1
- package/build/src/semantic/types.js +44 -2
- package/docs/android.md +79 -0
- package/docs/architecture.md +2 -0
- package/docs/ios.md +1 -1
- package/docs/language-support.md +8 -1
- package/docs/source-maps.md +2 -0
- package/docs/specification.md +5 -0
- package/docs/testing.md +15 -0
- package/index.js +9 -6
- package/package.json +1 -1
- package/src/backends/android.js +1370 -0
- package/src/backends/identifiers.js +6 -0
- package/src/backends/kotlin.js +11 -8
- package/src/backends/program.js +4 -3
- package/src/backends/shared.js +19 -0
- package/src/backends/writer.js +27 -2
- package/src/language-registry.js +10 -0
- package/src/semantic/types.js +46 -1
|
@@ -4,6 +4,7 @@ import {unsupportedCapability} from "../diagnostic.js"
|
|
|
4
4
|
|
|
5
5
|
/** @type {Record<Exclude<import("../semantic/types.js").BackendLanguage, "ios">, RegExp>} */
|
|
6
6
|
const identifierPatterns = {
|
|
7
|
+
android: /^(?:_|\p{L})(?:_|\p{L}|\p{Nd})*$/u,
|
|
7
8
|
dart: /^(?:_|\p{L})(?:_|\p{L}|\p{Nd})*$/u,
|
|
8
9
|
kotlin: /^(?:_|\p{L})(?:_|\p{L}|\p{Nd})*$/u,
|
|
9
10
|
swift: /^(?:_|\p{XID_Start})(?:_|\p{XID_Continue})*$/u,
|
|
@@ -23,6 +24,11 @@ const identifierPatterns = {
|
|
|
23
24
|
|
|
24
25
|
/** @type {Record<Exclude<import("../semantic/types.js").BackendLanguage, "ios">, Set<string>>} */
|
|
25
26
|
const reservedWords = {
|
|
27
|
+
android: new Set([
|
|
28
|
+
"as", "break", "class", "continue", "do", "else", "false", "for", "fun", "if", "in", "interface", "is", "null",
|
|
29
|
+
"object", "package", "return", "super", "this", "throw", "true", "try", "typealias", "typeof", "val", "var", "when",
|
|
30
|
+
"while", "_", "main", "println", "Long", "Boolean", "String", "Math", "ArithmeticException"
|
|
31
|
+
]),
|
|
26
32
|
dart: new Set([
|
|
27
33
|
"Function", "Never", "Object", "RangeError", "String", "abstract", "as", "assert", "async", "await", "base",
|
|
28
34
|
"bool", "break", "case", "catch", "class", "const", "continue", "covariant", "default", "deferred", "do",
|
package/src/backends/kotlin.js
CHANGED
|
@@ -47,12 +47,12 @@ export function generateKotlin(module, writer) {
|
|
|
47
47
|
writer.mapped(emitScalarType("kotlin", declaration.returnType), {mappingKind: "exact", node: declaration.returnType,
|
|
48
48
|
path: `${path}/returnType`, role: "type"})
|
|
49
49
|
writer.synthetic(" {\n", "Kotlin function body scaffold", [declaration], [path])
|
|
50
|
-
|
|
50
|
+
emitKotlinBlock(writer, declaration.body, " ", `${path}/body`)
|
|
51
51
|
writer.synthetic("}\n", "Kotlin function body scaffold", [declaration], [path])
|
|
52
52
|
})
|
|
53
53
|
|
|
54
54
|
writer.synthetic("\nfun main() {\n", "Kotlin generated main scaffold", [module.entryPoint], ["/entryPoint"])
|
|
55
|
-
|
|
55
|
+
emitKotlinBlock(writer, module.entryPoint.body, " ", "/entryPoint/body")
|
|
56
56
|
writer.synthetic("}\n", "Kotlin generated main scaffold", [module.entryPoint], ["/entryPoint"])
|
|
57
57
|
}
|
|
58
58
|
|
|
@@ -62,10 +62,12 @@ export function generateKotlin(module, writer) {
|
|
|
62
62
|
* @param {import("../semantic/types.js").Block} block - Semantic block.
|
|
63
63
|
* @param {string} indent - Current indentation.
|
|
64
64
|
* @param {string} path - Semantic block path.
|
|
65
|
+
* @param {string} [printTarget] - Target-owned print function spelling.
|
|
65
66
|
* @returns {void}
|
|
66
67
|
*/
|
|
67
|
-
function
|
|
68
|
-
block.statements.forEach((statement, index) =>
|
|
68
|
+
export function emitKotlinBlock(writer, block, indent, path, printTarget = "println") {
|
|
69
|
+
block.statements.forEach((statement, index) =>
|
|
70
|
+
emitKotlinStatement(writer, statement, indent, `${path}/statements/${index}`, printTarget))
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
/**
|
|
@@ -74,9 +76,10 @@ function emitBlock(writer, block, indent, path) {
|
|
|
74
76
|
* @param {import("../semantic/types.js").Statement} statement - Semantic statement.
|
|
75
77
|
* @param {string} indent - Current indentation.
|
|
76
78
|
* @param {string} path - Semantic statement path.
|
|
79
|
+
* @param {string} printTarget - Target-owned print function spelling.
|
|
77
80
|
* @returns {void}
|
|
78
81
|
*/
|
|
79
|
-
function
|
|
82
|
+
function emitKotlinStatement(writer, statement, indent, path, printTarget) {
|
|
80
83
|
writer.synthetic(indent, "indentation", [statement], [path])
|
|
81
84
|
if (statement.kind == "LocalDeclaration") {
|
|
82
85
|
writer.mapped(statement.mutable ? "var" : "val", {mappingKind: "anchor", node: statement, path})
|
|
@@ -111,7 +114,7 @@ function emitStatement(writer, statement, indent, path) {
|
|
|
111
114
|
return
|
|
112
115
|
}
|
|
113
116
|
if (statement.kind == "PrintStatement") {
|
|
114
|
-
writer.synthetic(
|
|
117
|
+
writer.synthetic(`${printTarget}(`, "Kotlin print plumbing", [statement], [path])
|
|
115
118
|
emitExpression(writer, statement.expression, `${path}/expression`, "kotlin", identity)
|
|
116
119
|
writer.synthetic(")\n", "Kotlin println plumbing", [statement], [path])
|
|
117
120
|
return
|
|
@@ -121,11 +124,11 @@ function emitStatement(writer, statement, indent, path) {
|
|
|
121
124
|
writer.synthetic(" (", "Kotlin conditional scaffold", [statement], [path])
|
|
122
125
|
emitExpression(writer, statement.condition, `${path}/condition`, "kotlin", identity)
|
|
123
126
|
writer.synthetic(") {\n", "Kotlin conditional scaffold", [statement.consequent], [`${path}/consequent`])
|
|
124
|
-
|
|
127
|
+
emitKotlinBlock(writer, statement.consequent, `${indent} `, `${path}/consequent`, printTarget)
|
|
125
128
|
writer.synthetic(`${indent}}`, "Kotlin conditional scaffold", [statement.consequent], [`${path}/consequent`])
|
|
126
129
|
if (statement.alternate) {
|
|
127
130
|
writer.synthetic(" else {\n", "Kotlin alternate scaffold", [statement.alternate], [`${path}/alternate`])
|
|
128
|
-
|
|
131
|
+
emitKotlinBlock(writer, statement.alternate, `${indent} `, `${path}/alternate`, printTarget)
|
|
129
132
|
writer.synthetic(`${indent}}`, "Kotlin alternate scaffold", [statement.alternate], [`${path}/alternate`])
|
|
130
133
|
}
|
|
131
134
|
writer.synthetic("\n", "line break", [statement], [path])
|
package/src/backends/program.js
CHANGED
|
@@ -23,7 +23,7 @@ import {programImportName, SourceWriter} from "./writer.js"
|
|
|
23
23
|
/** @type {Readonly<Set<import("../semantic/types.js").SemanticLanguage>>} */
|
|
24
24
|
const programTargets = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
25
25
|
/** @type {Readonly<Set<import("../semantic/types.js").BackendLanguage>>} */
|
|
26
|
-
const programApplicationTargets = new Set(["ios"])
|
|
26
|
+
const programApplicationTargets = new Set(["android", "ios"])
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Validates and prepares a complete semantic program for a non-text backend without allocating a writer.
|
|
@@ -68,8 +68,9 @@ export function preflightSemanticProgram({backendLanguage, diagnosticLanguage, p
|
|
|
68
68
|
* @param {import("../semantic/types.js").BackendLanguage} input.language - Registered program target.
|
|
69
69
|
* @param {import("../semantic/types.js").SemanticProgram} input.program - Complete semantic program.
|
|
70
70
|
* @param {"text" | "application"} [input.role] - Explicit artifact role; omitted preserves text generation.
|
|
71
|
-
* @param {import("../semantic/types.js").IosApplicationConfigurationInput} [input.configuration] -
|
|
72
|
-
* @param {import("../semantic/types.js").IosApplicationAssetInput[]} [input.assets] - Exact
|
|
71
|
+
* @param {import("../semantic/types.js").IosApplicationConfigurationInput | import("../semantic/types.js").AndroidApplicationConfigurationInput} [input.configuration] - Application configuration.
|
|
72
|
+
* @param {import("../semantic/types.js").IosApplicationAssetInput[] | import("../semantic/types.js").AndroidApplicationInputFile[]} [input.assets] - Exact caller assets.
|
|
73
|
+
* @param {import("../semantic/types.js").AndroidApplicationInputFile[]} [input.resources] - Exact Android caller resources.
|
|
73
74
|
* @returns {import("../semantic/types.js").GeneratedArtifactSet} Transactionally validated artifact set.
|
|
74
75
|
*/
|
|
75
76
|
export function generateProgramArtifacts(input) {
|
package/src/backends/shared.js
CHANGED
|
@@ -2255,6 +2255,12 @@ export function emitExpression(writer, expression, path, language, emitIdentifie
|
|
|
2255
2255
|
emitExpression(writer, argument, `${path}/arguments/${index}`, language, emitIdentifier)
|
|
2256
2256
|
if (dartMultiline) writer.synthetic(",\n", "Dart formatter argument separator", [expression], [path])
|
|
2257
2257
|
})
|
|
2258
|
+
const callArgumentSuffix = writer.callArgumentSuffixFor(expression)
|
|
2259
|
+
|
|
2260
|
+
if (callArgumentSuffix) {
|
|
2261
|
+
writer.synthetic(`${expression.arguments.length ? ", " : ""}${callArgumentSuffix}`,
|
|
2262
|
+
"target-container semantic call argument", [expression], [path])
|
|
2263
|
+
}
|
|
2258
2264
|
if (dartMultiline) writer.synthetic(dartIndent, "Dart call closing indentation", [expression], [path])
|
|
2259
2265
|
writer.mapped(")", {mappingKind: "anchor", node: expression, path})
|
|
2260
2266
|
return
|
|
@@ -2407,6 +2413,19 @@ export function emitExpression(writer, expression, path, language, emitIdentifie
|
|
|
2407
2413
|
return
|
|
2408
2414
|
}
|
|
2409
2415
|
|
|
2416
|
+
if (language == "kotlin" && writer.usesMethodStringEquality() &&
|
|
2417
|
+
(expression.operation == "StringEqual" || expression.operation == "StringNotEqual")) {
|
|
2418
|
+
writer.mapped("(", {mappingKind: "anchor", node: expression, path})
|
|
2419
|
+
if (expression.operation == "StringNotEqual") {
|
|
2420
|
+
writer.mapped("!", {mappingKind: "exact", node: expression, path, role: "operator"})
|
|
2421
|
+
}
|
|
2422
|
+
emitExpression(writer, expression.left, `${path}/left`, language, emitIdentifier)
|
|
2423
|
+
writer.mapped(".equals(", {mappingKind: "exact", node: expression, path, role: "operator"})
|
|
2424
|
+
emitExpression(writer, expression.right, `${path}/right`, language, emitIdentifier)
|
|
2425
|
+
writer.mapped("))", {mappingKind: "anchor", node: expression, path})
|
|
2426
|
+
return
|
|
2427
|
+
}
|
|
2428
|
+
|
|
2410
2429
|
if (language == "csharp" && ["IntegerAdd", "IntegerSubtract", "IntegerMultiply"].includes(expression.operation)) {
|
|
2411
2430
|
writer.mapped("checked(", {mappingKind: "anchor", node: expression, path})
|
|
2412
2431
|
emitExpression(writer, expression.left, `${path}/left`, language, emitIdentifier)
|
package/src/backends/writer.js
CHANGED
|
@@ -80,9 +80,13 @@ export class SourceWriter {
|
|
|
80
80
|
* @param {string[]} [options.stdlibProviderImports] - Ordered provider names this module must import.
|
|
81
81
|
* @param {string} [options.stdlibProviderOwnerModule] - Java owner module identity hosting the shared support block and shims.
|
|
82
82
|
* @param {string[]} [options.stdlibProviderShims] - Java owner program-wide used operations exposed as public static shims.
|
|
83
|
+
* @param {(expression: import("../semantic/types.js").CallExpression) => string} [options.callNameResolver] - Target-container call spelling override.
|
|
84
|
+
* @param {string} [options.callArgumentSuffix] - Synthetic trailing argument for every semantic function call.
|
|
85
|
+
* @param {boolean} [options.methodStringEquality] - Emit string equality through the platform String method.
|
|
83
86
|
*/
|
|
84
|
-
constructor({filename, language, module, program, programPaths, sources,
|
|
85
|
-
stdlibProviderPaths, stdlibProviderImports, stdlibProviderOwnerModule,
|
|
87
|
+
constructor({callArgumentSuffix, callNameResolver, filename, language, methodStringEquality, module, program, programPaths, sources,
|
|
88
|
+
stdlibProviderEntries, stdlibProviderPath, stdlibProviderPaths, stdlibProviderImports, stdlibProviderOwnerModule,
|
|
89
|
+
stdlibProviderShims}) {
|
|
86
90
|
const index = createGenerationIndex(module, sources)
|
|
87
91
|
|
|
88
92
|
this.filename = filename
|
|
@@ -90,6 +94,9 @@ export class SourceWriter {
|
|
|
90
94
|
this.module = module
|
|
91
95
|
this.program = program
|
|
92
96
|
this.programPaths = programPaths
|
|
97
|
+
this.callArgumentSuffix = callArgumentSuffix
|
|
98
|
+
this.callNameResolver = callNameResolver
|
|
99
|
+
this.methodStringEquality = methodStringEquality ?? false
|
|
93
100
|
this.index = index
|
|
94
101
|
this.stdlibProviderEntries = stdlibProviderEntries
|
|
95
102
|
this.stdlibProviderPath = stdlibProviderPath
|
|
@@ -386,6 +393,7 @@ export class SourceWriter {
|
|
|
386
393
|
* @returns {string} Target call name.
|
|
387
394
|
*/
|
|
388
395
|
callNameFor(expression) {
|
|
396
|
+
if (this.callNameResolver) return this.callNameResolver(expression)
|
|
389
397
|
const declarationId = expression.resolution?.declarationId
|
|
390
398
|
const owner = declarationId ? this.declarationModules.get(declarationId) : undefined
|
|
391
399
|
const imported = declarationId ? this.#programModule()?.imports.find((item) => item.declarationId == declarationId) : undefined
|
|
@@ -398,6 +406,23 @@ export class SourceWriter {
|
|
|
398
406
|
return declarationName(owner, declarationId)
|
|
399
407
|
}
|
|
400
408
|
|
|
409
|
+
/**
|
|
410
|
+
* Returns a target-container trailing argument for a semantic call.
|
|
411
|
+
* @param {import("../semantic/types.js").CallExpression} _expression - Resolved semantic call.
|
|
412
|
+
* @returns {string | undefined} Synthetic argument spelling, when configured.
|
|
413
|
+
*/
|
|
414
|
+
callArgumentSuffixFor(_expression) {
|
|
415
|
+
return this.callArgumentSuffix
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Reports whether string equality must avoid a target runtime helper.
|
|
420
|
+
* @returns {boolean} Whether to emit the platform String method.
|
|
421
|
+
*/
|
|
422
|
+
usesMethodStringEquality() {
|
|
423
|
+
return this.methodStringEquality
|
|
424
|
+
}
|
|
425
|
+
|
|
401
426
|
/** Marks emission as occurring inside one top-level reference class. */
|
|
402
427
|
beginReferenceClassEmission() {
|
|
403
428
|
this.referenceClassEmissionDepth += 1
|
package/src/language-registry.js
CHANGED
|
@@ -16,6 +16,7 @@ import {generateRuby} from "./backends/ruby.js"
|
|
|
16
16
|
import {generateTypeScript} from "./backends/typescript.js"
|
|
17
17
|
import {generateBrowserWasm} from "./backends/wasm.js"
|
|
18
18
|
import {generateIosApplication} from "./backends/ios.js"
|
|
19
|
+
import {generateAndroidApplication} from "./backends/android.js"
|
|
19
20
|
import {parseJava} from "./frontends/java.js"
|
|
20
21
|
import {parseKotlin} from "./frontends/kotlin.js"
|
|
21
22
|
import {parseCSharp} from "./frontends/csharp.js"
|
|
@@ -501,6 +502,15 @@ const records = [
|
|
|
501
502
|
mediaType: "application/wasm",
|
|
502
503
|
roundTrip: false
|
|
503
504
|
},
|
|
505
|
+
{
|
|
506
|
+
acceptance: {stages: ["generate", "compile", "validate", "instantiate", "execute"], toolchains: ["android"]},
|
|
507
|
+
applicationBackend: generateAndroidApplication,
|
|
508
|
+
artifactMultiplicity: "multiple",
|
|
509
|
+
features: {closedRecords: false, conditionControlledLoops: false, effectfulCapabilitiesAndResources: false, generalFunctionsAndCalls: false, immutableCollections: false, optionalValues: false, orderedListIteration: false, orderedMapIteration: false, referenceClasses: false, typedErrors: false, typeParametersAndGenerics: false},
|
|
510
|
+
id: "android",
|
|
511
|
+
mapping: {binaryRanges: true, richText: true, sourceMapV3: true},
|
|
512
|
+
roundTrip: false
|
|
513
|
+
},
|
|
504
514
|
{
|
|
505
515
|
acceptance: {stages: ["generate"], toolchains: []},
|
|
506
516
|
applicationBackend: generateIosApplication,
|
package/src/semantic/types.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
/** @typedef {"php" | "ruby" | "javascript" | "typescript" | "java" | "kotlin" | "python" | "csharp" | "go" | "c" | "cpp" | "rust" | "swift" | "dart"} SemanticLanguage */
|
|
4
4
|
/** @typedef {SemanticLanguage} TextBackendLanguage */
|
|
5
|
-
/** @typedef {TextBackendLanguage | "wasm" | "ios"} BackendLanguage */
|
|
5
|
+
/** @typedef {TextBackendLanguage | "wasm" | "ios" | "android"} BackendLanguage */
|
|
6
6
|
/** @typedef {SemanticLanguage | "html"} GeneratedTextLanguage */
|
|
7
7
|
/** @typedef {"integer" | "boolean" | "string"} SemanticTypeName */
|
|
8
8
|
/** @typedef {SemanticTypeName | "void"} FunctionReturnTypeName */
|
|
@@ -58,6 +58,51 @@
|
|
|
58
58
|
* @property {string} sha256 - Lowercase SHA-256 of exact UTF-8 text or bytes.
|
|
59
59
|
*/
|
|
60
60
|
|
|
61
|
+
/**
|
|
62
|
+
* @typedef AndroidApplicationConfigurationInput
|
|
63
|
+
* @property {string} [applicationId] - Lowercase reverse-DNS Android application identity.
|
|
64
|
+
* @property {string} [namespace] - Kotlin/Android namespace; defaults to applicationId.
|
|
65
|
+
* @property {string} [packageName] - Generated Kotlin package; defaults to namespace.
|
|
66
|
+
* @property {string} [activityClassName] - Launcher Activity class name.
|
|
67
|
+
* @property {string} [productName] - Deterministic project product name.
|
|
68
|
+
* @property {string} [displayName] - User-visible application label.
|
|
69
|
+
* @property {23} [minimumSdk] - Qualified minimum Android API.
|
|
70
|
+
* @property {35} [targetSdk] - Qualified target Android API.
|
|
71
|
+
* @property {35} [compileSdk] - Qualified compile Android API.
|
|
72
|
+
* @property {"35.0.0"} [buildToolsVersion] - Qualified Android Build Tools version.
|
|
73
|
+
* @property {number} [versionCode] - Positive Android version code.
|
|
74
|
+
* @property {string} [versionName] - Canonical application version name.
|
|
75
|
+
* @property {"unspecified"} [orientation] - Baseline orientation behavior.
|
|
76
|
+
* @property {"system"} [theme] - Baseline platform-native theme.
|
|
77
|
+
* @property {unknown[]} [permissions] - Validated as the closed empty permission set.
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @typedef AndroidApplicationConfiguration
|
|
82
|
+
* @property {string} applicationId - Validated Android application identity.
|
|
83
|
+
* @property {string} namespace - Validated Android namespace.
|
|
84
|
+
* @property {string} packageName - Generated Kotlin package.
|
|
85
|
+
* @property {string} activityClassName - Launcher Activity class name.
|
|
86
|
+
* @property {string} productName - Project product name.
|
|
87
|
+
* @property {string} displayName - User-visible application label.
|
|
88
|
+
* @property {23} minimumSdk - Qualified minimum Android API.
|
|
89
|
+
* @property {35} targetSdk - Qualified target Android API.
|
|
90
|
+
* @property {35} compileSdk - Qualified compile Android API.
|
|
91
|
+
* @property {"35.0.0"} buildToolsVersion - Qualified Android Build Tools version.
|
|
92
|
+
* @property {number} versionCode - Positive Android version code.
|
|
93
|
+
* @property {string} versionName - Canonical application version name.
|
|
94
|
+
* @property {"unspecified"} orientation - Baseline orientation behavior.
|
|
95
|
+
* @property {"system"} theme - Platform-native theme.
|
|
96
|
+
*/
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @typedef AndroidApplicationInputFile
|
|
100
|
+
* @property {string} path - Safe path relative to the selected fixed Android resource or asset root.
|
|
101
|
+
* @property {string | Uint8Array} content - Exact caller-provided text or bytes.
|
|
102
|
+
* @property {string} mediaType - Explicit artifact media type.
|
|
103
|
+
* @property {string} sha256 - Lowercase SHA-256 of exact UTF-8 text or bytes.
|
|
104
|
+
*/
|
|
105
|
+
|
|
61
106
|
/**
|
|
62
107
|
* @typedef LanguageRoleCapabilities
|
|
63
108
|
* @property {boolean} frontend - Parser adapter availability.
|