Using Parse::Lex, is there a way to return tokens only in certain states/conditions - perl

Assuming that i need to tokenize and parse only multiline comments, how will i do that using Parse::Lex. When using flex-bison, the default action for any pattern in the rules section of the lex file used to be 'skip'.
%%
.* ;
%%
How to do this here ?
[EDIT] Well, i tried that, i'm still missing something - here is my code - and result. Where have i gone wrong ??
my simplified lex file:
use Parse::Lex;
use Regexp::Common;
use YParser;
my $lexer;
my #token = (
qw|esp:TA abcdefgh|,
qw(esp:REST .|\n),
);
Parse::Lex->trace;
Parse::Lex->exclusive('esp');
$lexer = Parse::Lex->new(#token);
$lexer->from(\*STDIN);
$lexer->skip(qr! [ \t]+ | $RE{balanced}{-begin=>'/*'}{-end=>'*/'} !xms);
$lexer->start('esp');
my $j = YParser->new();
$j->YYParse(yylex => \&lex);
sub lex {
my $token = $lexer->next;
return ('', undef) if $lexer->eoi;
if ($token->name eq 'TA' || $token->name eq 'REST') {
return ($token->name, {LINENO => $lexer->line, TEXT => $token->text});
}
}
my simplified grammar file
% token TA REST
%%
Program: Element
| Program Element
;
Element: TA
| REST
;
%%
Input file:
abcdefgh
/*sdf*/
Result:
perl lexfile.pl < inputfile
Trace is ON in class Parse::Lex
Can't call method "name" on an undefined value at qnlex.pl line 26, <STDIN> line 1.

Use the skip setting, shown here using Regexp::Common to help construct a regexp matching balanced pairs of comment delimiters. I've assumed /* */ as the comment delimiters, but they could be anything.
$lexer->skip(qr! [ \t]+ | $RE{balanced}{-begin=>'/*'}{-end=>'*/'} !xms);
The [ \t]+ alternative is left in place since that's the default.

Well, i figured this out :) Very simple - all i have to do is make the lex get the next token when encountering tokens i want to skip. Below is code to skip passing the token 'REST' to the parser.
sub lex {
my $token;
NEXTTOKEN:
$token = $lexer->next;
return ('', undef) if $lexer->eoi;
if ($token->name eq 'TA') {
return ($token->name, {LINENO => $lexer->line, TEXT => $token->text});
}
elsif ($token->name eq 'REST') {
goto NEXTTOKEN;
}
}

Related

How can I omit the headers for a route when using Mojo?

Mojo seems to want add headers to the response. Is there any method to suppress headers given a context object?
$r->get('/')->to( cb => sub {
my $c = shift;
# No headers for this response
} );
In my case, I was using Mojo::Server::CGI. You can see the problems on line 29 and on line 35
return undef if $self->nph && !_write($res, 'get_start_line_chunk');
...
return undef unless _write($res, 'get_header_chunk');
You can get around this by mucking with the internals,
$c->res->content->_headers->{header_buffer} = '';
$c->res->{start_buffer} = '';
But an even better way to is detect if anything has been written to STDOUT and to suppress the whole request if so,
# We withhold headers if anything has written to
# STDOUT. This is neccessary because some scripts, in-transition
# to Mojo will still use `print`, and output headers
if ( tell(*STDOUT) != 0 ) {
return undef;
}
That what I did anyway when I published Mojo::Server::CGI::LegacyMigrate

Why Laravel Request object is replacing spaces with underscores on my form names?

I have a Form posting variables containing spaces in their names
e.g.
I perform my ajax request and i can see in chrome inspector that name is correctly passed "with blank space)
In my api.php:
Route::post('/user', 'UserController#get');
UserController
function get(Request $request)
{
dd($request->input('Name Surname')); //display null
dd($request->all()); //I notice the key's changed to Name_Surname
}
Taken that I can't change the names because they have to contain spaces (bad practice? ok but it has to be like that):
how can I avoid spaces to be replaced?
(maybe without to have to manipulate the request->all() returned array keys by hand....)
Short answer I don't believe there to be such a way.
You can map the response with a bit of string replace though:
$data = $request->all()->mapWithKeys(function($item, $key) {
return [str_replace("_", " ", $key) => $item];
});
If it's something you want to apply across the board, you could possible rig up some middleware to apply it to all requests.
If previous answer not work for you, try this:
$data = collect($request->all())->mapWithKeys(function($item, $key) {
return [str_replace("_", " ", $key) => $item];
})->toArray();
You may also normalize the Input Name if it is known...
$field_name = 'FIELD NAME WITH SPACES';
$value = request( str_replace( ' ', '_', $field_name ) );

Send request parameters when calling a PHP script via command line

When you run a PHP script through a browser it looks something like
http://somewebsite.com/yourscript?param1=val1&param2=val2.
I am trying to achieve the same thing via command line without having to rewrite the script to accept argv instead of $_REQUEST. Is there a way to do something like this:
php yourscript.php?param1=val1&param2=val2
such that the parameters you send show up in the $_REQUEST variable?
In case you don't want to modify running script, you can specify parameters using in -B parameter to specify code to run before the input file. But in this case you must also add -F tag to specify your input file:
php -B "\$_REQUEST = array('param1' => 'val1', 'param2' => 'val2');" -F yourscript.php
I can't take credit for this but I adopted this in my bootstrap file:
// Concatenate and parse string into $_REQUEST
if (php_sapi_name() === 'cli') {
parse_str(implode('&', array_slice($argv, 1)), $_REQUEST);
}
Upon executing a PHP file from the command line:
php yourscript.php param1=val1 param2=val2
The above will insert the keys and values into $_REQUEST for later retrieval.
No, there is no easy way to achieve that. The web server will split up the request string and pass it into the PHP interpreter, who will then store it in the $_REQUEST array.
If you run from the command line and you want to accept similar parameters, you'll have to parse them yourself. The command line has completely different syntax for passing parameters than HTTP has. You might want to look into getopt.
For a brute force approach that doesn't take user error into account, you can try this snippet:
<?php
foreach( $argv as $argument ) {
if( $argument == $argv[ 0 ] ) continue;
$pair = explode( "=", $argument );
$variableName = substr( $pair[ 0 ], 2 );
$variableValue = $pair[ 1 ];
echo $variableName . " = " . $variableValue . "\n";
// Optionally store the variable in $_REQUEST
$_REQUEST[ $variableName ] = $variableValue;
}
Use it like this:
$ php test.php --param1=val1 --param2=val2
param1 = val1
param2 = val2
I wrote a short function to handle this situation -- if command line arguments are present and the $_REQUEST array is empty (ie, when you're running a script from the command line instead of though a web interface), it looks for command line arguments in key=value pairs,
Argv2Request($argv);
print_r($_REQUEST);
function Argv2Request($argv) {
/*
When $_REQUEST is empty and $argv is defined,
interpret $argv[1]...$argv[n] as key => value pairs
and load them into the $_REQUEST array
This allows the php command line to subsitute for GET/POST values, e.g.
php script.php animal=fish color=red number=1 has_car=true has_star=false
*/
if ($argv !== NULL && sizeof($_REQUEST) == 0) {
$argv0 = array_shift($argv); // first arg is different and is not needed
foreach ($argv as $pair) {
list ($k, $v) = split("=", $pair);
$_REQUEST[$k] = $v;
}
}
}
The sample input suggested in the function's comment is:
php script.php animal=fish color=red number=1 has_car=true has_star=false
which yields the output:
Array
(
[animal] => fish
[color] => red
[number] => 1
[has_car] => true
[has_star] => false
)

Perl 'if' statement

Right now, I have the following Perl code
my $tmpl1="download1_video.html"
if $file->{file_name}=~/\.(avi|divx|mkv|flv|mp4|wmv)$/i;
$tmpl1||="download1.html";
so it's checking to see if the file is a video, and if so it directs it to the certain page. Although I'm just wondering how I can add another if statement in there to check if the extension is .mp3, and if so direct it to download1_audio.html.
if ( $file->{file_name} =~ m/\.(avi|divx|mkv|flv|mp4|wmv)$/i ){
## Download video
}
elsif($file->{file_name} =~ m/\.(mp3)$/i){
## Download Audio
}
Is this what you needed ?
if ($file->{file_name} =~ /\.(avi|divx|mkv|flv|mp4|mp3|wmv)$/i )
{
if ($1 eq "mp3")
{
# mp3 stuff
}
elsif ($1 eq "mp4")
{
# mp4 stuff
}
else
{
# all other file types
}
}
else
{
# It didn't match
}
A fancier way would be to create a hash keyed by your file types in advance with the info you needed for your next page; the filename I guess?
my %pageHash = ( "mp3" => "mp3Page.html", "divx" => "divxPage.html", ... );
...
$file->{file_name} =~ /\.(.*)$/i;
if (exists $pageHash{$1})
{
$page = $pageHash{$1};
}
else
{
# unknown file extension
}
Having just been burnt by this, I must advise you against declaring a variable with a conditional modifier. If the condition does not hold true, it runs no part of the other clause, which means that you are not declaring $tmpl1, but since it's already passed strict, it allows you to assign to an undefined position in memory.
There is a safer way to do what your predecessor is doing here, that can yet illustrate a solution.
my $tmpl1
= $file->{file_name} =~ /\.(avi|divx|mkv|flv|mp4|wmv)$/i
? 'download1_video.html'
: $file->{file_name} =~ m/\.mp3$/i
? 'download1_audio.html'
: 'download1.html'
;
Thus,
$tmpl1 is always declared
$tmpl1 is always assigned a value

check field formmail

i am trying to change this:
foreach $require (#Required) {
# If the required field is the email field, the syntax of the email #
# address if checked to make sure it passes a valid syntax. #
if ($require eq 'email' && !&check_email($Config{$require})) {
push(#error,$require);
}
//////////////////////////////////////////////////////////////////////////////////
sub check_email {
# Initialize local email variable with input to subroutine. #
$email = $_[0];
# If the e-mail address contains: #
if ($email =~ /(#.*#)|(\.\.)|(#\.)|(\.#)|(^\.)/ ||
# the e-mail address contains an invalid syntax. Or, if the #
# syntax does not match the following regular expression pattern #
# it fails basic syntax verification. #
$email !~ /^.+\#(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z0-9]+)(\]?)$/) {
# Basic syntax requires: one or more characters before the # sign, #
# followed by an optional '[', then any number of letters, numbers, #
# dashes or periods (valid domain/IP characters) ending in a period #
# and then 2 or 3 letters (for domain suffixes) or 1 to 3 numbers #
# (for IP addresses). An ending bracket is also allowed as it is #
# valid syntax to have an email address like: user#[255.255.255.0] #
# Return a false value, since the e-mail address did not pass valid #
# syntax. #
return 0;
}
else {
# Return a true value, e-mail verification passed. #
return 1;
}
}
into this:
foreach $require (#Required) {
if ($require eq 'fieldb' && !&check_fieldb($Config{$require})) {
push(#error,$require);
}
///////////////////////////////////////////////////////////////////////////////
sub check_fieldb {
# If field b is under 20% of field a: #
if ($fieldb <=($fielda/100)*20 ) {
# Return a false value, since field b is less than 20% of field a
return 0;
}
else {
# Return a true value, fieldb verification passed. #
return 1;
}
}
but it does not work, always returns as 0.
how would i fix this?
It's impossible to be sure what's wrong without knowing the values of $fielda and $fieldb. My diagnosis is that $fieldb is less than or equal to ($fielda/100)*20
You pass a value to check_fieldb, but you never use it. Why do you pass it? As a commenter noted you should be passing to the function the values you want to check. Are $fielda and $fieldb guaranteed to be correctly initialized before check_fieldb is called?
Do you meant to be saying
foreach my $require (#Required){
if($require eq 'fieldb' && !check_fieldb($value_of_fielda, $value_of_fieldb)){
push(#error, $require);
}
}
# ... later ...
sub check_fieldb($$){
my $fielda = shift;
my $fieldb = shift;
return !($fieldb <=($fielda/100)*20);
}
perhaps?