tabatkins / railroad-diagrams

:steam_locomotive: A small JS+SVG library for drawing railroad syntax diagrams, like on JSON.org. Now with a Python port!
MIT License
1.66k stars 153 forks source link

unexpected behaviour using Stack() #93

Closed njamescouk closed 2 years ago

njamescouk commented 2 years ago

Whilst messing about with railroad-diagrams I ended doing a diagram essentially for the regex (A B? C?)+ When the components of the regex are Stack()ed everything seems fine, when not stacked C is missing. The regex (A B? C?) renders as I would expect in both cases. HTML illustrating this below:

<!DOCTYPE html>
<style>
body {
    background-color: hsl(30,20%, 95%);
}
h1 {
    font-family: sans-serif;
    font-size: 1em;
}
script {
    display: none;
    white-space: pre;
    margin-top: -1em;
}
</style>
<link rel='stylesheet' href='railroad-diagrams.css'>
<script src='railroad-diagrams.js'></script>
<title>bug?</title><body>
<h2><pre>(A B? C?)+</pre></h2>

<p>stacked </p>
<script>
Diagram(
    OneOrMore
    (
        Stack
        (
            NonTerminal('A'),
            Optional
            (
                NonTerminal('B')
            ), 
            Optional
            (
                NonTerminal('C')
            )
        )
    )
).addTo();
</script>

<p>not stacked. huh?</p>
<script>
Diagram(
    OneOrMore
    (
        NonTerminal('A'),
        Optional
        (
            NonTerminal('B')
        ), 
        Optional
        (
            NonTerminal('C')
        )
    )
).addTo();
</script>

<hr>

<h2><pre>A B? C?</pre></h2>

<p>stacked </p>
<script>
Diagram(
    NonTerminal('A'),
    Optional
    (
        NonTerminal('B')
    ), 
    Optional
    (
        NonTerminal('C')
    )
).addTo();
</script>

<p>not stacked. fine.</p>
<script>
Diagram(
    Stack(
        NonTerminal('A'),
        Optional
        (
            NonTerminal('B')
        ), 
        Optional
        (
            NonTerminal('C')
        )
    )
).addTo();
</script>

</body>
</html>

Please advise.

tabatkins commented 2 years ago

That's because OneOrMore doesn't take N children, it takes one or two, with the second being the optional separator between each repetition. If you want them to be lined up horizontally you need to wrap them in an explicit Sequence there so they collectively form a single object to pass as the first argument.

(Note that your non-stacked example does not illustrate (A B?)+, but rather A (B? A)+, as expected if the B? was the separator between repetitions of A.)

njamescouk commented 2 years ago

Thanks for the clarification, will revisit my problem.