#!/usr/bin/env perl

use utf8;
use 5.016;

BEGIN {    # support for running sidef locally from everywhere
    if (-w __FILE__) {
        require File::Spec;
        require File::Basename;
        unshift @INC,
          File::Spec->catdir(
                             File::Basename::dirname(
                                                       File::Spec->file_name_is_absolute(__FILE__)
                                                     ? __FILE__
                                                     : File::Spec->rel2abs(__FILE__)
                                                    ),
                             File::Spec->updir,
                             'lib'
                            );
    }
}

binmode STDIN,  ":utf8";
binmode STDOUT, ":utf8";
binmode STDERR, ":utf8" if $^P == 0;    # to work under Devel::* modules

use Sidef;

my $name    = 'Sidef';
my $version = $Sidef::VERSION;

my %args;
if ($#ARGV != -1 and chr ord $ARGV[0] eq '-') {
    require Getopt::Std;
    $Getopt::Std::STANDARD_HELP_VERSION = 1;
    Getopt::Std::getopts('e:E:Dho:ivHWwbcrR:tCO:kP:M:sN:', \%args);
}

# Fix potential case mismatches for -R
if (defined $args{R}) {
    if (lc($args{R}) eq 'perl') {
        $args{R} = 'Perl';
    }
    elsif (lc($args{R}) eq 'sidef') {
        $args{R} = 'Sidef';
    }
}

# Help
if (defined $args{h}) {
    HELP_MESSAGE();
    exit 0;
}

# Version
if (defined $args{v}) {
    VERSION_MESSAGE();
    exit 0;
}

# Warnings stack backtrace
if (defined $args{w}) {
    $SIG{__WARN__} = sub {
        require Carp;
        Carp::cluck(@_);
    };
}

# Fatal warnings stack backtrace
if (defined $args{W}) {
    $SIG{__DIE__} = $SIG{__WARN__} = sub {
        require Carp;
        Carp::confess(@_);
    };
}

# Interactive help
if (defined $args{H}) {
    help_interactive();
    exit 0;
}

# Interactive coding
if (defined $args{i}) {
    code_interactive();
    exit 0;
}

# Precision
if (defined $args{P}) {
    require Sidef::Types::Number::Number;
    if ($args{P} <= 0) {
        die "Invalid precision: <<$args{P}>> (expected a positive integer)\n";
    }
    $Sidef::Types::Number::Number::PREC = $args{P} << 2;
}

# Other Number options
if (defined $args{N}) {
    require Sidef::Types::Number::Number;
    my @options = split(/\s*;\s*/, $args{N});
    foreach my $option (@options) {
        if ($option =~ /^\s*(\w+)\s*=\s*(\S+)/) {
            my ($name, $value) = ($1, $2);

            if ($value eq 'true') {
                $value = 1;
            }
            elsif ($value eq 'false') {
                $value = 0;
            }

            no strict 'refs';
            ${'Sidef::Types::Number::Number::' . $name} = $value;
        }
        else {
            die "Invalid format: <<$option>>!\nExpected: 'NAME1=VALUE1; NAME2=VALUE2;'";
        }
    }
}

# Test mode
if (defined $args{t}) {

    local $args{c} = 0;
    my @argv = splice(@ARGV);

    my @fails;
    require Encode;
    while (defined(my $script_name = shift @argv)) {

        my $script_name = Encode::decode_utf8($script_name);

        say "\n** Executing: $script_name";
        say "-" x 80;

        my $sidef = Sidef->new(opt  => \%args,
                               name => $script_name);

        my $code     = read_script($script_name);
        my $deparsed = eval { $sidef->compile_code($code, 'Perl') };

        my $slept = 0;
        if ($@) {
            warn "[ERROR] Can't parse the script `$script_name`: $@";
            push @fails, $script_name;
            sleep 2;
            $slept = 1;
        }
        else {
            local $SIG{INT} = sub {
                die "Stopped by user...";
            };

            if (defined $args{C}) {
                say "$script_name syntax OK";
                next;
            }

            $sidef->execute_perl($deparsed);
        }

        if (not($slept) and $@) {
            warn "[ERROR] Error encountered on script `$script_name`: $@";
            push @fails, $script_name;
            sleep(2) if @argv;
        }
    }

    if (@fails) {
        say "\n";
        say "-" x 80;
        say ":: The following scripts failed";
        say "-" x 80;
        say "$_" for @fails;
    }
}

# Default
else {
    my $script_name = '-';

    $args{E} = $args{e} if exists($args{e});

    my $code = exists($args{E})
      ? do {
        defined($args{E}) || die "No code specified for -E.\n";
        $script_name = '-E';
        require Encode;
        Encode::decode_utf8($args{E});
      }
      : defined($ARGV[0]) ? do {
        $script_name = shift @ARGV;
        if ($script_name eq '-') {
            local $/;
            <STDIN>;
        }
        else {
            read_script($script_name);
        }
      }
      : (-t STDIN) ? do { code_interactive(); exit 0; }
      :              do { local $/;           <STDIN> };

    $code // exit 2;

    my $sidef = Sidef->new(opt  => \%args,
                           name => $script_name);

    # Dump the AST
    if (defined $args{D}) {
        dump_ast($sidef->parse_code($code));
    }

    # Deparse code
    elsif (defined($args{r}) or defined($args{R})) {
        my $deparsed = $sidef->compile_code($code, $args{R});

        if (defined($args{R}) and $args{R} eq 'Perl') {

            require File::Basename;

            my $header =
                "\nuse lib (" . q{"}
              . quotemeta(File::Basename::dirname($INC{"Sidef.pm"})) . q{"}
              . ");\n\n"
              . "use Sidef;\n\n"
              . "binmode(STDIN, ':utf8');\n"
              . "binmode(STDOUT, ':utf8');\n"
              . "binmode(STDERR, ':utf8') if \$^P == 0;\n";

            $deparsed = $header . $deparsed;
        }

        output($deparsed);
    }

    # Compile the code to a Perl program
    elsif (defined $args{c}) {
        compile_to_perl(code => $sidef->compile_code($code, 'Perl'));
    }

    # Check the syntax
    elsif (defined $args{C}) {
        eval { $sidef->parse_code($code) };
        die $@ if $@;
        say "$script_name syntax OK";
    }

    # Execute the code
    else {
        $sidef->execute_code($code);
        die $@ if $@;
    }
}

#
## Subroutines
#

sub HELP_MESSAGE {
#<<<
    my %switches = (
                    '-c'         => 'compile the code into a stand-alone Perl program',
                    '-C'         => 'check syntax only (parse without execution)',
                    '-D'         => 'dump the Abstract Syntax Tree (AST) of the program',
                    '-E program' => 'execute a one-line program from the command line',
                    '-e program' => 'alias for -E (execute one-line program)',
                    '-H'         => 'enter interactive help mode for exploring documentation',
                    '-i [file]'  => 'execute program in interactive mode (REPL)',
                    '-k'         => 'keep track of potentially incorrect parser interpretations',
                    '-M mode'    => ['set the rounding mode for floating-point numbers',
                                     'valid modes: [near], zero, inf, +inf, -inf, faith',
                                     '  near  = round to nearest (default)',
                                     '  zero  = round towards zero',
                                     '  inf   = round away from zero',
                                     '  +inf  = round towards positive infinity',
                                     '  -inf  = round towards negative infinity',
                                     '  faith = faithful rounding'],
                    '-N options' => ['configure Number class variables (semicolon-separated)',
                                     "example: 'VERBOSE=1; USE_YAFU=1; USE_PRIMECOUNT=1'",
                                     'available options: PREC, ROUND, VERBOSE, USE_YAFU,',
                                     '  USE_PFGW, USE_PARI_GP, USE_FACTORDB, USE_PRIMESUM,',
                                     '  USE_PRIMECOUNT, USE_CONJECTURES, SPECIAL_FACTORS'],
                    '-o file'    => 'specify output file for compiled/deparsed code',
                    '-O level'   => ['perform code optimizations before execution',
                                     'valid levels: [0], 1, 2',
                                     '  0 = no optimization (default)',
                                     '  1 = constant folding on AST (recommended)',
                                     '  2 = aggressive optimization with reparse'],
                    '-P int'     => 'set precision of floating-point numbers in bits',
                    '-r'         => 'parse and deparse program back to Sidef code',
                    '-R lang'    => ['parse and deparse program into specified language',
                                     'valid languages: sidef, perl'],
                    '-s'         => 'enable precompilation (cache compiled code)',
                    '-t'         => 'test mode: treat all arguments as script files',
                    '-v'         => 'display version information and exit',
                    '-w'         => 'enable warnings with stack backtrace',
                    '-W'         => 'make warnings fatal (with stack backtrace)',
    );
#>>>
    require File::Basename;
    my $basename = File::Basename::basename($0);

    print <<"HEADER";

Sidef Programming Language - Version $version

Usage: $basename [options] [--] [programfile] [arguments]

OPTIONS:

HEADER

    require List::Util;
    my $max_width = List::Util::max(map { length } keys %switches);
    $max_width += 4;

    foreach my $key (sort { lc($a) cmp lc($b) or lc($b) cmp lc($a) or $b cmp $a } keys %switches) {
        if (ref $switches{$key} eq 'ARRAY') {
            printf "  %-${max_width}s%s\n", $key, $switches{$key}[0];
            foreach my $i (1 .. $#{$switches{$key}}) {
                printf "  %-${max_width}s%s\n", '', $switches{$key}[$i];
            }
        }
        else {
            printf "  %-${max_width}s%s\n", $key, $switches{$key};
        }
    }

    print <<"EXAMPLES";

EXAMPLES:

  # Execute a Sidef script
  $basename script.sf

  # Enter interactive mode (REPL)
  $basename
  $basename -i

  # Execute a Sidef script in interactive mode
  $basename -i script.sf

  # Execute one-line program
  $basename -E 'say "Hello, World!"'
  $basename -E '10.of { |i| say i**2 }'

  # Check syntax without execution
  $basename -C script.sf

  # Compile to stand-alone Perl program
  $basename -c -o output.pl script.sf

  # Set floating-point precision to 100 digits
  $basename -P 400 -E 'say sqrt(2)'

  # Enable optimizations
  $basename -O1 script.sf

  # Parse and display as Sidef code
  $basename -r script.sf

  # Deparse to Perl code
  $basename -Rperl script.sf

INTERACTIVE MODE (REPL):

  Run '$basename' without arguments to enter the interactive mode.

  Special REPL commands:
    ##                - show execution time of last command
    #n                - refer to nth output value (e.g., #1, #-1)
    # load file.sf    - load and execute file line by line
    # exec file.sf    - execute entire file
    # save file.sf    - save REPL session to file
    reset             - reset the REPL environment
    quit, exit, q     - exit REPL
    help              - display interactive help
    copyright         - show copyright information
    license           - show license information

DOCUMENTATION:

  Visit https://github.com/trizen/sidef for comprehensive documentation
  and tutorials, or use -H for interactive help on specific topics.

COPYRIGHT:

  Copyright © 2013-2026 Daniel Șuteu, Ioana Fălcușan
  Licensed under the Artistic License 2.0

EXAMPLES
}

sub VERSION_MESSAGE {
    print "$name $version\n";
}

sub read_script {
    my ($script_name) = @_;
    open my $fh, '<:utf8', $script_name
      or die qq{Can't open sidef script "$script_name": $!\n};
    local $/;
    <$fh>;
}

sub help_interactive {
    my ($term) = @_;

    require File::Spec;
    require File::Basename;

    require Encode;
    require Term::ReadLine;

    $term //= Term::ReadLine->new("$name $version -- help interactive mode");

    print <<"HELP";
Welcome to $name $version! This is the interactive help utility.

Enter the name of any object, keyword, or topic to get help on writing
$name programs and using $name modules.  To quit this help utility, just
type "quit".

HELP

    my $sidef = Sidef->new(
                           name       => '-H',
                           opt        => {i           => 1, %args},
                           parser_opt => {interactive => 1},
                          );

    {
        my $line = Encode::decode_utf8(
            $term->readline('help> ')
              // do { print "\n"; return }
        );

        my $ccode = eval { $sidef->compile_code($line, 'Perl') };

        if ($@) {

            # Valid keywords for 'exit'
            if ($line eq 'quit' or $line eq 'q' or $line eq 'exit') {
                return;
            }

            # Otherwise, a syntax error
            warn $@;
            redo;
        }

        my @refs = (map { ref($_) } $sidef->execute_perl($ccode));

        foreach my $ref (@refs) {
            $ref eq '' && do { warn "Not an object!\n"; next };
            my $name = $ref =~ s{::}{/}gr;
            my $file = $INC{$name . '.pm'};
            my $pod;
            foreach my $dir (@INC) {
                if (-e (my $f = File::Spec->catfile($dir, $name . '.pod'))) {
                    $pod = $f;
                    last;
                }
            }
            if (defined($pod)) {
                system 'perldoc', $pod;
                $? && system 'man', $ref;
            }
            else {
                system 'man', $ref;
                $? && system 'perldoc', $ref;
            }
        }

        redo;
    }
}

sub create_completion_tree {
    scalar {
            table       => {},
            special_key => "\0",
           };
}

sub add_tree_entry {
    my ($tree, $key, $value) = @_;

    my $ref = $tree->{table};

    foreach my $item (@$key) {
        $ref = $ref->{$item} //= {};
        undef $ref->{$tree->{special_key}}{$value};
    }

    $tree;
}

sub search_tree {
    my ($tree, $prefix) = @_;

    my $ref = $tree->{table};

    foreach my $item (@$prefix) {
        if (exists $ref->{$item}) {
            $ref = $ref->{$item};
        }
        else {
            return;
        }
    }

    sort keys %{$ref->{$tree->{special_key}} // {}};
}

sub add_class_methods_to_completion {
    my ($tree) = @_;

    my $modules_count = scalar(keys %INC);

    state %seen;
    state $included_modules = $modules_count - 1;

    if ($modules_count == $included_modules) {
        return 1;
    }

    foreach my $module (keys %INC) {

        next if $seen{$module}++;

        my $class = $module =~ s{\.pm\z}{}r =~ s{\W+}{::}gr;
        $class =~ /^Sidef::Types::/ or next;

        foreach my $method_name (keys %{(eval { $class->methods }) // {}}) {
            add_tree_entry($tree, [split(//, $method_name)], $method_name);
        }
    }

    $included_modules = $modules_count;
    return 1;
}

sub add_words_to_completion {
    my ($tree, $string) = @_;

    while ($string =~ /(\w+)/g) {
        my $word = $1;
        if (length($word) <= 50) {
            add_tree_entry($tree, [split(//, $word)], $word);
        }
    }

    return 1;
}

sub code_interactive {

    require Encode;
    require File::Spec;
    require Term::ReadLine;

    my $term = Term::ReadLine->new("$name $version -- interactive mode");

    my $sidef;
    my $init_sidef = sub {
        $sidef = Sidef->new(
                            name       => '-i',
                            opt        => {i           => 1, %args},
                            parser_opt => {interactive => 1},
                           );
        $sidef->execute_code('');    # warm-up
    };

    $init_sidef->();

    my ($copy_array, $copy_hash);

    $copy_array = sub {
        my ($array) = @_;

        my @copy;
        foreach my $item (@$array) {
            if (ref($item) eq 'ARRAY') {
                push @copy, __SUB__->($item);
            }
            elsif (ref($item) eq 'HASH') {
                push @copy, $copy_hash->($item);
            }
            else {
                push @copy, $item;
            }
        }

        \@copy;
    };

    $copy_hash = sub {
        my ($hash) = @_;

        my %copy;
        foreach my $key (keys %$hash) {
            my $value = $hash->{$key};

            if (ref($value) eq 'ARRAY') {
                $copy{$key} = $copy_array->($value);
            }
            elsif (ref($value) eq 'HASH') {
                $copy{$key} = __SUB__->($value);
            }
            else {
                $copy{$key} = $value;
            }
        }

        \%copy;
    };

    require Time::HiRes;

    print <<"EOT" if 0;
            **   **         ****   *           *********   *********
          * * ** * *        ****   **          ** ** **    ** ** **
           **   **          ****   ***         *********   *  *  *
  **        **        **    ****   *  *        ******      ******
* * *     * * *     * * *   ****   ** **       ** **       ** **
 **        **        **     ****   ******      ******      *  *
       **   **              ****   *  *  *     *********   ***
     * * ** * *             ****   ** ** **    ** ** **    **
      **   **               ****   *********   *********   *
EOT

    print <<"EOT";
Sidef $version, running on \u$^O, using Perl $^V.
Type "help", "copyright" or "license" for more information.
EOT

    my $valid_lines = '';
    my ($vars, $ref_vars_refs);

    my $completion_tree;
    my $history_support = $term->can('ReadHistory') && $term->can('Attribs');
    my $history_file    = File::Spec->catfile($sidef->get_sidef_config_dir(), 'sidef_history.txt');

    if ($history_support) {

        if (not -e $history_file) {
            open my $fh, '>', $history_file;
        }

        $completion_tree = create_completion_tree();

        my $attr = $term->Attribs;

        $attr->{basic_quote_characters} = q{};

        add_class_methods_to_completion($completion_tree);

        my @results;
        $attr->{completion_entry_function} = sub {
            my ($prefix, $state) = @_;

            my $root = '';
            if ($prefix !~ /^\w+\z/ and $prefix =~ /^(.*)\b(\w+)\z/) {
                $root   = $1;
                $prefix = $2;
            }

            if ($state == 0) {
                @results = search_tree($completion_tree, [split(//, $prefix)]);
            }

            @results || return undef;
            $root . shift(@results);
        };

        $term->ReadHistory($history_file);
    }

    my $tΔ = 0;

    my @values;
    my $FH = undef;

    if (@ARGV) {
        my $file = shift(@ARGV);
        open $FH, '<:utf8', $file
          or die "Can't open file <<$file>> for reading: $!\n";
    }

  MAINLOOP: {
        my $line = '';

      LINE: {

            if (defined($FH) and !eof($FH)) {
                chomp(my $curr_line = <$FH>);
                if ($line eq '' and $curr_line =~ /^\s*__(?:END|DATA)__\s*\z/) {
                    $curr_line .= "\n" . do { local $/; <$FH> };
                }
                if ($history_support and $curr_line ne '' and $line eq '') {
                    $term->addhistory($curr_line =~ s/\R/\r/gr);
                }
                $line .= $curr_line;
            }
            else {
                $line .= Encode::decode_utf8($term->readline($line eq '' ? '> ' : '  ') // return);
            }

            if ($line eq 'help') {
                help_interactive($term);
                redo MAINLOOP;
            }
            elsif ($line eq '##') {
                say "  ***   last result computed in $tΔ seconds";
                redo MAINLOOP;
            }
            elsif ($line =~ /^#+\h*load\h+(.+)/) {
                my $file = unpack('A*', $1);
                open $FH, '<:utf8', $file or do {
                    warn "Can't open file <<$file>> for reading: $!\n";
                    redo MAINLOOP;
                };
                redo MAINLOOP;
            }
            elsif ($line =~ /^#+\h*exec\h+(.+)/) {
                my $file = unpack('A*', $1);
                $init_sidef->();
                open my $fh, '<:utf8', $file or do {
                    warn "Can't open file <<$file>> for reading: $!\n";
                    redo MAINLOOP;
                };
                $line = do { local $/; <$fh> };
                close $fh;
            }
            elsif ($line =~ /^#+\h*save\h+(.+)/) {
                my $file = unpack('A*', $1);
                open my $fh, '>:utf8', $file or do {
                    warn "Can't open file <<$file>> for writing: $!\n";
                    redo MAINLOOP;
                };
                print $fh $valid_lines;
                close $fh;
                say "** Created file: $file";
            }
            elsif ($line eq 'copyright') {
                print <<'EOT';
Copyright © 2013-2026 Daniel Șuteu, Ioana Fălcușan
All Rights Reserved.
EOT
                redo MAINLOOP;
            }
            elsif ($line eq 'license') {
                print <<'EOT';

This program is free software; you can redistribute it
and/or modify it under the terms of the Artistic License (2.0).
For more details, see the full text in the LICENSE file.

This program is distributed in the hope that it will be
useful, but without any warranty; without even the implied
warranty of merchantability or fitness for a particular purpose.

For more information, see:
    https://github.com/trizen/sidef
    https://www.perlfoundation.org/artistic-license-20.html

EOT
                redo MAINLOOP;
            }
        }

        # Replace top-level variables and constants with globals
        if (not defined($args{r}) and not defined($args{R})) {
            $line =~ s/^\h*(?:var|define|const|static)\b/global/;
        }

        $vars          = $copy_hash->($sidef->{parser}{vars});
        $ref_vars_refs = $copy_hash->($sidef->{parser}{ref_vars_refs});

        $line =~ s{#(-?[1-9][0-9]*)\b}{(abs($1) <= scalar(@values)) ? ('(' . $values[($1 < 0) ? $1 : $1-1]->{value} . ')') : "#$1"}ge;

        # Last character was '\': read the next line
        if ($line =~ /\\\s*\z/) {
            $line .= "\n";
            goto LINE;
        }

        my $ccode = eval { $sidef->compile_code($line, $args{r} ? 'Sidef' : ($args{R} || 'Perl')) };

        if ($@) {

            # Valid keywords for 'exit'
            if ($line eq 'q' or $line eq 'exit' or $line eq 'quit') {
                return;
            }

            # Reset the parser
            if ($line eq 'reset') {
                $init_sidef->();
                undef $vars;
                undef $ref_vars_refs;
                @values = ();
                redo;
            }

            # Restore parser variables
            if (defined($vars) and defined($ref_vars_refs)) {
                %{$sidef->{parser}{vars}}          = %$vars;
                %{$sidef->{parser}{ref_vars_refs}} = %$ref_vars_refs;
            }

            # Give up if the previous line is blank,
            # or when it's impossible to recover from an error
            if (
                   $@ =~ /is not declared in the current scope/i
                or $@ =~ /invalid \S+ declaration/i
                or $@ =~ /attempt to (?:use|call|delete) /i
                or $@ =~ /not declared in the current scope/i
                or $@ =~ /expected a block after/i
                or $@ =~ /unexpected end-of-statement/i
                or (
                      $@    =~ /unbalanced|string terminator|delimiter/
                    ? $line =~ /\R\R\z/
                    : $line =~ /\R\z/
                   )
              ) {
                warn $@;
                redo;
            }

            $line .= "\n";
            goto LINE;
        }
        else {
            $valid_lines .= "$line\n";    # store valid lines
        }

        if ($history_support) {
            if ($line =~ /\R/) {
                $term->addhistory($line =~ s/\R/\r/gr);
            }
            $term->append_history(1, $history_file);
        }

        if (defined($args{r}) or defined($args{R})) {
            output($ccode);
        }
        elsif ($line =~ /\S/ and not $line =~ /^\s*#.*$/) {

            my $t0 = eval { [Time::HiRes::gettimeofday()] };

            my @results = $sidef->execute_perl($ccode);

            if ($@) {
                print $@;
            }
            elsif ($history_support) {
                add_words_to_completion($completion_tree, $line);
            }

            $tΔ = eval { Time::HiRes::tv_interval($t0) };

            # use overload;
            # overload::StrVal($_) ? "$_" : $_->dump;

            my $dump = join(
                ', ',
                map {
                        (ref($_) ? UNIVERSAL::can($_, 'dump') ? $_->dump : $_ : ($_ // 'nil'))
                      . ((ref($_) eq 'Sidef::Types::Number::Number' and ref($$_) eq 'Math::MPFR' and Math::MPFR::Rmpfr_number_p($$_)) ? 'f' : '')
                  } @results
            );

            $dump = "($dump)" if @results > 1;

            push @values,
              {
                type  => ((scalar(@results) == 1) ? 'scalar' : 'list'),
                value => $dump,
              };

            say "#" . scalar(@values) . " = $dump";

            if ($history_support) {
                add_class_methods_to_completion($completion_tree);
            }
        }
        redo;
    }
}

sub _get_loaded_modules {
    my @modules;
    foreach my $key (sort { length($a) <=> length($b) || $a cmp $b } keys %INC) {
        if ($key =~ /^(Sidef\b.*)\.pm\z/) {
            push @modules, $1 =~ s{/}{::}gr;
        }
    }
    return @modules;
}

sub output {
    my ($content) = @_;

    my $out_fh = \*STDOUT;

    if (defined $args{o}) {
        open $out_fh, '>:utf8', $args{o}
          or die "Can't open file '$args{o}' for write: $!\n";
    }
    print {$out_fh} $content;

    return $out_fh;
}

sub dump_ast {
    my ($ast) = @_;

    eval { require Data::Dump };

    if ($@) {
        die qq{** "Data::Dump" is not installed!\n};
    }
    else {
        my $out_fh = output('');

        my $requirify = sub {
            join('', map { "require '" . (s{::}{/}gr) . ".pm';\n" } @_);
        };

        print {$out_fh} $requirify->(_get_loaded_modules());
        print {$out_fh} Data::Dump::pp($ast) . "\n";
    }
}

sub compile_to_perl {
    my (%opt) = @_;

    require File::Spec;
    require File::Basename;

    my $path = File::Spec->catdir(File::Basename::dirname($INC{'Sidef.pm'}), 'Sidef');

    my $package_content = <<"HEAD";
#!$^X

eval 'exec $^X  -S \$0 \${1+"\$@"}'
    if 0; # not running under some shell

use utf8;

binmode STDIN,  ":utf8";
binmode STDOUT, ":utf8";
binmode STDERR, ":utf8" if \$^P == 0;    # to work under Devel::* modules

my %REQ;
my %MODULE;
HEAD

    $package_content .= "BEGIN { %MODULE = (\n";

    require File::Find;
    File::Find::find(
        {
         no_chdir => 1,
         wanted   => sub {
             if (/\.pm\z/ and -f) {

                 local $/;
                 open my $fh, '<:utf8', $_
                   or die "Can't open file `$_` for reading: $!";

                 my $token   = tr/A-Za-z0-9/_/cr;
                 my $content = <$fh>;

                 if ($content =~ /^package\h+([\w:]+)/) {
                     $package_content .= qq{'${1}' => };
                 }
                 else {
                     die qq{ERROR: can't get the package name from file `$_`};
                 }

                 $package_content .= qq{<<'${token}',\n};
                 $package_content .= $content;
                 $package_content .= "\n$token\n";

                 close $fh;
             }
         }
        } => ($path, $INC{'Sidef.pm'})
    );

    $package_content .= <<'FOOT';
);

sub __load_sidef_module__ {
    my ($name) = @_;
    if (not exists $REQ{$name}) {
        my $module = $name =~ s{::}{/}gr . '.pm';
        if (exists $MODULE{$name} and not exists $INC{$module}) {

            # Load the Sidef used modules
            $MODULE{$name} =~ s{^\h*
                  use \h+ (?:
                      parent \s+ qw\((.*?)\)
                    | (Sidef::[\w:]+)
                  )
            }{
                  join(
                  ";\n" => map{
                    exists($REQ{$_})
                        ? ()
                        : "BEGIN{ main::__load_sidef_module__('${_}') }" } split(' ', $+)
                  ) . (defined($1) ? "\nuse parent qw(-norequire $1);\n" : '')
            }gxmse;

            $INC{$module} = 1;
            eval($MODULE{$name});
            die "[FATAL ERROR] Can't load `$module`: $@" if $@;
        }
        else {
            require $module;
        }
        $REQ{$name} = 1;
    }
    return 1;
}

FOOT

    my $requirify = sub {
        join('', map { "__load_sidef_module__('${_}');\n" } grep { $_ ne 'Sidef::Optimizer' } @_);
    };

    $package_content .= $requirify->(_get_loaded_modules(), 'Sidef::Module::OO', 'Sidef::Module::Func');

    my @used_pkgs;
    while ($opt{code} =~ /^use (Sidef::\S+);$/gm) {
        push @used_pkgs, $1;
    }

    $package_content .= $requirify->(@used_pkgs) if @used_pkgs;
    $package_content .= "}\n\n";

    my $out_fh = output('');
    print {$out_fh} $package_content;
    print {$out_fh} $opt{code};
}

__END__

=encoding utf8

=head1 NAME

sidef - The Sidef Programming Language interpreter

                **   **         ****   *           *********   *********
              * * ** * *        ****   **          ** ** **    ** ** **
               **   **          ****   ***         *********   *  *  *
      **        **        **    ****   *  *        ******      ******
    * * *     * * *     * * *   ****   ** **       ** **       ** **
     **        **        **     ****   ******      ******      *  *
           **   **              ****   *  *  *     *********   ***
         * * ** * *             ****   ** ** **    ** ** **    **
          **   **               ****   *********   *********   *

=head1 SYNOPSIS

    sidef [options] [--] [programfile] [arguments]

    # Execute a script
    sidef script.sf

    # Interactive mode (REPL)
    sidef
    sidef -i

    # One-line program execution
    sidef -E 'say "Hello, World!"'

    # Check syntax
    sidef -C script.sf

    # Compile to Perl
    sidef -c -o output.pl script.sf

=head1 DESCRIPTION

Sidef is a modern, high-level programming language designed for writing
elegant and expressive code. This is the main interpreter for executing
Sidef programs, providing both script execution and an interactive
Read-Eval-Print Loop (REPL) environment.

Sidef features arbitrary-precision arithmetic, functional programming
constructs, object-oriented programming, powerful regex support, and
extensive built-in methods for common tasks. The language emphasizes
code readability and expressiveness while maintaining strong performance
through optimization and optional precompilation.

=head1 OPTIONS

=head2 Execution Options

=over 4

=item B<-E> I<program>

Execute a one-line program from the command line. The program is executed
immediately without requiring a file. This is useful for quick calculations,
text processing, or testing code snippets.

    sidef -E 'say "Hello, World!"'
    sidef -E '10.times { |i| say i**2 }'
    sidef -E '[1,2,3,4,5].grep{.is_prime}.say'

=item B<-e> I<program>

Alias for B<-E>. Execute a one-line program.

=item B<-i> [I<file>]

Enter interactive mode (REPL - Read-Eval-Print Loop). If a file is provided,
its contents are loaded and executed line by line in the interactive session.
The REPL provides immediate feedback, command history, tab completion, and
persistent variable state throughout the session.

    sidef -i                    # Start empty REPL
    sidef -i startup.sf         # Load file into REPL
    sidef -i < commands.txt     # Pipe commands

=item B<-t>

Test mode. Treat all command-line arguments as script files and execute them
sequentially. Useful for running test suites. Reports which scripts succeed
or fail and provides a summary at the end.

    sidef -t test1.sf test2.sf test3.sf
    sidef -t tests/*.sf

=item B<-->

End of options marker. Everything after this is treated as a program file or
arguments, even if it starts with a dash. Useful when script names begin
with hyphens.

    sidef -- -weird-script-name.sf

=back

=head2 Compilation and Analysis Options

=over 4

=item B<-c>

Compile the Sidef code into a stand-alone Perl program. The output includes
all necessary Sidef runtime modules embedded in a single file, making it
distributable without requiring a Sidef installation. The resulting program
can be executed directly with Perl.

    sidef -c -o myprogram.pl script.sf
    chmod +x myprogram.pl
    ./myprogram.pl

Note: Code using C<eval()> may not compile correctly as it requires
parse-time information that is lost during compilation.

=item B<-C>

Check syntax only. Parse the program and report any syntax errors without
executing the code. Useful for validating code structure before deployment
or as part of a continuous integration pipeline.

    sidef -C script.sf
    sidef -C *.sf               # Check all scripts

=item B<-D>

Dump the Abstract Syntax Tree (AST) of the program. The AST represents the
internal structure of the parsed program before execution. Useful for
debugging parser behavior, understanding how code is interpreted, and
developing language extensions.

    sidef -D script.sf > ast.dump
    sidef -D -E '1 + (2 * 3)' | less

Requires the C<Data::Dump> Perl module.

=item B<-r>

Parse and deparse the program back to Sidef code. This shows how the parser
interpreted your code and can help identify parsing ambiguities, verify
operator precedence, and understand method call transformations.

    sidef -r script.sf
    sidef -r -E '1 + 2/3'       # Shows: (1)->+((2)->/(3));

=item B<-R> I<language>

Parse and deparse the program into the specified target language. This
translates Sidef code into equivalent code in another language while
preserving semantics. Valid values are:

=over 8

=item * B<sidef> - Deparse to Sidef code (same as -r)

=item * B<perl> - Translate to Perl code with Sidef runtime

=back

    sidef -Rperl script.sf > script.pl
    perl script.pl

    sidef -Rsidef script.sf    # Normalize code style

=back

=head2 Optimization Options

=over 4

=item B<-O> I<level>

Perform code optimizations before execution. Higher optimization levels can
significantly improve runtime performance for computation-heavy scripts by
evaluating constant expressions at compile time. Valid levels:

=over 8

=item * B<0> - No optimization (default). Code is executed as parsed.

=item * B<1> - Constant folding on AST (recommended for production). Pure
expressions with constant operands are evaluated at compile time, reducing
runtime computation.

=item * B<2> - Aggressive optimization with deparse and reparse. Performs
constant folding, deparses to Sidef code, reparses, and does additional
constant folding on the new AST. Increases compile time but may provide
additional optimizations.

=back

    sidef -O0 script.sf         # Default, no optimization
    sidef -O1 script.sf         # Recommended
    sidef -O2 compute.sf        # Maximum optimization

Example optimization:

    # Source code
    var x = (2 + 3*4)

    # After -O1 optimization
    var x = 14

=item B<-s>

Enable precompilation caching. Compiled code is saved in a database indexed
by MD5 hash of the source code. On subsequent runs of the same code, the
compiled version is loaded from cache, significantly reducing startup time
for large programs.

    sidef -s large_script.sf    # Slow first run (compiles and caches)
    sidef -s large_script.sf    # Fast subsequent runs (loads from cache)

The cache is automatically maintained and periodically sanitized. Cache
location: C<~/.sidef/cache/>

=back

=head2 Number Configuration Options

Sidef provides arbitrary-precision arithmetic through the GMP and MPFR
libraries. These options control precision and behavior of numeric operations.

=over 4

=item B<-P> I<digits>

Set the precision of floating-point numbers in decimal digits. Higher precision
allows more accurate calculations but increases memory usage and computation time.
Default is approximately 58 decimal digits (192 bits).

    sidef -P 50 -E 'say sqrt(2)'          # 50 decimal digits
    sidef -P 100 -E 'say sqrt(2)'         # 100 decimal digits
    sidef -P 1000 -E 'say Num.pi'         # 1000 decimal digits

=item B<-M> I<mode>

Set the rounding mode for floating-point operations. Different rounding modes
are useful for different numerical applications (financial calculations,
scientific computing, interval arithmetic, etc.). Valid modes:

=over 8

=item * B<near> - Round to nearest (default). Standard rounding mode.

=item * B<zero> - Round towards zero (truncate). Discards fractional part.

=item * B<inf> - Round away from zero. Rounds up for positive, down for negative.

=item * B<+inf> - Round towards positive infinity (ceiling). Always rounds up.

=item * B<-inf> - Round towards negative infinity (floor). Always rounds down.

=item * B<faith> - Faithful rounding. Result is one of the two nearest
floating-point numbers.

=back

    sidef -M near -E 'say 7/3'            # 2.333... (nearest)
    sidef -M zero -E 'say 7/3'            # 2.333... (towards zero)
    sidef -M +inf -E 'say 7/3'            # 2.334... (ceiling)
    sidef -M -inf -E 'say 7/3'            # 2.333... (floor)

=item B<-N> I<options>

Configure Number class variables for fine-grained control over numeric
computation behavior. Multiple options can be separated by semicolons.
Format: C<'NAME1=VALUE1; NAME2=VALUE2'>

Available options:

=over 8

=item * B<PREC> - Floating-point precision in decimal digits (same as -P)

=item * B<ROUND> - Rounding mode as integer: 0=near, 1=zero, 2=inf, 3=+inf, 4=-inf, 5=faith

=item * B<VERBOSE> - Enable verbose/debug mode (true/false). Prints detailed
information about numeric operations.

=item * B<USE_YAFU> - Use YAFU (Yet Another Factorization Utility) for
factoring large integers (true/false). Requires YAFU installed.

=item * B<USE_PFGW> - Use PFGW64 (PrimeForm/GW) as a primality pretest for
large numbers (true/false). Requires PFGW64 installed.

=item * B<USE_PARI_GP> - Use PARI/GP for various number theory operations
(true/false). Requires PARI/GP installed.

=item * B<USE_FACTORDB> - Use factordb.com web service for factoring large
integers (true/false). Requires internet connection.

=item * B<USE_PRIMESUM> - Use Kim Walisch's primesum for computing sum of
primes (true/false). Requires primesum installed.

=item * B<USE_PRIMECOUNT> - Use Kim Walisch's primecount for computing prime
counting function π(n) (true/false). Requires primecount installed.

=item * B<USE_CONJECTURES> - Use conjectured (unproven but likely correct)
methods for better performance (true/false).

=item * B<SPECIAL_FACTORS> - Try to find factors of special form (Fermat,
Mersenne, etc.) when factoring (true/false).

=back

Examples:

    # Enable verbose mode and external factorization
    sidef -N 'VERBOSE=true; USE_FACTORDB=true' -E 'say factor(2**512 + 1)'

    # High precision with specific rounding
    sidef -N 'PREC=400; ROUND=3' script.sf

    # Use all available external tools
    sidef -N 'USE_YAFU=true; USE_PARI_GP=true; USE_PRIMECOUNT=true' script.sf

=back

=head2 Debugging and Warning Options

=over 4

=item B<-k>

Keep track of potentially incorrect parser interpretations. Enables warnings
when the parser makes assumptions that might not match your intent. This is
especially useful for catching typos in function names that get interpreted
as method calls.

    sidef -k script.sf

Example warning:

    func foo(n) { say n }
    fo(42)                      # Typo in function name

    # With -k:
    [INFO] `fo` is parsed as a prefix method-call at script.sf line 2

Common cases flagged:

=over 8

=item * Method calls without an explicit receiver

=item * Ambiguous operator precedence interpretations

=item * Potential variable/function name confusion

=back

=item B<-w>

Enable warnings with stack backtrace. When a warning occurs, displays a
complete call stack showing the sequence of function/method calls that led
to the warning. Useful for debugging complex code paths.

=item B<-W>

Make warnings fatal (with stack backtrace). The program terminates
immediately on the first warning, showing a full stack trace. This is
useful for strict error checking during development or testing.

=back

=head2 Output Options

=over 4

=item B<-o> I<file>

Specify output file for compiled or deparsed code. By default, output goes
to STDOUT. This option is typically used with B<-c>, B<-r>, or B<-R>.

    sidef -c -o program.pl script.sf        # Compile to file
    sidef -R perl -o script.pl script.sf    # Deparse to file
    sidef -D -o ast.txt script.sf           # Dump AST to file

=back

=head2 Help and Information Options

=over 4

=item B<-H>

Enter interactive help mode. Provides access to documentation for Sidef
objects, methods, and modules. You can enter any expression that evaluates
to an object, and the help system will display its documentation.

    sidef -H
    help> Array()
    help> Hash()
    help> Number()
    help> quit

=item B<-h>

Display help message with usage information and available options, then exit.

=item B<-v>

Print version information and exit. Shows the Sidef version number.

=back

=head1 INTERACTIVE MODE (REPL)

When invoked without a script file, Sidef enters interactive mode, providing
a Read-Eval-Print Loop (REPL) for exploring the language, testing code
snippets, and rapid prototyping.

=head2 Starting the REPL

    $ sidef
    Sidef 26.01, running on Linux, using Perl v5.42.0.
    Type "help", "copyright" or "license" for more information.
    >

Or explicitly with the C<-i> option:

    $ sidef -i
    $ sidef -i startup.sf       # Load file first

=head2 REPL Features

=over 4

=item * B<Persistent Variables>

All variables, functions, and classes defined in the REPL persist throughout
the session.

    > var x = 42
    #1 = 42
    > var y = x + 10
    #2 = 52
    > func double(n) { n * 2 }
    > double(x)
    #3 = 84

=item * B<Result History>

Every expression result is stored and can be referenced using C<#n> notation,
where C<n> is the result number. Negative indices count from the end.

    > 3 + 4
    #1 = 7
    > #1 * 2                    # Reference first result
    #2 = 14
    > #-1                       # Last result (same as #2)
    #3 = 14

=item * B<Multiline Input>

End lines with backslash (C<\>) to continue on the next line, or the REPL
will automatically continue incomplete expressions. The prompt changes from
C<E<gt>> to C<  > to indicate continuation mode.

    > func factorial(n) {
        n == 0 ? 1 : n*factorial(n-1)
      }
    > factorial(10)
    #2 = 3628800

=item * B<Tab Completion>

Press Tab to complete variable/function names, show available methods on
objects, and navigate command history.

=item * B<Command History>

Use Up/Down arrow keys to navigate previous commands. History persists
between sessions (saved to C<~/.sidef/sidef_history.txt>).

=back

=head2 Special REPL Commands

=over 4

=item B<##>

Display execution time of the last command. Useful for benchmarking and
performance analysis.

    > factor(2**128 + 1)
    #1 = [59649589127497217, 5704689200685129054721]
    > ##
      ***   last result computed in 0.364332 seconds

=item B<# load> I<filename>

Load and execute a Sidef file line by line in the REPL. Each line is executed
as if typed into the REPL.

    > # load mylib.sf

=item B<# exec> I<filename>

Execute an entire Sidef file in the current REPL session. Unlike C<# load>,
this executes the file as a complete unit, but within the REPL environment
where variables persist.

    > # exec script.sf

=item B<# save> I<filename>

Save all valid commands from the REPL session to a file. This creates a
replayable script containing all successfully executed code.

    > # save session.sf
    ** Created file: session.sf

=item B<reset>

Reset the REPL environment, clearing all variables, functions, and definitions.
Result history is also cleared.

=item B<help>

Enter interactive help mode to explore documentation.

=item B<copyright>

Display copyright information.

=item B<license>

Display license information (Artistic License 2.0).

=item B<quit>, B<exit>, B<q>

Exit the REPL and return to the shell. Alternatively, press Ctrl+D (EOF).

=back

=head1 EXAMPLES

=head2 Basic Execution

    # Execute a script
    sidef script.sf

    # With command-line arguments
    sidef script.sf arg1 arg2

    # Read from STDIN
    cat script.sf | sidef -
    sidef script.sf < input.txt

=head2 One-Liner Examples

Mathematical calculations:

    # Sum of squares
    sidef -E '100.of { |i| i**2 }.sum.say'

    # First 20 primes
    sidef -E '20.pn_primes.say'

    # Check if number is prime
    sidef -E 'say (2**127 - 1 -> is_prime)'

    # High-precision pi
    sidef -P 100 -E 'say Num.pi'

Text processing:

    # Convert to uppercase
    echo "hello" | sidef -E 'STDIN.slurp.uc.print'

    # Filter lines matching pattern
    sidef -E 'STDIN.lines.grep(/pattern/).each{.say}' < file.txt

    # Number lines
    sidef -E 'STDIN.lines.each_kv{|i,v| say "#{i+1}: #{v}"}' < file.txt

=head2 Development Workflow

Check syntax before running:

    sidef -C script.sf && sidef script.sf

Optimize and execute:

    sidef -O1 script.sf                     # Recommended
    sidef -O2 computation.sf                # Aggressive

Debug with parser warnings:

    sidef -k -w script.sf                   # Warnings with traces
    sidef -k -W script.sf                   # Fatal warnings

Enable precompilation for large scripts:

    sidef -s large_project.sf               # Cache compiled code

Combine options for maximum performance:

    sidef -O1 -s script.sf                  # Optimize and cache

=head2 Compilation Examples

Compile to standalone Perl program:

    sidef -c -o myapp.pl script.sf
    chmod +x myapp.pl
    ./myapp.pl

Translate to Perl for inspection:

    sidef -R perl script.sf | less

Normalize Sidef code style:

    sidef -r script.sf > normalized.sf

=head2 Number Theory with External Tools

    # Factor with YAFU
    sidef -N 'USE_YAFU=true' -E 'say factor(2**256 + 1)'

    # Count primes with primecount
    sidef -N 'USE_PRIMECOUNT=true' -E 'say prime_count(10**12)'

    # Use FactorDB
    sidef -N 'USE_FACTORDB=true' -E 'say factor(2**512 + 1)'

=head1 ENVIRONMENT

=over 4

=item B<SIDEF_CACHE_DIR>

Override default cache directory for precompiled code. Default:
C<~/.sidef/cache/>

    export SIDEF_CACHE_DIR=/tmp/sidef_cache

=item B<SIDEF_INC>

Additional directories to search for Sidef modules, separated by colons
(Unix) or semicolons (Windows).

    export SIDEF_INC=/usr/local/lib/sidef:/opt/sidef/modules

=item B<SIDEF_OPT>

Default options applied to every invocation. Useful for setting project-wide
defaults.

    export SIDEF_OPT="-O1 -P 100"

=back

=head1 FILES

=over 4

=item F<~/.sidef/>

User configuration directory. Created automatically on first run.

=item F<~/.sidef/sidef_history.txt>

REPL command history. Persists across sessions.

=item F<~/.sidef/cache/>

Precompiled code cache directory. Used when B<-s> flag is enabled.

=item F</usr/local/lib/sidef/>

System-wide module directory (typical location).

=item F<*.sf>

Sidef source files (conventional extension). Can use any extension or none.

=back

=head1 EXIT STATUS

=over 4

=item B<0>

Successful execution. Program ran without errors.

=item B<1>

General error. Includes syntax errors, runtime errors, unhandled exceptions,
file not found, and permission denied.

=item B<2>

No input provided when expected.

=back

=head1 DIAGNOSTICS

Common error messages and their meanings:

=over 4

=item B<Can't open sidef script "filename": No such file or directory>

The specified script file does not exist or is not readable. Check file name
spelling and permissions.

=item B<Syntax error at line N>

Parser encountered invalid syntax at the specified line. Use B<-k> for more
detailed diagnostics.

=item B<Invalid precision: E<lt>E<lt>valueE<gt>E<gt> (expected a positive integer)>

The value provided to B<-P> must be a positive integer.

=item B<No code specified for -E>

The B<-E> option requires a code argument.

=item B<Invalid format: E<lt>E<lt>optionE<gt>E<gt>!>

The B<-N> option format is incorrect. Expected format:
C<'NAME1=VALUE1; NAME2=VALUE2'>

=item B<"Data::Dump" is not installed!>

The B<-D> option requires the Perl module Data::Dump. Install with:
C<cpan Data::Dump> or C<cpanm Data::Dump>

=item B<Variable 'name' is not declared in the current scope>

Attempting to use an undeclared variable. Declare with C<var>, C<global>,
C<const>, or C<define>.

=back

=head1 BUGS AND LIMITATIONS

=head2 Known Limitations

=over 4

=item * Code using C<eval()> may not compile correctly with B<-c>

=item * Very large precision (>100,000 digits) may cause performance issues

=item * Precompilation cache can grow large with many unique scripts

=item * Some REPL features may not work identically to script execution

=back

=head2 Reporting Bugs

Report bugs on GitHub: L<https://github.com/trizen/sidef/issues>

Please include:

=over 4

=item * Sidef version (C<sidef -v>), OS, and Perl version (C<perl -v>)

=item * Complete error message with stack trace

=item * Minimal code example reproducing the issue

=item * Expected behavior vs. actual behavior

=back

=head1 SEE ALSO

=head2 Official Resources

=over 4

=item * B<GitHub Repository>: L<https://github.com/trizen/sidef>

=item * B<Language Tutorial>: L<https://codeberg.org/trizen/sidef/wiki>

=item * B<Official Documentation>: L<https://trizen.gitbook.io/sidef-lang/>

=item * B<Rosetta Code>: L<https://rosettacode.org/wiki/Sidef>

=back

=head2 Related Tools

=over 4

=item * B<perldoc>(1) - View Perl documentation

=item * B<perl>(1) - Perl interpreter

=item * B<gmp>(3) - GNU Multiple Precision Arithmetic Library

=item * B<mpfr>(3) - Multiple Precision Floating-Point Reliable Library

=back

=head2 Module Documentation

Access documentation for Sidef modules using the interactive help:

    sidef -H
    help> Array()

Or use C<perldoc> directly:

    perldoc Sidef::Types::Array::Array

=head1 AUTHORS

=over 4

=item * B<Daniel Șuteu> - Primary author and maintainer

=item * B<Ioana Fălcușan> - Contributor

=back

For a complete list of contributors, see:
L<https://github.com/trizen/sidef/graphs/contributors>

=head1 ACKNOWLEDGMENTS

Sidef builds upon:

=over 4

=item * Perl programming language and ecosystem

=item * GMP (GNU Multiple Precision Arithmetic Library)

=item * MPFR (Multiple Precision Floating-Point Reliable Library)

=item * MPC (Multiple Precision for Complex Arithmetic Library)

=item * Various CPAN modules

=item * Open source community contributions

=back

=head1 LICENSE AND COPYRIGHT

Copyright © 2013-2026 Daniel Șuteu, Ioana Fălcușan. All Rights Reserved.

This program is free software; you can redistribute it and/or modify it
under the terms of the B<Artistic License (2.0)>. You may obtain a copy
of the full license at:

L<https://www.perlfoundation.org/artistic-license-20.html>

Any use, modification, and distribution of the Standard or Modified
Versions is governed by this Artistic License. By using, modifying or
distributing the Package, you accept this license. Do not use, modify,
or distribute the Package, if you do not accept this license.

Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
AND CONTRIBUTORS "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

=cut
