2014-04-21 08:45:58 +04:00
|
|
|
package core;
|
|
|
|
use strict;
|
2014-04-22 05:08:18 +04:00
|
|
|
use warnings FATAL => qw(all);
|
2014-04-21 08:45:58 +04:00
|
|
|
use Exporter 'import';
|
|
|
|
our @EXPORT_OK = qw($core_ns);
|
2014-04-24 07:42:36 +04:00
|
|
|
use Time::HiRes qw(time);
|
2014-04-21 08:45:58 +04:00
|
|
|
|
2014-04-24 06:46:57 +04:00
|
|
|
use readline;
|
|
|
|
use types qw(_sequential_Q _equal_Q _clone $nil $true $false
|
2014-12-19 05:33:49 +03:00
|
|
|
_nil_Q _true_Q _false_Q
|
|
|
|
_symbol _symbol_Q _keyword _keyword_Q _list_Q _vector_Q
|
2014-04-24 06:46:57 +04:00
|
|
|
_hash_map _hash_map_Q _assoc_BANG _dissoc_BANG _atom_Q);
|
2014-04-22 05:48:16 +04:00
|
|
|
use reader qw(read_str);
|
2014-04-21 08:45:58 +04:00
|
|
|
use printer qw(_pr_str);
|
|
|
|
|
|
|
|
use Data::Dumper;
|
|
|
|
|
|
|
|
# String functions
|
|
|
|
|
|
|
|
sub pr_str {
|
2014-04-24 06:46:57 +04:00
|
|
|
return String->new(join(" ", map {_pr_str($_, 1)} @{$_[0]->{val}}));
|
2014-04-21 08:45:58 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
sub str {
|
2014-04-24 06:46:57 +04:00
|
|
|
return String->new(join("", map {_pr_str($_, 0)} @{$_[0]->{val}}));
|
2014-04-21 08:45:58 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
sub prn {
|
2014-04-24 06:46:57 +04:00
|
|
|
print join(" ", map {_pr_str($_, 1)} @{$_[0]->{val}}) . "\n";
|
2014-04-21 08:45:58 +04:00
|
|
|
return $nil
|
|
|
|
}
|
|
|
|
|
|
|
|
sub println {
|
2014-04-24 06:46:57 +04:00
|
|
|
print join(" ", map {_pr_str($_, 0)} @{$_[0]->{val}}) . "\n";
|
2014-04-21 08:45:58 +04:00
|
|
|
return $nil
|
|
|
|
}
|
|
|
|
|
2014-04-23 08:50:43 +04:00
|
|
|
sub mal_readline {
|
2014-04-24 06:46:57 +04:00
|
|
|
my $line = readline::mal_readline(${$_[0]});
|
|
|
|
return defined $line ? String->new($line) : $nil;
|
2014-04-23 08:50:43 +04:00
|
|
|
}
|
|
|
|
|
2014-04-22 05:48:16 +04:00
|
|
|
sub slurp {
|
2014-04-24 06:46:57 +04:00
|
|
|
my $fname = ${$_[0]};
|
|
|
|
open(my $fh, '<', $fname) or die "error opening '$fname'";
|
|
|
|
my $data = do { local $/; <$fh> };
|
2014-04-22 05:48:16 +04:00
|
|
|
String->new($data)
|
|
|
|
}
|
|
|
|
|
2014-04-23 08:50:43 +04:00
|
|
|
# Hash Map functions
|
|
|
|
|
|
|
|
sub assoc {
|
|
|
|
my $src_hsh = shift;
|
2014-04-24 06:46:57 +04:00
|
|
|
my $new_hsh = { %{$src_hsh->{val}} };
|
2014-04-23 08:50:43 +04:00
|
|
|
return _assoc_BANG($new_hsh, @_);
|
|
|
|
}
|
|
|
|
|
|
|
|
sub dissoc {
|
|
|
|
my $src_hsh = shift;
|
2014-04-24 06:46:57 +04:00
|
|
|
my $new_hsh = { %{$src_hsh->{val}} };
|
2014-04-23 08:50:43 +04:00
|
|
|
return _dissoc_BANG($new_hsh, @_);
|
|
|
|
}
|
2014-04-21 08:45:58 +04:00
|
|
|
|
2014-04-23 08:50:43 +04:00
|
|
|
|
|
|
|
sub get {
|
|
|
|
my ($hsh, $key) = @_;
|
|
|
|
return $nil if $hsh eq $nil;
|
2014-04-24 06:46:57 +04:00
|
|
|
return exists $hsh->{val}->{$$key} ? $hsh->{val}->{$$key} : $nil;
|
2014-04-23 08:50:43 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
sub contains_Q {
|
|
|
|
my ($hsh, $key) = @_;
|
|
|
|
return $nil if $hsh eq $false;
|
2014-04-24 06:46:57 +04:00
|
|
|
return (exists $hsh->{val}->{$$key}) ? $true : $false;
|
2014-04-23 08:50:43 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
sub mal_keys {
|
2014-04-24 06:46:57 +04:00
|
|
|
my @ks = map { String->new($_) } keys %{$_[0]->{val}};
|
2014-04-23 08:50:43 +04:00
|
|
|
return List->new(\@ks);
|
|
|
|
}
|
|
|
|
|
|
|
|
sub mal_vals {
|
2014-04-24 06:46:57 +04:00
|
|
|
my @vs = values %{$_[0]->{val}};
|
2014-04-23 08:50:43 +04:00
|
|
|
return List->new(\@vs);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# Sequence functions
|
2014-04-22 06:13:04 +04:00
|
|
|
|
|
|
|
sub cons {
|
|
|
|
my ($a, $b) = @_;
|
|
|
|
my @new_arr = @{[$a]};
|
2014-04-24 06:46:57 +04:00
|
|
|
push @new_arr, @{$b->{val}};
|
2014-04-22 06:13:04 +04:00
|
|
|
List->new(\@new_arr);
|
|
|
|
}
|
|
|
|
|
|
|
|
sub concat {
|
2014-04-22 06:47:36 +04:00
|
|
|
if (scalar(@_) == 0) { return List->new([]); }
|
|
|
|
my ($a) = shift;
|
2014-04-24 06:46:57 +04:00
|
|
|
my @new_arr = @{$a->{val}};
|
|
|
|
map { push @new_arr, @{$_->{val}} } @_;
|
2014-04-22 06:13:04 +04:00
|
|
|
List->new(\@new_arr);
|
|
|
|
}
|
|
|
|
|
2014-12-19 05:33:49 +03:00
|
|
|
sub nth {
|
|
|
|
my ($seq,$i) = @_;
|
|
|
|
if (@{$seq->{val}} > $i) {
|
|
|
|
return scalar($seq->nth($i));
|
|
|
|
} else {
|
|
|
|
die "nth: index out of bounds";
|
|
|
|
}
|
|
|
|
}
|
2014-04-22 06:47:36 +04:00
|
|
|
|
2016-01-29 05:36:44 +03:00
|
|
|
sub first {
|
|
|
|
my ($seq) = @_;
|
|
|
|
return $nil if (_nil_Q($seq));
|
|
|
|
return scalar(@{$seq->{val}}) > 0 ? $seq->nth(0) : $nil;
|
|
|
|
}
|
2014-04-22 06:47:36 +04:00
|
|
|
|
2016-01-29 05:36:44 +03:00
|
|
|
sub rest { return _nil_Q($_[0]) ? List->new([]) : $_[0]->rest(); }
|
2014-04-22 06:47:36 +04:00
|
|
|
|
2014-12-19 05:33:49 +03:00
|
|
|
sub count {
|
|
|
|
if (_nil_Q($_[0])) {
|
|
|
|
return Integer->new(0);
|
|
|
|
} else {
|
|
|
|
return Integer->new(scalar(@{$_[0]->{val}}))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-23 08:50:43 +04:00
|
|
|
sub apply {
|
2014-04-24 06:46:57 +04:00
|
|
|
my @all_args = @{$_[0]->{val}};
|
2014-04-23 08:50:43 +04:00
|
|
|
my $f = $all_args[0];
|
|
|
|
my @apply_args = @all_args[1..$#all_args];
|
|
|
|
my @args = @apply_args[0..$#apply_args-1];
|
2014-04-24 06:46:57 +04:00
|
|
|
push @args, @{$apply_args[$#apply_args]->{val}};
|
2014-04-23 08:50:43 +04:00
|
|
|
if ((ref $f) =~ /^Function/) {
|
|
|
|
return $f->apply(List->new(\@args));
|
|
|
|
} else {
|
|
|
|
return &{ $f }(List->new(\@args));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub mal_map {
|
|
|
|
my $f = shift;
|
|
|
|
my @arr;
|
|
|
|
if ((ref $f) =~ /^Function/) {
|
2014-04-24 06:46:57 +04:00
|
|
|
@arr = map { $f->apply(List->new([$_])) } @{$_[0]->{val}};
|
2014-04-23 08:50:43 +04:00
|
|
|
} else {
|
2014-04-24 06:46:57 +04:00
|
|
|
@arr = map { &{ $f}(List->new([$_])) } @{$_[0]->{val}};
|
2014-04-23 08:50:43 +04:00
|
|
|
}
|
|
|
|
return List->new(\@arr);
|
|
|
|
}
|
|
|
|
|
2015-07-16 17:16:40 +03:00
|
|
|
sub conj {
|
|
|
|
my ($lst, @args) = @{$_[0]->{val}};
|
|
|
|
my $new_lst = _clone($lst);
|
|
|
|
if (_list_Q($new_lst)) {
|
|
|
|
unshift @{$new_lst->{val}}, reverse @args;
|
|
|
|
} else {
|
|
|
|
push @{$new_lst->{val}}, @args;
|
|
|
|
}
|
|
|
|
return $new_lst;
|
|
|
|
}
|
|
|
|
|
2014-04-22 06:47:36 +04:00
|
|
|
|
2014-04-24 06:46:57 +04:00
|
|
|
# Metadata functions
|
|
|
|
sub with_meta {
|
|
|
|
my $new_obj = _clone($_[0]);
|
|
|
|
$new_obj->{meta} = $_[1];
|
|
|
|
return $new_obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub meta {
|
|
|
|
if ((ref $_[0]) && !((ref $_[0]) =~ /^CODE/)) {
|
|
|
|
return $_[0]->{meta};
|
|
|
|
} else {
|
|
|
|
return $nil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# Atom functions
|
|
|
|
sub swap_BANG {
|
|
|
|
my ($atm,$f,@args) = @_;
|
|
|
|
unshift @args, $atm->{val};
|
|
|
|
if ((ref $f) =~ /^Function/) {
|
|
|
|
return $atm->{val} = $f->apply(List->new(\@args));
|
|
|
|
} else {
|
|
|
|
return $atm->{val} = &{ $f }(List->new(\@args));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-22 06:13:04 +04:00
|
|
|
|
2014-04-21 08:45:58 +04:00
|
|
|
our $core_ns = {
|
2014-04-24 06:46:57 +04:00
|
|
|
'=' => sub { _equal_Q($_[0]->nth(0), $_[0]->nth(1)) ? $true : $false },
|
|
|
|
'throw' => sub { die $_[0]->nth(0) },
|
|
|
|
'nil?' => sub { _nil_Q($_[0]->nth(0)) ? $true : $false },
|
|
|
|
'true?' => sub { _true_Q($_[0]->nth(0)) ? $true : $false },
|
|
|
|
'false?' => sub { _false_Q($_[0]->nth(0)) ? $true : $false },
|
2014-12-19 05:33:49 +03:00
|
|
|
'symbol' => sub { Symbol->new(${$_[0]->nth(0)}) },
|
2014-04-24 06:46:57 +04:00
|
|
|
'symbol?' => sub { _symbol_Q($_[0]->nth(0)) ? $true : $false },
|
2014-12-19 05:33:49 +03:00
|
|
|
'keyword' => sub { _keyword(${$_[0]->nth(0)}) },
|
|
|
|
'keyword?' => sub { _keyword_Q($_[0]->nth(0)) ? $true : $false },
|
2014-04-21 08:45:58 +04:00
|
|
|
|
|
|
|
'pr-str' => sub { pr_str($_[0]) },
|
|
|
|
'str' => sub { str($_[0]) },
|
|
|
|
'prn' => sub { prn($_[0]) },
|
|
|
|
'println' => sub { println($_[0]) },
|
2014-04-24 06:46:57 +04:00
|
|
|
'readline' => sub { mal_readline($_[0]->nth(0)) },
|
|
|
|
'read-string' => sub { read_str(${$_[0]->nth(0)}) },
|
|
|
|
'slurp' => sub { slurp($_[0]->nth(0)) },
|
|
|
|
'<' => sub { ${$_[0]->nth(0)} < ${$_[0]->nth(1)} ? $true : $false },
|
|
|
|
'<=' => sub { ${$_[0]->nth(0)} <= ${$_[0]->nth(1)} ? $true : $false },
|
|
|
|
'>' => sub { ${$_[0]->nth(0)} > ${$_[0]->nth(1)} ? $true : $false },
|
|
|
|
'>=' => sub { ${$_[0]->nth(0)} >= ${$_[0]->nth(1)} ? $true : $false },
|
|
|
|
'+' => sub { Integer->new(${$_[0]->nth(0)} + ${$_[0]->nth(1)}) },
|
|
|
|
'-' => sub { Integer->new(${$_[0]->nth(0)} - ${$_[0]->nth(1)}) },
|
|
|
|
'*' => sub { Integer->new(${$_[0]->nth(0)} * ${$_[0]->nth(1)}) },
|
|
|
|
'/' => sub { Integer->new(${$_[0]->nth(0)} / ${$_[0]->nth(1)}) },
|
2014-04-24 07:42:36 +04:00
|
|
|
'time-ms' => sub { Integer->new(int(time()*1000)) },
|
2014-04-24 06:46:57 +04:00
|
|
|
|
|
|
|
'list' => sub { List->new($_[0]->{val}) },
|
|
|
|
'list?' => sub { _list_Q($_[0]->nth(0)) ? $true : $false },
|
|
|
|
'vector' => sub { Vector->new($_[0]->{val}) },
|
|
|
|
'vector?' => sub { _vector_Q($_[0]->nth(0)) ? $true : $false },
|
|
|
|
'hash-map' => sub { _hash_map(@{$_[0]->{val}}) },
|
|
|
|
'map?' => sub { _hash_map_Q($_[0]->nth(0)) ? $true : $false },
|
|
|
|
'assoc' => sub { assoc(@{$_[0]->{val}}) },
|
|
|
|
'dissoc' => sub { dissoc(@{$_[0]->{val}}) },
|
|
|
|
'get' => sub { get($_[0]->nth(0),$_[0]->nth(1)) },
|
|
|
|
'contains?' => sub { contains_Q($_[0]->nth(0),$_[0]->nth(1)) },
|
|
|
|
'keys' => sub { mal_keys(@{$_[0]->{val}}) },
|
|
|
|
'vals' => sub { mal_vals(@{$_[0]->{val}}) },
|
|
|
|
|
|
|
|
'sequential?' => sub { _sequential_Q($_[0]->nth(0)) ? $true : $false },
|
|
|
|
'nth' => sub { nth($_[0]->nth(0), ${$_[0]->nth(1)}) },
|
|
|
|
'first' => sub { first($_[0]->nth(0)) },
|
|
|
|
'rest' => sub { rest($_[0]->nth(0)) },
|
|
|
|
'cons' => sub { cons($_[0]->nth(0), $_[0]->nth(1)) },
|
|
|
|
'concat' => sub { concat(@{$_[0]->{val}}) },
|
|
|
|
'empty?' => sub { scalar(@{$_[0]->nth(0)->{val}}) == 0 ? $true : $false },
|
2014-12-19 05:33:49 +03:00
|
|
|
'count' => sub { count($_[0]->nth(0)) },
|
2014-04-23 08:50:43 +04:00
|
|
|
'apply' => sub { apply($_[0]) },
|
2014-04-24 06:46:57 +04:00
|
|
|
'map' => sub { mal_map($_[0]->nth(0), $_[0]->nth(1)) },
|
2015-07-16 17:16:40 +03:00
|
|
|
'conj' => \&conj,
|
2014-04-24 06:46:57 +04:00
|
|
|
|
|
|
|
'with-meta' => sub { with_meta($_[0]->nth(0), $_[0]->nth(1)) },
|
|
|
|
'meta' => sub { meta($_[0]->nth(0)) },
|
|
|
|
'atom' => sub { Atom->new($_[0]->nth(0)) },
|
|
|
|
'atom?' => sub { _atom_Q($_[0]->nth(0)) ? $true : $false },
|
|
|
|
'deref' => sub { $_[0]->nth(0)->{val} },
|
|
|
|
'reset!' => sub { $_[0]->nth(0)->{val} = $_[0]->nth(1) },
|
|
|
|
'swap!' => sub { swap_BANG(@{$_[0]->{val}}) },
|
2014-04-21 08:45:58 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
1;
|