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.
Files changed (42) hide show
  1. package/README.md +5 -2
  2. package/build/index.d.ts +12 -8
  3. package/build/index.d.ts.map +1 -1
  4. package/build/index.js +10 -7
  5. package/build/src/backends/android.d.ts +49 -0
  6. package/build/src/backends/android.d.ts.map +1 -0
  7. package/build/src/backends/android.js +1274 -0
  8. package/build/src/backends/identifiers.d.ts.map +1 -1
  9. package/build/src/backends/identifiers.js +7 -1
  10. package/build/src/backends/kotlin.d.ts +10 -0
  11. package/build/src/backends/kotlin.d.ts.map +1 -1
  12. package/build/src/backends/kotlin.js +11 -9
  13. package/build/src/backends/program.d.ts +6 -4
  14. package/build/src/backends/program.d.ts.map +1 -1
  15. package/build/src/backends/program.js +5 -4
  16. package/build/src/backends/shared.d.ts.map +1 -1
  17. package/build/src/backends/shared.js +17 -1
  18. package/build/src/backends/writer.d.ts +21 -1
  19. package/build/src/backends/writer.d.ts.map +1 -1
  20. package/build/src/backends/writer.js +25 -2
  21. package/build/src/language-registry.d.ts.map +1 -1
  22. package/build/src/language-registry.js +11 -1
  23. package/build/src/semantic/types.d.ts +182 -2
  24. package/build/src/semantic/types.d.ts.map +1 -1
  25. package/build/src/semantic/types.js +44 -2
  26. package/docs/android.md +79 -0
  27. package/docs/architecture.md +2 -0
  28. package/docs/ios.md +1 -1
  29. package/docs/language-support.md +8 -1
  30. package/docs/source-maps.md +2 -0
  31. package/docs/specification.md +5 -0
  32. package/docs/testing.md +15 -0
  33. package/index.js +9 -6
  34. package/package.json +1 -1
  35. package/src/backends/android.js +1370 -0
  36. package/src/backends/identifiers.js +6 -0
  37. package/src/backends/kotlin.js +11 -8
  38. package/src/backends/program.js +4 -3
  39. package/src/backends/shared.js +19 -0
  40. package/src/backends/writer.js +27 -2
  41. package/src/language-registry.js +10 -0
  42. 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",
@@ -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
- emitBlock(writer, declaration.body, " ", `${path}/body`)
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
- emitBlock(writer, module.entryPoint.body, " ", "/entryPoint/body")
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 emitBlock(writer, block, indent, path) {
68
- block.statements.forEach((statement, index) => emitStatement(writer, statement, indent, `${path}/statements/${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 emitStatement(writer, statement, indent, path) {
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("println(", "Kotlin println plumbing", [statement], [path])
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
- emitBlock(writer, statement.consequent, `${indent} `, `${path}/consequent`)
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
- emitBlock(writer, statement.alternate, `${indent} `, `${path}/alternate`)
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])
@@ -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] - iOS application configuration.
72
- * @param {import("../semantic/types.js").IosApplicationAssetInput[]} [input.assets] - Exact iOS caller assets.
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) {
@@ -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)
@@ -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, stdlibProviderEntries, stdlibProviderPath,
85
- stdlibProviderPaths, stdlibProviderImports, stdlibProviderOwnerModule, stdlibProviderShims}) {
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
@@ -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,
@@ -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.