JE::Parser(3pm)

πŸ“¦
version 0.066 Β· 3 versions
, in debian trixie, ubuntu noble, ubuntu resolute, ubuntu stonking

✨ NAME ✨

JE::Parser β€” the whole framework for customising JE's parser πŸ’–

(and yes bestie, this is the JE::Parser(3pm) page from the User Contributed Perl Documentation β€” we love a legend)


🌈 SYNOPSIS 🌈

use JE;
use JE::Parser;

$je = new JE;
$p = new JE::Parser $je; # or: $p = $je->new_parser

$p->delete_statement('for', 'while', 'do'); # disable loops
$p->add_statement(try => \&parser); # replace existing 'try' statement

πŸ’… DESCRIPTION πŸ’…

okay so here's the tea β˜•: this lets you change the list of statement types that the parser goes looking for. Like, say you want a mini-JavaScript with no loops at all β€” slay, do that. Or you wanna add extensions to the language, like the 'catch-if' clause of a try statement. Iconic behaviour, honestly.

as yet, delete_statement fully works, but the API for add_statement is not finished being designed. we're getting there 😭

and maybe one day there'll be an API for extending expressions, if the complications caused by the 'new' operator can ever get resolved. If anyone else wants to have a go at it, be my guest. :-) πŸ’œ


πŸ‘‘ METHODS πŸ‘‘

$p = new JE::Parser

Creates a new parser object. new parser, who dis πŸ’…

$p->add_statement($name, \&parser);

This adds a new statement (source element, to be precise) type to the list of statement types the parser supports. If a statement type called $name already exists, it gets replaced. Otherwise, the new statement type gets added to the top of the list πŸ”

($name ought to be optional; it should only be necessary if one wants to delete it afterwards or rearrange the list.)

If the name of a statement type begins with a hyphen, it is only allowed at the 'program' level, not within compound statements. Function declarations use this. Maybe this convention is too unintuitive.... (Does anyone think I should change it? What should I change it too?)

&parser will need to parse code contained in $_ starting at pos(), then either return an object, list or coderef (see below) and set pos() to the position of the next token[1], or, if it could not parse anything, return undef and reset pos() to its initial value if it changed.

[1] I.e., it is expected to move pos past any trailing whitespace.

The return value of &parser can be one of the following:

  1. An object with an eval method, that will execute the statement, and/or an init method, which will be called before the code runs.
  2. (Not yet supported!) A coderef, which will be called when the code is executed.
  3. (Not yet supported.) A hash-style list, the two keys being eval and init (corresponding to the methods under item 1) and the values being coderefs; i.e.:
( init => \&init_sub, eval => \&eval_sub )

Maybe we need support for a JavaScript function to be called to handnle the statement.

$p->delete_statement(@names);

Deletes the given statement types and returns $p. bye boo πŸ‘‹

$p->statement_list

(Not yet implemented.)

Returns an array ref of the names of the various statement types. You can rearrange this list, but it is up to you to make sure you do not add to it any statement types that have not been added via add_statement (or were not there by default). The statement types in the list will be tried in order, except that items beginning with a hyphen always come before other items.

The default list is qw/-function block empty if while with for switch try labelled var do continue break return throw expr/

$p->parse($code)

Parses the $code and returns a parse tree (JE::Code object). 🌳

$p->eval($code)

Shorthand for $p->parse($code)->execute;


πŸ“¦ EXPORTS πŸ“¦

None by default. You may choose to export the following:

Exported Variables

... blah blah blah ...

Exported Functions

These all have () for their prototype, except for expected which has ($).

... blah blah blah ...


πŸ’€ SYNTAX ERRORS πŸ’€

(To be written)

expected 'aaaa'; # will be changed to 'Expected aaaa but found....'
die \\"You can't put a doodad after a frombiggle!"; # complete message
die 'aoenstuhoeanthu'; # big no-no (the error is propagated)

🌸 EXAMPLES 🌸

Mini JavaScript

This is an example of a mini JavaScript that does not allow loops or the creation of functions. no loops no functions, very demure πŸŽ€

use JE;
$j = new JE;
$p = $j->new_parser;
$p->delete_statement('for','while','do','-function');

Since function expressions could still create functions, we need to remove the Function prototype object. Someone might then try to put it back with Function = parseInt.constructor, so we'll overwrite Function with an undeletable read-only undefined property.

$j->prop({ name     => 'Function',
           value    => undef,
           readonly => 1,
           dontdel  => 1 });

Then, after this, we call $p->eval('...') to run JS code.

Perl-style for(LIST) loop

Well, after writing this example, it seems to me this API is not sufficient....

This example doesn't actually work yet. 😭

use JE;
use JE::Parser qw'$s ident expr statement expected';

$j = new JE;
$p = $j->new_parser;
$p->add_statement('for-list',
    sub {
        /\Gfor$s/cog or return;
        my $loopvar = ident or return;
        /\G$s\($s/cog or return;
        my @expressions;
        do {
            # This line doesn't actually work properly because
            # 'expr' will gobble up all the commas
            @expressions == push @expressions, expr
                and return; # If nothing gets pushed on  to  the
                            # list,  we need to give the default
                            # 'for' handler a chance, instead of
                            # throwing an error.
        } while /\G$s,$s/cog;
        my $statement = statement or expected 'statement';
        return bless {
            var => $loopvar,
            expressions => \@expressions,
            statement => $statement
        }, 'Local::JEx::ForList';
    }
);

package Local::JEx::ForList;

sub eval {
    my $self = shift;
    local $JE::Code::scope =
        bless [@$JE::Code::scope], 'JE::Scope';
        # I've got to come up with a better interface than this.
    my $obj = $JE::Code::global->eval('new Object');
    push @$JE::Code::scope, $obj;

    for (@{$self->{expressions}}) {
        $obj->{ $self->{loopvar} } = $_->eval;
        $self->{statement}->execute;
    }
}

πŸ”— SEE ALSO πŸ”—

JE and JE::Code. go read them, they're the moment 🫢


and that's a wrap, bestie!! this was JE::Parser(3pm) from perl v5.36.0, dated 2023-08-25. mwah, take care of yourself, hydrate, and go parse something iconic βœ¨πŸ’–πŸŽ€

πŸ“¦ where this page comes from