Skip to content

where row conditions without a comparison operator parse as an ERROR node. #53

Description

@maxim-uvarov

please, check below the repro

{nushell: (version).version, topiary: (topiary --version | str trim)} | print $in
# => ╭─────────┬───────────────────╮
# => │ nushell │ 0.114.1           │
# => │ topiary │ topiary-cli 0.7.3 │
# => ╰─────────┴───────────────────╯

# Ask both parsers the same question: nushell runs the snippet, topiary formats it.
def check []: table<case: string, snippet: string> -> table {
    insert nushell {|r| (^nu -n -c $r.snippet | complete).exit_code == 0}
    | insert topiary {|r| ($"($r.snippet)\n" | topiary format --language nu | complete).exit_code == 0}
}

# Valid nushell, rejected by the grammar.
[
    [case snippet];
    ["bare column" "[[foo]; [true] [false]] | where foo"]
    ["cell path" "[[foo]; [{bar: true}]] | where foo.bar"]
    ["optional column" "[[foo]; [true]] | where foo?"]
    ["optional cell path (#177)" "[[a]; [{b: true}]] | where a?.b?"]
    ["negated" "[[foo]; [true] [false]] | where not foo"]
] | check | table --width 100 | print $in
# => ╭───┬───────────────────────────┬─────────────────────────────────────────┬─────────┬─────────╮
# => │ # │           case            │                 snippet                 │ nushell │ topiary │
# => ├───┼───────────────────────────┼─────────────────────────────────────────┼─────────┼─────────┤
# => │ 0 │ bare column               │ [[foo]; [true] [false]] | where foo     │ true    │ false   │
# => │ 1 │ cell path                 │ [[foo]; [{bar: true}]] | where foo.bar  │ true    │ false   │
# => │ 2 │ optional column           │ [[foo]; [true]] | where foo?            │ true    │ false   │
# => │ 3 │ optional cell path (#177) │ [[a]; [{b: true}]] | where a?.b?        │ true    │ false   │
# => │ 4 │ negated                   │ [[foo]; [true] [false]] | where not foo │ true    │ false   │
# => ╰───┴───────────────────────────┴─────────────────────────────────────────┴─────────┴─────────╯

# Accepted shapes, for contrast.
[
    [case snippet];
    ["comparison" "[[foo]; [1] [2]] | where foo == 1"]
    ["parenthesised expression" "[[foo]; [1] [2]] | where ($in.foo > 1)"]
    ["closure" "[[foo]; [true] [false]] | where {|r| $r.foo}"]
] | check | table --width 100 | print $in
# => ╭───┬──────────────────────────┬──────────────────────────────────────────────┬─────────┬─────────╮
# => │ # │           case           │                   snippet                    │ nushell │ topiary │
# => ├───┼──────────────────────────┼──────────────────────────────────────────────┼─────────┼─────────┤
# => │ 0 │ comparison               │ [[foo]; [1] [2]] | where foo == 1            │ true    │ true    │
# => │ 1 │ parenthesised expression │ [[foo]; [1] [2]] | where ($in.foo > 1)       │ true    │ true    │
# => │ 2 │ closure                  │ [[foo]; [true] [false]] | where {|r| $r.foo} │ true    │ true    │
# => ╰───┴──────────────────────────┴──────────────────────────────────────────────┴─────────┴─────────╯

# The diagnostic. A single such line makes the whole file unformattable.
("[[foo]; [true] [false]] | where foo\n" | topiary format --language nu | complete).stderr | print $in
# => [2026-07-24T00:11:16Z ERROR topiary::error]   × Parsing error between line 0, column 0 and line 0, column 35
# =>        ╭─[standard input:1:1]
# =>      1 │ [[foo]; [true] [false]] | where foo
# =>        · ──────────────────┬─────────────────
# =>        ·                   ╰── (ERROR) node
# =>        ╰────
# =>     

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions