t7x/deps/asmjit/tools/generator-cxx.js
2024-06-17 19:04:36 -04:00

271 lines
5.3 KiB
JavaScript

// This file is part of AsmJit project <https://asmjit.com>
//
// See asmjit.h or LICENSE.md for license and copyright information
// SPDX-License-Identifier: Zlib
// C++ code generation helpers.
const commons = require("./generator-commons.js");
const FATAL = commons.FATAL;
// Utilities to convert primitives to C++ code.
class Utils {
static toHex(val, pad) {
if (val < 0)
val = 0xFFFFFFFF + val + 1;
let s = val.toString(16);
if (pad != null && s.length < pad)
s = "0".repeat(pad - s.length) + s;
return "0x" + s.toUpperCase();
}
static capitalize(s) {
s = String(s);
return !s ? s : s[0].toUpperCase() + s.substr(1);
}
static camelCase(s) {
if (s == null || s === "")
return s;
s = String(s);
if (/^[A-Z]+$/.test(s))
return s.toLowerCase();
else
return s[0].toLowerCase() + s.substr(1);
}
static normalizeSymbolName(s) {
switch (s) {
case "and":
case "or":
case "xor":
return s + "_";
default:
return s;
}
}
static indent(s, indentation) {
if (typeof indentation === "number")
indentation = " ".repeat(indentation);
var lines = s.split(/\r?\n/g);
if (indentation) {
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
if (line)
lines[i] = indentation + line;
}
}
return lines.join("\n");
}
}
exports.Utils = Utils;
// A node that represents a C++ construct.
class Node {
constructor(kind) {
this.kind = kind;
}
};
exports.Node = Node;
// A single line of C++ code that declares a variable with optional initialization.
class Var extends Node {
constructor(type, name, init) {
super("var");
this.type = type;
this.name = name;
this.init = init || "";
}
toString() {
let s = this.type + " " + this.name;
if (this.init)
s += " = " + this.init;
return s + ";\n";
}
};
exports.Var = Var;
// A single line of C++ code, which should not contain any branch or a variable declaration.
class Line extends Node {
constructor(code) {
super("line");
this.code = code;
}
toString() {
return String(this.code) + "\n";
}
};
exports.Line = Line;
// A block containing an array of `Node` items (may contain nested blocks, etc...).
class Block extends Node {
constructor(nodes) {
super("block");
this.nodes = nodes || [];
}
isEmpty() {
return this.nodes.length === 0;
}
appendNode(node) {
if (!(node instanceof Node))
FATAL("Block.appendNode(): Node must be an instance of Node");
this.nodes.push(node);
return this;
}
prependNode(node) {
if (!(node instanceof Node))
FATAL("Block.prependNode(): Node must be an instance of Node");
this.nodes.unshift(node);
return this;
}
insertNode(index, node) {
if (!(node instanceof Node))
FATAL("Block.insertNode(): Node must be an instance of Node");
if (index >= this.nodes.length)
this.nodes.push(node);
else
this.nodes.splice(index, 0, node);
return this;
}
addVarDecl(type, name, init) {
let node = type;
if (!(node instanceof Var))
node = new Var(type, name, init);
let i = 0;
while (i < this.nodes.length) {
const n = this.nodes[i];
if (n.kind === "var" && n.name === node.name && n.init === node.init)
return this;
if (n.kind !== "var")
break;
i++;
}
this.insertNode(i, node);
return this;
}
addLine(code) {
if (typeof code !== "string")
FATAL("Block.addLine(): Line must be string");
this.nodes.push(new Line(code));
return this;
}
prependEmptyLine() {
if (!this.isEmpty())
this.nodes.splice(0, 0, new Line(""));
return this;
}
addEmptyLine() {
if (!this.isEmpty())
this.nodes.push(new Line(""));
return this;
}
toString() {
let s = "";
for (let node of this.nodes)
s += String(node);
return s;
}
}
exports.Block = Block;
// A C++ 'condition' (if statement) and its 'body' if it's taken.
class If extends Node {
constructor(cond, body) {
super("if");
if (body == null)
body = new Block();
if (!(body instanceof Block))
FATAL("If() - body must be a Block");
this.cond = cond;
this.body = body;
}
toString() {
const cond = String(this.cond);
const body = String(this.body);
return `if (${cond}) {\n` + Utils.indent(body, 2) + `}\n`;
}
}
exports.If = If;
//! A C++ switch statement.
class Case extends Node {
constructor(cond, body) {
super("case");
this.cond = cond;
this.body = body || new Block();
}
toString() {
let s = "";
for (let node of this.body.nodes)
s += String(node)
if (this.cond !== "default")
return `case ${this.cond}: {\n` + Utils.indent(s, 2) + `}\n`;
else
return `default: {\n` + Utils.indent(s, 2) + `}\n`;
}
};
exports.Case = Case;
class Switch extends Node {
constructor(expression, cases) {
super("switch");
this.expression = expression;
this.cases = cases || [];
}
addCase(cond, body) {
this.cases.push(new Case(cond, body));
return this;
}
toString() {
let s = "";
for (let c of this.cases) {
if (s)
s += "\n";
s += String(c);
}
return `switch (${this.expression}) {\n` + Utils.indent(s, 2) + `}\n`;
}
}
exports.Switch = Switch;