refactor(phoenix): Combine similar sed command classes

These make more sense combined into one Command which is controlled by
constructor parameters:

- b, t and T
- d and D
- g and G
- h and H
- p and P
This commit is contained in:
Sam Atkins 2024-05-31 14:11:20 +01:00
parent 0d4f907b66
commit 6de4c89c25
2 changed files with 61 additions and 146 deletions

View File

@ -144,13 +144,21 @@ export class AppendTextCommand extends Command {
} }
// 'b' - Branch to label // 'b' - Branch to label
// 't' - Branch if substitution successful
// 'T' - Branch if substitution unsuccessful
export class BranchCommand extends Command { export class BranchCommand extends Command {
constructor(addressRange, label) { constructor(addressRange, label, substitutionCondition) {
super(addressRange); super(addressRange);
this.label = label; this.label = label;
this.substitutionCondition = substitutionCondition;
} }
async run(context) { async run(context) {
if (typeof this.substitutionCondition === 'boolean') {
if (context.substitutionResult !== this.substitutionCondition)
return JumpLocation.None;
}
if (this.label) { if (this.label) {
context.jumpParameter = this.label; context.jumpParameter = this.label;
return JumpLocation.Label; return JumpLocation.Label;
@ -160,6 +168,7 @@ export class BranchCommand extends Command {
dump(indent) { dump(indent) {
return `${makeIndent(indent)}BRANCH:\n` return `${makeIndent(indent)}BRANCH:\n`
+ `${makeIndent(indent+1)}CONDITION: ${this.substitutionCondition ?? 'ALWAYS'}\n`
+ this.addressRange.dump(indent+1) + this.addressRange.dump(indent+1)
+ `${makeIndent(indent+1)}LABEL: ${this.label ? `'${this.label}'` : 'END'}\n`; + `${makeIndent(indent+1)}LABEL: ${this.label ? `'${this.label}'` : 'END'}\n`;
} }
@ -189,107 +198,73 @@ export class ReplaceCommand extends Command {
} }
// 'd' - Delete pattern // 'd' - Delete pattern
// 'D' - Delete first line of pattern
export class DeleteCommand extends Command { export class DeleteCommand extends Command {
constructor(addressRange) { constructor(addressRange, firstLine = false) {
super(addressRange); super(addressRange);
this.firstLine = firstLine;
} }
async run(context) { async run(context) {
if (this.firstLine) {
const [ first, rest ] = context.patternSpace.split('\n', 2);
context.patternSpace = rest ?? '';
if (rest === undefined)
return JumpLocation.EndOfCycle;
return JumpLocation.StartOfCycle;
}
context.patternSpace = ''; context.patternSpace = '';
return JumpLocation.EndOfCycle; return JumpLocation.EndOfCycle;
} }
dump(indent) { dump(indent) {
return `${makeIndent(indent)}DELETE:\n` return `${makeIndent(indent)}DELETE: ${this.firstLine ? 'LINE' : 'ALL'}\n`
+ this.addressRange.dump(indent+1);
}
}
// 'D' - Delete first line of pattern
export class DeleteLineCommand extends Command {
constructor(addressRange) {
super(addressRange);
}
async run(context) {
const [ firstLine, rest ] = context.patternSpace.split('\n', 2);
context.patternSpace = rest ?? '';
if (rest === undefined) {
return JumpLocation.EndOfCycle;
}
return JumpLocation.StartOfCycle;
}
dump(indent) {
return `${makeIndent(indent)}DELETE-LINE:\n`
+ this.addressRange.dump(indent+1); + this.addressRange.dump(indent+1);
} }
} }
// 'g' - Get the held line into the pattern // 'g' - Get the held line into the pattern
export class GetCommand extends Command {
constructor(addressRange) {
super(addressRange);
}
async run(context) {
context.patternSpace = context.holdSpace;
return JumpLocation.None;
}
dump(indent) {
return `${makeIndent(indent)}GET-HELD:\n`
+ this.addressRange.dump(indent+1);
}
}
// 'G' - Get the held line and append it to the pattern // 'G' - Get the held line and append it to the pattern
export class GetAppendCommand extends Command { export class GetCommand extends Command {
constructor(addressRange) { constructor(addressRange, append = false) {
super(addressRange); super(addressRange);
this.append = append;
} }
async run(context) { async run(context) {
context.patternSpace += '\n' + context.holdSpace; if (this.append) {
context.patternSpace += '\n' + context.holdSpace;
} else {
context.patternSpace = context.holdSpace;
}
return JumpLocation.None; return JumpLocation.None;
} }
dump(indent) { dump(indent) {
return `${makeIndent(indent)}GET-HELD-APPEND:\n` return `${makeIndent(indent)}GET-HELD: ${this.append ? 'APPEND' : 'ALL'}\n`
+ this.addressRange.dump(indent+1); + this.addressRange.dump(indent+1);
} }
} }
// 'h' - Hold the pattern // 'h' - Hold the pattern
export class HoldCommand extends Command {
constructor(addressRange) {
super(addressRange);
}
async run(context) {
context.holdSpace = context.patternSpace;
return JumpLocation.None;
}
dump(indent) {
return `${makeIndent(indent)}HOLD:\n`
+ this.addressRange.dump(indent+1);
}
}
// 'H' - Hold append the pattern // 'H' - Hold append the pattern
export class HoldAppendCommand extends Command { export class HoldCommand extends Command {
constructor(addressRange) { constructor(addressRange, append = false) {
super(addressRange); super(addressRange);
this.append = append;
} }
async run(context) { async run(context) {
context.holdSpace += '\n' + context.patternSpace; if (this.append) {
context.holdSpace += '\n' + context.patternSpace;
} else {
context.holdSpace = context.patternSpace;
}
return JumpLocation.None; return JumpLocation.None;
} }
dump(indent) { dump(indent) {
return `${makeIndent(indent)}HOLD-APPEND:\n` return `${makeIndent(indent)}HOLD: ${this.append ? 'APPEND' : 'ALL'}\n`
+ this.addressRange.dump(indent+1); + this.addressRange.dump(indent+1);
} }
} }
@ -354,36 +329,25 @@ export class DebugPrintCommand extends Command {
} }
// 'p' - Print pattern // 'p' - Print pattern
export class PrintCommand extends Command {
constructor(addressRange) {
super(addressRange);
}
async run(context) {
await context.out.write(context.patternSpace + '\n');
return JumpLocation.None;
}
dump(indent) {
return `${makeIndent(indent)}PRINT:\n`
+ this.addressRange.dump(indent+1);
}
}
// 'P' - Print first line of pattern // 'P' - Print first line of pattern
export class PrintLineCommand extends Command { export class PrintCommand extends Command {
constructor(addressRange) { constructor(addressRange, firstLine = false) {
super(addressRange); super(addressRange);
this.firstLine = firstLine;
} }
async run(context) { async run(context) {
const firstLine = context.patternSpace.split('\n', 2)[0]; if (this.firstLine) {
await context.out.write(firstLine + '\n'); const firstLine = context.patternSpace.split('\n', 2)[0];
await context.out.write(firstLine + '\n');
} else {
await context.out.write(context.patternSpace + '\n');
}
return JumpLocation.None; return JumpLocation.None;
} }
dump(indent) { dump(indent) {
return `${makeIndent(indent)}PRINT-LINE:\n` return `${makeIndent(indent)}PRINT: ${this.firstLine ? 'LINE' : 'ALL'}\n`
+ this.addressRange.dump(indent+1); + this.addressRange.dump(indent+1);
} }
} }
@ -478,35 +442,6 @@ export class SubstituteCommand extends Command {
} }
} }
// 't' - Branch if substitution successful
// 'T' - Branch if substitution unsuccessful
export class ConditionalBranchCommand extends Command {
constructor(addressRange, label, substitutionCondition) {
super(addressRange);
this.label = label;
this.substitutionCondition = substitutionCondition;
}
async run(context) {
if (context.substitutionResult !== this.substitutionCondition) {
return JumpLocation.None;
}
if (this.label) {
context.jumpParameter = this.label;
return JumpLocation.Label;
}
return JumpLocation.EndOfCycle;
}
dump(indent) {
return `${makeIndent(indent)}CONDITIONAL-BRANCH:\n`
+ this.addressRange.dump(indent+1)
+ `${makeIndent(indent+1)}LABEL: ${this.label ? `'${this.label}'` : 'END'}\n`
+ `${makeIndent(indent+1)}IF SUBSTITUTED = ${this.substitutionCondition}\n`;
}
}
// 'x' - Exchange hold and pattern // 'x' - Exchange hold and pattern
export class ExchangeCommand extends Command { export class ExchangeCommand extends Command {
constructor(addressRange) { constructor(addressRange) {

View File

@ -20,22 +20,17 @@ import { Address, AddressRange } from './address.js';
import { import {
AppendTextCommand, AppendTextCommand,
BranchCommand, BranchCommand,
ConditionalBranchCommand,
DebugPrintCommand, DebugPrintCommand,
DeleteCommand, DeleteCommand,
DeleteLineCommand,
ExchangeCommand, ExchangeCommand,
GetAppendCommand,
GetCommand, GetCommand,
GroupEndCommand, GroupEndCommand,
GroupStartCommand, GroupStartCommand,
HoldAppendCommand,
HoldCommand, HoldCommand,
InsertTextCommand, InsertTextCommand,
LabelCommand, LabelCommand,
LineNumberCommand, LineNumberCommand,
PrintCommand, PrintCommand,
PrintLineCommand,
QuitCommand, QuitCommand,
ReplaceCommand, ReplaceCommand,
SubstituteCommand, SubstituteCommand,
@ -295,7 +290,7 @@ export const parseScript = (script_string, options) => {
let group_depth = 0; let group_depth = 0;
for (const command of commands) { for (const command of commands) {
// Ensure branches all go to labels that exist // Ensure branches all go to labels that exist
if (command instanceof BranchCommand || command instanceof ConditionalBranchCommand) { if (command instanceof BranchCommand) {
// Note: Branches to the end of the script don't have a label. // Note: Branches to the end of the script don't have a label.
if (command.label && !labels.has(command.label)) if (command.label && !labels.has(command.label))
throw new Error(`Label "${command.label}" does not exist in the script.`); throw new Error(`Label "${command.label}" does not exist in the script.`);
@ -371,29 +366,20 @@ export const parseScript = (script_string, options) => {
require_max_address_count(2); require_max_address_count(2);
return new ReplaceCommand(address_range, func.value); return new ReplaceCommand(address_range, func.value);
} }
case 'd': { case 'd':
require_max_address_count(2);
return new DeleteCommand(address_range);
}
case 'D': { case 'D': {
require_max_address_count(2); require_max_address_count(2);
return new DeleteLineCommand(address_range); return new DeleteCommand(address_range, func.$ === 'D');
}
case 'g': {
require_max_address_count(2);
return new GetCommand(address_range);
} }
case 'g':
case 'G': { case 'G': {
require_max_address_count(2); require_max_address_count(2);
return new GetAppendCommand(address_range); return new GetCommand(address_range, func.$ === 'G');
}
case 'h': {
require_max_address_count(2);
return new HoldCommand(address_range);
} }
case 'h':
case 'H': { case 'H': {
require_max_address_count(2); require_max_address_count(2);
return new HoldAppendCommand(address_range); return new HoldCommand(address_range, func.$ === 'H');
} }
case 'i': { case 'i': {
require_max_address_count(1); require_max_address_count(1);
@ -403,21 +389,15 @@ export const parseScript = (script_string, options) => {
require_max_address_count(2); require_max_address_count(2);
return new DebugPrintCommand(address_range); return new DebugPrintCommand(address_range);
} }
case 'p': { case 'p':
require_max_address_count(2);
return new PrintCommand(address_range);
}
case 'P': { case 'P': {
require_max_address_count(2); require_max_address_count(2);
return new PrintLineCommand(address_range); return new PrintCommand(address_range, func.$ === 'P');
}
case 'q': {
require_max_address_count(1);
return new QuitCommand(address_range, false);
} }
case 'q':
case 'Q': { case 'Q': {
require_max_address_count(1); require_max_address_count(1);
return new QuitCommand(address_range, true); return new QuitCommand(address_range, func.$ === 'Q');
} }
case 's': { case 's': {
require_max_address_count(2); require_max_address_count(2);
@ -427,7 +407,7 @@ export const parseScript = (script_string, options) => {
case 't': case 't':
case 'T': { case 'T': {
require_max_address_count(2); require_max_address_count(2);
return new ConditionalBranchCommand(address_range, func.value, func.$ === 't'); return new BranchCommand(address_range, func.value, func.$ === 't');
} }
case 'x': { case 'x': {
require_max_address_count(2); require_max_address_count(2);