[ create a new paste ] login | about

Link: http://codepad.org/pr8hgOSB    [ raw code | fork ]

Perl, pasted on Jul 6:
use v6;
module Brainfuck;

my grammar Grammar {
    rule TOP    { ^ <code> $ }

    rule code   { <block>* }
    rule block  { <move> | <incr> | <print> | <ask> | <comment> | <loop> }
    rule loop   { '[' ~ ']' <code> }

    # Parse commutative blocks in one piece
    token move  { <[\s < >]>+ }
    token incr  { <[\s +\-]>+ }
    token print { <[\s  \.]>+ }
    token ask   { <[\s  \,]>+ }

    # Everything that's not an instruction is a comment.
    token comment { <-[ \[ \] + \- < > , .]>+ }
}

my class InstructionBlock {
    has Int $.n;
    method new(Int $n) { return self.bless(*, :$n); }
}

my class Incr  is InstructionBlock { }
my class Move  is InstructionBlock { }
my class Print is InstructionBlock { }
my class Ask   is InstructionBlock { }

my class Actions {
    method TOP($/)     { make $<code>.ast }
    method code($/)    { make $<block>>>.ast }
    method block($/)   { make $/.values.[0].ast; }
    method loop($/)    { make $<code>.ast }

    # Since +/- and >/< cancel each other out when they appear in direct succession, they
    # can be handled in large chunks. So for example ++-+-+ becomes Incr.new(2).
    method move($/)    { make Move.new($/.comb(/\>/).elems - $/.comb(/\</).elems); }
    method incr($/)    { make Incr.new($/.comb(/\+/).elems - $/.comb(/\-/).elems); }

    # . and , can be handled blockwise but not in a mix. This will be less useful than 
    # block handling for >/< and especially +/-, but since I'm already at it...
    method print($/)   { make Print.new($/.comb(/\./).elems); }
    method ask($/)     { make Ask.new($/.comb(/\,/).elems); }

    # Comments will be ignored. No special handling.
    method comment($/) { make ~$/; }
}

my class State {
    has Int @!buffer  = 0;
    has Int $!dataptr = 0;

    method get_data () returns Int { return @!buffer[$!dataptr]; }
    method set_data (Int $i) { @!buffer[$!dataptr] = $i; }
    method move     (Int $n) { $!dataptr += $n; }
    method increment(Int $n) {
	@!buffer[$!dataptr] += $n;
	@!buffer[$!dataptr] %= 256;
    }
}

class Interpreter {
    has @.ast;

    method new(Str $bf_code) {
	Grammar.parse($bf_code, actions => Actions.new);
	return self.bless(*, ast => $/.ast);
    }

    # run-recursive should be private, but rakudo doesn't seem to support
    # private multi methods. (yet?)
    multi method run-recursive(State $state, Str       $comment) { }
    multi method run-recursive(State $state, Move  $iblock ) { $state.move($iblock.n()); }
    multi method run-recursive(State $state, Incr  $iblock ) { $state.increment($iblock.n()); }
    multi method run-recursive(State $state, Print $iblock ) { print $state.get_data().chr x $iblock.n(); }
    multi method run-recursive(State $state, Ask   $iblock ) { $state.set_data($*IN.read($iblock.n())[*-1]); }
    multi method run-recursive(State $state, @p) {
	while $state.get_data() != 0 {
	    for @p {
		self.run-recursive($state, $_);
	    }
	}
    }

    method run {
	my State $state .= new;

	for @!ast {
	    self.run-recursive($state, $_);
	}
    }
}


Create a new paste based on this one


Comments: