Initial commit 0.001
authorMarius Gavrilescu <marius@ieval.ro>
Sat, 31 Mar 2018 19:38:24 +0000 (22:38 +0300)
committerMarius Gavrilescu <marius@ieval.ro>
Sat, 31 Mar 2018 19:38:24 +0000 (22:38 +0300)
Changes [new file with mode: 0644]
MANIFEST [new file with mode: 0644]
Makefile.PL [new file with mode: 0644]
README [new file with mode: 0644]
lib/Data/Dump/Sexp.pm [new file with mode: 0644]
t/Data-Dump-Sexp.t [new file with mode: 0644]

diff --git a/Changes b/Changes
new file mode 100644 (file)
index 0000000..47bd13d
--- /dev/null
+++ b/Changes
@@ -0,0 +1,4 @@
+Revision history for Perl extension Data::Dump::Sexp.
+
+0.001 2018-03-31T22:38+03:00
+ - Initial release
diff --git a/MANIFEST b/MANIFEST
new file mode 100644 (file)
index 0000000..6ffc16d
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,6 @@
+Changes
+Makefile.PL
+MANIFEST
+README
+t/Data-Dump-Sexp.t
+lib/Data/Dump/Sexp.pm
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644 (file)
index 0000000..83ebfa4
--- /dev/null
@@ -0,0 +1,22 @@
+use strict;
+use warnings;
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+       NAME              => 'Data::Dump::Sexp',
+       VERSION_FROM      => 'lib/Data/Dump/Sexp.pm',
+       ABSTRACT_FROM     => 'lib/Data/Dump/Sexp.pm',
+       AUTHOR            => 'Marius Gavrilescu <marius@ieval.ro>',
+       MIN_PERL_VERSION  => '5.14.0',
+       LICENSE           => 'perl',
+       SIGN              => 1,
+       PREREQ_PM         => {
+               qw/Data::SExpression 0.41/,
+       },
+       META_ADD          => {
+               dynamic_config => 0,
+               resources      => {
+                       repository   => 'https://git.ieval.ro/?p=data-dump-sexp.git',
+               },
+       }
+);
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..d08dd76
--- /dev/null
+++ b/README
@@ -0,0 +1,59 @@
+Data-Dump-Sexp version 0.001
+============================
+
+Data::Dump::Sexp converts Perl structures to S-expressions.
+
+The conversion rules are as follows:
+
+1. A blessed object with a to_sexp method is replaced with the result
+   of calling the method, and this procedure is restarted.
+
+2. An instance of Data::SExpression::Symbol is converted to a symbol.
+
+3. An instance of Data::SExpression::Cons is converted to a cons cell
+   (like (A . B)), a proper list (like (A B C)) or an improper list
+   (like (A B . C)), where A, B, C are S-expressions.
+
+4. undef is converted to the empty list.
+
+5. A defined scalar that looks like a number is left as-is.
+
+6. A defined scalar that does not look like a number is surrounded by
+   double quotes after any backslashes and double quote characters are
+   escaped with a backslash.
+
+7. An arrayref is converted to a proper list.
+
+8. A hashref is converted to an alist, which is a proper list of cons
+   cells (like ((A . B) (C . D) (E . F))).
+
+9. A scalarref or a reference to another ref is dereferenced and this
+   procedure is restarted.
+
+10. Anything else (regexp, filehandle, format, glob, version string)
+    causes an exception to be raised
+
+INSTALLATION
+
+To install this module type the following:
+
+   perl Makefile.PL
+   make
+   make test
+   make install
+
+DEPENDENCIES
+
+This module requires these other modules and libraries:
+
+* Data::SExpression
+
+COPYRIGHT AND LICENCE
+
+Copyright (C) 2018 by Marius Gavrilescu
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself, either Perl version 5.24.1 or,
+at your option, any later version of Perl 5 you may have available.
+
+
diff --git a/lib/Data/Dump/Sexp.pm b/lib/Data/Dump/Sexp.pm
new file mode 100644 (file)
index 0000000..ad2045c
--- /dev/null
@@ -0,0 +1,185 @@
+package Data::Dump::Sexp;
+
+use 5.014000;
+use strict;
+use warnings;
+use parent qw/Exporter/;
+
+our @EXPORT = qw/dump_sexp/;
+our @EXPORT_OK = @EXPORT;
+
+our $VERSION = '0.001';
+
+use Carp qw/croak/;
+use Data::SExpression;
+use Scalar::Util qw/reftype looks_like_number/;
+
+sub dump_sexp;
+
+sub dump_scalar {
+       my ($expr) = @_;
+       if (!defined $expr) {
+               "()"
+       } elsif (looks_like_number $expr) {
+               "$expr"
+       } else {
+               my $escaped = $expr;
+               $escaped =~ s,\\,\\\\,g;
+               $escaped =~ s,",\\",g;
+               qq,"$escaped",
+       }
+}
+
+sub dump_cons {
+       my ($expr) = @_;
+       my $cdr = $expr->cdr;
+       my $car = $expr->car;
+       my $acc = '(' . dump_sexp($car);
+       while (eval { $cdr->isa('Data::SExpression::Cons') }) {
+               $car = $cdr->car;
+               $cdr = $cdr->cdr;
+               $acc .= ' ' . dump_sexp($car);
+       }
+       if (defined $cdr) {
+               $acc .= ' . ' . dump_sexp($cdr);
+       }
+       $acc . ')'
+}
+
+sub dump_array {
+       my ($expr) = @_;
+       '(' . join (' ', map { dump_sexp($_) } @$expr). ')'
+}
+
+sub dump_hash {
+       my ($expr) = @_;
+       my @alist = map { Data::SExpression::cons $_, $expr->{$_} } sort keys %$expr;
+       dump_array \@alist
+}
+
+
+sub dump_sexp {
+       my ($expr) = @_;
+       my $type = reftype $expr;
+       if (eval { $expr->can('to_sexp') }) {
+               dump_sexp $expr->to_sexp
+       } elsif (eval { $expr->isa('Data::SExpression::Symbol') }) {
+               "$expr"
+       } elsif (eval { $expr->isa('Data::SExpression::Cons') }) {
+               dump_cons $expr
+       } elsif (!defined $type) {
+               dump_scalar $expr
+       } elsif ($type eq 'ARRAY') {
+               dump_array $expr
+       } elsif ($type eq 'HASH') {
+               dump_hash $expr
+       } elsif ($type eq 'SCALAR' || $type eq 'REF' || $type eq 'LVALUE') {
+               dump_sexp $$expr
+       } else {
+               croak "Cannot dump value of type $type as sexp"
+       }
+}
+
+1;
+__END__
+
+=encoding utf-8
+
+=head1 NAME
+
+Data::Dump::Sexp - convert arbitrary scalars to s-expressions
+
+=head1 SYNOPSIS
+
+  use Data::Dump::Sexp;
+  use Data::SExpression qw/cons/;
+  say dump_sexp 5;                    # 5
+  say dump_sexp "yes";                # "yes"
+  say dump_sexp [1, "yes", 2];        # (1 "yes" 2)
+  say dump_sexp { b => 5, a => "yes"} # (("a" . "yes") ("b" . 5))
+
+=head1 DESCRIPTION
+
+B<This module is not well-tested, proceed with caution>.
+
+Data::Dump::Sexp converts Perl structures to S-expressions.
+
+The conversion rules are as follows:
+
+=over
+
+=item 1
+
+A blessed object with a B<to_sexp> method is replaced with the result
+of calling the method, and this procedure is restarted.
+
+=item 2
+
+An instance of L<Data::SExpression::Symbol> is converted to a symbol.
+
+=item 3
+
+An instance of L<Data::SExpression::Cons> is converted to a cons cell
+(like C<(A . B)>), a proper list (like C<(A B C)>) or an improper list
+(like C<(A B . C)>), where A, B, C are S-expressions.
+
+=item 4
+
+undef is converted to the empty list.
+
+=item 5
+
+A defined scalar that looks like a number is left as-is.
+
+=item 6
+
+A defined scalar that does not look like a number is surrounded by
+double quotes after any backslashes and double quote characters are
+escaped with a backslash.
+
+=item 7
+
+An arrayref is converted to a proper list.
+
+=item 8
+
+A hashref is converted to an alist, which is a proper list of cons
+cells (like C<((A . B) (C . D) (E . F))>).
+
+=item 9
+
+A scalarref or a reference to another ref is dereferenced and this
+procedure is restarted.
+
+=item 10
+
+Anything else (regexp, filehandle, format, glob, version string)
+causes an exception to be raised
+
+=back
+
+A single function is exported by default:
+
+=over
+
+=item B<dump_sexp> I<$expr>
+
+Given any Perl scalar, convert it to a S-expression and return the
+sexp as a string.
+
+=back
+
+=head1 AUTHOR
+
+Marius Gavrilescu, E<lt>marius@ieval.roE<gt>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2018 by Marius Gavrilescu
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself, either Perl version 5.24.1 or,
+at your option, any later version of Perl 5 you may have available.
+
+
+=cut
diff --git a/t/Data-Dump-Sexp.t b/t/Data-Dump-Sexp.t
new file mode 100644 (file)
index 0000000..50e1b84
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+use Test::More tests => 10;
+BEGIN { use_ok('Data::Dump::Sexp') };
+
+use Data::SExpression;
+
+is dump_sexp(5), 5;
+is dump_sexp('yes'), '"yes"';
+is dump_sexp('"ha\\ha\\ha"'), '"\\"ha\\\\ha\\\\ha\\""';
+is dump_sexp([1, "yes", 2]), '(1 "yes" 2)';
+is dump_sexp({b => 5, a => "yes"}), '(("a" . "yes") ("b" . 5))';
+
+sub roundtrip_test {
+       my ($sexp) = @_;
+       my $ds = Data::SExpression->new({use_symbol_class => 1, fold_lists => 0});
+       my $parsed = $ds->read($sexp);
+       is dump_sexp($parsed), $sexp
+}
+
+roundtrip_test 'symbol';
+roundtrip_test '(HA-HA 111 "text")';
+roundtrip_test '(cons . cell)';
+roundtrip_test '(1 2 3 . 4)';
This page took 0.015799 seconds and 4 git commands to generate.