Marpa:...::Null(3pm)

πŸ’– Marpa::R2::Semantics::Null πŸ’–

okay bestie, pull up a chair 🫢 this one is the User Contributed Perl Documentation page for Marpa::R2::Semantics::Null, and it's literally all about how the SLIF evaluates null rules and symbols. yes we're getting philosophical about nothing. and yes it slaps. πŸ’…


✨ NAME ✨

Marpa::R2::Semantics::Null β€” how the SLIF evaluates null rules and symbols 🌸


πŸ‘‘ Overview πŸ‘‘

so in Marpa parses, rules and symbols can be nulled β€” which means, no cap, they can derive the zero-length (aka null) string. which symbols can be nulled, or are nulled, depends on the grammar and the input. and when a symbol or rule is not nulled, that symbol is said to be visible. πŸ‘€

and get this β€” even the start symbol can be nulled?? in which case the entire parse derives the null string. a parse where the start symbol is nulled is called a null parse. iconic honestly.

when you're evaluating a parse, nulled rules and symbols get assigned values the way the semantics document says. this doc here is the extra-detail version, specifically about assigning values to nulled symbols. πŸ’œ


πŸ¦‹ Description πŸ¦‹

🌈 Null values come from rules 🌈

nulled subtrees get pruned back to their topmost symbol. lexemes are never nulled, so a nulled symbol is always the LHS of a rule instance, and the action is determined from the rule alternative. periodt.

now here's the plot twist 😭 β€” there's a complication when the symbol appears on the LHS of more than one nullable rule alternative. because the symbol is nulled, the input gives you zero help figuring out which rule alternative to use. so which rule alternative's semantics get used for a nulled symbol is decided like this:

  • if all nullable rule alternatives have the same semantics, that semantics is used. πŸ’…
  • if one of the nullable rule alternatives is empty (as in, has a zero-length RHS), then the empty alternative's semantics are used.
  • in the remaining case, two or more of the rule alternatives have different action names, but none of the alternatives has a zero-length RHS. when that happens, Marpa throws an exception πŸ’€. one easy fix is to add an empty rule with the intended semantics. you're welcome bestie.

and when we're checking whether the semantics of two nullable rule alternatives are "the same", the blessing is taken into account. two rule alternatives are considered to have different semantics if they are blessed differently. no blessing, no matching. 🀝

the "lost" semantics of the non-topmost symbols and rules of null subtrees are usually not missed at all. nulled subtrees can't contain input, so they don't contain token symbols. so no token values are lost when nulled subtrees get pruned. πŸŽ€ and as bushy as a null subtree might be, all of its symbols and rules are nulled.

since nulled symbols and rules correspond to zero-length strings, we are literally dealing here with the "semantics of nothing". in theory the semantics of nothing can be arbitrarily complex 😭. in practice it should be possible to keep them simple. such a vibe.


🧚 Example 🧚

as already stated, Marpa prunes every null subtree back to its topmost null symbol. here's an example:

    sub do_L {
        shift;
        return 'L(' . ( join q{;}, map { $_ // '[ERROR!]' } @_ ) . ')';
    }

    sub do_R {
        return 'R(): I will never be called';
    }

    sub do_S {
        shift;
        return 'S(' . ( join q{;}, map { $_ // '[ERROR!]' } @_ ) . ')';
    }

    sub do_X { return 'X(' . $_[1] . ')'; }
    sub do_Y { return 'Y(' . $_[1] . ')'; }

    ## no critic (Variables::ProhibitPackageVars)
    our $null_A = 'null A';
    our $null_B = 'null B';
    our $null_L = 'null L';
    our $null_R = 'null R';
    our $null_X = 'null X';
    our $null_Y = 'null Y';
    ## use critic

    my $slg = Marpa::R2::Scanless::G->new(
        {   source => \<<'END_OF_DSL',
    :start ::= S
    S ::= L R     action => do_S
    L ::= A B X   action => do_L
    L ::=         action => null_L
    R ::= A B Y   action => do_R
    R ::=         action => null_R
    A ::=         action => null_A
    B ::=         action => null_B
    X ::=         action => null_X
    X ::= 'x'     action => do_X
    Y ::=         action => null_Y
    Y ::= 'y'     action => do_Y
    END_OF_DSL
        }
    );

    my $slr = Marpa::R2::Scanless::R->new(
        {   grammar           => $slg,
            semantics_package => 'main',
        }
    );

    $slr->read( \'x' );

if we write the unpruned parse tree in pre-order, depth-first, indenting children below their parents, we get something like this:

        0: Visible Rule: S := L R
             1: Visible Rule L := A B X
                 1.1: Nulled Symbol A
                 1.2: Nulled Symbol B
                 1.3: Token, Value is 'x'
             2: Nulled Rule, Rule R := A B Y
                 2.1: Nulled Symbol A
                 2.2: Nulled Symbol B
                 2.3: Nulled Symbol Y

in this example, five symbols and a rule are nulled. the rule and three of the symbols are in a single subtree: 2, 2.1, 2.2 and 2.3. Marpa prunes every null subtree back to its topmost symbol, which in this case is the LHS of the rule numbered 2. βœ‚οΈ

the pruned tree looks like this 🌸

        0: Visible Rule: S := L R
             1: Visible Rule L := A B X
                 1.1: Nulled Symbol A
                 1.2: Nulled Symbol B
                 1.3: Token, Value is 'x'
             2: LHS of Nulled Rule, Symbol R

nulled nodes 1.1, 1.2 and 2 were all kept, because they are topmost in their nulled subtree. all the other nulled nodes were discarded. bye πŸ’…

here is the output:

    S(L(null A;null B;X(x));null R)

in the output we see πŸ‘‡

  • the null value for symbol 1.1: "null A". this comes from the empty rule for "A".
  • the null value for symbol 1.2: "null B". this comes from the empty rule for "B".
  • the token value for symbol 1.3: "x".
  • an application of the rule evaluation closure for the rule "L := A B X".
  • the null value for rule 2: "null R". this comes from the empty rule for "R".
  • an application of the rule evaluation closure for the rule "S := L R"

and we do not see any output for symbols 2.1 ("A"), 2.2 ("B"), or 2.3 ("Y") because they were not topmost in the pruned subtree. 😭 and we do not see an application of the rule evaluation closure for rule "R := A B Y", because there is an empty rule for "R", and that takes priority. it ate, honestly. πŸ”₯


      Copyright 2022 Jeffrey Kegler
      This file is part of Marpa::R2.  Marpa::R2 is free software: you can
      redistribute it and/or modify it under the terms of the GNU Lesser
      General Public License as published by the Free Software Foundation,
      either version 3 of the License, or (at your option) any later version.

      Marpa::R2 is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      Lesser General Public License for more details.

      You should have received a copy of the GNU Lesser
      General Public License along with Marpa::R2.  If not, see
      http://www.gnu.org/licenses/.

okay that's the whole page bestie, you're officially a null-semantics scholar now πŸ‘‘βœ¨ go forth and prune with confidence. mwah 🫢

this little gem was brought to you by perl v5.42.3, dated 2026-08-20 β€” Marpa:...::Null(3pm), user contributed perl documentation, and honestly? it's giving main character energy. πŸ¦‹πŸ’– no cap.

πŸ“¦ where this page comes from