package Scheme::Generator; use strict; use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); use Carp; $VERSION = '0.01'; @ISA = qw(Exporter); @EXPORT_OK = qw(generate); use Data::Dumper; use Scheme::Builtins; sub _gensym { return sprintf "G%04d",shift->{gensym}++; } sub _add_inst { my $self = shift; push @{$self->{instruction}},[@_]; } #------------------------------------ sub _new_regs { { I => { map { $_ => 0 } (0..31) }, N => { map { $_ => 0 } (0..31) }, S => { map { $_ => 0 } (0..31) }, P => { map { $_ => 0 } (0..31) }, }; }; sub _save { my $self = shift; my $count = shift; my $type = shift || 'I'; die "No registers to save" unless $count and $count>0; die "Illegal register type" unless $type and $type=~/^[INPS]$/; my @temp; for(0..31) { next if $self->{regs}->{$type}{$_} == 1; last if $count<=0; push @temp,"$type$_"; $self->{regs}->{$type}{$_}=1; $count--; } @temp; } sub _save_set { my $self = shift; my %regs = %{$self->{regs}}; for my $type (keys %regs) { for my $count (0..31) { $self->_add_inst ('', 'save', ["$type$count"]) if $regs{$type}->{$count}; } } } sub _save_1 { my $self = shift; my $type = shift || 'I'; my @temp = $self->_save(1, $type); $temp[0]; } sub _restore { my $self = shift; die "Nothing to restore" unless defined @_; foreach my $reg (@_) { next if grep { $_ eq $reg } qw (none); $reg =~ /^(\w)(\d+)/; die "Missing register type" unless defined $1; if ($self->{regs}->{$1}{$2}) { $self->{regs}->{$1}{$2} = 0; } } } sub _restore_set { my $self = shift; my %regs = %{$self->{regs}}; for my $type (reverse keys %regs) { for (my $count=31; $count>=0; $count--) { $self->_add_inst ('','restore',["$type$count"]) if $regs{$type}->{$count}; } } } sub _num_arg { my ($node, $expected, $name) = @_; my $args = scalar @{$node->{children}} - 1; confess "$name: Wrong number of arguments (expected $expected, got $args).\n" if ($args != $expected); } sub _get_arg { my ($node, $num) = @_; $node->{children}->[$num]; } sub _get_args { my ($node, $num) = @_; $num = 1 unless defined $num; my @args = splice @{$node->{children}}, $num; return @args; } # until there is a working find_lex/store_lex sub _find_lex { my ($self, $symbol) = @_; my $return = $self->_save_1 ('P'); $self->_add_inst ('','find_lex',[$return,"\"$symbol\""]); return $return; } sub _store_lex { my ($self, $symbol,$value) = @_; $self->_add_inst ('','store_lex',["\"$symbol\"",$value]); } sub _new_lex { my ($self, $symbol, $value) = @_; $self->_add_inst ('','store_lex',[-1,"\"$symbol\"",$value]); $self->{scope}->{$symbol} = $value; } sub _new_pair { my ($self) = @_; my $return = $self->_save_1('P'); $self->_add_inst('', 'new', [$return,'.Array']); $self->_add_inst('', 'set', [$return, 2]); return $return; } #------------------------------------ my $type_map = { INTEGER => [ 1, 'I' ], RATIONAL => [ 2, 'I' ], REAL => [ 1, 'N' ], COMPLEX => [ 2, 'I' ], STRING => [ 1, 'S' ], }; #------------------------------------ sub _constant { my ($self, $value) = @_; my $return; if ($value =~ /^[-+]?\d+$/) { $return = $self->_save_1 ('I'); $self->_add_inst ('', 'set', [$return,$value]); } elsif ($value =~ /^[-+]?((\d+\.\d*)|(\.d+))([eE][-+]?\d+)?$/) { $return = $self->_save_1 ('N'); $self->_add_inst ('', 'set', [$return,$value]); } else { $return = $self->_save_1 ('I'); $self->_add_inst ('', 'set', [$return,0]); } return $return; } sub _morph { my ($self, $to, $from) = @_; if ($to =~ /P/) { if ($from =~ /P/) { $self->_add_inst ('', 'clone',[$to,$from]); } elsif ($from =~ /I/) { $self->_add_inst ('', 'new',[$to,'.PerlInt']); $self->_add_inst ('', 'set',[$to,$from]); } elsif ($from =~ /N/) { $self->_add_inst ('', 'new',[$to,'.PerlNum']); $self->_add_inst ('', 'set',[$to,$from]); } } } #---- Section 4 ---- sub __quoted { my ($self, $node, $return, $special) = @_; if (exists $node->{value}) { my $value = $node->{value}; if ($value =~ /^[-+]?\d+$/) { $self->_add_inst ('', 'new',[$return,'.PerlInt']); $self->_add_inst ('', 'set',[$return,$value]); } elsif ($value =~ /^[-+]?((\d+\.\d*)|(\.d+))([eE][-+]?\d+)?$/) { $self->_add_inst ('', 'new',[$return,'.PerlNum']); $self->_add_inst ('', 'set',[$return,$value]); } else { # assume its a symbol $self->_add_inst ('', 'new',[$return,'.PerlString']); $self->_add_inst ('', 'set',[$return,"\"$value\""]); } } elsif (exists $node->{children}) { my $children = $node->{children}; $self->_add_inst ('', 'new', [$return,'.PerlUndef']); for (reverse @$children) { if (exists $_->{children}) { my $arg0 = _get_arg($_, 0); if (exists $arg0->{value}) { my $value = $arg0->{value}; if (exists $special->{$value}) { _num_arg($_, 1); $special->{$value}->($self, _get_arg($_, 1), $return); next; } } } my $item = $self->_save_1 ('P'); __quoted ($self, $_, $item, $special); my $pair = $self->_new_pair(); $self->_add_inst ('', 'set', [$pair.'[0]',$item]); $self->_add_inst ('', 'set', [$pair.'[1]',$return]); $self->_add_inst ('', 'set', [$return,$pair]); $self->_restore ($item, $pair); } } return $return; } sub _op_quote { my ($self, $node) = @_; my $return = $self->_save_1 ('P'); _num_arg ($node, 1, 'quote'); my $item = _get_arg($node,1); return __quoted ($self, $item, $return, {}); } sub _op_quasiquote { my ($self, $node) = @_; my $return = $self->_save_1 ('P'); my $special = { unquote => \&_qq_unquote, 'unquote-splicing' => \&_qq_unquote_splicing }; _num_arg ($node, 1, 'quote'); my $item = _get_arg($node,1); __quoted ($self, $item, $return, $special); } # helper functions for quasiquote sub _qq_unquote { my ($self, $node, $return) = @_; my $item = $self->_generate($node); if ($item =~ /^[INS]/) { my $temp = $self->_save_1('P'); $self->_morph($temp, $item); $self->_restore($item); $item = $temp; } my $pair = $self->_new_pair; $self->_add_inst('', 'set', [$pair.'[0]',$item]); $self->_add_inst('', 'set', [$pair.'[1]',$return]); $self->_add_inst('', 'set', [$return,$pair]); $self->_restore($item, $pair); return $return; } sub _qq_unquote_splicing { my ($self, $node, $return) = @_; my $list = $self->_generate($node); die "unquote-splicing called on no list" if ($list =~ /^[INS]/); my $type = $self->_save_1('I'); my $head = $self->_save_1('P'); my $label = $self->_gensym; # check for empty list $self->_add_inst('', 'typeof', [$type, $list]); $self->_add_inst('', 'eq', [$type,'.PerlUndef',"DONE_$label"]); my $copy = $self->_new_pair; $self->_add_inst('', 'set', [$head, $copy]); # maybe ensure that $type is a pair here my $temp = $self->_save_1('P'); $self->_add_inst("ITER_$label", 'set', [$temp,$list.'[0]']); $self->_add_inst('', 'set', [$copy.'[0]',$temp]); $self->_restore($temp); $self->_add_inst('', 'set', [$list,$list.'[1]']); $self->_add_inst('', 'typeof', [$type,$list]); $self->_add_inst('', 'eq', [$type,'.PerlUndef',"FINISH_$label"]); $temp = $self->_new_pair; $self->_add_inst('', 'set', [$copy.'[1]',$temp]); $self->_add_inst('', 'set', [$copy,$temp]); $self->_add_inst('', 'branch', ["ITER_$label"]); $self->_restore($temp); # append the rest to the end of list $self->_add_inst("FINISH_$label", 'set', [$copy.'[1]',$return]); $self->_add_inst('', 'set', [$return,$head]); $self->_add_inst("DONE_$label"); $self->_restore($list, $copy, $head, $type); return $return; } sub _op_lambda { my ($self,$node) = @_; my $return; my $label = $self->_gensym(); my $temp; $return = $self->_save_1 ('P'); $self->_add_inst ('', 'newsub',[$return,'.Closure',"LAMBDA_$label"]); $self->_add_inst ('', 'branch',["DONE_$label"]); $self->_add_inst ("LAMBDA_$label"); # caller saved => start a new frame push @{$self->{frames}}, $self->{regs}; $self->{regs} = _new_regs; # P1 is the return contination $self->{regs}{P}{1} = 1; # expand the lexical scope $self->_add_inst('', 'new_pad', [-1]); my $oldscope = $self->{scope}; $self->{scope} = { '*UP*' => $oldscope }; my $num = 5; my @args = @{_get_arg($node,1)->{children}}; for (@args) { my $arg = $_->{value}; $self->_new_lex($arg, "P$num"); $num++ } $temp = 'none'; for (_get_args($node,2)) { $self->_restore ($temp); $temp = $self->_generate($_); } $self->_add_inst('', 'set', ['P5', $temp]); $self->_add_inst('', 'pop_pad'); $self->_add_inst('', 'invoke P1'); $self->_add_inst("DONE_$label"); $self->{regs} = pop @{$self->{frames}}; $self->{scope} = $self->{scope}->{'*UP*'}; return $return; } sub _op_if { my ($self,$node) = @_; my $return; my $label = $self->_gensym(); my $cond = $self->_generate(_get_arg($node,1)); $self->_add_inst('','eq',[$cond,0,"FALSE_$label"]); $self->_restore($cond); $return = $self->_save_1 ('P'); my $true = $self->_generate(_get_arg($node,2)); $self->_morph($return,$true); $self->_add_inst('','branch',["DONE_$label"]); $self->_restore($true); $self->_add_inst("FALSE_$label"); my $false = $self->_generate(_get_arg($node,3)); $self->_morph($return,$false); $self->_restore($false); $self->_add_inst("DONE_$label"); return $return; } sub _op_define { my ($self, $node) = @_; _num_arg ($node, 2, 'define'); my ($symbol, $lambda, $value); if (exists _get_arg($node,1)->{children}) { my @formals; ($symbol, @formals) = @{_get_arg($node,1)->{children}}; $symbol = $symbol->{value}; $lambda = { children => [ { value => 'lambda' }, { children => [ @formals ] }, _get_args ($node, 2) ] }; } else { $symbol = _get_arg($node,1)->{value}; $lambda = _get_arg($node,2); } if (exists $self->{scope}->{$symbol}) { die "define: $symbol is already defined\n"; } else { $self->{scope}->{$symbol} = '*unknown*'; } $value = $self->_generate($lambda); if ($value !~ /^P/) { my $pmc = $self->_save_1 ('P'); $self->_morph ($pmc, $value); $self->_restore ($value); $value = $pmc; } $self->_new_lex ($symbol,$value); return $value; } sub _op_set_bang { my ($self, $node) = @_; _num_arg ($node, 2, 'set!'); my $symbol = _get_arg ($node, 1)->{value}; my $temp = $self->_generate(_get_arg($node,2)); if ($temp !~ /^P/) { my $pmc = $self->_save_1 ('P'); $self->_morph ($pmc, $temp); $self->_restore ($temp); $temp = $pmc; } $self->_store_lex ($symbol,$temp); return $temp; } sub _op_cond { my ($self, $node) = @_; my @clauses = _get_args($node); my $transnode; if ($clauses[-1]->{children}->[0]->{value} eq 'else') { my $elseclause = pop @clauses; $transnode = { children => [ { value => 'begin'}, _get_args($elseclause) ] }; } else { $transnode = { value => '#f' }; } for my $clause ( reverse @clauses ) { $transnode = { children => [ { value => 'if' }, _get_arg($clause,0), { children => [ { value => 'begin' }, _get_args($clause,1) ] }, $transnode ] }; } $self->_generate($transnode); } sub _op_case { } sub _op_and { my ($self,$node) = @_; my $return; my $label = $self->_gensym(); $return = $self->_constant(0); for(_get_args($node)) { my $temp = $self->_generate($_); $self->_add_inst('' ,'eq' ,[$temp,0,"DONE_$label"]); $self->_restore($temp); } $self->_add_inst('' ,'set',[$return,1]); $self->_add_inst("DONE_$label"); return $return; } sub _op_or { my ($self,$node) = @_; my $return; my $label = $self->_gensym(); $return = $self->_constant(1); for(_get_args($node)) { my $temp = $self->_generate($_); $self->_add_inst('' ,'eq' ,[$temp,1,"DONE_$label"]); $self->_restore($temp); } $self->_add_inst('' ,'set',[$return,0]); $self->_add_inst("DONE_$label"); return $return; } sub _op_let { my ($self, $node) = @_; my $return; my ($locals, @body) = _get_args ($node,1); my (@variables, @values); for (@{$locals->{children}}) { _num_arg ($_, 1, 'let locals'); my ($var, $val) = _get_args ($_, 0); push @variables, $var; push @values, $val; } my $let = { children => [ { children => [ { value => 'lambda' }, { children => [ @variables ] }, @body ]}, @values ]}; $return = $self->_generate($let); return $return; } sub _op_let_star { } sub _op_letrec { } sub _op_begin { my ($self, $node) = @_; my $temp = 'none'; my @args = _get_args ($node); for (@args) { $self->_restore ($temp); $temp = $self->_generate ($_); } return $temp; } sub _op_do { } sub _op_delay { } #---- Section 6 ---- sub _op_not { my ($self,$node) = @_; my $return = $self->_save_1 ('I'); $self->_generate(_get_arg($node,1)); $self->_add_inst('','not',[$return,$return]); $return; } sub _op_boolean_p { } sub _op_eqv_p { } sub _op_eq_p { } sub _op_equal_p { } sub _op_pair_p { my ($self, $node) = @_; my $return; my $label = $self->_gensym(); _num_arg ($node, 1, 'pair?'); my $item = $self->_generate(_get_arg($node,1)); $return = $self->_save_1 ('I'); if ($item =~ /^[INS]/) { $self->_add_inst ('', 'set', [$return,0]); } else { $self->_add_inst ('', 'typeof', [$return,$item]); $self->_add_inst ('', 'ne', [$return,'.Array',"FAIL_$label"]); $self->_add_inst ('', 'set', [$return,$item]); $self->_add_inst ('', 'ne', [$return,2,"FAIL_$label"]); $self->_add_inst ('', 'set', [$return,1]); $self->_add_inst ('', 'branch', ["DONE_$label"]); $self->_add_inst ("FAIL_$label", 'set', [$return,0]); $self->_add_inst ("DONE_$label"); } return $return; } sub _op_cons { my ($self, $node) = @_; my $return; _num_arg ($node, 2, 'cons'); my $car = $self->_generate(_get_arg($node,1)); $return = $self->_save_1('P'); $self->_add_inst ('', 'new', [$return,'.Array']); $self->_add_inst ('', 'set', [$return,2]); $self->_add_inst ('', 'set', [$return.'[0]',$car]); $self->_restore ($car); my $cdr = $self->_generate(_get_arg($node,2)); $self->_add_inst ('', 'set', [$return.'[1]', $cdr]); $self->_restore ($cdr); return $return; } sub _op_car { my ($self, $node) = @_; _num_arg ($node, 1, 'car'); my $return = $self->_generate (_get_arg($node,1)); die "car: Element not pair\n" unless $return =~ /^P/; $self->_add_inst ('', 'set', [$return,$return.'[0]']); return $return; } sub _op_cdr { my ($self, $node) = @_; _num_arg ($node, 1, 'cdr'); my $return = $self->_generate (_get_arg($node,1)); die "cdr: Element not pair\n" unless $return =~ /^P/; $self->_add_inst ('', 'set', [$return,$return.'[1]']); return $return; } sub _op_set_car_bang { my ($self, $node) = @_; _num_arg ($node, 2, 'set-car!'); my $return = $self->_generate (_get_arg($node,1)); die "set-car!: Element not pair\n" unless $return =~ /^P/; my $value = $self->_generate (_get_arg($node,2)); $self->_add_inst ('', 'set', [$return.'[0]',$value]); $self->_restore ($value); return $return; } sub _op_set_cdr_bang { my ($self, $node) = @_; _num_arg ($node, 2, 'set-cdr!'); my $return = $self->_generate (_get_arg($node,1)); die "set-cdr!: Element not pair\n" unless $return =~ /^P/; my $value = $self->_generate (_get_arg($node,2)); $self->_add_inst ('', 'set', [$return.'[1]',$value]); $self->_restore ($value); return $return; } sub _op_null_p { my ($self, $node) = @_; my $return = $self->_save_1 ('I'); my $label = $self->_gensym(); _num_arg ($node, 1, 'null?'); my $temp = $self->_generate(_get_arg($node,1)); $self->_add_inst ('', 'typeof',[$return,$temp]); $self->_add_inst ('', 'ne', [$return,'.PerlUndef',"FAIL_$label"]); $self->_add_inst ('', 'set', [$return,1]); $self->_add_inst ('', 'branch', ["DONE_$label"]); $self->_add_inst ("FAIL_$label", 'set', [$return,0]); $self->_add_inst ("DONE_$label"); $self->_restore ($temp); return $return; } sub _op_list_p { } sub _op_list { my ($self, $node) = @_; my $label = $self->_gensym (); my $return = $self->_save_1 ('P'); $self->_add_inst ('', 'new',[$return,'.PerlUndef']); my @reverse = reverse _get_args($node); for (@reverse) { my $item = $self->_generate($_); my $pair = $self->_save_1 ('P'); $self->_add_inst ('', 'new',[$pair,'.Array']); $self->_add_inst ('', 'set',[$pair,2]); $self->_add_inst ('', 'set',[$pair.'[0]',$item]); $self->_add_inst ('', 'set',[$pair.'[1]',$return]); $self->_add_inst ('', 'set',[$return,$pair]); $self->_restore($item, $pair); } return $return; } sub _op_length { my ($self, $node) = @_; my $label = $self->_gensym (); my $return = $self->_save_1 ('I'); _num_arg ($node, 1, 'length'); my $list = $self->_generate(_get_arg($node,1)); $self->_add_inst ('', 'set',[$return,'0']); my $type = $self->_save_1 ('I'); $self->_add_inst ("NEXT_$label", 'typeof',[$type,$list]); $self->_add_inst ('', 'eq',[$type,'.PerlUndef', "DONE_$label"]); $self->_add_inst ('', 'ne',[$type,'.Array', "ERR_$label"]); $self->_add_inst ('', 'inc',[$return]); $self->_add_inst ('', 'set',[$list,$list.'[1]']); $self->_add_inst ('', 'branch',["NEXT_$label"]); # XXX Use exceptions here $self->_add_inst ("ERR_$label", 'print',['"Object is not a list\n"']); $self->_add_inst ("DONE_$label"); return $return; } sub _op_append { } sub _op_reverse { } sub _op_list_ref { } sub _op_memq { } sub _op_memv { } sub _op_member { } sub _op_assq { } sub _op_assv { } sub _op_assoc { } sub _op_symbol_p { } sub _op_symbol_string { } sub _op_string_symbol { } sub _op_number_p { } sub _op_complex_p { } sub _op_real_p { } sub _op_rational_p { } sub _op_integer_p { } sub _op_exact_p { } sub _op_inexact_p { } sub _op_eq { my ($self,$node) = @_; my $return; my $label = $self->_gensym(); $return = $self->_constant(0); my $temp_0 = $self->_generate($node->{children}[1]); for(2..$#{$node->{children}}) { my $temp_1 = $self->_generate($node->{children}[$_]); if (substr ($temp_0, 0, 1) ne substr ($temp_1, 0, 1)) { my $temp_2 = $self->_save_1(substr ($temp_0, 0, 1)); $self->_morph($temp_2, $temp_1); $self->_restore ($temp_1); $temp_1 = $temp_2; } $self->_add_inst ('', 'ne', [$temp_0,$temp_1,"DONE_$label"]); $self->_restore($temp_1); } $self->_add_inst('','set',[$return,1]); $self->_add_inst("DONE_$label"); $self->_restore($temp_0); return $return; } sub _op_lt { my ($self,$node) = @_; my $return; my $label = $self->_gensym(); $return = $self->_constant(0); my $temp_0 = $self->_generate($node->{children}[1]); for(2..$#{$node->{children}}) { my $temp_1 = $self->_generate($node->{children}[$_]); $self->_add_inst('','ge',[$temp_0,$temp_1,"DONE_$label"]); $self->_restore($temp_1); } $self->_add_inst('','set',[$return,1]); $self->_add_inst("DONE_$label"); $self->_restore($temp_0); return $return; } sub _op_gt { my ($self,$node) = @_; my $return; my $label = $self->_gensym(); $return = $self->_constant(0); my $temp_0 = $self->_generate($node->{children}[1]); for(2..$#{$node->{children}}) { my $temp_1 = $self->_generate($node->{children}[$_]); $self->_add_inst('','le',[$temp_0,$temp_1,"DONE_$label"]); $self->_restore($temp_1); } $self->_add_inst('','set',[$return,1]); $self->_add_inst("DONE_$label"); $self->_restore($temp_0); return $return; } sub _op_leq { my ($self,$node) = @_; my $return; my $label = $self->_gensym(); $return = $self->_constant(0); my $temp_0 = $self->_generate($node->{children}[1]); for(2..$#{$node->{children}}) { my $temp_1 = $self->_generate($node->{children}[$_]); $self->_add_inst('','gt',[$temp_0,$temp_1,"DONE_$label"]); $self->_restore($temp_1); } $self->_add_inst('','set',[$return,1]); $self->_add_inst("DONE_$label"); $self->_restore($temp_0); return $return; } sub _op_geq { my ($self,$node) = @_; my $return; my $label = $self->_gensym(); $return = $self->_constant(0); my $temp_0 = $self->_generate($node->{children}[1]); for(2..$#{$node->{children}}) { my $temp_1 = $self->_generate($node->{children}[$1]); $self->_add_inst('','lt',[$temp_0,$temp_1,"DONE_$label"]); $self->_restore($temp_1); } $self->_add_inst('','set',[$return,1]); $self->_add_inst("DONE_$label"); $self->_restore($temp_0); return $return; } sub _op_zero_p { my ($self,$node) = @_; my $return; my $label = $self->_gensym(); $return = $self->_constant(0); $self->_add_inst('' ,'set' ,[$return,1]); my $temp = $self->_generate($node->{children}[1]); $self->_add_inst('' ,'eq' ,[$temp,0,"DONE_$label"]); $self->_restore($temp); $self->_add_inst('' ,'set' ,[$return,0]); $self->_add_inst("DONE_$label"); return $return; } sub _op_positive_p { my ($self,$node) = @_; my $return; my $label = $self->_gensym(); $return = $self->_constant(1); my $temp = $self->_generate($node->{children}[1]); $self->_add_inst('' ,'gt' ,[$temp,0,"DONE_$label"]); $self->_restore($temp); $self->_add_inst('' ,'set' ,[$return,0]); $self->_add_inst("DONE_$label"); return $return; } sub _op_negative_p { my ($self,$node) = @_; my $return; my $label = $self->_gensym(); $return = $self->_constant(1); my $temp = $self->_generate($node->{children}[1]); $self->_add_inst('' ,'lt' ,[$temp,0,"DONE_$label"]); $self->_restore($temp); $self->_add_inst('' ,'set' ,[$return,0]); $self->_add_inst("DONE_$label"); return $return; } sub _op_odd_p { my ($self,$node) = @_; my $return; my $label = $self->_gensym(); my $temp_0 = $self->_generate($node->{children}[1]); $return = $self->_constant(1); my $temp_1 = $self->_constant(2); $self->_add_inst('' ,'mod' ,[$temp_0,$temp_0,$temp_1]); $self->_add_inst('' ,'eq' ,[$temp_0,1,"DONE_$label"]); $self->_add_inst('' ,'set' ,[$return,0]); $self->_add_inst("DONE_$label"); $self->_restore($temp_0,$temp_1); return $return; } sub _op_even_p { my ($self,$node) = @_; my $return; my $label = $self->_gensym(); my $temp_0 = $self->_generate($node->{children}[1]); $return = $self->_constant(1); my $temp_1 = $self->_constant(2); $self->_add_inst('' ,'mod' ,[$temp_0,$temp_0,$temp_1]); $self->_add_inst('' ,'eq' ,[$temp_0,0,"DONE_$label"]); $self->_add_inst('' ,'set' ,[$return,0]); $self->_add_inst("DONE_$label"); $self->_restore($temp_0,$temp_1); return $return; } sub _op_max { my ($self,$node) = @_; my $return; my $label = $self->_gensym(); $return = $self->_generate($node->{children}[1]); for(2..$#{$node->{children}}) { my $temp = $self->_generate($node->{children}[$_]); my $label = $self->_gensym(); $self->_add_inst('','gt', [$return,$temp,"NEXT_$label"]); $self->_add_inst('','set',[$return,$temp]); $self->_add_inst("NEXT_$label"); $self->_restore($temp); } return $return; } sub _op_min { my ($self,$node) = @_; my $return; my $label = $self->_gensym(); $return = $self->_generate($node->{children}[1]); for(2..$#{$node->{children}}) { my $temp = $self->_generate($node->{children}[$_]); my $label = $self->_gensym(); $self->_add_inst('','lt', [$return,$temp,"NEXT_$label"]); $self->_add_inst('','set',[$return,$temp]); $self->_add_inst("NEXT_$label"); $self->_restore($temp); } return $return; } sub _op_plus { my ($self,$node) = @_; my $return; my $num_children = defined $node->{children} ? @{$node->{children}} - 1 : 0; if($num_children==0) { $return = $self->_constant(0); } elsif($num_children==1) { $return = $self->_generate($node->{children}[1]); if ($return =~ /^P/) { my $temp = $self->_save_1 ('P'); $self->_morph ($temp, $return); $self->_restore ($return); $return = $temp; } } else { $return = $self->_generate($node->{children}[1]); if ($return =~ /^P/) { my $temp = $self->_save_1 ('P'); $self->_morph ($temp, $return); $self->_restore ($return); $return = $temp; } for(2..$#{$node->{children}}) { my $temp = $self->_generate($node->{children}[$_]); $self->_add_inst('','add',[$return,$return,$temp]); $self->_restore($temp); } } return $return; } sub _op_minus { my ($self,$node) = @_; my $return; my $num_children = defined $node->{children} ? @{$node->{children}} - 1 : 0; if($num_children==0) { $return = $self->_constant(0); } elsif($num_children==1) { $return = $self->_generate($node->{children}[1]); if ($return =~ /^P/) { my $temp = $self->_save_1 ('P'); $self->_morph ($temp, $return); $self->_restore ($return); $return = $temp; } my $temp = $self->_constant(0); $self->_add_inst('','sub',[$return,$temp,$return]); $self->_restore($temp); } else { $return = $self->_generate($node->{children}[1]); if ($return =~ /^P/) { my $temp = $self->_save_1 ('P'); $self->_morph ($temp, $return); $self->_restore ($return); $return = $temp; } for(2..$#{$node->{children}}) { my $temp = $self->_generate($node->{children}[$_]); $self->_add_inst('','sub',[$return,$return,$temp]); $self->_restore($temp); } } return $return; } sub _op_times { my ($self,$node) = @_; my $return; my $num_children = defined $node->{children} ? @{$node->{children}} - 1: 0; if($num_children==0) { $return = $self->_constant(0); } elsif($num_children==1) { $return = $self->_generate($node->{children}[1]); if ($return =~ /^P/) { my $temp = $self->_save_1 ('P'); $self->_morph ($temp, $return); $self->_restore ($return); $return = $temp; } } else { $return = $self->_generate($node->{children}[1]); if ($return =~ /^P/) { my $temp = $self->_save_1 ('P'); $self->_morph ($temp, $return); $self->_restore ($return); $return = $temp; } for(2..$#{$node->{children}}) { my $temp = $self->_generate($node->{children}[$_]); $self->_add_inst('','mul',[$return,$return,$temp]); $self->_restore($temp); } } return $return; } sub _op_divide { my ($self,$node) = @_; my $return; my $num_children = defined $node->{children} ? @{$node->{children}} - 1 : 0; if($num_children==0) { $return = $self->_constant(0); } elsif($num_children==1) { $return = $self->_generate($node->{children}[1]); if ($return =~ /^P/) { my $temp = $self->_save_1 ('P'); $self->_morph ($temp, $return); $self->_restore ($return); $return = $temp; } my $temp = $self->_constant(1); $self->_add_inst('','div',[$return,$temp,$return]); $self->_restore($temp); } else { $return = $self->_generate($node->{children}[1]); if ($return =~ /^P/) { my $temp = $self->_save_1 ('P'); $self->_morph ($temp, $return); $self->_restore ($return); $return = $temp; } for(2..$#{$node->{children}}) { my $temp = $self->_generate($node->{children}[$_]); $self->_add_inst('','div',[$return,$return,$temp]); $self->_restore($temp); } } return $return; } sub _op_abs { my ($self,$node) = @_; my $return; my $label = $self->_gensym(); $return = $self->_generate($node->{children}[1]); $self->_add_inst('', 'gt', [$return,0,"DONE_$label"]); my $temp = $self->_constant(-1); $self->_add_inst('', 'mul',[$return,$return,$temp]); $self->_restore($temp); $self->_add_inst("DONE_$label"); return $return; } sub _op_quotient { } sub _op_remainder { } sub _op_modulo { } sub _op_gcd { } sub _op_lcm { } sub _op_numerator { } sub _op_denominator { } sub _op_floor { } sub _op_ceiling { } sub _op_truncate { } sub _op_round { } sub _op_rationalize { } sub _op_exp { } sub _op_log { } sub _op_sin { } sub _op_cos { } sub _op_tan { } sub _op_asin { } sub _op_acos { } sub _op_atan { } sub _op_sqrt { } sub _op_expt { } sub _op_make_rectangular { } sub _op_make_point { } sub _op_real_part { } sub _op_imag_part { } sub _op_magnitude { } sub _op_angle { } sub _op_exact_inexact { } sub _op_inexact_exact { } sub _op_number_string { } sub _op_string_number { } sub _op_char_p { } sub _op_char_eq_p { } sub _op_char_lt_p { } sub _op_char_gt_p { } sub _op_char_le_p { } sub _op_char_ge_p { } sub _op_char_ci_eq_p { } sub _op_char_ci_lt_p { } sub _op_char_ci_gt_p { } sub _op_char_ci_le_p { } sub _op_char_ci_ge_p { } sub _op_char_alphabetic_p { } sub _op_char_numeric_p { } sub _op_char_whitespace_p { } sub _op_char_upper_case_p { } sub _op_char_lower_case_p { } sub _op_char_integer { } sub _op_integer_char { } sub _op_char_upcase { } sub _op_char_downcase { } sub _op_string_p { } sub _op_make_string { } sub _op_string { } sub _op_string_length { } sub _op_string_ref { } sub _op_string_set_bang { } sub _op_string_eq_p { } sub _op_string_lt_p { } sub _op_string_gt_p { } sub _op_string_le_p { } sub _op_string_ge_p { } sub _op_string_ci_eq_p { } sub _op_string_ci_lt_p { } sub _op_string_ci_gt_p { } sub _op_string_ci_le_p { } sub _op_string_ci_ge_p { } sub _op_substring { } sub _op_string_append { } sub _op_string_list { } sub _op_list_string { } sub _op_string_copy { } sub _op_string_fill_bang { } sub _op_vector_p { } sub _op_make_vector { } sub _op_vector { } sub _op_vector_length { } sub _op_vector_ref { } sub _op_vector_set_bang { } sub _op_vector_list { } sub _op_list_vector { } sub _op_vector_fill_bang { } sub _op_procedure_p { my ($self, $node) = @_; my $return; _check_num_arg ($node, 1, 'procedure?'); $return = $self->_constant(0); my $temp = $self->_generate(_get_arg($node,1)); if ($temp =~ /^P/) { } return $return; } sub _op_apply { my ($self, $node) = @_; my $return; my $func = $self->_generate(_get_arg ($node, 1)); my @args = _get_args ($node, 2); die "apply: wrong number of args\n" unless @args; $return = $self->_call_function_sym('apply'); return $return; } sub _op_map { } sub _op_for_each { } sub _op_force { } sub _op_call_with_current_continuation { } sub _op_call_with_input_file { } sub _op_call_with_output_file { } sub _op_input_port_p { } sub _op_output_port_p { } sub _op_current_input_port { } sub _op_current_output_port { } sub _op_with_input_to_file { } sub _op_with_output_from_file { } sub _op_open_input_file { } sub _op_open_output_file { } sub _op_close_input_port { } sub _op_close_output_port { } sub _op_read { } sub _op_read_char { } sub _op_peek_char { } sub _op_eof_object_p { } sub _op_char_ready_p { } sub _op_write { my ($self,$node) = @_; my $temp = 'none'; for(_get_args($node)) { $self->_restore ($temp); $temp = $self->_generate($_); if ($temp =~ /[INS]/) { $self->_add_inst('','print',[$temp]); } else { $self->_call_function_sym('write',$temp); } } return $temp; # We need to return something } sub _op_display { } sub _op_newline { } sub _op_write_char { } sub _op_load { } sub _op_transcript_on { } sub _op_transcript_off { } sub _op_let_syntax { } sub _op_letrec_syntax { } sub _op_define_syntax { } sub _op_syntax_rules { } sub _op_syntax { } sub _op_identifier_p { } sub _op_unwrap_syntax { } sub _op_free_identifier_eq_p { } sub _op_bound_identifier_eq_p { } sub _op_identifier_symbol { } sub _op_generate_identifier { } sub _op_construct_identifier { } #------------------------------------------------------------------------------ my %global_ops = ( #---------------------- # # Section 4 Expressions # #---------------------- 'quote' => \&_op_quote, 'lambda' => \&_op_lambda, 'if' => \&_op_if, 'define' => \&_op_define, 'set!' => \&_op_set_bang, 'cond' => \&_op_cond, 'case' => \&_op_case, 'and' => \&_op_and, 'or' => \&_op_or, 'let' => \&_op_let, 'let*' => \&_op_let_star, 'letrec' => \&_op_letrec, 'begin' => \&_op_begin, 'do' => \&_op_do, 'delay' => \&_op_delay, 'quasiquote' => \&_op_quasiquote, #---------------------- # # Section 6 Expressions # #---------------------- ### ### Logical expression ### 'not' => \&_op_not, ### ### Equivalency ### 'boolean' => \&_op_boolean_p, 'eqv?' => \&_op_eqvp, 'eq?' => \&_op_eqp, 'equal?' => \&_op_equalp, ### ### Pairs and Lists ### 'pair?' => \&_op_pair_p, 'cons' => \&_op_cons, 'car' => \&_op_car, 'cdr' => \&_op_cdr, 'set-car!' => \&_op_set_car_bang, 'set-cdr!' => \&_op_set_cdr_bang, # Not adding caar/cadr/cdar/whatever 'null?' => \&_op_null_p, 'list?' => \&_op_list_p, 'list' => \&_op_list, 'length' => \&_op_length, 'append' => \&_op_append, 'reverse' => \&_op_reverse, 'list-ref' => \&_op_list_ref, 'memq' => \&_op_memq, 'memv' => \&_op_memv, 'member' => \&_op_member, 'assq' => \&_op_assq, 'assv' => \&_op_assv, 'assoc' => \&_op_assoc, ### ### Symbols ### 'symbol?' => \&_op_symbol_p, 'symbol->string' => \&_op_symbol_string, 'string->symbol' => \&_op_string_symbol, ### ### Numerics ### 'number?' => \&_op_number_p, 'complex?' => \&_op_complex_p, 'real?' => \&_op_real_p, 'rational?' => \&_op_rational_p, 'integer?' => \&_op_integer_p, 'exact?' => \&_op_exact_p, 'inexact?' => \&_op_inexact_p, '=' => \&_op_eq, '<' => \&_op_lt, '>' => \&_op_gt, '<=' => \&_op_leq, '>=' => \&_op_geq, 'zero?' => \&_op_zero_p, 'positive?' => \&_op_positive_p, 'negative?' => \&_op_negative_p, 'odd?' => \&_op_odd_p, 'even?' => \&_op_even_p, 'max' => \&_op_max, 'min' => \&_op_min, '+' => \&_op_plus, '-' => \&_op_minus, '*' => \&_op_times, '/' => \&_op_divide, 'abs' => \&_op_abs, 'quotient' => \&_op_quotient, 'remainder' => \&_op_remainder, 'modulo' => \&_op_modulo, 'gcd' => \&_op_gcd, 'lcm' => \&_op_lcm, 'numerator' => \&_op_numerator, 'denominator' => \&_op_denominator, 'floor' => \&_op_floor, 'ceiling' => \&_op_ceiling, 'truncate' => \&_op_truncate, 'round' => \&_op_round, 'rationalize' => \&_op_rationalize, 'exp' => \&_op_exp, 'log' => \&_op_log, 'sin' => \&_op_sin, 'cos' => \&_op_cos, 'tan' => \&_op_tan, 'asin' => \&_op_asin, 'acos' => \&_op_acos, 'atan' => \&_op_atan, 'sqrt' => \&_op_sqrt, 'expt' => \&_op_expt, 'make-rectangular' => \&_op_make_rectangular, 'make-polar' => \&_op_make_point, 'real-part' => \&_op_real_part, 'imag-part' => \&_op_imag_part, 'magnitude' => \&_op_magnitude, 'angle' => \&_op_angle, 'exact->inexact' => \&_op_exact_inexact, 'inexact->exact' => \&_op_inexact_exact, ### ### Numerical input and output ### 'number->string' => \&_op_number_string, 'string->number' => \&_op_string_number, ### ### Character ### 'char?' => \&_op_char_p, 'char=?' => \&_op_char_eq_p, 'char \&_op_char_lt_p, 'char>?' => \&_op_char_gt_p, 'char<=?' => \&_op_char_le_p, 'char>=?' => \&_op_char_ge_p, 'char-ci=?' => \&_op_char_eq_p, 'char-ci \&_op_char_ci_lt_p, 'char-ci>?' => \&_op_char_ci_gt_p, 'char-ci<=?' => \&_op_char_ci_le_p, 'char-ci>=?' => \&_op_char_ci_ge_p, 'char-alphabetic?' => \&_op_char_alphabetic_p, 'char-numeric?' => \&_op_char_numeric_p, 'char-whitespace?' => \&_op_char_whitespace_p, 'char-upper-case?' => \&_op_char_upper_case_p, 'char-lower-case?' => \&_op_char_lower_case_p, 'char->integer' => \&_op_char_integer, 'integer->char' => \&_op_integer_char, 'char-upcase' => \&_op_char_upcase, 'char-downcase' => \&_op_char_downcase, ### ### Strings ### 'string?' => \&_op_string_p, 'make-string' => \&_op_make_string, 'string' => \&_op_string, 'string-length' => \&_op_string_length, 'string-ref' => \&_op_string_ref, 'string-set!' => \&_op_string_set_bang, 'string=?' => \&_op_string_eq_p, 'string \&_op_string_lt_p, 'string>?' => \&_op_string_gt_p, 'string<=?' => \&_op_string_le_p, 'string>=?' => \&_op_string_ge_p, 'string-ci=?' => \&_op_string_ci_eq_p, 'string-ci \&_op_string_ci_lt_p, 'string-ci>?' => \&_op_string_ci_gt_p, 'string-ci<=?' => \&_op_string_ci_le_p, 'string-ci>=?' => \&_op_string_ci_ge_p, 'substring' => \&_op_substring, 'string-append' => \&_op_string_append, 'string->list' => \&_op_string_list, 'list->string' => \&_op_list_string, 'string-copy' => \&_op_string_copy, 'string-fill!' => \&_op_string_fill_bang, ### ### Vector ### 'vector?' => \&_op_vector_p, 'make-vector' => \&_op_make_vector, 'vector' => \&_op_vector, 'vector-length' => \&_op_vector_length, 'vector-ref' => \&_op_vector_ref, 'vector-set!' => \&_op_vector_set_bang, 'vector->list' => \&_op_vector_list, 'list->vector' => \&_op_list_vector, 'vector-fill!' => \&_op_vector_fill_bang, ### ### Control features ### 'procedure?' => \&_op_procedure_p, 'apply' => \&_op_apply, 'map' => \&_op_map, 'for-each' => \&_op_for_each, 'force' => \&_op_force, 'call-with-currrent-continuation' => \&_op_call_with_current_continuation, ### ### Input and Output ### #### #### Ports #### 'call-with-input-file' => \&_op_call_with_input_file, 'call-with-output-file' => \&_op_call_with_output_file, 'input-port?' => \&_op_input_port_p, 'output-port?' => \&_op_output_port_p, 'current-input-port' => \&_op_current_input_port, 'current-output-port' => \&_op_current_output_port, 'with-input-to-file' => \&_op_with_input_to_file, 'with-output-from-file' => \&_op_with_output_from_file, 'open-input-file' => \&_op_open_input_file, 'open-output-file' => \&_op_open_output_file, 'close-input-port' => \&_op_close_input_port, 'close-output-port' => \&_op_close_output_port, #### #### Input #### 'read' => \&_op_read, 'read-char' => \&_op_read_char, 'peek-char' => \&_op_peek_char, 'eof-object?' => \&_op_eof_object_p, 'char-ready?' => \&_op_char_ready_p, 'write' => \&_op_write, 'display' => \&_op_display, 'newline' => \&_op_newline, 'write-char' => \&_op_write_char, #### #### System Interface #### 'load' => \&_op_load, 'transcript-on' => \&_op_transcript_on, 'transcript-off' => \&_op_transcript_off, #-------------------- # # Macros # #-------------------- 'let-syntax' => \&_op_let_syntax, 'letrec-syntax' => \&_op_letrec_syntax, 'define-syntax' => \&_op_define_syntax, 'syntax-rules' => \&_op_syntax_rules, 'let-syntax' => \&_op_let_syntax, 'syntax' => \&_op_syntax, 'identifer?' => \&_op_identifier_p, 'unwrap-syntax' => \&_op_unwrap_syntax, 'bound-identfier=?' => \&_op_bound_identifier_eq_p, 'identifier->symbol' => \&_op_identifier_symbol, 'free-identfier=?' => \&_op_free_identifier_eq_p, 'generate-identfier' => \&_op_generate_identifier, 'construct-identfier' => \&_op_construct_identifier, ); #------------------------------------------------------------------------------ sub __max_lengths { my $colref = shift; my @max_len=(0)x3; for my $row(@$colref) { for(0..$#{$row}) { $max_len[$_]=length($row->[$_]) if length $row->[$_] > $max_len[$_]; } } @max_len; } sub _call_function_sym { my $self = shift; my $symbol = shift; my $func_obj = $self->_find_lex($symbol); my $scope = $self->{scope}; while ($scope && !exists $scope->{$symbol}) { $scope = $scope->{'*UP*'}; } if (!$scope) { push @{$self->{functions}}, $symbol unless grep { $_ eq $symbol} @{$self->{functions}}; } my $return = $self->_call_function_obj($func_obj, @_); $self->_restore($func_obj); return $return; } sub _call_function_obj { my $self = shift; my $func_obj = shift; my $return = $self->_save_1 ('P'); $self->_restore ($return); # dont need to save this $self->_save_set; my $count = 5; my $empty = $return; while (my $arg = shift) { if ($arg ne "P$count") { if ($arg =~ /^[INS]/) { $self->_morph("P$count", $arg); $count++; next; } # Check if any later argument needs the old value of P$count my $moved; for (@_) { if ($_ eq "P$count") { $moved = $_; $_ = $empty; } } if ($moved) { $self->_add_inst ('', 'set',[$empty,"P$count"]); $empty = $moved; } $self->_add_inst ('','set',["P$count",$arg]); } $count++; } $self->_add_inst ('', 'set', ['P0', $func_obj]) unless $func_obj eq 'P0'; $self->_add_inst ('', 'invokecc'); $self->_add_inst ('', 'set', [$return,'P5']) unless $return eq 'P5'; $self->_restore_set; $return =~ /(\w)(\d+)/; $self->{regs}->{$1}->{$2} = 1; return $return; } sub _format_columns { my $self = shift; my $colref = $self->{instruction}; my @max_len = __max_lengths($colref); $self->{code} = ''; for my $row(@$colref) { my $label; $label = $row->[0]; $label .= ":" if $label; $self->{code} .= $label . ' ' x ($max_len[0]-length($label)+2); if(defined $row->[1]) { $label = $row->[1]; $self->{code} .= $label . ' ' x ($max_len[1]-length($label)+2); $label = $row->[2]; $self->{code} .= join ", ",@$label if $label; } $self->{code} .= "\n"; } } sub new { my $class = shift; my $tree = shift; my $self = { tree => $tree, regs => _new_regs, frames => [], gensym => 0, functions=> [], scope => {}, }; bless $self,$class; } sub prettyprint { my $node = shift; my $depth = shift; print ' ' x $depth; print "($node->{value}\n"; if(defined $node->{children}) { for(@{$node->{children}}) { prettyprint($_,$depth+1); } } print ' ' x $depth; print ")\n"; } sub _generate { my ($self,$node) = @_; my $return; if (exists $node->{children}) { my $func = _get_arg ($node, 0); if (exists $func->{value}) { my $symbol = $func->{value}; if (exists $global_ops{$symbol}) { $return = $global_ops{$symbol}->($self, $node); } else { my @args = map { $self->_generate($_); } _get_args($node); $return = $self->_call_function_sym($symbol, @args); $self->_restore(@args); } } else { my @args = map { $self->_generate($_); } _get_args($node, 0); $return = $self->_call_function_obj(@args); $self->_restore(@args); } } else { my $value = $node->{value}; if ($value =~ /^[a-zA-Z]/) { $return = $self->_find_lex($value); } else { $return = $self->_constant($node->{value}); } } return $return; } sub generate { my $tree = shift; my $self = Scheme::Generator->new({}); my $temp; $self->{scope} = {}; $temp = $self->_generate($tree); $self->_restore($temp); $self->_add_inst('',"end"); $self->_format_columns; # not need any more $self->{instruction} = undef; $self->{regs} = undef; return $self; } 1; __END__ =head1 NAME Scheme::Generator - The Scheme code generator =head1 SYNOPSIS use Scheme:Generator; my @code = Scheme::Generator->new($code_tree)->generate(); =head1 DESCRIPTION The code generator reads in a tree structure, and walks that to generate the output. =head1 AUTHOR Jeffrey Goff, drforr@hargray.com =head1 SEE ALSO L, L =cut