1: #!/usr/local/bin/perl -w 3: package SGML::ElementMap::Example::Calculator; 4: use strict; 5: use vars qw($trace $trace_processing); 6: $trace = 0; 7: $trace_processing = 0; 10: sub accum { 11: my ($eng,$val) = @_; 12: #return if $val eq 'void'; 13: my $op = $eng->stack->{'op'}; 14: if ($op eq 'push') { 15: push @{ $eng->stack->{'val'} }, $val; 16: } elsif ($op eq 'set') { 17: $eng->stack->{'val'} = $val; 18: } 19: } 21: sub Initialize { 22: my ($engine) = @_; 24: #this mode will skip all content 25: $engine->mode_set('NOEVAL'); 26: $engine->cdata('',sub { }); 27: $engine->element('','suppress'); 28: 29: #this mode will skip the first element it encounters, and then pop the mode 30: #so, it should only be entered after a push 31: $engine->mode_set('SKIPNEXT'); 32: $engine->cdata('',sub { }); 33: $engine->element('',sub { 34: my ($eng,$el) = @_; 35: $eng->suppress_content; 36: $eng->mode_set_pop; 37: }); 39: #this mode evaluates normally and does the bulk of the work 40: $engine->mode_set('EVAL'); 41: 42: $engine->pi('','suppress'); 43: $engine->cdata('',sub { }); 44: $engine->cdata('VAR|INT|_SAVE_TEXT/', sub { 45: my ($eng,$obj) = @_; 46: $eng->stack->{'val'} .= $obj->{'Data'}; 47: }); 48: $engine->element('_SAVE_TEXT',sub { 49: my ($eng,$el) = @_; 50: my ($res) = $eng->process_content_filt('val',''); 51: $res =~ s/^\s+//; 52: $res =~ s/\s+$//; 53: accum($eng,$res); 54: }); 55: 56: $engine->element('INT',sub { 57: my ($eng,$el) = @_; 58: my ($res) = $eng->process_content_filt('val','','op','set'); 59: $res =~ s/^\s+//; 60: $res =~ s/\s+$//; 61: if ($res ne '') { 62: accum($eng,$res); 63: warn "INT:$res\n" if $trace; 64: } else { 65: warn "Empty integer element ignored\n"; 66: } 67: }); 68: 69: $engine->element('VAR',sub { 70: my ($eng,$el) = @_; 71: my ($res) = $eng->process_content_filt('val','','op','set'); 72: $res =~ s/^\s+//; 73: $res =~ s/\s+$//; 74: if ($res ne '') { 75: accum($eng, $eng->stack->{'vars'}->{$res}); 76: warn "VAR:$res:".$eng->stack->{'vars'}->{$res}."\n" if $trace; 77: } else { 78: warn "Empty variable element ignored\n"; 79: } 80: }); 81: 82: 83: $engine->element('ADD',sub { 84: my ($eng,$el) = @_; 85: my (@args,$res); 86: $eng->process_content_filt('val',\@args,'op','push'); 87: $res = 0; 88: foreach (@args) { $res += $_; } 89: accum($eng,$res); 90: warn "ADD:$res\n" if $trace; 91: }); 92: 93: $engine->element('MULT',sub { 94: my ($eng,$el) = @_; 95: my (@args,$res); 96: @args = (); 97: $eng->process_content_filt('val',\@args,'op','push'); 98: $res = 1; 99: foreach (@args) { 100: $res *= $_; 101: } 102: accum($eng,$res); 103: warn "MULT:$res\n" if $trace; 104: }); 106: $engine->element('EQUAL',sub { 107: my ($eng,$el) = @_; 108: my (@args,$res,$arg); 109: $eng->process_content_filt('val',\@args,'op','push'); 110: $arg = shift @args; 111: $res = 1; 112: foreach (@args) { 113: $res = 0 if ($_ != $arg); 114: } 115: accum($eng,$res); 116: warn "EQUAL:$res\n" if $trace; 117: }); 118: 119: $engine->element('SCOPE',sub { 120: my ($eng,$el) = @_; 121: my $state = $eng->stack->opaque; 122: $eng->stack->{'val'} = ''; 123: $eng->stack->{'op'} = 'set'; 124: $eng->stack->set_layer($state); 125: $eng->insert_pseudo_element('_SAVE_TEXT'); 126: my $cmd = $eng->stack->{'val'}; 127: 128: my $vardata = $eng->stack->{'vars'}; 129: if ($cmd eq 'push') { 130: $vardata->push; 131: } elsif ($cmd eq 'pop') { 132: $vardata->pop; 133: } elsif ($cmd =~ /^(\w+)\s+default$/) { 134: $vardata->set_default($1); 135: } else { 136: $vardata->set_layer($cmd); 137: } 138: warn "SCOPE:".$vardata->id.":$cmd\n" if $trace; 139: }); 140: 141: $engine->element('SET',sub { 142: my ($eng,$el) = @_; 143: my ($name,$res) = $eng->process_content_filt('name','','val','void', 144: 'op','set'); 145: $eng->stack->{'vars'}->{$name} = $res; 146: accum($eng,$res); 147: warn "SET:$name:$res\n" if $trace; 148: }); 149: 150: $engine->element('SET/VAR',sub { 151: my ($eng,$el) = @_; 152: my ($res) = $eng->process_content_filt('val',''); 153: $res =~ s/^\s+//; 154: $res =~ s/\s+$//; 155: $eng->stack->{'name'} = $res; 156: warn "VAR:$res\n" if $trace; 157: }); 158: 159: $engine->element('BLOCK',sub { 160: my ($eng,$el) = @_; 161: my ($res) = $eng->process_content_filt('val','void','op','set'); 162: accum($eng,$res); 163: warn "BLOCK:$res\n" if $trace; 164: }); 165: $engine->element('SHOW',sub { 166: my ($eng,$el) = @_; 167: my ($res) = $eng->process_content_filt('val','void','op','set'); 168: print $res . "\n"; 169: accum($eng,$res); 170: warn "SHOW:$res\n" if $trace; 171: }); 172: 173: $engine->element('MSG',sub { 174: my ($eng,$el) = @_; 175: my $state = $eng->stack->opaque; 176: $eng->stack->{'val'} = ''; 177: $eng->stack->{'op'} = 'set'; 178: $eng->stack->set_layer($state); 179: $eng->insert_pseudo_element('_SAVE_TEXT'); 180: my $msg = $eng->stack->{'val'}; 181: print $msg . "\n"; 182: warn "MSG:$msg\n" if $trace; 183: }); 184: 185: $engine->element('COM','suppress'); 187: $engine->element('COND',sub { 188: my ($eng,$el) = @_; 189: $eng->mode_set_push('COND'); 190: my ($res) = $eng->process_content_filt('val','void','op','set', 191: 'cond','undef'); 192: $eng->mode_set_pop; 193: accum($eng,$res); 194: warn "COND:$res\n" if $trace; 195: }); 196: 197: #this mode is used because default handlers are searched only after 198: #no other handlers match, but each mode is searched completely in turn 199: #That is, you need modes for a default handler to override a non-default 200: $engine->mode_set('COND'); 202: $engine->element('COND/',sub { #default for direct COND subels 203: my ($eng,$el) = @_; 204: my $state = $eng->stack->{'cond'}; 205: if ($state eq 'true') { 206: $eng->reprocess_pseudo_element('_COND_EVAL'); 207: $eng->mode_set('NOEVAL'); #replaces COND 208: } elsif ($state eq 'false') { 209: $eng->suppress_content; 210: $eng->stack->{'cond'} = 'undef'; 211: } elsif ($state eq 'undef') { 212: $eng->reprocess_pseudo_element('_COND_TEST'); 213: } else { 214: die "unknown COND state '".$state."'\n"; 215: } 216: }); 217: 218: $engine->element('COND/_COND_EVAL',sub { 219: my ($eng,$el) = @_; 220: my @modes = $eng->mode_set_pop; 221: $eng->process_content; 222: $eng->mode_set_push(@modes); 223: }); 224: 225: $engine->element('COND/_COND_TEST',sub { 226: my ($eng,$el) = @_; 227: my @modes = $eng->mode_set_pop; 228: my ($res) = $eng->process_content_filt('val','void','op','set'); 229: $eng->mode_set_push(@modes); 230: if ($res ne 'void' && $res != 0) { 231: $eng->stack->{'cond'} = 'true'; 232: } else { 233: $eng->stack->{'cond'} = 'false'; 234: } 235: warn "COND:TEST:".$eng->stack->{'cond'}."\n" if $trace; 236: }); 237: 238: $engine->element('COND/ELSE',sub { 239: my ($eng,$el) = @_; 240: my @modes = $eng->mode_set_pop; 241: $eng->process_content; 242: $eng->mode_set_push(@modes); 243: $eng->mode_set('NOEVAL'); #replaces COND 244: warn "COND:ELSE\n" if $trace; 245: }); 248: $engine->mode_set('EVAL'); 249: $engine->stack->default_cascade; 250: $engine->stack->{'op'} = 'nop'; 251: $engine->stack->{'vars'} = new Hash::Layered; 252: if ($trace) { 253: select STDERR; $| = 1; 254: select STDOUT; $| = 1; 255: } 256: $engine->debug('general','element_path','handler_lookup','modes', 257: 'visitors') if $trace_processing; 258: $engine->stack->{'vars'}->trace if ($trace > 3); 260: return 1; 261: } 264: 1;