Fixed multiline ASCIZ MACRO-10 pseudo-op, and expressions with binary shift suffixes finally evaluate correctly in parseExpression()

This commit is contained in:
Jeff 2017-03-16 16:50:47 -07:00 committed by Jeff Parsons
commit 01e9bcf0fe
9 changed files with 1381 additions and 1363 deletions

View file

@ -2850,8 +2850,8 @@ class DebuggerPDP10 extends Debugger {
if (dbgAddr.nBase) this.nBase = dbgAddr.nBase;
var size = (sCmd == "db"? 1 : 2);
var nWords = len || 32;
var nWordsPerLine = (size == 1? 2 : 4);
var nWords = len || 16;
var nWordsPerLine = (size == 1? 1 : 4);
var nLines = (((nWords + nWordsPerLine - 1) / nWordsPerLine)|0) || 1;
var sDump = "";
@ -2874,7 +2874,9 @@ class DebuggerPDP10 extends Debugger {
var nBits = 7;
var shift = 36 - nBits;
for (var i = 0; size == 1 && shift >= 0; i++) {
var c = ((w / Math.pow(2, shift)) % Math.pow(2, nBits)) + (nBits == 6? 0x20 : 0);
var c = ((w / Math.pow(2, shift)) % Math.pow(2, nBits));
sData += this.toStrBase(c, nBits) + ' ';
c += (nBits == 6? 0x20 : 0);
sChars += (c < 0x20? '.' : String.fromCharCode(c));
shift -= nBits;
}

View file

@ -348,7 +348,7 @@ class Macro10 {
sLine = this.addASCII(sLine);
}
var reLine = /\s*([A-Z$%._][0-9A-Z$%.]*[:=]|)\s*([A-Z$%.][0-9A-Z$%.]*|)\s*([^;]+|)\s*(;?.*)/i;
var reLine = /\s*([A-Z$%._][0-9A-Z$%.]*[:=]|)\s*([A-Z$%.][0-9A-Z$%.]*|)\s*([^;]+|)(;?[\s\S]*)/i;
var match = sLine.match(reLine);
if (!match || match[4] && match[4].slice(0, 1) != ';') {
this.error("failed to parse line: " + sLine);
@ -358,7 +358,9 @@ class Macro10 {
var sLabel = match[1];
var sOperator = match[2].toUpperCase();
var sOperands = match[3].trim();
var sComment = match[4].slice(1);
var sComment = match[4];
var sRemainder = match[3] + match[4];
if (sLabel) {
var chSep = sLabel.slice(-1);
sLabel = sLabel.slice(0, -1);
@ -370,8 +372,6 @@ class Macro10 {
}
}
sOperands = sOperands.trim();
if (!sOperator && !sOperands) return true;
/*
@ -413,7 +413,7 @@ class Macro10 {
case Macro10.PSEUDO_OP.ASCII:
case Macro10.PSEUDO_OP.ASCIZ:
case Macro10.PSEUDO_OP.SIXBIT:
this.addASCII(sOperands);
this.addASCII(sRemainder);
break;
case Macro10.PSEUDO_OP.XWD:

View file

@ -103,8 +103,6 @@ class Debugger extends Component
*/
this.nBase = +parmsDbg['base'] || 16;
this.nBaseSave = -1; // used to provide one level of saveBase()/restoreBase()
/*
* Default number of bits of integer precision; it can be overridden by the Debugger
* but there is no command to adjust it.
@ -481,35 +479,6 @@ class Debugger extends Component
return v;
}
/**
* saveBase(nBase)
*
* @this {Debugger}
* @param {number} nBase
*/
saveBase(nBase)
{
if (this.nBaseSave < 0) {
this.nBaseSave = this.nBase;
this.nBase = nBase;
return;
}
if (DEBUG) this.println("saveBase() overflow");
}
/**
* restoreBase()
*
* @this {Debugger}
*/
restoreBase()
{
if (this.nBaseSave > 0) {
this.nBase = this.nBaseSave;
this.nBaseSave = -1;
}
}
/**
* evalOps(aVals, aOps, cOps)
*
@ -622,7 +591,6 @@ class Debugger extends Component
valNew = Math.trunc(valNew / Math.pow(2, -val2));
}
}
this.restoreBase();
break;
default:
return false;
@ -632,6 +600,113 @@ class Debugger extends Component
return true;
}
/**
* parseArray(asValues, iValue, iLimit, nBase, fQuiet)
*
* Helper function for parseExpression().
*
* Imagine the original expression was "2*{3+{4/2}}". parseExpression() divides it into array elements:
*
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
* - - - - - - - - - - -- -- -- -- --
* 2 * _ { 3 + _ { 4 / 2 } _ } _
*
* @param {Array.<string>} asValues
* @param {number} iValue
* @param {number} iLimit
* @param {number} nBase
* @param {boolean} [fQuiet]
* @return {number|undefined}
*/
parseArray(asValues, iValue, iLimit, nBase, fQuiet)
{
var value;
var sValue, sOp;
var fError = false;
var fNegate = false;
var aVals = [], aOps = [];
var nBasePrev = this.nBase;
this.nBase = nBase;
while (iValue < iLimit) {
var v;
sValue = asValues[iValue++].trim();
sOp = (iValue < iLimit? asValues[iValue++] : "");
if (!sValue) {
if (sOp == '{') {
var cOpen = 1;
var iStart = iValue;
while (iValue < iLimit) {
sValue = asValues[iValue++].trim();
sOp = (iValue < asValues.length? asValues[iValue++] : "");
if (sOp == '{') {
cOpen++;
} else if (sOp == '}') {
if (!--cOpen) break;
}
}
v = this.parseArray(asValues, iStart, iValue-1, this.nBase, fQuiet);
sValue = (iValue < iLimit? asValues[iValue++].trim() : "");
sOp = (iValue < iLimit? asValues[iValue++] : "");
}
else {
if (sOp != '-') {
fError = true;
break;
}
fNegate = true;
continue;
}
}
else {
v = this.parseValue(sValue, null, fQuiet, fNegate);
}
if (v === undefined) {
if (this.sUndefined == null && fQuiet) {
this.sUndefined = sValue;
v = 0;
} else {
fError = true;
fQuiet = !fQuiet;
break;
}
}
aVals.push(this.truncate(v));
if (!sOp) break;
this.assert(Debugger.aBinOpPrecedence[sOp] != null);
if (aOps.length && Debugger.aBinOpPrecedence[sOp] < Debugger.aBinOpPrecedence[aOps[aOps.length - 1]]) {
this.evalOps(aVals, aOps, 1);
}
aOps.push(sOp);
/*
* The MACRO-10 binary shifting operator assumes a base-10 shift count, regardless of the current
* base, so we must override the current base to ensure the count is parsed correctly.
*/
this.nBase = (sOp == '><')? 10 : nBase;
fNegate = false;
}
if (!this.evalOps(aVals, aOps) || aVals.length != 1) {
fError = true;
}
if (!fError) {
value = aVals.pop();
} else if (fQuiet === false) {
this.println("parse error (" + (sValue || sOp) + ")");
}
this.nBase = nBasePrev;
return value;
}
/**
* parseExpression(sExp, fQuiet)
*
@ -649,19 +724,8 @@ class Debugger extends Component
* ...
*
* We pop 1 "binop" from aOps and 2 values from aVals whenever a "binop" of lower priority than its
* predecessor is encountered, evaluate, and push the result back onto aVals. Unary operators like
* '~' and ternary operators like '?:' are not supported.
*
* parseReference() makes it possible to write parenthetical-style sub-expressions by using whatever
* characters achGroup contains (default is braces}. Address references are resolved using the characters
* in achAddress (default is brackets).
*
* Why not always use parentheses for sub-expressions? Because parseReference() serves multiple purposes,
* the other being reference replacement in message strings passing through replaceRegs(), and some
* Debuggers don't want parentheses taking on a new meaning in message strings.
*
* However, a Debugger can override these choices by modifying achGroup and/or achAddress, if there's no
* conflict in its replaceRegs() implementation.
* predecessor is encountered, evaluate, and push the result back onto aVals. Only selected unary
* operators are supported (eg, minus), and ternary operators like '?:' are not supported at all.
*
* @this {Debugger}
* @param {string|undefined} sExp
@ -672,18 +736,21 @@ class Debugger extends Component
{
var value;
/*
* First process (and eliminate) any references, aka sub-expressions.
*/
this.sUndefined = null;
if (sExp) sExp = this.parseReference(sExp);
if (sExp) {
var i = 0;
var fError = false;
var fNegate = false;
var sExpOrig = sExp;
var aVals = [], aOps = [];
/*
* The default grouping characters for sub-expressions are braces; they can be changed by altering
* achGroup, but when that happens, we replace them all with braces anyway, for consistent parsing.
*
* Why not just always use parentheses for sub-expressions? Because some debuggers use parseReference()
* to perform parenthetical value replacements in message strings, and they don't want parentheses taking
* on a new meaning in those strings.
*/
if (this.achGroup[0] != '{') {
sExp = sExp.split(this.achGroup[0]).join('{').split(this.achGroup[1]).join('}');
}
/*
* All browsers (including, I believe, IE9 and up) support the following idiosyncrasy of a RegExp split():
@ -699,80 +766,26 @@ class Debugger extends Component
* MACRO-10 uses prefixes like "^D", "^O" and "^B" with numeric constants to indicate a base override, and
* I've added '!' as an alias for '|' to perform bitwise inclusive-or.
*
* The MACRO-10 binary shifting operator ('B') is a bit more problematic, since a capital B can also appear
* inside symbols. So I pre-scan for that operator and replace occurrences with an internal operator ('><').
* Note that Str.parseInt(), which parseValue() relies on, also support numeric constants with an embedded
* 'B' shifting operator, just to be safe.
* The MACRO-10 binary shifting suffix ('B') is a bit more problematic, since a capital B can also appear
* inside symbols. So I pre-scan for that operator and replace appropriate occurrences with an internal
* operator ('><').
*
* Note that Str.parseInt(), which parseValue() relies on, supports both the MACRO-10 base prefix overrides
* and the binary shifting suffix.
*
* MACRO-10 supports only a subset of all the PCjs operators; for example, MACRO-10 doesn't support bitwise
* exclusive-or, shift operators, or any of the boolean logical/compare operators. But unless we run into
* conflicts, I prefer sticking with this common set of operators.
*/
sExp = sExp.replace(/([^A-Z0-9$%.][0-9]+)B/, "$1><");
/*
*
* WARNING: Whenever you make changes to this RegExp, make sure you update aBinOpPrecedence as needed, too.
*/
var regExp = /(\|\||&&|\||\^\^|><|&|!=|!|==|>=|>>>|>>|>|<=|<<|<|-|\+|%|\/|\*)/;
var regExp = /(\{|}|\|\||&&|\||\^\^|><|&|!=|!|==|>=|>>>|>>|>|<=|<<|<|-|\+|%|\/|\*)/;
sExp = sExp.replace(/(^|[^A-Z0-9$%.])([0-9]+)B/, "$1$2><");
var asValues = sExp.split(regExp);
while (i < asValues.length) {
var sValue = asValues[i++];
var cchValue = sValue.length;
var sOp = null, cchOp = 0;
if (i < asValues.length) {
sOp = asValues[i++]; cchOp = sOp.length;
}
sValue = Str.trim(sValue);
if (!sValue) {
if (sOp != '-') {
fError = true;
break;
}
fNegate = true;
}
else {
var v = this.parseValue(sValue, null, fQuiet, fNegate);
if (v === undefined) {
if (this.sUndefined == null && fQuiet) {
this.sUndefined = sValue;
v = 0;
} else {
fError = true;
fQuiet = !fQuiet;
break;
}
}
aVals.push(this.truncate(v));
if (!sOp) break;
this.assert(Debugger.aBinOpPrecedence[sOp] != null);
if (aOps.length && Debugger.aBinOpPrecedence[sOp] < Debugger.aBinOpPrecedence[aOps[aOps.length - 1]]) {
this.evalOps(aVals, aOps, 1);
}
aOps.push(sOp);
/*
* The MACRO-10 binary shifting operator assumes a base-10 shift count, regardless of the current
* base, so we must override the current base to ensure the count is parsed correctly.
*
* TODO: The following kludge will not save us if the binary shift count is a sub-expression,
* since we currently evaluate all sub-expressions first, via parseReference(). We will have to
* rewrite this code to fold sub-expression parsing into the main loop if that becomes a problem.
*/
if (sOp == '><') this.saveBase(10);
fNegate = false;
}
sExp = sExp.substr(cchValue + cchOp);
value = this.parseArray(asValues, 0, asValues.length, this.nBase, fQuiet);
if (value !== undefined && fQuiet === false) {
this.printValue(null, value);
}
if (!this.evalOps(aVals, aOps) || aVals.length != 1) {
fError = true;
}
if (!fError) {
value = aVals.pop();
if (fQuiet === false) this.printValue(null, value);
} else {
if (fQuiet === false) this.println("error parsing '" + sExpOrig + "' at character " + (sExpOrig.length - sExp.length));
}
this.restoreBase();
}
return value;
}
@ -1050,7 +1063,9 @@ if (DEBUGGER) {
'%': 9, // remainder
'/': 9, // division
'*': 9, // multiplication
'><': 10 // internal binary shifting (MACRO-10-style)
'><': 10, // internal binary shifting (MACRO-10-style)
'{': 11,
'}': 11
};
/*