| Inline-SMITH documentation | Contained in the Inline-SMITH distribution. |
Inline::SMITH - write Perl subs in SMITH
use Inline SMITH => <<EOF;
function ascii_table {{
; Print ASCII table in descending order in SMITH v1
; (relatively easy)
MOV R0, 126 ; Initialize register with top character
MOV TTY, R0 ; -> Print character to terminal
SUB R0, 1 ; -> Decrement character
MOV R1, R0 ; -> Is character zero?
NOT R1 ; -> Boolean NOT it twice to find out
NOT R1 ; -> Result is 1 if true, 0 if false
MUL R1, 7 ; -> Multiply result by seven instructions
COR +1, -6, R1 ; -> Copy that many instructions forward
}}
EOF
ascii_table();
The Inline::SMITH module allows you to put SMITH source code
directly "inline" in a Perl script or module.
Using Inline::SMITH will seem very similar to using a another
Inline language, thanks to Inline's consistent look and feel.
For more details on Inline, see perldoc Inline.
The recommended way of using Inline is the following:
use Inline SMITH => <<EOF;
smith source code here
EOF
...
But there are many more ways to use Inline. You'll find them in
perldoc Inline.
Functions are defined in the following form:
function function_name {{ }}
The function name can contain letters, numbers and underscores. It is published into the main perl namespace, so choose something that
a) you haven't used for your own perl functions b) perl doesn't use for one of it's own functions
The first parameter passed to an Inline::SMITH function is converted to a stream of bytes. This stream is then accessable using the TTY command in SMITH.
If you pass a hash instead of a string, then Inline::SMITH can change it's IO behavoir. The following keys are recognised:
A plain old input buffer (a string)
Set to 1 to enable echoing of output to the screen. It is turned off by default when passing a hash.
A function ref which is called each time a character of input is needed. The function should return a 0 to indicate end of input.
A function ref which is called whenever a byte needs outputting. The byte is passed as a single character string in the first argument.
A SMITH function returns it's output buffer as a string. If echo was enabled, or if it was implicitly on by using the scalar calling method, then this buffer will have already been echo'd. The buffer is always returned, regardless of the state of the echo flag or the existence of an output callback.
Cal Henderson, <cal@iamcal.com>
Thanks to:
Inline module.| Inline-SMITH documentation | Contained in the Inline-SMITH distribution. |
package Inline::SMITH; $VERSION = '0.03'; require Inline; @ISA = qw(Inline); use strict; use Carp; sub register { return { language => 'SMITH', aliases => ['Smith', 'smith'], type => 'interpreted', suffix => 'smt', }; } sub usage_config { my $key = shift; "'$key' is not a valid config option for Inline::SMITH\n"; } sub validate { } sub build { my $o = shift; my $code = $o->{API}{code}; my $pattern = $o->{ILSM}{PATTERN}; $code = smith_load($code); { package Inline::SMITH::Loader; eval $code; } croak "Brainfuck build failed:\n$@" if $@; my $path = "$o->{API}{install_lib}/auto/$o->{API}{modpname}"; my $obj = $o->{API}{location}; $o->mkpath($path) unless -d $path; open FOO_OBJ, "> $obj" or croak "Can't open $obj for output\n$!"; print FOO_OBJ $code; close \*FOO_OBJ; } sub load { my $o = shift; my $obj = $o->{API}{location}; open FOO_OBJ, "< $obj" or croak "Can't open $obj for output\n$!"; my $code = join '', <FOO_OBJ>; close \*FOO_OBJ; eval "package $o->{API}{pkg};\n$code"; croak "Unable to load Foo module $obj:\n$@" if $@; } sub info { my $o = shift; } sub smith_load { my ($code) = @_; my $out = ""; while($code =~ m/function(\s+)([a-z0-9_]+)(\s*){{(.*?)}}/isg){ my $func_name = $2; my $func_code = $4; # print "loaded function $func_name\n"; $func_code =~ s/\|/\\|/g; $out .= "sub $func_name { return Inline::SMITH::smith_run(q|$func_code|, \$_[0]); }\n"; } return $out; } sub smith_run { my ($code, $data) = @_; my $buffer = ""; my @data; my $input_callback; my $output_callback; my $echo = 1; if (ref $data eq 'HASH'){ @data = split(//, ${$data}{input}) if ${$data}{input}; $input_callback = ${$data}{input_callback} || 0; $output_callback = ${$data}{output_callback} || 0; $echo = ${$data}{echo} || 0; }else{ @data = split(//, $data); } my $mem = []; my $reg = []; my $debug = 0; my $cont = 0; my $quiet = 0; my $pause = 0; my ($ggg, $hhh); # # load the code into $mem # my @lines = split(/\r?\n/, $code); my $line = ''; my $i = 0; for $line(@lines){ $line = $' if $line =~ /^\s*/; $line = $` if $line =~ /\s*$/; $line =~ s/\s*;.*?$//; $line =~ s/\*/$i/ge; if ($line =~ /^\S+/){ my $reps = 1; my $j; if ($line =~ /^REP\s*(\d+)\s*/){ $line = $'; $reps = $1; } for($j = 0; $j < $reps; $j++){ $mem->[$i] = $line; # print "Load $i = $mem->[$i]\n" if $showload; $i++; } } } # # run the code # my $pc = 0; while($mem->[$pc] ne 'STOP') { if ($mem->[$pc] =~ /^MOV\s*R(\d+)\s*,\s*\#?(\d+)$/) { # MOV reg, imm $reg->[$1] = $2; } elsif ($mem->[$pc] =~ /^MOV\s*R(\d+)\s*,\s*R(\d+)$/) { # MOV reg, reg $reg->[$1] = $reg->[$2]; } elsif ($mem->[$pc] =~ /^MOV\s*R\[R(\d+)\]\s*,\s*R(\d+)$/) { # MOV [reg], reg $reg->[$reg->[$1]] = $reg->[$2]; } elsif ($mem->[$pc] =~ /^MOV\s*R(\d+)\s*,\s*R\[R(\d+)\]$/) { # MOV reg, [reg] $reg->[$1] = $reg->[$reg->[$2]]; } elsif ($mem->[$pc] =~ /^MOV\s*R\[R(\d+)\]\s*,\s*\"(.*?)\"$/) { # MOV [reg], "string" my $i = $reg->[$1]; my $s = $2; while($i < ($reg->[$1] + length($s))) { $reg->[$i] = ord(substr($s, ($i-$reg->[$1]), 1)); $i++; } } elsif ($mem->[$pc] =~ /^MOV\s*R(\d+)\s*,\s*PC$/) { # MOV reg, PC $reg->[$1] = $pc; } elsif ($mem->[$pc] =~ /^MOV\s*TTY\s*,\s*R(\d+)$/) { # MOV TTY, reg print chr($reg->[$1]) if $echo; $buffer .= chr($reg->[$1]); &{$output_callback}(chr($reg->[$1])) if $output_callback; } elsif ($mem->[$pc] =~ /^MOV\s*TTY\s*,\s*R\[R(\d+)\]$/) { # MOV TTY, [reg] print chr($reg->[$reg->[$1]]) if $echo; $buffer .= chr($reg->[$reg->[$1]]); &{$output_callback}(chr($reg->[$reg->[$1]])) if $output_callback; } elsif ($mem->[$pc] =~ /^MOV\s*R(\d+)\s*,\s*TTY$/) { # MOV reg, TTY $reg->[$1] = ($input_callback)?&{$input_callback}:shift @data; if ($reg->[$1]) { $reg->[$1] = ord($reg->[$1]); } else { $reg->[$1] = 0; } } elsif ($mem->[$pc] =~ /^MOV\s*R\[R(\d+)\]\s*,\s*TTY$/) { # MOV [reg], TTY $reg->[$reg->[$1]] = ($input_callback)?&{$input_callback}:shift @data; if ($reg->[$reg->[$1]]) { $reg->[$reg->[$1]] = ord($reg->[$reg->[$1]]); } else { $reg->[$reg->[$1]] = 0; } } elsif ($mem->[$pc] =~ /^SUB\s*R(\d+)\s*,\s*\#?(\d+)$/) { # SUB reg, imm $reg->[$1] -= $2; } elsif ($mem->[$pc] =~ /^SUB\s*R(\d+)\s*,\s*R(\d+)$/) { # SUB reg, reg $reg->[$1] -= $reg->[$2]; } elsif ($mem->[$pc] =~ /^MUL\s*R(\d+)\s*,\s*\#?(\d+)$/) { # MUL reg, imm $reg->[$1] *= $2; } elsif ($mem->[$pc] =~ /^MUL\s*R(\d+)\s*,\s*R(\d+)$/) { # MUL reg, reg $reg->[$1] *= $reg->[$2]; } elsif ($mem->[$pc] =~ /^NOT\s*R(\d+)$/) { # NOT reg if($reg->[$1] != 0) { $reg->[$1] = 0; } else { $reg->[$1] = 1; } } elsif ($mem->[$pc] =~ /^COR\s*([-+]\d+)\s*,\s*([-+]\d+)\s*,\s*R(\d+)\s*$/) { # COR imm, imm, reg my $dst = 0+$pc+$1; my $src = 0+$pc+$2; my $lrg = 0+$3; my $i; { for ($i = 0; $i < $reg->[$lrg]; $i++) { $mem->[$dst+$i] = $mem->[$src+$i]; $ggg = $dst + $i; $hhh = $src + $i; } } } elsif ($mem->[$pc] =~ /^COR\s*([-+]\d+)\s*,\s*R(\d+)\s*,\s*R(\d+)\s*$/) { # COR imm, reg, reg my $dst = 0+$pc+$1; my $src = 0+$pc+$reg->[$2]; my $lrg = 0+$3; my $i; { for ($i = 0; $i < $reg->[$lrg]; $i++) { $mem->[$dst+$i] = $mem->[$src+$i]; $ggg = $dst + $i; $hhh = $src + $i; } } } elsif ($mem->[$pc] =~ /^BLA\s*([-+]\d+)\s*,\s*(\w+)\s*,\s*R(\d+)\s*$/) { # BLA imm, OPC, reg my $dst = 0+$pc+$1; my $src = $2; my $lrg = 0+$3; my $i; { for ($i = 0; $i < $reg->[$lrg]; $i++) { $mem->[$dst+$i] = $src; $ggg = $dst + $i; } } } elsif ($mem->[$pc] =~ /^NOP$/) { # NOP # Nothing happens here. } else { print "Invalid instruction $mem->[$pc]!\n"; $pc = $#{$mem} + 1 if not $cont; } $pc++; $mem->[$pc] = 'STOP' if $pc > $#{$mem}; } # # we're done # return $buffer; } 1; __END__