.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.43) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "JE::Parser 3pm" .TH JE::Parser 3pm "2023-08-25" "perl v5.36.0" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" JE::Parser \- Framework for customising JE's parser .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 2 \& use JE; \& use JE::Parser; \& \& $je = new JE; \& $p = new JE::Parser $je; # or: $p = $je\->new_parser \& \& $p\->delete_statement(\*(Aqfor\*(Aq, \*(Aqwhile\*(Aq, \*(Aqdo\*(Aq); # disable loops \& $p\->add_statement(try => \e&parser); # replace existing \*(Aqtry\*(Aq statement .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" This allows one to change the list of statement types that the parser looks for. For instance, one could disable loops for a mini-JavaScript, or add extensions to the language, such as the 'catch\-if' clause of a \f(CW\*(C`try\*(C'\fR statement. .PP As yet, \f(CW\*(C`delete_statement\*(C'\fR works, but I've not finished designing the \s-1API\s0 for \f(CW\*(C`add_statement\*(C'\fR. .PP I might provide an \s-1API\s0 for extending expressions, if I can resolve the complications caused by the 'new' operator. If anyone else wants to have a go at it, be my guest. :\-) .SH "METHODS" .IX Header "METHODS" .ie n .IP "$p = new JE::Parser" 4 .el .IP "\f(CW$p\fR = new JE::Parser" 4 .IX Item "$p = new JE::Parser" Creates a new parser object. .ie n .IP "$p\->add_statement($name, \e&parser);" 4 .el .IP "\f(CW$p\fR\->add_statement($name, \e&parser);" 4 .IX Item "$p->add_statement($name, &parser);" This adds a new statement (source element, to be precise) type to the list of statements types the parser supports. If a statement type called \&\f(CW$name\fR already exists, it will be replaced. Otherwise, the new statement type will be added to the top of the list. .Sp (\f(CW$name\fR ought to be optional; it should only be necessary if one wants to delete it afterwards or rearrange the list.) .Sp 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?) .Sp \&\f(CW&parser\fR will need to parse code contained in \f(CW$_\fR starting at \f(CW\*(C`pos()\*(C'\fR, then either return an object, list or coderef (see below) and set \f(CW\*(C`pos()\*(C'\fR to the position of the next token[1], or, if it could not parse anything, return undef and reset \f(CW\*(C`pos()\*(C'\fR to its initial value if it changed. .Sp [1] I.e., it is expected to move \f(CW\*(C`pos\*(C'\fR past any trailing whitespace. .Sp The return value of \f(CW&parser\fR can be one of the following: .RS 4 .IP "1)" 4 .IX Item "1)" An object with an \f(CW\*(C`eval\*(C'\fR method, that will execute the statement, and/or an \f(CW\*(C`init\*(C'\fR method, which will be called before the code runs. .IP "2)" 4 .IX Item "2)" \&\fB(Not yet supported!)\fR A coderef, which will be called when the code is executed. .IP "3)" 4 .IX Item "3)" \&\fB(Not yet supported.)\fR A hash-style list, the two keys being \f(CW\*(C`eval\*(C'\fR and \f(CW\*(C`init\*(C'\fR (corresponding to the methods under item 1) and the values being coderefs; i.e.: .Sp .Vb 1 \& ( init => \e&init_sub, eval => \e&eval_sub ) .Ve .RE .RS 4 .Sp Maybe we need support for a JavaScript function to be called to handnle the statement. .RE .ie n .IP "$p\->delete_statement(@names);" 4 .el .IP "\f(CW$p\fR\->delete_statement(@names);" 4 .IX Item "$p->delete_statement(@names);" Deletes the given statement types and returns \f(CW$p\fR. .ie n .IP "$p\->statement_list" 4 .el .IP "\f(CW$p\fR\->statement_list" 4 .IX Item "$p->statement_list" \&\fB(Not yet implemented.)\fR .Sp 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 \f(CW\*(C`add_statement\*(C'\fR (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. .Sp The default list is \f(CW\*(C`qw/\-function block empty if while with for switch try labelled var do continue break return throw expr/\*(C'\fR .ie n .IP "$p\->parse($code)" 4 .el .IP "\f(CW$p\fR\->parse($code)" 4 .IX Item "$p->parse($code)" Parses the \f(CW$code\fR and returns a parse tree (JE::Code object). .ie n .IP "$p\->eval($code)" 4 .el .IP "\f(CW$p\fR\->eval($code)" 4 .IX Item "$p->eval($code)" Shorthand for \f(CW$p\fR\->parse($code)\->execute; .SH "EXPORTS" .IX Header "EXPORTS" None by default. You may choose to export the following: .SS "Exported Variables" .IX Subsection "Exported Variables" \&... blah blah blah ... .SS "Exported Functions" .IX Subsection "Exported Functions" These all have \f(CW\*(C`()\*(C'\fR for their prototype, except for \f(CW\*(C`expected\*(C'\fR which has \&\f(CW\*(C`($)\*(C'\fR. .PP \&... blah blah blah ... .SH "SYNTAX ERRORS" .IX Header "SYNTAX ERRORS" (To be written) .PP .Vb 3 \& expected \*(Aqaaaa\*(Aq; # will be changed to \*(AqExpected aaaa but found....\*(Aq \& die \e\e"You can\*(Aqt put a doodad after a frombiggle!"; # complete message \& die \*(Aqaoenstuhoeanthu\*(Aq; # big no\-no (the error is propagated) .Ve .SH "EXAMPLES" .IX Header "EXAMPLES" .SS "Mini JavaScript" .IX Subsection "Mini JavaScript" This is an example of a mini JavaScript that does not allow loops or the creation of functions. .PP .Vb 4 \& use JE; \& $j = new JE; \& $p = $j\->new_parser; \& $p\->delete_statement(\*(Aqfor\*(Aq,\*(Aqwhile\*(Aq,\*(Aqdo\*(Aq,\*(Aq\-function\*(Aq); .Ve .PP Since function expressions could still create functions, we need to remove the Function prototype object. Someone might then try to put it back with \&\f(CW\*(C`Function = parseInt.constructor\*(C'\fR, so we'll overwrite Function with an undeletable read-only undefined property. .PP .Vb 4 \& $j\->prop({ name => \*(AqFunction\*(Aq, \& value => undef, \& readonly => 1, \& dontdel => 1 }); .Ve .PP Then, after this, we call \f(CW\*(C`$p\->eval(\*(Aq...\*(Aq)\*(C'\fR to run \s-1JS\s0 code. .SS "Perl-style for(\s-1LIST\s0) loop" .IX Subsection "Perl-style for(LIST) loop" Well, after writing this example, it seems to me this \s-1API\s0 is not sufficient.... .PP This example doesn't actually work yet. .PP .Vb 2 \& use JE; \& use JE::Parser qw\*(Aq$s ident expr statement expected\*(Aq; \& \& $j = new JE; \& $p = $j\->new_parser; \& $p\->add_statement(\*(Aqfor\-list\*(Aq, \& sub { \& /\eGfor$s/cog or return; \& my $loopvar = ident or return; \& /\eG$s\e($s/cog or return; \& my @expressions; \& do { \& # This line doesn\*(Aqt actually work properly because \& # \*(Aqexpr\*(Aq 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 \& # \*(Aqfor\*(Aq handler a chance, instead of \& # throwing an error. \& } while /\eG$s,$s/cog; \& my $statement = statement or expected \*(Aqstatement\*(Aq; \& return bless { \& var => $loopvar, \& expressions => \e@expressions, \& statement => $statement \& }, \*(AqLocal::JEx::ForList\*(Aq; \& } \& ); \& \& package Local::JEx::ForList; \& \& sub eval { \& my $self = shift; \& local $JE::Code::scope = \& bless [@$JE::Code::scope], \*(AqJE::Scope\*(Aq; \& # I\*(Aqve got to come up with a better interface than this. \& my $obj = $JE::Code::global\->eval(\*(Aqnew Object\*(Aq); \& push @$JE::Code::scope, $obj; \& \& for (@{$self\->{expressions}}) { \& $obj\->{ $self\->{loopvar} } = $_\->eval; \& $self\->{statement}\->execute; \& } \& } .Ve .SH "SEE ALSO" .IX Header "SEE ALSO" \&\s-1JE\s0 and JE::Code.