aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/analyzer.js398
-rw-r--r--src/jsifier.js36
-rw-r--r--src/parseTools.js1
-rw-r--r--src/settings.js2
-rw-r--r--src/utility.js26
-rw-r--r--tests/runner.py4
6 files changed, 185 insertions, 282 deletions
diff --git a/src/analyzer.js b/src/analyzer.js
index aa9a8031..54f766cc 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -298,10 +298,22 @@ function analyzer(data) {
// Tools
- function replaceLabels(line, labelId, toLabelId) {
+ function cleanLabel(label) {
+ if (label[0] == 'B') {
+ return label.substr(5);
+ } else {
+ return label;
+ }
+ }
+
+ function replaceLabels(line, labelIds, toLabelId) {
+ var ret = [];
function process(item) {
['label', 'labelTrue', 'labelFalse', 'toLabel', 'unwindLabel', 'defaultLabel'].forEach(function(id) {
- if (item[id] && item[id] == labelId) {
+ if (item[id] && item[id] in labelIds) {
+ dprint('relooping', 'zz replace ' + item[id] + ' with ' + toLabelId);
+ ret.push(item[id]);
+ item['old_' + id] = item[id]; // Save it; we need this later for labels before breaks, when we have multiple entries later
item[id] = toLabelId;
}
});
@@ -312,22 +324,15 @@ function analyzer(data) {
process(line);
line.switchLabels.forEach(process);
}
+ return ret;
}
- function replaceLabelLabels(label, labelId, toLabelId) {
- label.lines.forEach(function(line) { replaceLabels(line, labelId, toLabelId) });
- return label;
- }
-
- function replaceInLabels(labels, toReplace, replaceWith) {
- assertEq(!replaceWith || toReplace.length == 1, true); // TODO: implement other case
+ function replaceLabelLabels(labels, labelIds, toLabelId) {
+ ret = [];
labels.forEach(function(label) {
- ['inLabels'].forEach(function(l) {
- label[l] = label[l].map(function(labelId) { return toReplace.indexOf(labelId) == -1 ? labelId : replaceWith})
- .filter(function(labelId) { return !!labelId });
- });
+ ret = ret.concat(replaceLabels(label.lines[label.lines.length-1], labelIds, toLabelId));
});
- return labels;
+ return ret;
}
function calcLabelBranchingData(labels, labelsDict) {
@@ -368,8 +373,6 @@ function analyzer(data) {
labels.forEach(function(label) {
label.allInLabels = [];
label.allOutLabels = [];
- //! MustGetTo ignores return - |if (x) return; else Y| must get to Y.
- label.mustGetHere = [];
});
var worked = true;
@@ -393,278 +396,145 @@ function analyzer(data) {
});
}
- // Find all mustGetTos
- labels.forEach(function(label) {
- function walk(path, label) {
- // If all we are is a break/return - then stop here. Otherwise, continue to other path
- if (label.hasReturn || (label.hasBreak && label.outLabels.length == 0)) {
- return [path.concat([label])]
- };
- if (path.indexOf(label) != -1) {
- return []; // loop - drop this path
- }
- path = path.concat([label]);
- return label.outLabels.map(function(outLabelId) { return walk(path, labelsDict[outLabelId]) })
- .reduce(function(a, b) { return a.concat(b) }, [])
- .filter(function(path) { return path.length > 0 });
- }
- var paths = walk([], label).map(function(path) { return getLabelIds(path) });
- var possibles = dedup(paths.reduce(function(a,b) { return a.concat(b) }, []));
- label.mustGetTo = possibles.filter(function(possible) {
- return paths.filter(function(path) { return path.indexOf(possible) == -1 }) == 0;
- }).filter(function(possible) { return possible != label.ident });
- });
-
- labels.forEach(function(label) {
- label.mustGetTo.forEach(function (mustGetTo) {
- labelsDict[mustGetTo].mustGetHere.push(label.ident);
- });
- });
-
if (dcheck('relooping')) {
labels.forEach(function(label) {
- print('// label: ' + label.ident + ' :out : ' + JSON.stringify(label.outLabels));
- print('// ' + label.ident + ' :in : ' + JSON.stringify(label.inLabels));
- print('// ' + label.ident + ' :ALL out : ' + JSON.stringify(label.allOutLabels));
- print('// ' + label.ident + ' :ALL in : ' + JSON.stringify(label.allInLabels));
- print('// ZZZZZZ ' + label.ident + ' must get to all of ' + JSON.stringify(label.mustGetTo) + '\n');
+ dprint('// label: ' + label.ident + ' :out : ' + JSON.stringify(label.outLabels));
+ dprint('// ' + label.ident + ' :in : ' + JSON.stringify(label.inLabels));
+ dprint('// ' + label.ident + ' :ALL out : ' + JSON.stringify(label.allOutLabels));
+ dprint('// ' + label.ident + ' :ALL in : ' + JSON.stringify(label.allInLabels));
+
+ // Convert to searchables, for speed (we mainly do lookups here) and code clarity (x in Xlabels)
+ // FIXME TODO XXX do we need all these?
+ label.outLabels = searchable(label.outLabels);
+ label.inLabels = searchable(label.inLabels);
+ label.allOutLabels = searchable(label.allOutLabels);
+ label.allInLabels = searchable(label.allInLabels);
});
}
}
-/* // Disabled merging as it seems it just removes a return now and then.
- function mergeLabels(label1Ident, label2Ident) {
- var label1 = func.labelsDict[label1Ident];
- var label2 = func.labelsDict[label2Ident];
- label1.lines.pop();
- label1.lines = label1.lines.concat(label2.lines);
- label1.outLabels = label2.outLabels;
- label2.lines = null;
- func.labels = func.labels.filter(function(label) { return !!label.lines });
- delete func.labelsDict[label2.ident];
- replaceInLabels(func.labels, [label2.ident], label1.ident);
- }
+ // There are X main kinds of blocks:
+ //
+ // 'emulated': A soup of labels, implemented as a barbaric switch in a loop
+ //
+ // 'reloop': That is a block of the following shape:
+ //
+ // loopX: while(1) {
+ // // internal labels, etc. Labels are internal to the current one, if
+ // // they can return to it.
+ // //
+ // // Such labels can either do |continue loopX| to get back to the entry label,
+ // // or set __label__ and do |break loopX| to get to any of the external entries
+ // // they need to get to. External labels, of course, are those that cannot
+ // // get to the entry
+ // }
+ // // external labels
+ //
+ // 'multiple': A block that branches into multiple subblocks, somehow.
+ //
+ // @param exitLabels Labels which we should not implement; our parent block will
+ // do them. These are the external labels for our parent. Note that
+ // they include BCONT etc., as the labels have been replaced to be that way.
+ // @param exitLabelsHit Exit labels which were actually encountered - we update that. XXX do we need this?!
+ function makeBlock(labels, entries, labelsDict, exitLabels, exitLabelsHit) {
+ dprint('relooping', 'prelooping: ' + entries + ',' + labels.length + ' labels');
+ assert(entries);
- // Merge trivial labels
- var worked = true;
- while (worked) {
- worked = false;
- func.labels.forEach(function(label) {
- if (label.lines === null) return; // We were deleted
- if (label.outLabels.length == 1 &&
- label.lines.slice(-1)[0].intertype == 'branch' &&
- label.lines.slice(-1)[0].label == label.outLabels[0] &&
- func.labelsDict[label.outLabels[0]].inLabels.length == 1) {
- mergeLabels(label.ident, label.outLabels[0]);
- worked = true;
+ calcLabelBranchingData(labels, labelsDict);
+
+ function emulated() {
+ labels.forEach(function(label) {
+ for (l in label.outLabels) {
+ exitLabelsHit[l] = true;
}
});
+ assert(entries[0]);
+ return {
+ type: 'emulated',
+ labels: labels,
+ entries: entries.slice(0),
+ };
}
-*/
+ if (!RELOOP) return emulated();
- // 'block': A self-enclosed part of the program, for example a loop or an if
- function makeBlock(labels, entry, labelsDict) {
- var def = {
- type: 'emulated', // a block we cannot map to a nicer structure like a loop. We emulate it with a barbaric switch
- labels: labels,
- entry: entry,
- };
- if (!RELOOP) return def;
-
- if (!entry) {
- assertTrue(labels.length == 0);
- return null; // Empty block - not even an entry
- }
+ if (entries.length > 1) return emulated();
+ var entry = entries[0];
+ assert(entry);
+ dprint('relooping', 'Relooping: ' + entry + ',' + labels.length + ' labels');
- function getLastLine(block) {
- if (!block) return null; // Completely empty block (probably had only a branch,
- // which was optimized out)
- dprint('relooping', 'get last line at: ' + block.labels[0].ident);
- if (block.next) return getLastLine(block.next);
- switch(block.type) {
- case 'loop':
- return getLastLine(block.rest);
- case 'if':
- case 'breakingif':
- return getLastLine(block.ifTrue);
- case 'emulated':
- case 'simple':
- if (block.labels.length == 1) {
- return block.labels[0].lines.slice(-1)[0];
- } else {
- return null;
- }
- }
- }
- function getAll(fromId, beforeIds) {
- beforeIds = beforeIds ? beforeIds : [];
- if (beforeIds && beforeIds.indexOf(fromId) != -1) return [];
- var from = labelsDict[fromId];
- return dedup([from].concat(
- from.outLabels.map(function(outLabel) { return getAll(outLabel, beforeIds.concat(fromId)) })
- .reduce(function(a,b) { return a.concat(b) }, [])
- ), 'ident');
- }
- function isolate(label) {
- label.inLabels = [];
- label.outLabels = [];
- return label;
- }
- dprint('relooping', "\n\n// XXX MAKEBLOCK " + entry + ', num labels: ' + labels.length + ' and they are: ' + getLabelIds(labels));
- if (labels.length == 0 || !entry) {
- dprint('relooping', '//empty labels or entry');
- return;
- }
- function forLabelLines(labels, func) {
- labels.forEach(function(label) {
- label.lines.forEach(function(line) { func(line, label) });
- });
- }
+ var entryLabel = labelsDict[entry];
+ assert(entryLabel);
+ var lastLine = entryLabel.lines.slice(-1)[0];
+ var others = labels.filter(function(label) { return label.ident != entry });
- // Begin
+ var canReturn = values(entryLabel.inLabels).length > 0;
- calcLabelBranchingData(labels, labelsDict);
+ // === (simple) 'emulated' ===
- var split = splitter(labels, function(label) { return label.ident == entry });
- var first = split.splitOut[0];
- if (!first) {
- dprint('relooping', "//no first line");
- return;
- }
- var others = split.leftIn;
- var lastLine = first.lines.slice(-1)[0];
- dprint('relooping', "// makeBlock " + entry + ' : ' + getLabelIds(labels) + ' IN: ' + first.inLabels + ' OUT: ' + first.outLabels);
- // If we have one outgoing, and none incoming - make this a block of 1,
- // and move on the others (forgetting ourself, so they are now also
- // totally self-enclosed, once we start them)
- if (first.inLabels.length == 0 && first.outLabels.length == 1) {
- dprint('relooping', ' Creating simple emulated');
+ if (!canReturn && values(entryLabel.outLabels).length == 1) {
+ dprint('relooping', ' Creating simple emulated, outlabels: ' + keys(entryLabel.outLabels));
assertEq(lastLine.intertype, 'branch');
-// assertEq(!!lastLine.label, true);
+ var next = keys(entryLabel.outLabels)[0];
+ if (next in exitLabels) {
+ exitLabelsHit[next] = true;
+ next = null;
+ } else {
+ replaceLabelLabels(others, searchable(entry));
+ }
+ dprint('relooping', ' next: ' + next);
+ replaceLabelLabels([entryLabel], searchable(next), 'BNOPP'); // remove unneeded branch
return {
- type: 'simple',
- labels: [replaceLabelLabels(first, first.outLabels[0], 'BNOPP')],
+ type: 'emulated',
+ labels: [entryLabel],
entry: entry,
- next: makeBlock(replaceInLabels(others, entry), first.outLabels[0], labelsDict),
+ next: next ? makeBlock(others, [next], labelsDict, exitLabels, exitLabelsHit) : null,
};
}
- // Looping structures - in some way, we can get back to here
- if (first.outLabels.length > 0 && first.allInLabels.indexOf(entry) != -1) {
- // Look for outsiders - labels no longer capable of getting here. Those must be
- // outside the loop. Insiders are those that can get back to the entry
- var split2 = splitter(others, function(label) { return label.allOutLabels.indexOf(entry) == -1 });
- var outsiders = split2.splitOut;
- var insiders = split2.leftIn;
- // Hopefully exactly one of the outsiders is a 'pivot' - a label to which all those leaving
- // the loop must go. Then even some |if (falala) { ... break; }| will get ...
- // as an outsider, but it will actually still be in the loop
- var pivots =
- outsiders.filter(function(outsider) {
- return insiders.filter(function(insider) {
- return insider.mustGetTo.indexOf(outsider.ident) == -1;
- }) == 0;
- });
- // Find the earliest pivot. They must be staggered, each leading to another,
- // as all insiders must go through *all* of these. So we seek a pivot that
- // is never reached by another pivot. That must be the one with fewest
- // mustGetTo
- var pivot = null;
- if (pivots.length >= 1) {
- pivots.sort(function(a, b) { return b.mustGetTo.length - a.mustGetTo.length });
- pivot = pivots[0];
- if (!(pivot.ident in searchable(first.outLabels))) {
- // pivot must be right outside - no intermediates
- // TODO: Handle this, so we can reloop a lot more stuff
- pivot = null;
- }
- }
- if (pivot) { // We have ourselves a loop
- dprint('relooping', ' Creating LOOP : ' + entry + ' insiders: ' + getLabelIds(insiders) + ' to pivot: ' + pivot.ident);
- var otherLoopLabels = insiders;
- var loopLabels = insiders.concat([first]);
- var nextLabels = outsiders;
- // Rework branches out of the loop into new 'break' labels
- forLabelLines(loopLabels, function(line) {
- replaceLabels(line, pivot.ident, 'BREAK' + entry);
- });
- // Rework branches to the inc part of the loop into |continues|
- forLabelLines(loopLabels, function(line, label) {
- if (0) {// XXX - make this work :label.outLabels.length == 1 && label.outLabels[0] == entry && !label.hasBreak && !label.hasReturn) {
- // it can remove unneeded continues (but does too much right now, as the continue might have been
- // placed into an emulated while(1) switch { }
- replaceLabels(line, entry, 'BNOPP');
- } else {
- replaceLabels(line, entry, 'BCONT' + entry);
- }
- });
- // Fix inc branch into rest
- var nextEntry = null;
- first.outLabels.forEach(function(outLabel) {
- if (outLabel != pivot.ident) {
- replaceLabels(lastLine, outLabel, 'BNOPP');
- nextEntry = outLabel;
- }
- });
- if (nextEntry == entry) { // We loop back to ourselves, the entire loop is just us
- nextEntry = null;
- }
- dprint('relooping', "Entry into next: " + nextEntry);
- var ret = {
- type: 'loop',
- labels: loopLabels,
- entry: entry,
- inc: makeBlock([isolate(first)], entry, labelsDict),
- rest: makeBlock(replaceInLabels(otherLoopLabels, entry), nextEntry, labelsDict),
- };
- dprint('relooping', ' getting last line for block starting with ' + entry + ' and nextEntry: ' + nextEntry);
- var lastLoopLine = getLastLine(nextEntry ? ret.rest : ret.inc);
- if (lastLoopLine) {
- replaceLabels(lastLoopLine, 'BCONT' + entry, 'BNOPP'); // Last line will feed back into the loop anyhow
- }
- ret.next = makeBlock(replaceInLabels(nextLabels, getLabelIds(loopLabels)), pivot.ident, labelsDict);
- return ret;
- }
- }
- // Try an 'if' structure
- if (first.outLabels.length == 2) {
- if (labelsDict[first.outLabels[1]].mustGetTo.indexOf(first.outLabels[0]) != -1) {
- first.outLabels.push(first.outLabels.shift()); // Reverse order - normalize. Very fast check anyhow
- }
- if (labelsDict[first.outLabels[0]].mustGetTo.indexOf(first.outLabels[1]) != -1) {
- var ifLabelId = first.outLabels[0];
- var outLabelId = first.outLabels[1];
- // 0 - the if area. 1 - where we all exit to later
- var ifTrueLabels = getAll(ifLabelId, [outLabelId]);
- var ifLabels = ifTrueLabels.concat([first]);
- var nextLabels = getAll(outLabelId);
- // If we can get to the outside in more than 2 ways (one from if, one from True clause) - have breaks
- var breaking = labelsDict[outLabelId].allInLabels.length > 2;
- dprint('relooping', ' Creating XXX IF: ' + getLabelIds(ifTrueLabels) + ' to ' + outLabelId + ' ==> ' + getLabelIds(nextLabels) + ' breaking: ' + breaking);
- if (breaking) {
- // Rework branches out of the if into new 'break' labels
- forLabelLines(ifTrueLabels, function(line) {
- replaceLabels(line, outLabelId, 'BREAK' + entry);
- });
- }
- // Remove branching op - we will do it manually
- replaceLabels(lastLine, ifLabelId, 'BNOPP');
- replaceLabels(lastLine, outLabelId, 'BNOPP');
- return {
- type: (breaking ? 'breaking' : '') + 'if',
- labels: ifLabels,
- entry: entry,
- ifVar: lastLine.ident,
- check: makeBlock([isolate(first)], entry, labelsDict),
- ifTrue: makeBlock(replaceInLabels(ifTrueLabels, entry), ifLabelId, labelsDict),
- next: makeBlock(replaceInLabels(nextLabels, getLabelIds(ifLabels)), outLabelId, labelsDict),
- };
- }
+ // === 'reloop' away a loop, if there is one ===
+
+ if (canReturn) {
+ var ret = {
+ type: 'reloop',
+ entry: entry,
+ labels: labels,
+ };
+
+ // Find internal and external labels
+ var split_ = splitter(labels, function(label) { return !(entry in label.allOutLabels) });
+ var externals = split_.splitOut;
+ var internals = split_.leftIn;
+ var currExitLabels = set(getLabelIds(externals));
+
+ dprint('relooping', function() { return ' Creating reloop: Inner: ' + dump(getLabelIds(internals)) + ', Exxer: ' + dump(currExitLabels) });
+
+ replaceLabelLabels(internals, searchable(entry), 'BCONT' + entry); // we will be in a loop, |continue| gets us back to the entry
+ dprint('relooping', 'for exit purposes, Replacing: ' + dump(currExitLabels));
+ var enteredExitLabels = replaceLabelLabels(internals, currExitLabels, 'BREAK' + entry); // to get to any of our (not our parents') exit labels,
+ // we will break.
+ dprint('relooping', dump(currExitLabels) + ' enteredExitLabels: ' + dump(enteredExitLabels));
+ enteredExitLabels = enteredExitLabels.map(cleanLabel);
+ dprint('relooping', ' enteredExitLabels: ' + dump(enteredExitLabels));
+
+ // inner
+ var allExitLabels = mergeInto(set(currExitLabels), exitLabels);
+ var currExitLabelsHit = {};
+ ret.inner = makeBlock(internals, [entry], labelsDict, allExitLabels, currExitLabelsHit);
+
+ // outer
+ ret.outer = makeBlock(externals, enteredExitLabels, labelsDict, exitLabels, currExitLabelsHit);
+ mergeInto(exitLabelsHit, setSub(currExitLabelsHit, currExitLabels)); // Don't really need setSub, but nicer
+
+ return ret;
}
+ // === handle multiple branches from the entry with a 'multiple' ===
+
+ // TODO
+
// Give up on this structure - emulate it
dprint('relooping', ' Creating complex emulated');
- return def;
+ return emulated();
}
// TODO: each of these can be run in parallel
@@ -674,7 +544,7 @@ function analyzer(data) {
func.labels.forEach(function(label) {
func.labelsDict[label.ident] = label;
});
- func.block = makeBlock(func.labels, toNiceIdent('%entry'), func.labelsDict);
+ func.block = makeBlock(func.labels, [toNiceIdent('%entry')], func.labelsDict, {}, {});
});
return finish();
diff --git a/src/jsifier.js b/src/jsifier.js
index 4e47bdf6..a4d7b96e 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -244,6 +244,9 @@ function JSify(data) {
func.JS += ' stackEnter();\n';
if (LABEL_DEBUG) func.JS += " print(INDENT + ' Entering: " + func.ident + "'); INDENT += ' ';\n";
+ if (true) { // TODO: optimize away when not needed
+ func.JS += ' var __label__;\n';
+ }
if (hasVarArgs) {
func.JS += ' __numArgs__ = ' + params.length + ';\n';
}
@@ -254,6 +257,8 @@ function JSify(data) {
// Walk function blocks and generate JS
function walkBlock(block, indent) {
if (!block) return '';
+ if (!block.entry && block.entries.length == 1) block.entry = block.entries[0];
+ dprint('relooping', 'walking block: ' + block.type + ',' + block.entry + ',' + block.entries + ' : ' + block.labels.length);
function getLabelLines(label, indent) {
var ret = '';
if (LABEL_DEBUG) {
@@ -269,25 +274,26 @@ function JSify(data) {
return ret + label.lines.map(function(line) { return indent + line.JS + (line.comment ? ' // ' + line.comment : '') }).join('\n');
}
var ret = '';
- if (block.type == 'emulated' || block.type == 'simple') {
+ if (block.type == 'emulated') {
if (block.labels.length > 1) {
- ret += indent + 'var __label__ = ' + getLabelId(block.entry) + '; /* ' + block.entry + ' */\n';
+ if (block.entry) {
+ ret += indent + '__label__ = ' + getLabelId(block.entry) + '; /* ' + block.entry + ' */\n';
+ } // otherwise, should have been set before!
ret += indent + 'while(1) switch(__label__) {\n';
ret += block.labels.map(function(label) {
return indent + ' case ' + getLabelId(label.ident) + ': // ' + label.ident + '\n'
+ getLabelLines(label, indent + ' ');
}).join('\n');
- ret += '\n' + indent + '}';
+ ret += '\n' + indent + ' default: assert(0, "bad label: " + __label__);\n' + indent + '}';
} else {
ret += getLabelLines(block.labels[0], indent);
}
ret += '\n';
- } else if (block.type == 'loop') {
-// if (mustGetTo(first.outLabels[0], [first.ident, first.outLabels[1]])) { /* left branch must return here, or go to right branch */
+ } else if (block.type == 'reloop') {
ret += indent + block.entry + ': while(1) {\n';
- ret += walkBlock(block.inc, indent + ' ');
- ret += walkBlock(block.rest, indent + ' ');
+ ret += walkBlock(block.inner, indent + ' ');
ret += indent + '}\n';
+ ret += walkBlock(block.outer, indent);
} else if (block.type == 'breakingif') {
ret += walkBlock(block.check, indent);
ret += indent + block.entry + ': do { if (' + block.ifVar + ') {\n';
@@ -417,10 +423,12 @@ function JSify(data) {
return LABEL_IDs[label] = LABEL_ID_COUNTER ++;
}
- function makeBranch(label) {
+ function makeBranch(label, oldLabel) {
if (label[0] == 'B') {
if (label[1] == 'R') {
- return 'break ' + label.substr(5) + ';';
+ assert(oldLabel);
+ return '__label__ = ' + getLabelId(oldLabel) + '; ' + // TODO: optimize away
+ 'break ' + label.substr(5) + ';';
} else if (label[1] == 'C') {
return 'continue ' + label.substr(5) + ';';
} else { // NOPP
@@ -436,8 +444,8 @@ function JSify(data) {
if (!item.ident) {
return makeBranch(item.label);
} else {
- var labelTrue = makeBranch(item.labelTrue);
- var labelFalse = makeBranch(item.labelFalse);
+ var labelTrue = makeBranch(item.labelTrue, item.old_labelTrue);
+ var labelFalse = makeBranch(item.labelFalse, item.old_labelFalse);
if (labelTrue == ';' && labelFalse == ';') return ';';
var head = 'if (' + item.ident + ') { ';
var head2 = 'if (!(' + item.ident + ')) { ';
@@ -462,11 +470,11 @@ function JSify(data) {
first = false;
}
ret += 'if (' + item.ident + ' == ' + switchLabel.value + ') {\n';
- ret += ' ' + makeBranch(switchLabel.label) + '\n';
+ ret += ' ' + makeBranch(switchLabel.label, switchLabel.old_label) + '\n';
ret += '}\n';
});
ret += 'else {\n';
- ret += makeBranch(item.defaultLabel) + '\n';
+ ret += makeBranch(item.defaultLabel, item.old_defaultLabel) + '\n';
ret += '}\n';
if (item.value) {
ret += ' ' + toNiceIdent(item.value);
@@ -490,7 +498,7 @@ function JSify(data) {
+ '__THREW__ = false } catch(e) { '
+ '__THREW__ = true; '
+ (EXCEPTION_DEBUG ? 'print("Exception: " + e + " : " + (new Error().stack)); ' : '')
- + '} })(); if (!__THREW__) { ' + makeBranch(item.toLabel) + ' } else { ' + makeBranch(item.unwindLabel) + ' }';
+ + '} })(); if (!__THREW__) { ' + makeBranch(item.toLabel, item.old_toLabel) + ' } else { ' + makeBranch(item.unwindLabel, item.old_unwindLabel) + ' }';
return ret;
});
makeFuncLineZyme('load', function(item) {
diff --git a/src/parseTools.js b/src/parseTools.js
index 401a065c..da26727e 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -48,6 +48,7 @@ function pointingLevels(type) {
}
function toNiceIdent(ident) {
+ assert(ident);
if (parseFloat(ident) == ident) return ident;
if (ident == 'null') return '0'; // see parseNumerical
return ident.replace(/[" \.@%:<>,\*]/g, '_');
diff --git a/src/settings.js b/src/settings.js
index 67ba8d10..5e6f16f7 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -28,7 +28,7 @@ EXCEPTION_DEBUG = 1; // Print out exceptions in emscriptened code
EXECUTION_TIMEOUT = -1; // Throw an exception after X seconds - useful to debug infinite loops
// Compiler debugging options
-DEBUG_TAGS_SHOWING = ['enzymatic'];
+DEBUG_TAGS_SHOWING = ['enzymatic', 'relooping'];
// Some useful items:
// gconst
// types
diff --git a/src/utility.js b/src/utility.js
index 8907eb91..3d8ce86e 100644
--- a/src/utility.js
+++ b/src/utility.js
@@ -145,6 +145,14 @@ function splitter(array, filter) {
function dcheck(tag) {
return DEBUG_TAGS_SHOWING.indexOf(arguments[0]) != -1;
}
+DPRINT_INDENT = '';
+function dprint_indent() {
+ DPRINT_INDENT += ' ';
+}
+function dprint_unindent() {
+ DPRINT_INDENT = DPRINT_INDENT.substr(3);
+}
+
function dprint() {
var text;
if (arguments[1]) {
@@ -153,7 +161,10 @@ function dprint() {
} else {
text = arguments[0];
}
- text = '// ' + text;
+ if (typeof text === 'function') {
+ text = text(); // Allows deferred calculation, so dprints don't slow us down when not needed
+ }
+ text = DPRINT_INDENT + '// ' + text;
print(text);
}
@@ -178,3 +189,16 @@ function mergeInto(obj, other) {
return obj;
}
+// Sets
+
+set = searchable; // Create a 'set'
+function setSub(x, y) {
+ var ret = set(values(x));
+ for (yy in y) {
+ if (yy in ret) {
+ delete ret.yy;
+ }
+ }
+ return ret;
+}
+
diff --git a/tests/runner.py b/tests/runner.py
index 0a48ede7..c8116ada 100644
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -22,7 +22,7 @@ def timeout_run(proc, timeout, note):
while time.time() - start < timeout and proc.poll() is None:
time.sleep(0.1)
if proc.poll() is None:
- proc.kill()
+ proc.kill() # XXX bug: killing emscripten.py does not kill it's child process!
raise Exception("Timed out: " + note)
return proc.communicate()[0]
@@ -69,7 +69,7 @@ class T(unittest.TestCase):
if DEBUG: print output
# Run Emscripten
emscripten_settings = ['{ "QUANTUM_SIZE": %d }' % QUANTUM_SIZE]
- timeout_run(Popen([EMSCRIPTEN, filename + '.o.llvm', PARSER_ENGINE] + emscripten_settings, stdout=open(filename + '.o.js', 'w'), stderr=STDOUT), 120, 'Compiling')
+ timeout_run(Popen([EMSCRIPTEN, filename + '.o.llvm', PARSER_ENGINE] + emscripten_settings, stdout=open(filename + '.o.js', 'w'), stderr=STDOUT), 2400, 'Compiling')
output = open(filename + '.o.js').read()
if output_processor is not None:
output_processor(output)