-
Notifications
You must be signed in to change notification settings - Fork 2
/
jml.pl
64 lines (53 loc) · 1.69 KB
/
jml.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# a very simple implementation of example: jml in perl
# TEST: (echo "[macro dec \$A][- \$A 1][/macro][* [x] 77 [+ 3 4 [* 7 2] [map inc [iota 1 10]]]] DEC=[dec 7]" ; echo "[+ 10 20 30] ARGS=[args 1 2 3]") | perl jml.pl
# set to 1 to get trace of substitutions
$trace = 1;
my %funs = ();
sub fun {
my($name, $fun) = @_;
if (ref($fun) eq "CODE") {
$funs{$name} = $fun;
} else {
# if it's a string, create a function using closure
$funs{$name} = sub {
my @args = @_;
local($a, $b, $c, $d, $e, $f, $g) = @args;
return eval $fun;
};
}
}
sub macro {
local($name, $vars, $expr) = @_;
fun($name, eval "sub { local($vars) = \@_; return \"$expr\"; };");
return "";
}
# define function as simple string expression or pass sub
fun('+', sub { my $r = 0; $r += $_ for @_; return $r; });
fun('-', '$a-$b');
fun('*', sub { my $r = 1; $s *= $_ for @_; return $r; });
fun('/', '$a/$b');
fun('inc', '$a+1');
fun('iota', 'join(" ", ($a..$b))');
fun('args', 'join(" ", @args)');
fun('map', 'join(" ", map { "[$a $_]" } @args[1..$#args])');
# read-eval loop
print jml($_) for <>;
sub jml {
local($line) = @_;
while ($line =~ s/\[macro (\S+)\s*([^\[\]]*)\](.*?)\[\/macro\]/macro($1, $2, $3)/ge) {}
print STDERR ">>>$line" if $trace;
while ($line =~ s/\[(\S+)\s*([^\[\]]*?)\]/&replace($1, $2)/ge) {
print STDERR ">>>$line" if $trace
}
return $line;
}
sub replace {
local($name, $args) = @_;
return "($name $args)" if ($name.$args) =~ /\(%FAIL:/;
my $fun = $funs{$name};
if (ref($fun) eq "CODE") {
return ''. $fun->(split(/\s+/, $args));
} else {
return "(%FAIL:@_)";
}
}