271 lines
5.3 KiB
JavaScript
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("./gencommons.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;
|