More MACRO-10 parsing improvements

This commit is contained in:
Jeff 2017-03-19 12:03:45 -07:00 • committed by Jeff Parsons
commit fb83d1e503
8 changed files with 869 additions and 806 deletions

View file

@ -1,3 +1,22 @@
; From the MACR0-10 ASSEMBLER PROGRAMMER'S REFERENCE MANUAL (June 1972):
;
; Example 4 (Figure 7-4) shows nested macros which use IRPC. The desired operation
; is to take an ASCII text string and store the characters four per word, left-justified,
; with the character count stored in the first nine bits of the first word.
;
; The TEXT macro counts the string characters and invokes the CODE macro to store the
; characters four per word. The CODE macro invokes a SHIFT macro which left-justifies the
; last word if it is not already left-justified. The first part of the example shows the
; normal listing, then SALL is set to show what code the macros are generating.
;
; The original listing encodes the following two lines as follows:
;
; IFN ZZ,<DEFINE SHIFT ;;REMAINDER LEFT
; IFE ZZ&777B8,> ;;IF NOT LEFT JUSTIFIED
;
; The April 1978 MACRO-10 manual says that a macro body "must be enclosed in angle brackets",
; so I'm not sure how the above lines make sense.
;
TITLE STORE TEXT CHARACTER BY CHARACTER
SUBTTL %1 5-APR-72
@ -13,10 +32,10 @@
IFN ZZ&777B8,<
EXP ZZ ;;WORD FULL
ZZ==0> ;;START AGAIN WITH 0
ZZ=ZZ+9+"C"> ;;END OF IRPC
ZZ==ZZ_9+"C"> ;;END OF IRPC
IFN ZZ,<DEFINE SHIFT< ;;REMAINDER LEFT
IFE ZZ&777B8,< ;;IF NOT LEFT JUSTIFIED
ZZ=ZZ+9 ;;MOVE LEFT 9 BITS
ZZ==ZZ_9 ;;MOVE LEFT 9 BITS
SHIFT> ;;RECURSE
> ;;END OF DEFINE
SHIFT ;;START MOVING LEFT

File diff suppressed because one or more lines are too long

View file

@ -492,11 +492,12 @@ class Macro10 {
case Macro10.PSEUDO_OP.IRP:
case Macro10.PSEUDO_OP.IRPC:
case Macro10.PSEUDO_OP.REPEAT:
this.addMacro(sOperator, sOperands);
this.addMacro(sOperator, sRemainder);
break;
case Macro10.PSEUDO_OP.PAGE: // TODO
case Macro10.PSEUDO_OP.SUBTTL: // TODO
case Macro10.PSEUDO_OP.TITLE: // TODO
break;
default:
@ -899,7 +900,7 @@ class Macro10 {
/*
* This is a DEFINE (macro) block.
*/
match = sOperands.match(/([A-Z$%.][0-9A-Z$%.]*)\s*(\([^)]*\)|)\s*(<|)(.*)/i);
match = sOperands.match(/([A-Z$%.][0-9A-Z$%.]*)\s*(\([^)]*\)|)\s*(<|)([\s\S]*)/i);
if (!match) {
this.error("unrecognized " + sOperator + ": " + sOperands);
return sOperands;
@ -940,7 +941,7 @@ class Macro10 {
if (!this.macroCall) {
this.error(sOperator + " outside of macro");
}
match = sOperands.match(/([A-Z$%.][0-9A-Z$%.]*)\s*,\s*(<|)(.*)/i);
match = sOperands.match(/([A-Z$%.][0-9A-Z$%.]*)\s*,\s*(<|)([\s\S]*)/i);
if (!match) {
this.error("unrecognized " + sOperator + ": " + sOperands);
return sOperands;
@ -973,7 +974,7 @@ class Macro10 {
}
sOperands = sOperands.substr(sOperand.length + 1);
sOperand = sOperand.trim();
match = sOperands.match(/\s*(<|)(.*)/i);
match = sOperands.match(/\s*(<|)([\s\S]*)/i);
name = '?' + sOperator;
/*
* The expression is either a repeat count or a condition. Either way, we must be able to
@ -990,24 +991,14 @@ class Macro10 {
*/
name = name.toUpperCase();
this.nMacroDef = 1;
var sText = "";
if (match[iMatch]) { // if there IS also an opening bracket...
this.nMacroDef = 2; // then the macro definition has begun
sText = match[iMatch + 1];
if (sText.slice(-1) == this.chMacroClose) { // and if there is ALSO a closing bracket...
this.nMacroDef = 0; // the macro definition has ended as well
sText = sText.slice(0, -1);
}
}
this.tblMacros[name] = {name, nOperand, aParms, aDefaults, aValues, sText, nLine: this.nLine};
if (!this.nMacroDef) {
this.parseMacro(name);
} else {
this.sMacroDef = name;
this.tblMacros[name] = {name, nOperand, aParms, aDefaults, aValues, sText: "", nLine: this.nLine};
if (match[iMatch]) { // if there is an opening bracket
this.nMacroDef = 2; // then the macro definition has started
this.appendMacro(match[iMatch + 1]);
}
return name;
}
@ -1087,7 +1078,7 @@ class Macro10 {
addWord(sOperator, sOperands)
{
var w;
var sExp = sOperator + sOperands;
var sExp = sOperator + ' ' + sOperands;
if (sExp.indexOf(",,") >= 0 || !sOperator && sExp.indexOf('@') < 0 && sExp.indexOf('(') < 0) {
w = this.parseExpression(sExp, true);
} else {
@ -1232,6 +1223,7 @@ Macro10.PSEUDO_OP = {
REPEAT: "REPEAT",
SIXBIT: "SIXBIT",
SUBTTL: "SUBTTL",
TITLE: "TITLE",
XWD: "XWD"
};

View file

@ -617,7 +617,7 @@ class Debugger extends Component
* - - - - - - - - - - -- -- -- -- --
* 2 * { 3 + { 4 / 2 } }
*
* This function takes care of recursively processing sub-expressions, by processing subsets of the array,
* This function takes care of recursively processing grouped expressions, by processing subsets of the array,
* as well as handling certain base overrides (eg, temporarily switching to base-10 for binary shift suffixes).
*
* @param {Array.<string>} asValues
@ -720,6 +720,39 @@ class Debugger extends Component
return value;
}
/**
* parseASCII(sExp, chDelim, nBits, nMax)
*
* @this {Debugger}
* @param {string} sExp
* @param {string} chDelim
* @param {number} nBits
* @param {number} nMax
* @return {string}
*/
parseASCII(sExp, chDelim, nBits, nMax)
{
var i;
while ((i = sExp.indexOf(chDelim)) >= 0) {
var v = 0;
var n = nMax;
var j = i + 1;
while (n--) {
var ch = sExp[j++];
if (ch == chDelim) break;
var c = ch.charCodeAt(0);
if (nBits == 7) {
c &= 0x7F;
} else {
c = (c - 0x20) & 0x3F;
}
v = this.truncate(v * Math.pow(2, nBits) + c, nBits * nMax, true);
}
sExp = sExp.substr(0, i) + this.toStrBase(v, -1) + sExp.substr(j);
}
return sExp;
}
/**
* parseExpression(sExp, fQuiet)
*
@ -754,19 +787,26 @@ class Debugger extends Component
if (sExp) {
/*
* The default grouping characters for sub-expressions are braces; they can be changed by altering
* The default delimiting characters for grouped expressions are braces; they can be changed by altering
* achGroup, but when that happens, instead of changing our regular expressions and operator tables,
* we simply replace all achGroup characters with braces in the given expression.
*
* 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 different meaning. And for some machines, like the PDP-10, the convention is to use parentheses
* for indexed addressing and angle brackets for sub-expressions.
* Why not use parentheses for grouped expressions? Because some debuggers use parseReference() to perform
* parenthetical value replacements in message strings, and they don't want parentheses taking on a different
* meaning. And for some machines, like the PDP-10, the convention is to use parentheses for other things,
* like indexed addressing, and to use angle brackets for grouped expressions.
*/
if (this.achGroup[0] != '{') {
sExp = sExp.split(this.achGroup[0]).join('{').split(this.achGroup[1]).join('}');
}
/*
* Quoted ASCII characters can have a numeric value, too, which must be converted now, to avoid any
* conflicts with the operators below.
*/
sExp = this.parseASCII(sExp, '"', 7, 5); // MACRO-10 packs up to 5 7-bit ASCII codes in the value
sExp = this.parseASCII(sExp, "'", 6, 6); // MACRO-10 packs up to 6 6-bit ASCII (SIXBIT) codes in the value
/*
* All browsers (including, I believe, IE9 and up) support the following idiosyncrasy of a RegExp split():
* when the RegExp uses a capturing pattern, the resulting array will include entries for all the pattern
@ -903,17 +943,24 @@ class Debugger extends Component
} else {
value = this.getVariable(sValue);
if (value == null) {
/*
* A feature of MACRO-10 is that any single-digit number is automatically interpreted as base-10.
*/
var nBase = sValue.length > 1? this.nBase : 10;
if (iNegate < 0) {
sValue = '-' + sValue;
iNegate = 0;
}
value = Str.parseInt(sValue, this.nBase);
value = Str.parseInt(sValue, nBase);
}
}
if (value != null) {
if (iNegate < 0) {
value = -value;
} else if (iNegate > 0) {
/*
* This is easier than adding an evalNOT()....
*/
value = this.evalXOR(value, -1);
}
} else {
@ -1101,8 +1148,8 @@ if (DEBUGGER) {
'*': 9, // multiplication
'_': 10, // MACRO-10 shift operator
'^_': 10, // MACRO-10 internal shift operator (converted from 'B' suffix form that MACRO-10 uses)
'{': 11, // open sub-expression (achGroup[0] default)
'}': 11 // close sub-expression (achGroup[1] default)
'{': 11, // open grouped expression (achGroup[0] default)
'}': 11 // close grouped expression (achGroup[1] default)
};
/*

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long