generator-cxx.js (5535B)
1 // This file is part of AsmJit project <https://asmjit.com> 2 // 3 // See <asmjit/core.h> or LICENSE.md for license and copyright information 4 // SPDX-License-Identifier: Zlib 5 6 // C++ code generation helpers. 7 const commons = require("./generator-commons.js"); 8 const FATAL = commons.FATAL; 9 10 // Utilities to convert primitives to C++ code. 11 class Utils { 12 static toHexRaw(val, pad) { 13 if (val < 0) 14 val = 0xFFFFFFFF + val + 1; 15 16 let s = val.toString(16); 17 if (pad != null && s.length < pad) 18 s = "0".repeat(pad - s.length) + s; 19 20 return s.toUpperCase(); 21 } 22 23 static toHex(val, pad) { 24 return "0x" + Utils.toHexRaw(val, pad); 25 } 26 27 static capitalize(s) { 28 s = String(s); 29 return !s ? s : s[0].toUpperCase() + s.substr(1); 30 } 31 32 static camelCase(s) { 33 if (s == null || s === "") 34 return s; 35 36 s = String(s); 37 if (/^[A-Z]+$/.test(s)) 38 return s.toLowerCase(); 39 else 40 return s[0].toLowerCase() + s.substr(1); 41 } 42 43 static normalizeSymbolName(s) { 44 switch (s) { 45 case "and": 46 case "or": 47 case "xor": 48 return s + "_"; 49 default: 50 return s; 51 } 52 } 53 54 static indent(s, indentation) { 55 if (typeof indentation === "number") 56 indentation = " ".repeat(indentation); 57 58 var lines = s.split(/\r?\n/g); 59 if (indentation) { 60 for (var i = 0; i < lines.length; i++) { 61 var line = lines[i]; 62 if (line) 63 lines[i] = indentation + line; 64 } 65 } 66 67 return lines.join("\n"); 68 } 69 } 70 exports.Utils = Utils; 71 72 // A node that represents a C++ construct. 73 class Node { 74 constructor(kind) { 75 this.kind = kind; 76 } 77 }; 78 exports.Node = Node; 79 80 // A single line of C++ code that declares a variable with optional initialization. 81 class Var extends Node { 82 constructor(type, name, init) { 83 super("var"); 84 85 this.type = type; 86 this.name = name; 87 this.init = init || ""; 88 } 89 90 toString() { 91 let s = this.type + " " + this.name; 92 if (this.init) 93 s += " = " + this.init; 94 return s + ";\n"; 95 } 96 }; 97 exports.Var = Var; 98 99 // A single line of C++ code, which should not contain any branch or a variable declaration. 100 class Line extends Node { 101 constructor(code) { 102 super("line"); 103 104 this.code = code; 105 } 106 107 toString() { 108 return String(this.code) + "\n"; 109 } 110 }; 111 exports.Line = Line; 112 113 // A block containing an array of `Node` items (may contain nested blocks, etc...). 114 class Block extends Node { 115 constructor(nodes) { 116 super("block"); 117 118 this.nodes = nodes || []; 119 } 120 121 isEmpty() { 122 return this.nodes.length === 0; 123 } 124 125 appendNode(node) { 126 if (!(node instanceof Node)) 127 FATAL("Block.appendNode(): Node must be an instance of Node"); 128 129 this.nodes.push(node); 130 return this; 131 } 132 133 prependNode(node) { 134 if (!(node instanceof Node)) 135 FATAL("Block.prependNode(): Node must be an instance of Node"); 136 137 this.nodes.unshift(node); 138 return this; 139 } 140 141 insertNode(index, node) { 142 if (!(node instanceof Node)) 143 FATAL("Block.insertNode(): Node must be an instance of Node"); 144 145 if (index >= this.nodes.length) 146 this.nodes.push(node); 147 else 148 this.nodes.splice(index, 0, node); 149 150 return this; 151 } 152 153 addVarDecl(type, name, init) { 154 let node = type; 155 156 if (!(node instanceof Var)) 157 node = new Var(type, name, init); 158 159 let i = 0; 160 while (i < this.nodes.length) { 161 const n = this.nodes[i]; 162 if (n.kind === "var" && n.name === node.name && n.init === node.init) 163 return this; 164 165 if (n.kind !== "var") 166 break; 167 168 i++; 169 } 170 171 this.insertNode(i, node); 172 return this; 173 } 174 175 addLine(code) { 176 if (typeof code !== "string") 177 FATAL("Block.addLine(): Line must be string"); 178 179 this.nodes.push(new Line(code)); 180 return this; 181 } 182 183 prependEmptyLine() { 184 if (!this.isEmpty()) 185 this.nodes.splice(0, 0, new Line("")); 186 return this; 187 } 188 189 addEmptyLine() { 190 if (!this.isEmpty()) 191 this.nodes.push(new Line("")); 192 return this; 193 } 194 195 toString() { 196 let s = ""; 197 for (let node of this.nodes) 198 s += String(node); 199 return s; 200 } 201 } 202 exports.Block = Block; 203 204 // A C++ 'condition' (if statement) and its 'body' if it's taken. 205 class If extends Node { 206 constructor(cond, body) { 207 super("if"); 208 209 if (body == null) 210 body = new Block(); 211 212 if (!(body instanceof Block)) 213 FATAL("If() - body must be a Block"); 214 215 this.cond = cond; 216 this.body = body; 217 } 218 219 toString() { 220 const cond = String(this.cond); 221 const body = String(this.body); 222 223 return `if (${cond}) {\n` + Utils.indent(body, 2) + `}\n`; 224 } 225 } 226 exports.If = If; 227 228 //! A C++ switch statement. 229 class Case extends Node { 230 constructor(cond, body) { 231 super("case"); 232 233 this.cond = cond; 234 this.body = body || new Block(); 235 } 236 237 toString() { 238 let s = ""; 239 for (let node of this.body.nodes) 240 s += String(node) 241 242 if (this.cond !== "default") 243 return `case ${this.cond}: {\n` + Utils.indent(s, 2) + `}\n`; 244 else 245 return `default: {\n` + Utils.indent(s, 2) + `}\n`; 246 } 247 }; 248 exports.Case = Case; 249 250 class Switch extends Node { 251 constructor(expression, cases) { 252 super("switch"); 253 254 this.expression = expression; 255 this.cases = cases || []; 256 } 257 258 addCase(cond, body) { 259 this.cases.push(new Case(cond, body)); 260 return this; 261 } 262 263 toString() { 264 let s = ""; 265 for (let c of this.cases) { 266 if (s) 267 s += "\n"; 268 s += String(c); 269 } 270 271 return `switch (${this.expression}) {\n` + Utils.indent(s, 2) + `}\n`; 272 } 273 } 274 exports.Switch = Switch;