@specs-feup/lara
Version:
A js port of the popular framework for building source-to-source compilers
384 lines • 11.4 kB
JavaScript
import { LaraJoinPoint } from "../../LaraJoinPoint.js";
import JoinPoints from "../../weaver/JoinPoints.js";
import { isJoinPoint } from "../core/LaraCore.js";
/**
* Logger object, for inserting code that prints/saves information to files.
*
* @param isGlobal - Not implemented, please ignore
* @param filename - If set, instead of printing, will insert code for writing output to this file
*
*/
export default class LoggerBase {
isGlobal;
filename;
currentElements = [];
functionMap = {};
afterJp = undefined;
Type = new Map();
constructor(isGlobal = false, filename) {
if (isGlobal) {
console.log("[Logger-warning] global Logger is not implemented yet, reverting to local Logger");
isGlobal = false;
}
this.isGlobal = isGlobal;
this.filename = filename;
this.Type.set("NORMAL", 1);
this.Type.set("INT", 2);
this.Type.set("DOUBLE", 3);
this.Type.set("STRING", 4);
this.Type.set("CHAR", 5);
this.Type.set("HEX", 6);
this.Type.set("OCTAL", 7);
this.Type.set("LITERAL", 8);
this.Type.set("LONG", 9);
}
/**
* Used for both C and Java printf functions
*/
printfFormat = {
1: undefined,
2: "%d",
3: "%f",
4: "%s",
5: "%c",
6: "%x",
7: "%o",
8: undefined,
9: "%ld",
};
isGlobalFn() {
console.log("Is Global Fn:", this.isGlobal);
}
/**
* The 'last' join point after .log() is called.
*
*/
getAfterJp() {
return this.afterJp;
}
clear() {
this.currentElements = [];
}
/**
* Helper method which call 'log' with 'insertBefore' set to true
*
*/
logBefore($jp) {
this.log($jp, true);
}
/**
* Verifies that the given $jp is inside a function.
*
* Requires global attribute 'ancestor'.
*
* @returns true if $jp is inside a function, false otherwise
*/
// TODO: This function should receive LaraJoinPoints but they do not have the getAncestor method
_validateJp($jp, functionJpName = "function") {
const $function = $jp.getAncestor(functionJpName);
if ($function === undefined) {
console.log("Logger: tried to insert log around joinpoint " +
$jp.joinPointType +
", but is not inside a function");
this.clear();
return false;
}
return true;
}
_insert($jp, insertBefore, code) {
this._insertCode($jp, insertBefore, code);
// Clear internal state
this.clear();
}
/**
* Inserts the given code before/after the given join point.
*
* Override this method if you need to specialize the insertion.
*/
// TODO: This function should receive LaraJoinPoints but they do not have the insertAfter method
_insertCode($jp, insertBefore, code) {
const insertBeforeString = insertBefore ? "before" : "after";
if (insertBefore) {
$jp.insert(insertBeforeString, code);
this.afterJp = $jp;
}
else {
this.afterJp = $jp.insertAfter(code);
}
}
/**
* Appends the given string to the current buffer.
*
* @param text - The text to append
* @returns The current logger instance
*/
append(text) {
return this._append_private(text, this.Type.get("NORMAL"));
}
/**
* The same as 'append'.
*
* @param text - the text to append
* @returns The current logger instance
*/
text(text) {
return this.append(text);
}
/**
* The same as 'append', but adds a new line at the end of the buffer.
*
* @param text - the text to append
* @returns The current logger instance
*/
appendln(text) {
return this.append(text).ln();
}
/**
* Appends a new line to the buffer.
*
* @returns The current logger instance
*/
ln() {
return this._append_private("\\n", this.Type.get("NORMAL"));
}
/**
* Appends a tab to the buffer.
*
* @returns The current logger instance
*/
tab() {
return this.append("\\t");
}
/**
* Appends an expression that represents a double.
*
* @param expr - The expression to append
* @returns The current logger instance
*/
appendDouble(expr) {
return this._append_private(expr, this.Type.get("DOUBLE"));
}
/**
* The same as 'appendDouble'.
*
* @param expr - The expression to append
* @returns The current logger instance
*/
double(expr) {
return this.appendDouble(expr);
}
/**
* Appends an expression that represents a int.
*
* @param expr - The expression to append
* @returns The current logger instance
*/
appendInt(expr) {
return this._append_private(expr, this.Type.get("INT"));
}
/**
* The same as 'appendInt'.
*
* @param expr - The expression to append
* @returns The current logger instance
*/
int(expr) {
return this.appendInt(expr);
}
/**
* Appends an expression that represents a long.
*
* @param expr - The expression to append
* @returns The current logger instance
*/
appendLong(expr) {
return this._append_private(expr, this.Type.get("LONG"));
}
/**
* The same as 'appendLong'.
*
* @param expr - The expression to append
* @returns The current logger instance
*/
long(expr) {
return this.appendLong(expr);
}
/**
* Appends an expression that represents a string.
*
* @param expr - The expression to append
* @returns The current logger instance
*/
appendString(expr) {
return this._append_private(expr, this.Type.get("STRING"));
}
/**
* The same as 'appendString'.
*
* @param expr - The expression to append
* @returns The current logger instance
*/
string(expr) {
return this.appendString(expr);
}
/**
* Appends an expression that represents a char.
*
* @param expr - The expression to append
* @returns The current logger instance
*/
appendChar(expr) {
return this._append_private(expr, this.Type.get("CHAR"));
}
/**
* The same as 'appendChar'.
*
* @param expr - The expression to append
* @returns The current logger instance
*/
char(expr) {
return this.appendChar(expr);
}
/**
* Appends an expression that represents a hex number.
*
* @param expr - The expression to append
* @returns The current logger instance
*/
appendHex(expr) {
return this._append_private(expr, this.Type.get("HEX"));
}
/**
* The same as 'appendHex'.
*
* @param expr - The expression to append
* @returns The current logger instance
*/
hex(expr) {
return this.appendHex(expr);
}
/**
* Appends an expression that represents an octal.
*
* @param expr - The expression to append
* @returns The current logger instance
*/
appendOctal(expr) {
return this._append_private(expr, this.Type.get("OCTAL"));
}
/**
* The same as 'appendOctal'.
*
* @param expr - The expression to append
* @returns The current logger instance
*/
octal(expr) {
return this.appendOctal(expr);
}
/**** PRIVATE METHODS ****/
_append_private(message, type) {
// If message is a join point, convert to code first
if (isJoinPoint(message) || message instanceof LaraJoinPoint) {
message = JoinPoints.getCode(message);
}
// Do not push message if empty
if (message === "" || type === undefined) {
return this;
}
this.currentElements.push({ content: message, type: type });
return this;
}
_warn(message) {
console.log("[Logger Warning]", message);
}
_info(message) {
console.log("[Logger]", message);
}
// Receives an element{content, type} and returns the content with or without quotation marks, accordingly
_getPrintableContent(element) {
const enumType = this.Type;
let content = element.content;
if (element.type === enumType.get("LITERAL")) {
return String(content);
}
if (element.type === enumType.get("NORMAL") ||
element.type === enumType.get("STRING")) {
return '"' + content + '"';
}
if (element.type === enumType.get("CHAR")) {
return "'" + content + "'";
}
// Test if it has a decimal point
if (element.type === enumType.get("DOUBLE")) {
if (typeof content !== "number") {
return content;
}
const indexOfDecimal = String(content).indexOf(".");
if (indexOfDecimal === -1) {
content = String(content) + ".0";
}
return String(content);
}
return String(content);
}
/**
* Generates printf like code for c and java
*
* @param printFunctionName - The name of the function to use (printf for C, System.out.println for Java)
*/
_printfFormat(printFunctionName, prefix = "(", suffix = ");", delimiter = '"') {
// Create code from elements
let code = printFunctionName +
prefix +
delimiter +
this.currentElements
.map((element) => {
const enumType = this.Type;
if (element.type === enumType.get("NORMAL")) {
return element.content;
}
return this.printfFormat[element.type];
})
.join("") +
delimiter;
const valuesCode = this.currentElements
// Filter only non-NORMAL types
.filter((element) => {
const enumType = this.Type;
return element.type !== enumType.get("NORMAL");
})
.map((element) => {
// Even though _getPrintableContent tests an always unmet condition (type === NORMAL) it represents a reusable piece of code for both C and C++
return this._getPrintableContent(element);
})
.join(", ");
if (valuesCode.length > 0) {
code = code + ", " + valuesCode;
}
code = code + suffix;
return code;
}
/**
*
*
* @param $function - Function where name will be declared
* @param nameGenerator - Function that receives no arguments and generates a new name
*/
_declareName(functionId, nameGenerator) {
let name = this.functionMap[functionId];
let alreadyDeclared = false;
if (name !== undefined) {
alreadyDeclared = true;
}
else {
name = nameGenerator();
this.functionMap[functionId] = name;
alreadyDeclared = false;
}
return {
name: name,
alreadyDeclared: alreadyDeclared,
};
}
}
//# sourceMappingURL=LoggerBase.js.map