semantifold 0.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/LICENSE +15 -0
- package/README.md +71 -0
- package/build/index.d.ts +54 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +45 -0
- package/build/src/backends/identifiers.d.ts +19 -0
- package/build/src/backends/identifiers.d.ts.map +1 -0
- package/build/src/backends/identifiers.js +93 -0
- package/build/src/backends/index.d.ts +35 -0
- package/build/src/backends/index.d.ts.map +1 -0
- package/build/src/backends/index.js +149 -0
- package/build/src/backends/java.d.ts +8 -0
- package/build/src/backends/java.d.ts.map +1 -0
- package/build/src/backends/java.js +172 -0
- package/build/src/backends/javascript.d.ts +8 -0
- package/build/src/backends/javascript.d.ts.map +1 -0
- package/build/src/backends/javascript.js +173 -0
- package/build/src/backends/php.d.ts +8 -0
- package/build/src/backends/php.d.ts.map +1 -0
- package/build/src/backends/php.js +175 -0
- package/build/src/backends/ruby.d.ts +8 -0
- package/build/src/backends/ruby.d.ts.map +1 -0
- package/build/src/backends/ruby.js +155 -0
- package/build/src/backends/scalars.d.ts +15 -0
- package/build/src/backends/scalars.d.ts.map +1 -0
- package/build/src/backends/scalars.js +138 -0
- package/build/src/backends/shared.d.ts +26 -0
- package/build/src/backends/shared.d.ts.map +1 -0
- package/build/src/backends/shared.js +424 -0
- package/build/src/backends/typescript.d.ts +8 -0
- package/build/src/backends/typescript.d.ts.map +1 -0
- package/build/src/backends/typescript.js +160 -0
- package/build/src/backends/writer.d.ts +87 -0
- package/build/src/backends/writer.d.ts.map +1 -0
- package/build/src/backends/writer.js +207 -0
- package/build/src/diagnostic.d.ts +67 -0
- package/build/src/diagnostic.d.ts.map +1 -0
- package/build/src/diagnostic.js +96 -0
- package/build/src/frontends/index.d.ts +14 -0
- package/build/src/frontends/index.d.ts.map +1 -0
- package/build/src/frontends/index.js +36 -0
- package/build/src/frontends/java.d.ts +12 -0
- package/build/src/frontends/java.d.ts.map +1 -0
- package/build/src/frontends/java.js +630 -0
- package/build/src/frontends/javascript-typescript.d.ts +19 -0
- package/build/src/frontends/javascript-typescript.d.ts.map +1 -0
- package/build/src/frontends/javascript-typescript.js +633 -0
- package/build/src/frontends/php.d.ts +12 -0
- package/build/src/frontends/php.d.ts.map +1 -0
- package/build/src/frontends/php.js +523 -0
- package/build/src/frontends/ruby.d.ts +12 -0
- package/build/src/frontends/ruby.d.ts.map +1 -0
- package/build/src/frontends/ruby.js +515 -0
- package/build/src/frontends/scalars.d.ts +19 -0
- package/build/src/frontends/scalars.d.ts.map +1 -0
- package/build/src/frontends/scalars.js +71 -0
- package/build/src/mapping.d.ts +160 -0
- package/build/src/mapping.d.ts.map +1 -0
- package/build/src/mapping.js +1120 -0
- package/build/src/semantic/location.d.ts +61 -0
- package/build/src/semantic/location.d.ts.map +1 -0
- package/build/src/semantic/location.js +142 -0
- package/build/src/semantic/operators.d.ts +16 -0
- package/build/src/semantic/operators.d.ts.map +1 -0
- package/build/src/semantic/operators.js +24 -0
- package/build/src/semantic/provenance.d.ts +74 -0
- package/build/src/semantic/provenance.d.ts.map +1 -0
- package/build/src/semantic/provenance.js +577 -0
- package/build/src/semantic/scalars.d.ts +19 -0
- package/build/src/semantic/scalars.d.ts.map +1 -0
- package/build/src/semantic/scalars.js +40 -0
- package/build/src/semantic/types.d.ts +1003 -0
- package/build/src/semantic/types.d.ts.map +1 -0
- package/build/src/semantic/types.js +279 -0
- package/build/src/semantic/validate.d.ts +60 -0
- package/build/src/semantic/validate.d.ts.map +1 -0
- package/build/src/semantic/validate.js +472 -0
- package/docs/architecture.md +39 -0
- package/docs/coding-standards.md +7 -0
- package/docs/goals.md +7 -0
- package/docs/language-support.md +69 -0
- package/docs/plans/2026-09-02-initial-toolchain.md +54 -0
- package/docs/source-maps.md +134 -0
- package/docs/testing.md +25 -0
- package/index.js +63 -0
- package/package.json +69 -0
- package/src/backends/identifiers.js +101 -0
- package/src/backends/index.js +159 -0
- package/src/backends/java.js +182 -0
- package/src/backends/javascript.js +184 -0
- package/src/backends/php.js +184 -0
- package/src/backends/ruby.js +163 -0
- package/src/backends/scalars.js +120 -0
- package/src/backends/shared.js +456 -0
- package/src/backends/typescript.js +168 -0
- package/src/backends/writer.js +224 -0
- package/src/diagnostic.js +103 -0
- package/src/frontends/index.js +36 -0
- package/src/frontends/java.js +735 -0
- package/src/frontends/javascript-typescript.js +718 -0
- package/src/frontends/php.js +593 -0
- package/src/frontends/ruby.js +622 -0
- package/src/frontends/scalars.js +77 -0
- package/src/mapping.js +1260 -0
- package/src/semantic/location.js +154 -0
- package/src/semantic/operators.js +28 -0
- package/src/semantic/provenance.js +629 -0
- package/src/semantic/scalars.js +44 -0
- package/src/semantic/types.js +319 -0
- package/src/semantic/validate.js +529 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import {emitExpression} from "./shared.js"
|
|
4
|
+
import {emitScalarType} from "./scalars.js"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Emits an independently executable PHP program through the source-aware writer.
|
|
8
|
+
* @param {import("../semantic/types.js").SemanticModule} module - Semantic module.
|
|
9
|
+
* @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
|
|
10
|
+
* @returns {void}
|
|
11
|
+
*/
|
|
12
|
+
export function generatePhp(module, writer) {
|
|
13
|
+
writer.synthetic("<?php\ndeclare(strict_types=1);\n\n", "PHP program scaffolding", [module])
|
|
14
|
+
|
|
15
|
+
module.functions.forEach((declaration, functionIndex) => {
|
|
16
|
+
if (functionIndex > 0) writer.synthetic("\n\n", "declaration separator", [declaration])
|
|
17
|
+
|
|
18
|
+
writer.mapped("function", {mappingKind: "anchor", node: declaration})
|
|
19
|
+
writer.synthetic(" ", "function spacing", [declaration])
|
|
20
|
+
writer.mapped(declaration.name, {mappingKind: "exact", node: declaration, role: "name"})
|
|
21
|
+
writer.mapped("(", {mappingKind: "anchor", node: declaration})
|
|
22
|
+
declaration.parameters.forEach((parameter, index) => {
|
|
23
|
+
const parameterPath = `/functions/${functionIndex}/parameters/${index}`
|
|
24
|
+
|
|
25
|
+
if (index > 0) writer.synthetic(", ", "parameter separator", [declaration])
|
|
26
|
+
writer.mapped(emitScalarType("php", parameter.type), {
|
|
27
|
+
mappingKind: "exact",
|
|
28
|
+
node: parameter.type,
|
|
29
|
+
path: `${parameterPath}/type`,
|
|
30
|
+
role: "type"
|
|
31
|
+
})
|
|
32
|
+
writer.synthetic(" ", "parameter spacing", [parameter], [parameterPath])
|
|
33
|
+
writer.mapped(`$${parameter.name}`, {mappingKind: "exact", node: parameter, path: parameterPath, role: "name"})
|
|
34
|
+
})
|
|
35
|
+
writer.mapped(")", {mappingKind: "anchor", node: declaration})
|
|
36
|
+
writer.synthetic(": ", "return type separator", [declaration])
|
|
37
|
+
writer.mapped(emitScalarType("php", declaration.returnType), {
|
|
38
|
+
mappingKind: "exact",
|
|
39
|
+
node: declaration.returnType,
|
|
40
|
+
path: `/functions/${functionIndex}/returnType`,
|
|
41
|
+
role: "type"
|
|
42
|
+
})
|
|
43
|
+
writer.synthetic("\n", "line break", [declaration])
|
|
44
|
+
writer.mapped("{", {mappingKind: "anchor", node: declaration})
|
|
45
|
+
writer.synthetic("\n", "line break", [declaration])
|
|
46
|
+
|
|
47
|
+
emitBlock(writer, declaration.body, " ", `/functions/${functionIndex}/body`)
|
|
48
|
+
writer.mapped("}", {mappingKind: "anchor", node: declaration})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
writer.synthetic("\n\n", "entry-point separator", [module.entryPoint])
|
|
52
|
+
emitBlock(writer, module.entryPoint.body, "", "/entryPoint/body")
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Emits one ordered PHP block body.
|
|
57
|
+
* @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
|
|
58
|
+
* @param {import("../semantic/types.js").Block} block - Semantic block.
|
|
59
|
+
* @param {string} indent - Indentation.
|
|
60
|
+
* @param {string} path - Exact block path.
|
|
61
|
+
* @returns {void}
|
|
62
|
+
*/
|
|
63
|
+
function emitBlock(writer, block, indent, path) {
|
|
64
|
+
block.statements.forEach((statement, index) => emitStatement(writer, statement, indent, `${path}/statements/${index}`))
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Emits one PHP statement.
|
|
69
|
+
* @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
|
|
70
|
+
* @param {import("../semantic/types.js").Statement} statement - Semantic statement.
|
|
71
|
+
* @param {string} indent - Indentation.
|
|
72
|
+
* @param {string} path - Exact statement path.
|
|
73
|
+
* @returns {void}
|
|
74
|
+
*/
|
|
75
|
+
function emitStatement(writer, statement, indent, path) {
|
|
76
|
+
if (statement.kind == "LocalDeclaration" || statement.kind == "AssignmentStatement") return emitLocal(writer, statement, indent, path)
|
|
77
|
+
writer.synthetic(indent, "indentation", [statement], [path])
|
|
78
|
+
if (statement.kind == "ReturnStatement") {
|
|
79
|
+
writer.mapped("return", {mappingKind: "anchor", node: statement, path})
|
|
80
|
+
writer.synthetic(" ", "return spacing", [statement], [path])
|
|
81
|
+
emitExpression(writer, statement.expression, `${path}/expression`, "php", phpIdentifier)
|
|
82
|
+
writer.mapped(";", {mappingKind: "anchor", node: statement, path})
|
|
83
|
+
writer.synthetic("\n", "line break", [statement], [path])
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
if (statement.kind == "PrintStatement") {
|
|
87
|
+
writer.mapped("echo", {mappingKind: "anchor", node: statement, path})
|
|
88
|
+
writer.synthetic(" ", "print spacing", [statement], [path])
|
|
89
|
+
emitExpression(writer, statement.expression, `${path}/expression`, "php", phpIdentifier)
|
|
90
|
+
writer.synthetic(", ", "PHP print separator", [statement], [path])
|
|
91
|
+
writer.mapped("PHP_EOL", {mappingKind: "anchor", node: statement, path})
|
|
92
|
+
writer.mapped(";", {mappingKind: "anchor", node: statement, path})
|
|
93
|
+
writer.synthetic("\n", "line break", [statement], [path])
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
writer.mapped("if", {mappingKind: "anchor", node: statement, path})
|
|
97
|
+
writer.synthetic(" ", "conditional spacing", [statement], [path])
|
|
98
|
+
writer.mapped("(", {mappingKind: "anchor", node: statement, path})
|
|
99
|
+
emitExpression(writer, statement.condition, `${path}/condition`, "php", phpIdentifier)
|
|
100
|
+
writer.mapped(")", {mappingKind: "anchor", node: statement, path})
|
|
101
|
+
writer.synthetic(" ", "conditional spacing", [statement], [path])
|
|
102
|
+
writer.mapped("{", {mappingKind: "anchor", node: statement.consequent, path: `${path}/consequent`})
|
|
103
|
+
writer.synthetic("\n", "line break", [statement], [path])
|
|
104
|
+
emitBlock(writer, statement.consequent, `${indent} `, `${path}/consequent`)
|
|
105
|
+
writer.synthetic(indent, "indentation", [statement], [path])
|
|
106
|
+
writer.mapped("}", {mappingKind: "anchor", node: statement.consequent, path: `${path}/consequent`})
|
|
107
|
+
if (statement.alternate) {
|
|
108
|
+
writer.synthetic(" ", "conditional spacing", [statement], [path])
|
|
109
|
+
writer.mapped("else", {mappingKind: "anchor", node: statement, path})
|
|
110
|
+
writer.synthetic(" ", "conditional spacing", [statement], [path])
|
|
111
|
+
writer.mapped("{", {mappingKind: "anchor", node: statement.alternate, path: `${path}/alternate`})
|
|
112
|
+
writer.synthetic("\n", "line break", [statement], [path])
|
|
113
|
+
emitBlock(writer, statement.alternate, `${indent} `, `${path}/alternate`)
|
|
114
|
+
writer.synthetic(indent, "indentation", [statement], [path])
|
|
115
|
+
writer.mapped("}", {mappingKind: "anchor", node: statement.alternate, path: `${path}/alternate`})
|
|
116
|
+
}
|
|
117
|
+
writer.synthetic("\n", "line break", [statement], [path])
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Emits one PHP local statement with its exact Semantifold profile metadata.
|
|
122
|
+
* @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
|
|
123
|
+
* @param {import("../semantic/types.js").LocalStatement} statement - Local statement.
|
|
124
|
+
* @param {string} indent - Leading indentation.
|
|
125
|
+
* @param {string} statementPath - Exact JSON Pointer for this statement occurrence.
|
|
126
|
+
* @returns {void}
|
|
127
|
+
*/
|
|
128
|
+
function emitLocal(writer, statement, indent, statementPath) {
|
|
129
|
+
writer.synthetic(indent, "indentation", [statement], [statementPath])
|
|
130
|
+
|
|
131
|
+
if (statement.kind == "AssignmentStatement") {
|
|
132
|
+
writer.mapped(`$${statement.target.name}`, {mappingKind: "exact", node: statement.target, path: `${statementPath}/target`, role: "name"})
|
|
133
|
+
writer.synthetic(" ", "assignment spacing", [statement], [statementPath])
|
|
134
|
+
writer.mapped("=", {mappingKind: "exact", node: statement, path: statementPath, role: "operator"})
|
|
135
|
+
writer.synthetic(" ", "assignment spacing", [statement], [statementPath])
|
|
136
|
+
emitExpression(writer, statement.expression, `${statementPath}/expression`, "php", phpIdentifier)
|
|
137
|
+
writer.mapped(";", {mappingKind: "anchor", node: statement, path: statementPath})
|
|
138
|
+
writer.synthetic("\n", "line break", [statement], [statementPath])
|
|
139
|
+
return
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (statement.mutable) {
|
|
143
|
+
writer.synthetic("/** @var ", "PHP local type scaffolding", [statement], [statementPath])
|
|
144
|
+
writer.mapped(emitScalarType("php", statement.type), {
|
|
145
|
+
mappingKind: "exact",
|
|
146
|
+
node: statement.type,
|
|
147
|
+
path: `${statementPath}/type`,
|
|
148
|
+
role: "type"
|
|
149
|
+
})
|
|
150
|
+
writer.synthetic(" ", "PHP local type scaffolding", [statement], [statementPath])
|
|
151
|
+
writer.mapped(`$${statement.name}`, {mappingKind: "exact", node: statement, path: statementPath, role: "name"})
|
|
152
|
+
writer.synthetic(" */\n", "PHP local type scaffolding", [statement], [statementPath])
|
|
153
|
+
} else {
|
|
154
|
+
writer.synthetic("/**\n", "PHP local type scaffolding", [statement], [statementPath])
|
|
155
|
+
writer.synthetic(`${indent} * @var `, "PHP local type scaffolding", [statement], [statementPath])
|
|
156
|
+
writer.mapped(emitScalarType("php", statement.type), {
|
|
157
|
+
mappingKind: "exact",
|
|
158
|
+
node: statement.type,
|
|
159
|
+
path: `${statementPath}/type`,
|
|
160
|
+
role: "type"
|
|
161
|
+
})
|
|
162
|
+
writer.synthetic(" ", "PHP local type scaffolding", [statement], [statementPath])
|
|
163
|
+
writer.mapped(`$${statement.name}`, {mappingKind: "exact", node: statement, path: statementPath, role: "name"})
|
|
164
|
+
writer.synthetic(`\n${indent} * @semantifold-immutable\n${indent} */\n`, "PHP local type scaffolding", [statement], [statementPath])
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
writer.synthetic(indent, "indentation", [statement], [statementPath])
|
|
168
|
+
writer.mapped(`$${statement.name}`, {mappingKind: "exact", node: statement, path: statementPath, role: "name"})
|
|
169
|
+
writer.synthetic(" ", "assignment spacing", [statement], [statementPath])
|
|
170
|
+
writer.mapped("=", {mappingKind: "exact", node: statement, path: statementPath, role: "operator"})
|
|
171
|
+
writer.synthetic(" ", "assignment spacing", [statement], [statementPath])
|
|
172
|
+
emitExpression(writer, statement.initializer, `${statementPath}/initializer`, "php", phpIdentifier)
|
|
173
|
+
writer.mapped(";", {mappingKind: "anchor", node: statement, path: statementPath})
|
|
174
|
+
writer.synthetic("\n", "line break", [statement], [statementPath])
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Formats one PHP variable identifier.
|
|
179
|
+
* @param {string} name - Semantic identifier.
|
|
180
|
+
* @returns {string} PHP identifier.
|
|
181
|
+
*/
|
|
182
|
+
function phpIdentifier(name) {
|
|
183
|
+
return `$${name}`
|
|
184
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import {emitExpression} from "./shared.js"
|
|
4
|
+
import {emitScalarType} from "./scalars.js"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Emits an independently executable Ruby program through the source-aware writer.
|
|
8
|
+
* @param {import("../semantic/types.js").SemanticModule} module - Semantic module.
|
|
9
|
+
* @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
|
|
10
|
+
* @returns {void}
|
|
11
|
+
*/
|
|
12
|
+
export function generateRuby(module, writer) {
|
|
13
|
+
module.functions.forEach((declaration, functionIndex) => {
|
|
14
|
+
if (functionIndex > 0) writer.synthetic("\n\n", "declaration separator", [declaration])
|
|
15
|
+
|
|
16
|
+
for (const [parameterIndex, parameter] of declaration.parameters.entries()) {
|
|
17
|
+
const parameterPath = `/functions/${functionIndex}/parameters/${parameterIndex}`
|
|
18
|
+
|
|
19
|
+
writer.synthetic("# @param ", "Ruby type scaffolding", [parameter], [parameterPath])
|
|
20
|
+
writer.mapped(parameter.name, {mappingKind: "exact", node: parameter, path: parameterPath, role: "name"})
|
|
21
|
+
writer.synthetic(" [", "Ruby type scaffolding", [parameter], [parameterPath])
|
|
22
|
+
writer.mapped(emitScalarType("ruby", parameter.type), {
|
|
23
|
+
mappingKind: "exact",
|
|
24
|
+
node: parameter.type,
|
|
25
|
+
path: `${parameterPath}/type`,
|
|
26
|
+
role: "type"
|
|
27
|
+
})
|
|
28
|
+
writer.synthetic("]\n", "Ruby type scaffolding", [parameter], [parameterPath])
|
|
29
|
+
}
|
|
30
|
+
writer.synthetic("# @return [", "Ruby type scaffolding", [declaration])
|
|
31
|
+
writer.mapped(emitScalarType("ruby", declaration.returnType), {
|
|
32
|
+
mappingKind: "exact",
|
|
33
|
+
node: declaration.returnType,
|
|
34
|
+
path: `/functions/${functionIndex}/returnType`,
|
|
35
|
+
role: "type"
|
|
36
|
+
})
|
|
37
|
+
writer.synthetic("]\n", "Ruby type scaffolding", [declaration])
|
|
38
|
+
writer.mapped("def", {mappingKind: "anchor", node: declaration})
|
|
39
|
+
writer.synthetic(" ", "function spacing", [declaration])
|
|
40
|
+
writer.mapped(declaration.name, {mappingKind: "exact", node: declaration, role: "name"})
|
|
41
|
+
writer.mapped("(", {mappingKind: "anchor", node: declaration})
|
|
42
|
+
declaration.parameters.forEach((parameter, index) => {
|
|
43
|
+
if (index > 0) writer.synthetic(", ", "parameter separator", [declaration])
|
|
44
|
+
writer.mapped(parameter.name, {
|
|
45
|
+
mappingKind: "exact",
|
|
46
|
+
node: parameter,
|
|
47
|
+
path: `/functions/${functionIndex}/parameters/${index}`,
|
|
48
|
+
role: "name"
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
writer.mapped(")", {mappingKind: "anchor", node: declaration})
|
|
52
|
+
writer.synthetic("\n", "line break", [declaration])
|
|
53
|
+
|
|
54
|
+
emitBlock(writer, declaration.body, " ", `/functions/${functionIndex}/body`)
|
|
55
|
+
writer.mapped("end", {mappingKind: "anchor", node: declaration})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
writer.synthetic("\n\n", "entry-point separator", [module.entryPoint])
|
|
59
|
+
emitBlock(writer, module.entryPoint.body, "", "/entryPoint/body")
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Emits one ordered Ruby block body.
|
|
64
|
+
* @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
|
|
65
|
+
* @param {import("../semantic/types.js").Block} block - Semantic block.
|
|
66
|
+
* @param {string} indent - Indentation.
|
|
67
|
+
* @param {string} path - Exact block path.
|
|
68
|
+
* @returns {void}
|
|
69
|
+
*/
|
|
70
|
+
function emitBlock(writer, block, indent, path) {
|
|
71
|
+
block.statements.forEach((statement, index) => emitStatement(writer, statement, indent, `${path}/statements/${index}`))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Emits one Ruby statement.
|
|
76
|
+
* @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
|
|
77
|
+
* @param {import("../semantic/types.js").Statement} statement - Semantic statement.
|
|
78
|
+
* @param {string} indent - Indentation.
|
|
79
|
+
* @param {string} path - Exact statement path.
|
|
80
|
+
* @returns {void}
|
|
81
|
+
*/
|
|
82
|
+
function emitStatement(writer, statement, indent, path) {
|
|
83
|
+
if (statement.kind == "LocalDeclaration" || statement.kind == "AssignmentStatement") return emitLocal(writer, statement, indent, path)
|
|
84
|
+
writer.synthetic(indent, "indentation", [statement], [path])
|
|
85
|
+
if (statement.kind == "ReturnStatement") {
|
|
86
|
+
writer.mapped("return", {mappingKind: "anchor", node: statement, path})
|
|
87
|
+
writer.synthetic(" ", "return spacing", [statement], [path])
|
|
88
|
+
emitExpression(writer, statement.expression, `${path}/expression`, "ruby", identity)
|
|
89
|
+
writer.synthetic("\n", "line break", [statement], [path])
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
if (statement.kind == "PrintStatement") {
|
|
93
|
+
writer.mapped("puts", {mappingKind: "anchor", node: statement, path})
|
|
94
|
+
writer.synthetic(" ", "print spacing", [statement], [path])
|
|
95
|
+
emitExpression(writer, statement.expression, `${path}/expression`, "ruby", identity)
|
|
96
|
+
writer.synthetic("\n", "line break", [statement], [path])
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
writer.mapped("if", {mappingKind: "anchor", node: statement, path})
|
|
100
|
+
writer.synthetic(" ", "conditional spacing", [statement], [path])
|
|
101
|
+
emitExpression(writer, statement.condition, `${path}/condition`, "ruby", identity)
|
|
102
|
+
writer.synthetic("\n", "line break", [statement], [path])
|
|
103
|
+
emitBlock(writer, statement.consequent, `${indent} `, `${path}/consequent`)
|
|
104
|
+
if (statement.alternate) {
|
|
105
|
+
writer.synthetic(indent, "indentation", [statement], [path])
|
|
106
|
+
writer.mapped("else", {mappingKind: "anchor", node: statement, path})
|
|
107
|
+
writer.synthetic("\n", "line break", [statement], [path])
|
|
108
|
+
emitBlock(writer, statement.alternate, `${indent} `, `${path}/alternate`)
|
|
109
|
+
}
|
|
110
|
+
writer.synthetic(indent, "indentation", [statement], [path])
|
|
111
|
+
writer.mapped("end", {mappingKind: "anchor", node: statement, path})
|
|
112
|
+
writer.synthetic("\n", "line break", [statement], [path])
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Emits one Ruby local statement with exact Semantifold profile metadata.
|
|
117
|
+
* @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
|
|
118
|
+
* @param {import("../semantic/types.js").LocalStatement} statement - Local statement.
|
|
119
|
+
* @param {string} indent - Leading indentation.
|
|
120
|
+
* @param {string} statementPath - Exact JSON Pointer for this statement occurrence.
|
|
121
|
+
* @returns {void}
|
|
122
|
+
*/
|
|
123
|
+
function emitLocal(writer, statement, indent, statementPath) {
|
|
124
|
+
writer.synthetic(indent, "indentation", [statement], [statementPath])
|
|
125
|
+
|
|
126
|
+
if (statement.kind == "AssignmentStatement") {
|
|
127
|
+
writer.mapped(statement.target.name, {mappingKind: "exact", node: statement.target, path: `${statementPath}/target`, role: "name"})
|
|
128
|
+
writer.synthetic(" ", "assignment spacing", [statement], [statementPath])
|
|
129
|
+
writer.mapped("=", {mappingKind: "exact", node: statement, path: statementPath, role: "operator"})
|
|
130
|
+
writer.synthetic(" ", "assignment spacing", [statement], [statementPath])
|
|
131
|
+
emitExpression(writer, statement.expression, `${statementPath}/expression`, "ruby", identity)
|
|
132
|
+
writer.synthetic("\n", "line break", [statement], [statementPath])
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
writer.synthetic("# @type [", "Ruby local type scaffolding", [statement], [statementPath])
|
|
137
|
+
writer.mapped(emitScalarType("ruby", statement.type), {
|
|
138
|
+
mappingKind: "exact",
|
|
139
|
+
node: statement.type,
|
|
140
|
+
path: `${statementPath}/type`,
|
|
141
|
+
role: "type"
|
|
142
|
+
})
|
|
143
|
+
writer.synthetic("]\n", "Ruby local type scaffolding", [statement], [statementPath])
|
|
144
|
+
if (!statement.mutable) {
|
|
145
|
+
writer.synthetic(`${indent}# @semantifold-immutable\n`, "Ruby immutability scaffolding", [statement], [statementPath])
|
|
146
|
+
}
|
|
147
|
+
writer.synthetic(indent, "indentation", [statement], [statementPath])
|
|
148
|
+
writer.mapped(statement.name, {mappingKind: "exact", node: statement, path: statementPath, role: "name"})
|
|
149
|
+
writer.synthetic(" ", "assignment spacing", [statement], [statementPath])
|
|
150
|
+
writer.mapped("=", {mappingKind: "exact", node: statement, path: statementPath, role: "operator"})
|
|
151
|
+
writer.synthetic(" ", "assignment spacing", [statement], [statementPath])
|
|
152
|
+
emitExpression(writer, statement.initializer, `${statementPath}/initializer`, "ruby", identity)
|
|
153
|
+
writer.synthetic("\n", "line break", [statement], [statementPath])
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Returns an unchanged Ruby identifier.
|
|
158
|
+
* @param {string} name - Identifier.
|
|
159
|
+
* @returns {string} Identifier.
|
|
160
|
+
*/
|
|
161
|
+
function identity(name) {
|
|
162
|
+
return name
|
|
163
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/** @type {Readonly<Record<import("../semantic/types.js").SemanticLanguage, Readonly<Record<import("../semantic/types.js").SemanticTypeName, string>>>>} */
|
|
4
|
+
const targetScalarTypes = Object.freeze({
|
|
5
|
+
java: Object.freeze({boolean: "boolean", integer: "int", string: "String"}),
|
|
6
|
+
javascript: Object.freeze({boolean: "boolean", integer: "number", string: "string"}),
|
|
7
|
+
php: Object.freeze({boolean: "bool", integer: "int", string: "string"}),
|
|
8
|
+
ruby: Object.freeze({boolean: "bool", integer: "Integer", string: "String"}),
|
|
9
|
+
typescript: Object.freeze({boolean: "boolean", integer: "number", string: "string"})
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Emits one target-language scalar type spelling.
|
|
14
|
+
* @param {import("../semantic/types.js").SemanticLanguage} language - Target language.
|
|
15
|
+
* @param {import("../semantic/types.js").TypeReference} type - Semantic scalar type.
|
|
16
|
+
* @returns {string} Target type spelling.
|
|
17
|
+
*/
|
|
18
|
+
export function emitScalarType(language, type) {
|
|
19
|
+
return targetScalarTypes[language][type.name]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Emits a safely escaped target-language string literal.
|
|
24
|
+
* @param {import("../semantic/types.js").SemanticLanguage} language - Target language.
|
|
25
|
+
* @param {string} value - Valid Unicode scalar string.
|
|
26
|
+
* @returns {string} Target string literal.
|
|
27
|
+
*/
|
|
28
|
+
export function emitStringLiteral(language, value) {
|
|
29
|
+
if (language == "php") return emitPhpString(value)
|
|
30
|
+
if (language == "ruby") return emitRubyString(value)
|
|
31
|
+
if (language == "java") return emitJavaString(value)
|
|
32
|
+
|
|
33
|
+
const literal = JSON.stringify(value)
|
|
34
|
+
|
|
35
|
+
if (typeof literal != "string") throw new TypeError("A validated semantic string did not serialize.")
|
|
36
|
+
|
|
37
|
+
return literal.replaceAll("\u2028", "\\u2028").replaceAll("\u2029", "\\u2029")
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Emits a PHP double-quoted string while preventing interpolation.
|
|
42
|
+
* @param {string} value - Valid Unicode scalar string.
|
|
43
|
+
* @returns {string} PHP string literal.
|
|
44
|
+
*/
|
|
45
|
+
function emitPhpString(value) {
|
|
46
|
+
let emitted = "\""
|
|
47
|
+
|
|
48
|
+
for (const character of value) {
|
|
49
|
+
const codePoint = character.codePointAt(0)
|
|
50
|
+
|
|
51
|
+
if (character == "\"") emitted += "\\\""
|
|
52
|
+
else if (character == "\\") emitted += "\\\\"
|
|
53
|
+
else if (character == "$") emitted += "\\$"
|
|
54
|
+
else if (character == "\n") emitted += "\\n"
|
|
55
|
+
else if (character == "\r") emitted += "\\r"
|
|
56
|
+
else if (character == "\t") emitted += "\\t"
|
|
57
|
+
else if (character == "\f") emitted += "\\f"
|
|
58
|
+
else if (character == "\v") emitted += "\\v"
|
|
59
|
+
else if (codePoint == 27) emitted += "\\e"
|
|
60
|
+
else if (codePoint !== undefined && (codePoint < 32 || codePoint == 127)) {
|
|
61
|
+
emitted += `\\x${codePoint.toString(16).padStart(2, "0")}`
|
|
62
|
+
} else emitted += character
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return `${emitted}"`
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Emits a Ruby double-quoted string while preventing interpolation.
|
|
70
|
+
* @param {string} value - Valid Unicode scalar string.
|
|
71
|
+
* @returns {string} Ruby string literal.
|
|
72
|
+
*/
|
|
73
|
+
function emitRubyString(value) {
|
|
74
|
+
let emitted = "\""
|
|
75
|
+
|
|
76
|
+
for (const character of value) {
|
|
77
|
+
const codePoint = character.codePointAt(0)
|
|
78
|
+
|
|
79
|
+
if (character == "\"") emitted += "\\\""
|
|
80
|
+
else if (character == "\\") emitted += "\\\\"
|
|
81
|
+
else if (character == "#") emitted += "\\#"
|
|
82
|
+
else if (character == "\n") emitted += "\\n"
|
|
83
|
+
else if (character == "\r") emitted += "\\r"
|
|
84
|
+
else if (character == "\t") emitted += "\\t"
|
|
85
|
+
else if (character == "\f") emitted += "\\f"
|
|
86
|
+
else if (character == "\v") emitted += "\\v"
|
|
87
|
+
else if (codePoint == 27) emitted += "\\e"
|
|
88
|
+
else if (codePoint !== undefined && (codePoint < 32 || codePoint == 127)) {
|
|
89
|
+
emitted += `\\u{${codePoint.toString(16)}}`
|
|
90
|
+
} else emitted += character
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return `${emitted}"`
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Emits a Java string using standard or fixed-width octal control escapes.
|
|
98
|
+
* @param {string} value - Valid Unicode scalar string.
|
|
99
|
+
* @returns {string} Java string literal.
|
|
100
|
+
*/
|
|
101
|
+
function emitJavaString(value) {
|
|
102
|
+
let emitted = "\""
|
|
103
|
+
|
|
104
|
+
for (const character of value) {
|
|
105
|
+
const codePoint = character.codePointAt(0)
|
|
106
|
+
|
|
107
|
+
if (character == "\"") emitted += "\\\""
|
|
108
|
+
else if (character == "\\") emitted += "\\\\"
|
|
109
|
+
else if (character == "\b") emitted += "\\b"
|
|
110
|
+
else if (character == "\t") emitted += "\\t"
|
|
111
|
+
else if (character == "\n") emitted += "\\n"
|
|
112
|
+
else if (character == "\f") emitted += "\\f"
|
|
113
|
+
else if (character == "\r") emitted += "\\r"
|
|
114
|
+
else if (codePoint !== undefined && (codePoint < 32 || codePoint == 127)) {
|
|
115
|
+
emitted += `\\${codePoint.toString(8).padStart(3, "0")}`
|
|
116
|
+
} else emitted += character
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return `${emitted}"`
|
|
120
|
+
}
|