Coder Social home page Coder Social logo

jo-37 / debug-filter-printexpr Goto Github PK

View Code? Open in Web Editor NEW
0.0 2.0 1.0 101 KB

turn perl comments into debug print statements

Perl 100.00%
perl debugging comments filter print-statements expression value line-numbers context scalar list hash array string integer floating-point evaluation

debug-filter-printexpr's Introduction

NAME

Debug::Filter::PrintExpr - Convert comment lines to debug print statements

SYNOPSIS

    use Debug::Filter::PrintExpr;

    my $s = 'a scalar';
    my @a = qw(this is an array);
    my %h = (key1 => 'value1', key2 => 'value2', '' => 'empty', undef => undef);
    my $ref = \%h;
    

    #${$s}
    #@{@a}
    #%{ %h }
    #${ calc: @a * 2 }
    #\{$ref}

This produces an output like:

    L13: $s = 'a scalar';
    L14: @a = ('this', 'is', 'an', 'array');
    L15: %h = ('' => 'empty', 'key1' => 'value1', 'key2' => 'value2', 'undef' => undef);
    calc: @a * 2 = 8;
    L17: dump($ref);
         $_[0] = {
                   '' => 'empty',
                   'key1' => 'value1',
                   'key2' => 'value2',
                   'undef' => undef
                 };

DESCRIPTION

The Problem

Providing debug output often results in a couple of print statements that display the value of some expression and some kind of description. When the program development is finished, these statements must be made conditional on some variable or turned into comments.

Often the contents of arrays or hashes need to be presented in a readable way, leading to repeated lines of similar code.

C programmers use the preprocessor to solve this problem. As Perl has it's own filter mechanism for preprocessing, this leads to a similar solution in Perl.

A Solution

The Filter::Simple module by Damian Conway provides a convenient way of implementing Perl filters.

Debug::Filter::PrintExpr makes use of Filter::Simple to transform specially formed comment lines into print statements for various debugging purposes. (Besides, there is Smart::Comments from Damian, that does something very similar but more advanced.)

Just by removing the "use" of Debug::Filter::PrintExpr completely, disabling it partially by

    no Debug::Filter::PrintExpr;

or making the usage conditional (e.g. on environment variable DEBUG) by

    use if $ENV{DEBUG}, 'Debug::Filter::PrintExpr';

all these lines (or a part of them) lose their magic and remain simple comments.

The comment lines to be transformed must follow this format:

# sigil { [label:] [expression] }

or more formally must be matched by the following regexp:

qr{
       ^\h*\#
       (?<type>[%@\$\\"#])
       \{\h*
       (?<label>[[:alpha:]_]\w*:)?
       \h*
       (?<expr>\V+)?
       \}\h*$
}x

where type represents the sigil, label an optional label and expr an optional expression.

If the label is omitted, it defaults to L_n_:, where n is the line number in the program.

The sigil determines the evaluation context for the given expression and the output format of the result:

  • $

    The expression is evaluated in scalar context. Strings are printed inside single quotes, integer and floating point numbers are printed unquoted and dual valued variables are shown in the form dualvar(_numval_, '_stringval_'). Undefined values are represented by the unquoted string undef. Hash and array references are shown in their usual string representation as e.g. ARRAY(0x19830d0) or HASH(0xccba88). Blessed references are shown by the class they are belong to as blessed(class).

  • "

    The expression is evaluated in scalar context as a string.

  • #

    The expression is evaluated in scalar context as a numeric value.

  • @

    The expression is evaluated in list context and the elements of the list are printed like single scalars, separated by commas and gathered in parentheses.

  • %

    The expression is evaluated as a list of key-value pairs and is presented in the form 'key' => value,... inside parentheses. value is formatted like a single scalar.

  • \

    The expression shall evaluate to a list of references. These will be evaluated using Data::Dumper and named like parameters in a subroutine, i.e. $_[_n_].

The usage and difference between #${}, #"{} and ##{} is best described by example:

    my $dt = DateTime->now;
    #${$dt}         # Ln: $dt = blessed(DateTime);
    #"{$dt}         # Ln: $dt = '2019-10-27T15:54:28';

    my $num = ' 42 ';
    #${$num}        # Ln: $num = ' 42 ';
    $num + 0;
    #${$num}        # Ln: $num = dualvar(42, ' 42 ');
    #"{$num}        # Ln: $num = ' 42 ';
    ##{$num}        # Ln: $num = 42;

The forms #${}, #"{}, ##{} and #@{} may be used for any type of expression and inside the #%{} form, arrays are permitted too. With the varibles $s, @a and %h as defined above, it is possible to use:

    #@{scalar_as_array: $s}
    #${array_as_scalar :@a}
    #@{hash_as_array: %h}

and produce these results:

    scalar_as_array: $s = ('this is a scalar');
    array_as_scalar: @a = 4;
    hash_as_array: %h = ('k1', 'v1', 'k2', 'v2');
    

Regular expressions may be evaluated too:

    #@{"a<b>c<d><e>f<g>h" =~ /\w*<(\w+)>/g}

gives:

    Ln: "a<b>c<d><e>f<g>h" =~ /\w*<(\w+)>/g = ('b', 'd', 'e', 'g');

If the expression is omitted, only the label will be printed. The sigil $ should be used in this case.

Requirements for the expression are:

  • It must be a valid Perl expression.
  • In case of the #%{}-form, it must evaluate to a list of pairs, e.g. a hash.

A PrintExpr will be resolved to a block and therefore may be located anywhere in the program where a block is valid. Do not put it in a place, where a block is required (e.g. after a conditional) as this would break the code when running without the filter.

As a code snippet of the form {label: expr} is a valid perl expression and the generated code will result in a braced expression, a simple consistency check can be done by removing hash and sigil from the PrintExpr line: The resulting code must still be valid and should only emit a warning about a useless use of something in void context.

Usage

The use statement for Debug::Filter::PrintExpr may contain following arguments:

  • -debug

    This option causes the resulting source code after comment transformation to be written to STDERR.

Variables

  • $Debug::Filter::PrintExpr::handle

    The filehandle that is referenced by this variable is used for printing the generated output. The default is STDERR and may be changed by the caller.

SEE ALSO

Damian Conway's module Smart::Comments provides something similar and more advanced.

While Smart::Comments has lots of features for visualizing the program flow, this module focuses on data representation. The main requirements for this module were:

  • Always print the source line number or a user provide label.
  • Always print the literal expression along with its evaluation.
  • Give a defined context where the expression is evaluated. Especially provide scalar and list context or perform an iteration over the key-value pairs of a hash. The usage of Data::Dumper was adopted later from Damian's implementation.
  • Trailing whitespace in values should be clearly visible.
  • Distinguish between the numeric and string value of a variable.
  • undefined values should be clearly distinguishable from empty values.

The first three requirements are not met by Smart::Comments as there is an extra effort needed to display a line number, the display of a label and the literal expression are mutual exclusive and a specific context is not enforced by the module.

All in all, the module presented here is not much more than a programming exercise.

Other related modules: Scalar::Util, Data::Dumper

AUTHOR

Jörg Sommrey

LICENCE AND COPYRIGHT

Copyright (c) 2018-2020, Jörg Sommrey. All rights reserved.

This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.

debug-filter-printexpr's People

Contributors

jo-37 avatar manwar avatar

Watchers

 avatar  avatar

Forkers

manwar

debug-filter-printexpr's Issues

Undeclared dependency IO::String

t/nofilter.t fails without IO::String:

Can't locate IO/String.pm in @INC (you may need to install the IO::String module) (@INC contains: /usr/home/cpansand/.cpan/build/2020022721/Debug-Filter-PrintExpr-0.16-0/blib/lib /usr/home/cpansand/.cpan/build/2020022721/Debug-Filter-PrintExpr-0.16-0/blib/arch /home/cpansand/.cpan/build/2020022721/Exporter-Tiny-1.002001-0/blib/arch /home/cpansand/.cpan/build/2020022721/Exporter-Tiny-1.002001-0/blib/lib /home/cpansand/.cpan/build/2020022721/Exporter-Tiny-1.002001-0/blib/arch /home/cpansand/.cpan/build/2020022721/Exporter-Tiny-1.002001-0/blib/lib /usr/perl5.31.8p/lib/site_perl/5.31.8/amd64-freebsd /usr/perl5.31.8p/lib/site_perl/5.31.8 /usr/perl5.31.8p/lib/5.31.8/amd64-freebsd /usr/perl5.31.8p/lib/5.31.8) at t/nofilter.t line 9.
BEGIN failed--compilation aborted at t/nofilter.t line 9.
t/nofilter.t ... 
Dubious, test returned 2 (wstat 512, 0x200)
No subtests run 

BTW, I think that nowadays there's no need to use IO::String --- the normal perl open can do "in memory" files. Only for older perls (5.6 and older?) this is not possible, but your module aims for newer perls anyway.

Undeclared dependency Exporter::Tiny

The test suite fails if Exporter::Tiny is not installed:

Bailout called.  Further testing stopped:  

#   Failed test 'use Debug::Filter::PrintExpr;'
#   at t/00-load.t line 10.
#     Tried to use 'Debug::Filter::PrintExpr'.
#     Error:  Can't locate Exporter/Tiny.pm in @INC (you may need to install the Exporter::Tiny module) (@INC contains: ... ) at /home/cpansand/.cpan/build/2019110420/Debug-Filter-PrintExpr-0.10-Y9lwf5/blib/lib/Debug/Filter/PrintExpr.pm line 6.
# BEGIN failed--compilation aborted at /home/cpansand/.cpan/build/2019110420/Debug-Filter-PrintExpr-0.10-Y9lwf5/blib/lib/Debug/Filter/PrintExpr.pm line 6.
# Compilation failed in require at t/00-load.t line 10.
# BEGIN failed--compilation aborted at t/00-load.t line 10.
Use of uninitialized value $Debug::Filter::PrintExpr::VERSION in concatenation (.) or string at t/00-load.t line 13.
# Looks like your test exited with 255 just after 1.
FAILED--Further testing stopped.
Makefile:1032: recipe for target 'test_dynamic' failed
make: *** [test_dynamic] Error 255

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.