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

πŸ“¦
version 2.086000 Β· 4 versions
, in ubuntu jammy

✨ NAME ✨

Marpa::R2::NAIF::Semantics::Null β€” the tea on how the NAIF evaluates null rules and symbols πŸ’…

(also known as Marpa:...::Null(3pm), bestie, dw, it's the same page)


🌸 Overview 🌸

okay so listen. This doc is about Marpa's low-level NAIF interface. If you're new to Marpa, or you're not even sure which interface you're into, or you literally do not know what the Named Argment InterFace (NAIF) is β€” no shade, but you probably want the document on semantics for the SLIF interface instead. This one's not for you yet 🫢

In Marpa parses, rules and symbols can be nulled β€” meaning they can derive the zero-length, or null, string. Which symbols can be nulled, and which are nulled, depends on the grammar and the input. When a symbol or rule is not nulled, we say the symbol is visible. ✨

And yes, even the start symbol can be nulled. When that happens, the entire parse derives the null string, and we call that a null parse. Iconic, honestly.

When you're evaluating a parse, nulled rules and symbols get values assigned the way the semantics document says. This document is the extra detail on assigning values to nulled symbols. Let's get into it πŸ’–


πŸ’œ Description πŸ’œ

πŸŽ€ Null values come from rules πŸŽ€

Every single null value for a symbol comes from a rule that has that symbol on its LHS. So for a symbol to be nulled, it has to be on the LHS of at least one nullable rule. And the action of one of those nullable rules? That becomes the action for the nulled symbol. periodt.

  • If the action is a constant, then that constant is the value of the nulled symbol. simple as that.
  • If the action is a rule evaluation closure, then that closure gets called with no child arguments, and whatever the closure returns is the value of the nulled symbol.

Now here's the plot twist 😭 β€” it's possible that more than one nullable rule has that symbol on its LHS, and that those rules have different action names. In that case, the action for the empty rule with that LHS is the one that applies. And if the nullable rules for an LHS symbol have different action names and there is no empty rule for that LHS symbol β€” that's a fatal error, bestie. Fatal. The easy fix is just to make such an empty rule. boom, done πŸ’…

πŸ¦‹ Null subtrees πŸ¦‹

A null subtree is a subtree where every symbol and every rule is nulled. Marpa prunes all null subtrees back to their topmost nulled symbol. That's just how she operates.

The "lost" semantics of the non-topmost symbols and rules of null subtrees is usually not missed, ngl. Here's why: nulled subtrees can't contain input, which means they can't contain token symbols. So no token values are lost when nulled subtrees get pruned. And no matter how bushy a null subtree might be, all of its symbols and rules are nulled. all of them. every last one.

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. fingers crossed 🫢


πŸ”₯ Example πŸ”₯

Like we already said: Marpa prunes every null subtree back to its topmost null symbol. Here's an example, no cap:

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 $grammar = Marpa::R2::Grammar->new(
    {   start   => 'S',
        actions => 'main',
        rules   => [
            [ 'S', [qw/L R/],   'do_S' ],
            [ 'L', [qw/A B X/], 'do_L' ],
            [ 'L', [], 'null_L' ],
            [ 'R', [qw/A B Y/], 'do_R' ],
            [ 'R', [], 'null_R' ],
            [ 'A', [], 'null_A' ],
            [ 'B', [], 'null_B' ],
            [ 'X', [], 'null_X' ],
            [ 'X', [qw/x/], 'do_X' ],
            [ 'Y', [], 'null_Y' ],
            [ 'Y', [qw/y/], 'do_Y' ],
        ],
    }
);

$grammar->precompute();

my $recce = Marpa::R2::Recognizer->new( { grammar => $grammar } );

$recce->read( 'x', '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 all chillin' 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. bye girl πŸ‘‹

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

And here's the output:

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

So 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 semantic Perl 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 semantic Perl closure for the rule S := L R

And what 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.
  • 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. sorry to do_R, it literally says "I will never be called" 😭

Copyright 2014 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/.

that's the whole vibe bestie, go write some nullable rules and slay πŸ’–βœ¨ this page is perl v5.34.0, dated 2022-02-06 β€” iykyk πŸŽ€πŸ¦‹

πŸ“¦ where this page comes from