/*
Copyright 2008-2023
Matthias Ehmann,
Michael Gerhaeuser,
Carsten Miller,
Bianca Valentin,
Alfred Wassermann,
Peter Wilfahrt
This file is part of JSXGraph.
JSXGraph is free software dual licensed under the GNU LGPL or MIT License.
You can redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version
OR
* MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT
JSXGraph is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License and
the MIT License along with JSXGraph. If not, see
and .
*/
/**
* @param {String} string A string containing construction(s) in JSXGraph Construction Syntax.
* @param {String} mode Possible values seem are "normal" or "macro"
* @param {Array} params Parameters, only used in macro mode
* @param {Array} paraIn Parameters, only used in macro mode
* @param {String} macroName Name of the macro, only used in macro mode
* @type Object
* @returns An object consisting of several arrays (lines, circles, points, angles, ...) where the created elements are stored.
*/
JXG.Board.prototype.construct = function (string, mode, params, paraIn, macroName) {
var splitted,
i,
j,
output = {},
objName,
defElements,
obj,
type,
possibleNames,
tmp,
noMacro,
k,
l,
pattern,
createdNames,
found,
mac,
prop,
propName,
propValue,
attributes;
if (!JXG.exists(mode)) {
mode = "normal";
} else {
// mode = 'macro'
createdNames = [];
}
output.lines = [];
output.circles = [];
output.points = [];
output.intersections = [];
output.angles = [];
output.macros = [];
output.functions = [];
output.texts = [];
output.polygons = [];
if (string.search(/\{/) != -1) {
// Macros finden! Innerhalb der {} darf nicht am ; getrennt werden. Noch nicht getestet: mehrere Makros hintereinander in einem construct.
tmp = string.match(/\{/);
tmp = tmp.length;
l = 0;
for (j = 0; j < tmp; j++) {
k = string.slice(l).search(/\{/);
mac = string.slice(k);
mac = mac.slice(0, mac.search(/\}/) + 1);
mac = mac.replace(/;/g, "?"); // Achtung! Fragezeichen duerfen daher nicht im Code eines Macros vorkommen!
string = string.slice(0, k) + mac + string.slice(k + mac.length);
l = k + 1;
}
}
splitted = string.split(";");
for (i = 0; i < splitted.length; i++) {
// Leerzeichen am Anfang und am Ende entfernen
splitted[i] = splitted[i].replace(/^\s+/, "").replace(/\s+$/, "");
if (splitted[i].search(/\{/) != -1) {
splitted[i] = splitted[i].replace(/\?/g, ";");
}
if (splitted[i].search(/Macro/) != -1) {
this.addMacro(splitted[i]);
} else {
if (splitted[i].length > 0) {
prop = false;
if (splitted[i].search(/=/) != -1) {
objName = splitted[i].split("=");
propValue = objName[1];
propValue = propValue.replace(/^\s+/, "").replace(/\s+$/, "");
if (objName[0].search(/\./) != -1) {
prop = true;
objName = objName[0].split(".");
propName = objName[objName.length - 1];
propName = propName.replace(/^\s+/, "").replace(/\s+$/, "");
objName.pop();
objName = objName.join(".");
if (mode == "macro") {
for (j = 0; j < params.length; j++) {
if (objName == params[j]) {
objName = paraIn[j];
}
}
}
//alert("_"+objName+"_"+propName+"_"+propValue+"_");
//alert(JXG.getReference(this,objName).name);
JXG.getReference(this, objName).setAttribute(
propName + ":" + propValue
);
}
}
if (!prop) {
// nicht nur eine Eigenschaft setzen, sondern neues Element konstruieren
if (splitted[i].search(/=/) != -1) {
objName = splitted[i].split("=");
splitted[i] = objName[1].replace(/^\s+/, ""); // Leerzeichen am Anfang entfernen
objName = objName[0].replace(/\s+$/, ""); // Leerzeichen am Ende entfernen
} else {
objName = "";
}
attributes = {};
found = true;
while (found) {
if (splitted[i].search(/(.*)draft$/) != -1) {
attributes.draft = true;
splitted[i] = RegExp.$1;
splitted[i] = splitted[i].replace(/\s+$/, ""); // Leerzeichen am Ende entfernen
}
if (splitted[i].search(/(.*)invisible$/) != -1) {
attributes.visible = false;
splitted[i] = RegExp.$1;
splitted[i] = splitted[i].replace(/\s+$/, ""); // Leerzeichen am Ende entfernen
}
if (splitted[i].search(/(.*)nolabel$/) != -1) {
attributes.withLabel = false;
splitted[i] = RegExp.$1;
splitted[i] = splitted[i].replace(/\s+$/, ""); // Leerzeichen am Ende entfernen
}
if (splitted[i].search(/nolabel|invisible|draft/) == -1) {
found = false;
}
}
noMacro = true;
if (this.definedMacros) {
for (j = 0; j < this.definedMacros.macros.length; j++) {
pattern = new RegExp(
"^" + this.definedMacros.macros[j][0] + "\\s*\\("
);
if (splitted[i].search(pattern) != -1) {
// TODO: testen, was mit den Macros xxx und yxxx passiert
//alert("MACRO!"+splitted[i]+"_"+this.definedMacros.macros[j][2]);
noMacro = false;
// Parameter aufdroeseln
splitted[i].match(/\((.*)\)/);
tmp = RegExp.$1;
tmp = tmp.split(",");
for (k = 0; k < tmp.length; k++) {
tmp[k].match(/\s*(\S*)\s*/);
tmp[k] = RegExp.$1;
}
output[objName] = this.construct(
this.definedMacros.macros[j][2],
"macro",
this.definedMacros.macros[j][1],
tmp,
objName
);
output.macros.push(output[objName]);
break;
}
}
}
if (noMacro) {
// splitted[i] war kein Macro-Aufruf
if (splitted[i].search(/^[\[\]].*[\[\]]$/) != -1) {
// Gerade, Halbgerade oder Segment
splitted[i].match(/([\[\]])(.*)([\[\]])/);
attributes.straightFirst = RegExp.$1 != "[";
attributes.straightLast = RegExp.$3 == "[";
defElements = RegExp.$2.replace(/^\s+/, "").replace(/\s+$/, "");
if (defElements.search(/ /) != -1) {
defElements.match(/(\S*) +(\S*)/);
defElements = [];
defElements[0] = RegExp.$1;
defElements[1] = RegExp.$2;
} // sonst wird die Gerade durch zwei Punkte definiert, die einen Namen haben, der aus nur jeweils einem Buchstaben besteht
if (objName != "") {
if (!JXG.exists(attributes.withLabel)) {
attributes.withLabel = true;
}
attributes.name = objName;
if (mode == "macro") {
createdNames.push(objName);
}
}
if (mode == "macro") {
if (macroName != "") {
for (j = 0; j < createdNames.length; j++) {
// vorher oder nachher?
if (defElements[0] == createdNames[j]) {
defElements[0] = macroName + "." + defElements[0];
}
if (defElements[1] == createdNames[j]) {
defElements[1] = macroName + "." + defElements[1];
}
}
}
for (j = 0; j < params.length; j++) {
if (defElements[0] == params[j]) {
defElements = [paraIn[j], defElements[1]];
}
if (defElements[1] == params[j]) {
defElements = [defElements[0], paraIn[j]];
}
}
if (macroName != "") {
attributes.id = macroName + "." + objName;
}
}
if (typeof defElements == "string") {
defElements = [
JXG.getReference(this, defElements.charAt(0)),
JXG.getReference(this, defElements.charAt(1))
];
} else {
defElements = [
JXG.getReference(this, defElements[0]),
JXG.getReference(this, defElements[1])
];
}
output.lines.push(this.create("line", defElements, attributes));
if (objName != "") {
output[objName] = output.lines[output.lines.length - 1];
}
} else if (splitted[i].search(/k\s*\(.*/) != -1) {
// Kreis
splitted[i].match(/k\s*\(\s*(\S.*\S|\S)\s*,\s*(\S.*\S|\S)\s*\)/);
defElements = [];
defElements[0] = RegExp.$1;
defElements[1] = RegExp.$2;
for (j = 0; j <= 1; j++) {
if (defElements[j].search(/[\[\]]/) != -1) {
// Linie, definiert durch [P_1 P_2] , ist bei den Parametern dabei
defElements[j].match(/^[\[\]]\s*(\S.*\S)\s*[\[\]]$/);
defElements[j] = RegExp.$1;
if (defElements[j].search(/ /) != -1) {
defElements[j].match(/(\S*) +(\S*)/);
defElements[j] = [];
defElements[j][0] = RegExp.$1;
defElements[j][1] = RegExp.$2;
} // sonst wird die Gerade durch zwei Punkte definiert, die einen Namen haben, der aus nur jeweils einem Buchstaben besteht
if (mode == "macro") {
if (macroName != "") {
for (k = 0; k < createdNames.length; k++) {
// vorher oder nachher?
if (defElements[j][0] == createdNames[k]) {
defElements[j][0] =
macroName + "." + defElements[j][0];
}
if (defElements[j][1] == createdNames[k]) {
defElements[j][1] =
macroName + "." + defElements[j][1];
}
}
}
for (k = 0; k < params.length; k++) {
if (defElements[j][0] == params[k]) {
defElements[j] = [paraIn[k], defElements[j][1]];
}
if (defElements[j][1] == params[k]) {
defElements[j] = [defElements[j][0], paraIn[k]];
}
}
}
if (typeof defElements[j] == "string") {
defElements[j] = (function (el, board) {
return function () {
return JXG.getReference(
board,
el.charAt(0)
).Dist(JXG.getReference(board, el.charAt(1))); // TODO
};
})(defElements[j], this);
} else {
defElements[j] = (function (el, board) {
return function () {
return JXG.getReference(board, el[0]).Dist(
JXG.getReference(board, el[1])
); // TODO
};
})(defElements[j], this);
}
} else if (defElements[j].search(/[0-9\.\s]+/) != -1) {
// Radius als Zahl
defElements[j] = 1.0 * defElements[j];
} else {
// Element mit Name
if (mode == "macro") {
if (macroName != "") {
for (k = 0; k < createdNames.length; k++) {
// vorher oder nachher?
if (defElements[j] == createdNames[k]) {
defElements[j] =
macroName + "." + createdNames[k];
}
}
}
for (k = 0; k < params.length; k++) {
if (defElements[j] == params[k]) {
defElements[j] = paraIn[k];
}
}
}
defElements[j] = JXG.getReference(this, defElements[j]);
}
}
if (objName != "") {
if (!JXG.exists(attributes.withLabel)) {
attributes.withLabel = true;
}
attributes.name = objName;
if (mode == "macro") {
if (macroName != "") {
attributes.id = macroName + "." + objName;
}
createdNames.push(objName);
}
}
output.circles.push(this.create("circle", defElements, attributes));
if (objName != "") {
output[objName] = output.circles[output.circles.length - 1];
}
} else if (
splitted[i].search(
/^[A-Z]+.*\(\s*[0-9\.\-]+\s*[,\|]\s*[0-9\.\-]+\s*\)/
) != -1 &&
splitted[i].search(/Macro\((.*)\)/) == -1
) {
// Punkt, startet mit einem Grossbuchstaben! (definiert durch Koordinaten)
splitted[i].match(/^([A-Z]+\S*)\s*\(\s*(.*)\s*[,\|]\s*(.*)\s*\)$/);
objName = RegExp.$1; // Name
attributes.name = objName;
if (mode == "macro") {
if (macroName != "") {
attributes.id = macroName + "." + objName;
}
createdNames.push(objName);
}
output.points.push(
this.create(
"point",
[1.0 * RegExp.$2, 1.0 * RegExp.$3],
attributes
)
);
output[objName] = output.points[output.points.length - 1];
} else if (
splitted[i].search(/^[A-Z]+.*\(.+(([,\|]\s*[0-9\.\-]+\s*){2})?/) !=
-1 &&
splitted[i].search(/Macro\((.*)\)/) == -1
) {
// Gleiter, mit oder ohne Koordinaten
splitted[i].match(/([A-Z]+.*)\((.*)\)/);
objName = RegExp.$1;
defElements = RegExp.$2;
objName = objName.replace(/^\s+/, "").replace(/\s+$/, "");
defElements = defElements.replace(/^\s+/, "").replace(/\s+$/, "");
if (defElements.search(/[,\|]/) != -1) {
// Koordinaten angegeben
defElements.match(
/(\S*)\s*[,\|]\s*([0-9\.]+)\s*[,\|]\s*([0-9\.]+)\s*/
);
defElements = [];
defElements[0] = RegExp.$1;
defElements[1] = 1.0 * RegExp.$2;
defElements[2] = 1.0 * RegExp.$3;
} else {
// keine Koordinaten
obj = defElements;
defElements = [];
defElements[0] = obj; // Name des definierenden Elements
defElements[1] = 0; // (0,0) als Gleiterkoordinaten vorgeben...
defElements[2] = 0;
}
attributes.name = objName;
if (mode == "macro") {
if (macroName != "") {
for (k = 0; k < createdNames.length; k++) {
// vorher oder nachher?
if (defElements[0] == createdNames[k]) {
defElements[0] = macroName + "." + createdNames[k];
}
}
}
for (k = 0; k < params.length; k++) {
if (defElements[0] == params[k]) {
defElements[0] = paraIn[k];
}
}
if (macroName != "") {
attributes.id = macroName + "." + objName;
}
createdNames.push(objName);
}
output.points.push(
this.create(
"glider",
[
defElements[1],
defElements[2],
JXG.getReference(this, defElements[0])
],
attributes
)
);
output[objName] = output.points[output.points.length - 1];
} else if (splitted[i].search(/&/) != -1) {
// Schnittpunkt
splitted[i].match(/(.*)&(.*)/);
defElements = [];
defElements[0] = RegExp.$1;
defElements[1] = RegExp.$2;
defElements[0] = defElements[0].replace(/\s+$/, ""); // Leerzeichen am Ende entfernen
defElements[1] = defElements[1].replace(/^\s+/, ""); // Leerzeichen am Anfang entfernen
if (mode == "macro") {
for (j = 0; j <= 1; j++) {
if (macroName != "") {
for (k = 0; k < createdNames.length; k++) {
// vorher oder nachher?
if (defElements[j] == createdNames[k]) {
defElements[j] =
macroName + "." + createdNames[k];
}
}
}
for (k = 0; k < params.length; k++) {
if (defElements[j] == params[k]) {
defElements[j] = paraIn[k];
}
}
}
}
defElements[0] = JXG.getReference(this, defElements[0]);
defElements[1] = JXG.getReference(this, defElements[1]);
if (
(defElements[0].elementClass == JXG.OBJECT_CLASS_LINE ||
defElements[0].elementClass == JXG.OBJECT_CLASS_CURVE) &&
(defElements[1].elementClass == JXG.OBJECT_CLASS_LINE ||
defElements[1].elementClass == JXG.OBJECT_CLASS_CURVE)
) {
if (objName != "") {
attributes.name = objName;
if (mode == "macro") {
if (macroName != "") {
attributes.id = macroName + "." + objName;
}
createdNames.push(objName);
}
}
obj = this.create(
"intersection",
[defElements[0], defElements[1], 0],
attributes
);
output.intersections.push(obj);
if (objName != "") {
output[attributes.name] = obj;
}
} else {
if (objName != "") {
attributes.name = objName + "_1";
if (mode == "macro") {
if (macroName != "") {
attributes.id = macroName + "." + objName + "_1";
}
createdNames.push(objName + "_1");
}
}
obj = this.create(
"intersection",
[defElements[0], defElements[1], 0],
attributes
);
output.intersections.push(obj);
if (objName != "") {
output[attributes.name] = obj;
}
if (objName != "") {
attributes.name = objName + "_2";
if (mode == "macro") {
if (macroName != "") {
attributes.id = macroName + "." + objName + "_2";
}
createdNames.push(objName + "_2");
}
}
obj = this.create(
"intersection",
[defElements[0], defElements[1], 1],
attributes
);
output.intersections.push(obj);
if (objName != "") {
output[attributes.name] = obj;
}
}
} else if (splitted[i].search(/\|[\|_]\s*\(/) != -1) {
// Parallele oder Senkrechte
splitted[i].match(/\|([\|_])\s*\(\s*(\S*)\s*,\s*(\S*)\s*\)/);
type = RegExp.$1;
if (type == "|") {
type = "parallel";
} else {
// type == '_'
type = "normal";
}
defElements = [];
defElements[0] = RegExp.$2;
defElements[1] = RegExp.$3;
if (mode == "macro") {
for (j = 0; j <= 1; j++) {
if (macroName != "") {
for (k = 0; k < createdNames.length; k++) {
// vorher oder nachher?
if (defElements[j] == createdNames[k]) {
defElements[j] =
macroName + "." + createdNames[k];
}
}
}
for (k = 0; k < params.length; k++) {
if (defElements[j] == params[k]) {
defElements[j] = paraIn[k];
}
}
}
}
if (objName != "") {
attributes.name = objName;
if (!JXG.exists(attributes.withLabel)) {
attributes.withLabel = true;
}
if (mode == "macro") {
if (macroName != "") {
attributes.id = macroName + "." + objName;
}
createdNames.push(objName);
}
}
output.lines.push(
this.create(
type,
[
JXG.getReference(this, defElements[0]),
JXG.getReference(this, defElements[1])
],
attributes
)
);
if (objName != "") {
output[objName] = output.lines[output.lines.length - 1];
}
} else if (splitted[i].search(/^) != -1) {
// Winkel
splitted[i].match(/<\s*\(\s*(\S*)\s*,\s*(\S*)\s*,\s*(\S*)\s*\)/);
defElements = [];
defElements[0] = RegExp.$1;
defElements[1] = RegExp.$2;
defElements[2] = RegExp.$3;
if (mode == "macro") {
for (j = 0; j <= 2; j++) {
if (macroName != "") {
for (k = 0; k < createdNames.length; k++) {
// vorher oder nachher?
if (defElements[j] == createdNames[k]) {
defElements[j] =
macroName + "." + createdNames[k];
}
}
}
for (k = 0; k < params.length; k++) {
if (defElements[j] == params[k]) {
defElements[j] = paraIn[k];
}
}
}
}
if (objName == "") {
output.lines.push(
this.create(
"angle",
[
JXG.getReference(this, defElements[0]),
JXG.getReference(this, defElements[1]),
JXG.getReference(this, defElements[2])
],
attributes
)
);
} else {
possibleNames = [
"alpha",
"beta",
"gamma",
"delta",
"epsilon",
"zeta",
"eta",
"theta",
"iota",
"kappa",
"lambda",
"mu",
"nu",
"xi",
"omicron",
"pi",
"rho",
"sigmaf",
"sigma",
"tau",
"upsilon",
"phi",
"chi",
"psi",
"omega"
];
type = "";
for (j = 0; j < possibleNames.length; j++) {
if (objName == possibleNames[j]) {
attributes.text = "&" + objName + ";";
attributes.name = "&" + objName + ";";
type = "greek";
break;
} else {
if (j == possibleNames.length - 1) {
attributes.text = objName;
attributes.name = objName;
}
}
}
if (!JXG.exists(attributes.withLabel)) {
attributes.withLabel = true;
}
if (mode == "macro") {
if (macroName != "") {
attributes.id = macroName + "." + objName;
}
createdNames.push(objName);
}
output.angles.push(
this.create(
"angle",
[
JXG.getReference(this, defElements[0]),
JXG.getReference(this, defElements[1]),
JXG.getReference(this, defElements[2])
],
attributes
)
);
output[objName] = output.angles[output.angles.length - 1];
}
} else if (
splitted[i].search(
/([0-9]+)\/([0-9]+)\(\s*(\S*)\s*,\s*(\S*)\s*\)/
) != -1
) {
// Punkt mit Teilverhaeltnis, z.B. Mittelpunkt
defElements = [];
defElements[0] = (1.0 * RegExp.$1) / (1.0 * RegExp.$2);
defElements[1] = RegExp.$3;
defElements[2] = RegExp.$4;
if (mode == "macro") {
for (j = 1; j <= 2; j++) {
if (macroName != "") {
for (k = 0; k < createdNames.length; k++) {
// vorher oder nachher?
if (defElements[j] == createdNames[k]) {
defElements[j] =
macroName + "." + createdNames[k];
}
}
}
for (k = 0; k < params.length; k++) {
if (defElements[j] == params[k]) {
defElements[j] = paraIn[k];
}
}
}
}
defElements[1] = JXG.getReference(this, RegExp.$3);
defElements[2] = JXG.getReference(this, RegExp.$4);
obj = [];
obj[0] = (function (el, board) {
return function () {
return (
(1 - el[0]) * el[1].coords.usrCoords[1] +
el[0] * el[2].coords.usrCoords[1]
);
};
})(defElements, this);
obj[1] = (function (el, board) {
return function () {
return (
(1 - el[0]) * el[1].coords.usrCoords[2] +
el[0] * el[2].coords.usrCoords[2]
);
};
})(defElements, this);
if (objName != "") {
attributes.name = objName;
if (mode == "macro") {
if (macroName != "") {
attributes.id = macroName + "." + objName;
}
createdNames.push(objName);
}
}
output.points.push(
this.create("point", [obj[0], obj[1]], attributes)
);
if (objName != "") {
output[objName] = output.points[output.points.length - 1];
}
} else if (splitted[i].search(/(\S*)\s*:\s*(.*)/) != -1) {
// Funktionsgraph
objName = RegExp.$1;
tmp = JXG.GeonextParser.geonext2JS(RegExp.$2, this);
defElements = [new Function("x", "var y = " + tmp + "; return y;")];
attributes.name = objName;
output.functions.push(
this.create("functiongraph", defElements, attributes)
);
output[objName] = output.functions[output.functions.length - 1];
} else if (
splitted[i].search(/#(.*)\(\s*([0-9])\s*[,|]\s*([0-9])\s*\)/) != -1
) {
// Text element
defElements = []; // [0-9\.\-]+
defElements[0] = RegExp.$1;
defElements[1] = 1.0 * RegExp.$2;
defElements[2] = 1.0 * RegExp.$3;
defElements[0] = defElements[0]
.replace(/^\s+/, "")
.replace(/\s+$/, ""); // trim
output.texts.push(
this.create(
"text",
[defElements[1], defElements[2], defElements[0]],
attributes
)
);
} else if (splitted[i].search(/(\S*)\s*\[(.*)\]/) != -1) {
// Polygon
attributes.name = RegExp.$1;
if (!JXG.exists(attributes.withLabel)) {
attributes.withLabel = true;
}
defElements = RegExp.$2;
defElements = defElements.split(",");
for (j = 0; j < defElements.length; j++) {
defElements[j] = defElements[j]
.replace(/^\s+/, "")
.replace(/\s+$/, ""); // trim
if (mode == "macro") {
if (macroName != "") {
for (k = 0; k < createdNames.length; k++) {
// vorher oder nachher?
if (defElements[j] == createdNames[k]) {
defElements[j] =
macroName + "." + createdNames[k];
}
}
}
for (k = 0; k < params.length; k++) {
if (defElements[j] == params[k]) {
defElements[j] = paraIn[k];
}
}
}
defElements[j] = JXG.getReference(this, defElements[j]);
}
output.polygons.push(
this.create("polygon", defElements, attributes)
);
output[attributes.name] =
output.polygons[output.polygons.length - 1];
}
}
}
}
}
}
this.update();
return output;
};
/**
* Parses a string like
* <macro-name> = Macro(A, B, C) { ; ...; }
* and adds it as a macro so it can be used in the JSXGraph Construction Syntax.
* @param {String} string A string like the one in the methods description.
* @see #construct
*/
JXG.Board.prototype.addMacro = function (string) {
var defHead,
defBody,
defName = "",
i;
string.match(/(.*)\{(.*)\}/);
defHead = RegExp.$1;
defBody = RegExp.$2;
if (defHead.search(/=/) != -1) {
defHead.match(/\s*(\S*)\s*=.*/);
defName = RegExp.$1;
defHead = defHead.split("=")[1];
}
defHead.match(/Macro\((.*)\)/);
defHead = RegExp.$1;
defHead = defHead.split(",");
for (i = 0; i < defHead.length; i++) {
defHead[i].match(/\s*(\S*)\s*/);
defHead[i] = RegExp.$1;
}
if (this.definedMacros == null) {
this.definedMacros = {};
this.definedMacros.macros = [];
}
this.definedMacros.macros.push([defName, defHead, defBody]);
if (defName != "") {
this.definedMacros.defName =
this.definedMacros.macros[this.definedMacros.macros.length - 1];
}
};