diff --git a/packages/phoenix/src/puter-shell/coreutils/sed/command.js b/packages/phoenix/src/puter-shell/coreutils/sed/command.js index 4a2b4d37..56b8c84c 100644 --- a/packages/phoenix/src/puter-shell/coreutils/sed/command.js +++ b/packages/phoenix/src/puter-shell/coreutils/sed/command.js @@ -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) { diff --git a/packages/phoenix/src/puter-shell/coreutils/sed/parser.js b/packages/phoenix/src/puter-shell/coreutils/sed/parser.js index 086efd47..b1baf5d7 100644 --- a/packages/phoenix/src/puter-shell/coreutils/sed/parser.js +++ b/packages/phoenix/src/puter-shell/coreutils/sed/parser.js @@ -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);