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
// 't' - Branch if substitution successful
// 'T' - Branch if substitution unsuccessful
export class BranchCommand extends Command {
constructor(addressRange, label) {
constructor(addressRange, label, substitutionCondition) {
super(addressRange);
this.label = label;
this.substitutionCondition = substitutionCondition;
}
async run(context) {
if (typeof this.substitutionCondition === 'boolean') {
if (context.substitutionResult !== this.substitutionCondition)
return JumpLocation.None;
}
if (this.label) {
context.jumpParameter = this.label;
return JumpLocation.Label;
@ -160,6 +168,7 @@ export class BranchCommand extends Command {
dump(indent) {
return `${makeIndent(indent)}BRANCH:\n`
+ `${makeIndent(indent+1)}CONDITION: ${this.substitutionCondition ?? 'ALWAYS'}\n`
+ this.addressRange.dump(indent+1)
+ `${makeIndent(indent+1)}LABEL: ${this.label ? `'${this.label}'` : 'END'}\n`;
}
@ -189,107 +198,73 @@ export class ReplaceCommand extends Command {
}
// 'd' - Delete pattern
// 'D' - Delete first line of pattern
export class DeleteCommand extends Command {
constructor(addressRange) {
constructor(addressRange, firstLine = false) {
super(addressRange);
this.firstLine = firstLine;
}
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 = '';
return JumpLocation.EndOfCycle;
}
dump(indent) {
return `${makeIndent(indent)}DELETE:\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`
return `${makeIndent(indent)}DELETE: ${this.firstLine ? 'LINE' : 'ALL'}\n`
+ this.addressRange.dump(indent+1);
}
}
// '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
export class GetAppendCommand extends Command {
constructor(addressRange) {
export class GetCommand extends Command {
constructor(addressRange, append = false) {
super(addressRange);
this.append = append;
}
async run(context) {
context.patternSpace += '\n' + context.holdSpace;
if (this.append) {
context.patternSpace += '\n' + context.holdSpace;
} else {
context.patternSpace = context.holdSpace;
}
return JumpLocation.None;
}
dump(indent) {
return `${makeIndent(indent)}GET-HELD-APPEND:\n`
return `${makeIndent(indent)}GET-HELD: ${this.append ? 'APPEND' : 'ALL'}\n`
+ this.addressRange.dump(indent+1);
}
}
// '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
export class HoldAppendCommand extends Command {
constructor(addressRange) {
export class HoldCommand extends Command {
constructor(addressRange, append = false) {
super(addressRange);
this.append = append;
}
async run(context) {
context.holdSpace += '\n' + context.patternSpace;
if (this.append) {
context.holdSpace += '\n' + context.patternSpace;
} else {
context.holdSpace = context.patternSpace;
}
return JumpLocation.None;
}
dump(indent) {
return `${makeIndent(indent)}HOLD-APPEND:\n`
return `${makeIndent(indent)}HOLD: ${this.append ? 'APPEND' : 'ALL'}\n`
+ this.addressRange.dump(indent+1);
}
}
@ -354,36 +329,25 @@ export class DebugPrintCommand extends Command {
}
// '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
export class PrintLineCommand extends Command {
constructor(addressRange) {
export class PrintCommand extends Command {
constructor(addressRange, firstLine = false) {
super(addressRange);
this.firstLine = firstLine;
}
async run(context) {
const firstLine = context.patternSpace.split('\n', 2)[0];
await context.out.write(firstLine + '\n');
if (this.firstLine) {
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;
}
dump(indent) {
return `${makeIndent(indent)}PRINT-LINE:\n`
return `${makeIndent(indent)}PRINT: ${this.firstLine ? 'LINE' : 'ALL'}\n`
+ 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
export class ExchangeCommand extends Command {
constructor(addressRange) {

View File

@ -20,22 +20,17 @@ import { Address, AddressRange } from './address.js';
import {
AppendTextCommand,
BranchCommand,
ConditionalBranchCommand,
DebugPrintCommand,
DeleteCommand,
DeleteLineCommand,
ExchangeCommand,
GetAppendCommand,
GetCommand,
GroupEndCommand,
GroupStartCommand,
HoldAppendCommand,
HoldCommand,
InsertTextCommand,
LabelCommand,
LineNumberCommand,
PrintCommand,
PrintLineCommand,
QuitCommand,
ReplaceCommand,
SubstituteCommand,
@ -295,7 +290,7 @@ export const parseScript = (script_string, options) => {
let group_depth = 0;
for (const command of commands) {
// 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.
if (command.label && !labels.has(command.label))
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);
return new ReplaceCommand(address_range, func.value);
}
case 'd': {
require_max_address_count(2);
return new DeleteCommand(address_range);
}
case 'd':
case 'D': {
require_max_address_count(2);
return new DeleteLineCommand(address_range);
}
case 'g': {
require_max_address_count(2);
return new GetCommand(address_range);
return new DeleteCommand(address_range, func.$ === 'D');
}
case 'g':
case 'G': {
require_max_address_count(2);
return new GetAppendCommand(address_range);
}
case 'h': {
require_max_address_count(2);
return new HoldCommand(address_range);
return new GetCommand(address_range, func.$ === 'G');
}
case 'h':
case 'H': {
require_max_address_count(2);
return new HoldAppendCommand(address_range);
return new HoldCommand(address_range, func.$ === 'H');
}
case 'i': {
require_max_address_count(1);
@ -403,21 +389,15 @@ export const parseScript = (script_string, options) => {
require_max_address_count(2);
return new DebugPrintCommand(address_range);
}
case 'p': {
require_max_address_count(2);
return new PrintCommand(address_range);
}
case 'p':
case 'P': {
require_max_address_count(2);
return new PrintLineCommand(address_range);
}
case 'q': {
require_max_address_count(1);
return new QuitCommand(address_range, false);
return new PrintCommand(address_range, func.$ === 'P');
}
case 'q':
case 'Q': {
require_max_address_count(1);
return new QuitCommand(address_range, true);
return new QuitCommand(address_range, func.$ === 'Q');
}
case 's': {
require_max_address_count(2);
@ -427,7 +407,7 @@ export const parseScript = (script_string, options) => {
case 't':
case 'T': {
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': {
require_max_address_count(2);