Public
Edited
Oct 16, 2024
1 star
Insert cell
Insert cell
Insert cell
input = `
function log10Floor (n: number) : number {
return Math.floor(Math.log10(n));
}

function arrJoin(arr : string[], v: string): string {
return arr.join(v);
}
/**
* When the ToRawPrecision abstract operation is called with arguments x (which
* must be a finite non-negative number), minPrecision, and maxPrecision (both
* must be integers between 1 and 21) the following steps are taken:
*/
function ToRawPrecision (x , minPrecision , maxPrecision ){
// 1. Let p be maxPrecision.
let p = maxPrecision;

let m, e;

// 2. If x = 0, then
if (x === 0) {
// a. Let m be the String consisting of p occurrences of the character "0".
m = arrJoin.call(Array (p + 1), '0');
// b. Let e be 0.
e = 0;
}
// 3. Else
else {
// a. Let e and n be integers such that 10ᵖ⁻¹ ≤ n < 10ᵖ and for which the
// exact mathematical value of n × 10ᵉ⁻ᵖ⁺¹ – x is as close to zero as
// possible. If there are two such sets of e and n, pick the e and n for
// which n × 10ᵉ⁻ᵖ⁺¹ is larger.
e = log10Floor(Math.abs(x));

// Easier to get to m from here
let f = Math.round(Math.exp((Math.abs(e - p + 1)) * Math.LN10));

// b. Let m be the String consisting of the digits of the decimal
// representation of n (in order, with no leading zeroes)
m = String(Math.round(e - p + 1 < 0 ? x * f : x / f));
}

// 4. If e ≥ p, then
if (e >= p)
// a. Return the concatenation of m and e-p+1 occurrences of the character "0".
return m + arrJoin.call(Array(e-p+1 + 1), '0');

// 5. If e = p-1, then
else if (e === p - 1)
// a. Return m.
return m;

// 6. If e ≥ 0, then
else if (e >= 0)
// a. Let m be the concatenation of the first e+1 characters of m, the character
// ".", and the remaining p–(e+1) characters of m.
m = m.slice(0, e + 1) + '.' + m.slice(e + 1);

// 7. If e < 0, then
else if (e < 0)
// a. Let m be the concatenation of the String "0.", –(e+1) occurrences of the
// character "0", and the string m.
m = '0.' + arrJoin.call(Array (-(e+1) + 1), '0') + m;

// 8. If m contains the character ".", and maxPrecision > minPrecision, then
if (m.indexOf(".") >= 0 && maxPrecision > minPrecision) {
// a. Let cut be maxPrecision – minPrecision.
let cut = maxPrecision - minPrecision;

// b. Repeat while cut > 0 and the last character of m is "0":
while (cut > 0 && m.charAt(m.length-1) === '0') {
// i. Remove the last character from m.
m = m.slice(0, -1);

// ii. Decrease cut by 1.
cut--;
}

// c. If the last character of m is ".", then
if (m.charAt(m.length-1) === '.')
// i. Remove the last character from m.
m = m.slice(0, -1);
}
// 9. Return m.
return m;
}
`
Insert cell
output = {
for (const sourceFile of program.getSourceFiles()) {
if (!sourceFile.isDeclarationFile) {
return handleNode(sourceFile);
}
}
}
Insert cell
ts = require("typescript@5.6.3/lib/typescript.js").catch(() => window["ts"])
Insert cell
bfs = require("browserfs@1.4.3/dist/browserfs.js")
Insert cell
fs = bfs.BFSRequire("fs")
Insert cell
path = bfs.BFSRequire("process")
Insert cell
program = {
ts.setSys({
useCaseSensitiveFileNames: true,
getExecutingFilePath() {
return "";
},
getCurrentDirectory() {
return "";
},
directoryExists() {
return true;
},
getDirectories() {
return [];
}
});
const tsOptions = {
strict: true,
allowUnreachableCode: false,
allowUnusedLabels: false,
exactOptionalPropertyTypes: true,
noImplicitReturns: true
};
const files = {
"input.ts": ts.createSourceFile("input.ts", input, ts.ScriptTarget.Latest)
};
console.log(ts.getDefaultCompilerOptions());
const host = ts.createCompilerHost({ useCaseSensitiveFileNames: true });
const orig = host.getSourceFile;
host.getSourceFile = (fileName, languageVersion, onError) => {
if (files[fileName]) return files[fileName];
return orig(fileName, languageVersion, onError);
};

let program = ts.createProgram(["input.ts"], tsOptions, host);
let checker = program.getTypeChecker();
const diagnostics = program.getBindAndCheckDiagnostics(files["input.ts"]);
if (diagnostics && diagnostics.length > 0) {
console.warn(diagnostics);
throw `Failed to compile: ${diagnostics
.map((d) => d.messageText)
.join("\n")}`;
}
return program;
}
Insert cell
checker = program.getTypeChecker()
Insert cell
handleNode = {
let name;
const handleNode = (node, context = "stmt") => {
// Note: there are over 300 cases for node.kind, so this would take a while
// to implement...
switch (node.kind) {
case ts.SyntaxKind.SourceFile:
return `module Output exposing (${node.statements
.filter((s) =>
s.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)
)
.flatMap((s) =>
s.declarationList.declarations.map((d) => d.name.escapedText)
)})\n\n${node.statements.map(handleNode).join("\n\n")}`;

case ts.SyntaxKind.TypeAliasDeclaration:
return `type alias ${node.name.getText()} =\n ${handleNode(
node.type,
"type"
)}`;

case ts.SyntaxKind.TypeLiteral:
return `{ ${node.members
.map((n) => handleNode(n, "type"))
.join("\n , ")} }`;

case ts.SyntaxKind.PropertySignature:
return `${node.name.getText()} : ${handleNode(node.type, "type")}`;

case ts.SyntaxKind.NumberKeyword:
return `Float`;

case ts.SyntaxKind.FunctionType:
return `${node.parameters
.map((n) => handleNode(n, "type"))
.join(" -> ")} -> ${handleNode(node.type, "type")}`;

case ts.SyntaxKind.Parameter:
if (context == "type") {
return handleNode(node.type);
} else {
return node.name.escapedText;
}

case ts.SyntaxKind.TypeReference:
return node.typeName.escapedText;

case ts.SyntaxKind.ArrayType:
console.log(node);
return `List ${handleNode(node.elementType, "type")}`;

case ts.SyntaxKind.VariableStatement:
return handleNode(node.declarationList);

case ts.SyntaxKind.VariableDeclarationList:
return node.declarations.map(handleNode).join("\n\n");

case ts.SyntaxKind.VariableDeclaration:
name = node.name.escapedText;
return `${name} : ${makeTypeSignature(node)}\n${name} =\n${indent(
handleNode(node.initializer)
)}`;

case ts.SyntaxKind.FunctionDeclaration:
name = node.name.escapedText;
return `${name} : ${makeTypeSignature(node)}\n${name} ${node.parameters
.map(handleNode)
.join(" ")} =\n${indent(handleNode(node.body))}`;

case ts.SyntaxKind.ArrowFunction:
return `(\\${node.parameters.map(handleNode).join(" ")} -> ${handleNode(
node.body
)})`;

case ts.SyntaxKind.Block:
if (
node.statements.length == 1 &&
node.statements[0].kind === ts.SyntaxKind.ReturnStatement
) {
return handleNode(node.statements[0].expression);
} else if (
node.statements[node.statements.length - 1].kind ===
ts.SyntaxKind.ReturnStatement
) {
const statemets = [...node.statements];
const returnS = statemets.pop().expression;
return `\n let\n${indent(
indent(statemets.map(handleNode).join("\n\n "))
)}\n in\n ${handleNode(returnS)}`;
} else {
throw `Can't translate an Arrow Function that is only run for side effects`;
}

case ts.SyntaxKind.BinaryExpression:
switch (node.operatorToken.getText()) {
case "%":
return `${handleNode(node.left)} |> modBy ${handleNode(
node.right
)}`;
default:
return `${handleNode(
node.left
)} ${node.operatorToken.getText()} ${handleNode(node.right)}`;
}

case ts.SyntaxKind.PropertyAccessExpression:
return `${handleNode(node.expression)}.${node.name.escapedText}`;

case ts.SyntaxKind.Identifier:
return node.escapedText;

case ts.SyntaxKind.NumericLiteral:
return node.text;

case ts.SyntaxKind.CallExpression:
return translateApiCalls(node);

case ts.SyntaxKind.ObjectLiteralExpression:
return `{ ${node.properties.map(handleNode).join(", ")} }`;

case ts.SyntaxKind.PropertyAssignment:
return `${node.name.escapedText} = ${handleNode(node.initializer)}`;

case ts.SyntaxKind.ArrayLiteralExpression:
return `[ ${node.elements.map(handleNode).join(", ")} ]`;

default:
console.log(node);
throw `node kind ${node.kind} is not supported`;
}
};

const makeTypeSignature = (node) => {
const type = checker.getTypeAtLocation(node);
console.log(type, checker.typeToString(type));
if (type.isLiteral()) {
if (type.isNumberLiteral()) {
return "Float";
} else {
throw "unsupported literal";
}
} else {
return handleNode(
checker.typeToTypeNode(checker.getTypeAtLocation(node))
);
}
};

const translateApiCalls = (node) => {
// there is probably a better way to do this using signatures and/or symbols
if (node.expression.kind == ts.SyntaxKind.PropertyAccessExpression) {
const t = checker.getTypeAtLocation(node.expression.expression);
// List
if (checker.isArrayType(t)) {
switch (node.expression.name.escapedText) {
case "map":
if (node.arguments.length !== 1) throw "Can't handle this map call";
return `List.map ${handleNode(node.arguments[0])} ${handleNode(
node.expression.expression
)}`;
case "filter":
if (node.arguments.length !== 1)
throw "Can't handle this filter call";
return `List.filter ${forceCastToBool(
node.arguments[0]
)} (${handleNode(node.expression.expression)})`;
}
}
}

return `${handleNode(node.expression)} ${node.arguments
.map(handleNode)
.join(" ")}`;
};

const forceCastToBool = (node) => {
let t;
if (
node.kind === ts.SyntaxKind.ArrowFunction &&
!checker.isTypeAssignableTo(
(t = checker.getTypeAtLocation(node.body)),
checker.getBooleanType()
)
) {
if (checker.isTypeAssignableTo(t, checker.getNumberType()))
return `(\\${node.parameters.map(handleNode).join(" ")} -> ${handleNode(
node.body
)} /= 0)`;
}
return handleNode(node);
};

return handleNode;
}
Insert cell
indent = (s) => " " + s.split("\n").join("\n ")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more