Skip to content

Command substitutions inside arithmetic are not represented structurally in the AST #1

@jdiamond

Description

@jdiamond

Hi, thank you so much for this project! While using it, I've run into a small limitation.

unbash parses command substitutions as structured CommandExpansion nodes in normal shell-word contexts, but inside arithmetic they are currently flattened into opaque ArithmeticWord nodes.

Problem

In arithmetic contexts such as:

  • $(( $(cmd) + 1 ))
  • (( $(cmd) + 1 ))

the embedded $(cmd) is preserved only as raw text inside ArithmeticWord.value, rather than being represented structurally in the arithmetic AST.

Repro

Arithmetic expansion

import { parse } from "unbash";

const ast = parse("echo $(( $(rm -rf /) + 1 ))");
console.log(JSON.stringify(ast, null, 2));

The arithmetic part is currently shaped like this:

{
  "type": "ArithmeticExpansion",
  "text": "$(( $(rm -rf /) + 1 ))",
  "expression": {
    "type": "ArithmeticBinary",
    "operator": "+",
    "left": {
      "type": "ArithmeticWord",
      "value": "$(rm -rf /)"
    },
    "right": {
      "type": "ArithmeticWord",
      "value": "1"
    }
  }
}

Arithmetic command

import { parse } from "unbash";

const ast = parse("(( $(rm -rf /) + 1 ))");
const cmd = ast.commands[0].command;
console.log(cmd.body);
console.log(cmd.expression);

Current behavior:

  • top-level node is ArithmeticCommand
  • body is " $(rm -rf /) + 1 "
  • expression is an ArithmeticBinary
  • expression.left is an ArithmeticWord with value: "$(rm -rf /)"

Why this matters

This makes arithmetic inconsistent with normal shell-word parsing, where command substitutions are exposed structurally and can be recursively analyzed.

For downstream consumers that walk the AST to inspect nested commands, arithmetic currently creates a blind spot and forces substring/reparse workarounds.

Expected behavior

Command substitutions inside arithmetic should ideally be represented structurally in the AST, rather than flattened into ArithmeticWord.

From reading the source, this appears to come from src/arithmetic.ts, where readDollarAtom() currently treats $(...) as an ArithmeticWord.

I’d be happy to help with a PR if this sounds like a change you’d be open to.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions