| File: | blib/lib/Language/Nouse.pm |
| Coverage: | 80.8% |
| line | stmt | bran | cond | sub | time | code |
|---|---|---|---|---|---|---|
| 1 | package Language::Nouse; | |||||
| 2 | ||||||
| 3 | 3 3 3 | 15 6 22 | use strict; | |||
| 4 | 3 3 3 | 20 6 30 | use warnings; | |||
| 5 | ||||||
| 6 | our $VERSION = '0.04'; | |||||
| 7 | ||||||
| 8 | sub new { | |||||
| 9 | 3 | 19 | my $class = shift; | |||
| 10 | 3 | 13 | my $self = bless {}, $class; | |||
| 11 | ||||||
| 12 | 3 | 10 | my $options = shift; | |||
| 13 | 3 | 23 | $self->clear(); | |||
| 14 | ||||||
| 15 | 3 | 14 | $self->{sub_get} = \&NOUSE_DEFAULT_get; | |||
| 16 | 3 | 12 | $self->{sub_put} = \&NOUSE_DEFAULT_put; | |||
| 17 | ||||||
| 18 | 3 | 64 | return $self; | |||
| 19 | } | |||||
| 20 | ||||||
| 21 | sub clear { | |||||
| 22 | 8 | 25 | my ($self) = @_; | |||
| 23 | 8 | 31 | $self->{ring} = []; | |||
| 24 | 8 | 26 | $self->{stack} = []; | |||
| 25 | 8 | 26 | $self->{ring_pointer} = 0; | |||
| 26 | } | |||||
| 27 | ||||||
| 28 | sub set_get { | |||||
| 29 | 0 | 0 | my ($self, $new) = @_; | |||
| 30 | 0 | 0 | $self->{sub_get} = $new; | |||
| 31 | } | |||||
| 32 | ||||||
| 33 | sub set_put { | |||||
| 34 | 1 | 4 | my ($self, $new) = @_; | |||
| 35 | 1 | 4 | $self->{sub_put} = $new; | |||
| 36 | } | |||||
| 37 | ||||||
| 38 | sub NOUSE_DEFAULT_get { | |||||
| 39 | 0 | 0 | return getc; | |||
| 40 | } | |||||
| 41 | ||||||
| 42 | sub NOUSE_DEFAULT_put { | |||||
| 43 | 0 | 0 | print $_[0]; | |||
| 44 | } | |||||
| 45 | ||||||
| 46 | sub load_linenoise { | |||||
| 47 | 4 | 19 | my ($self, $input) = @_; | |||
| 48 | ||||||
| 49 | 4 | 14 | $self->_reset_ring(); | |||
| 50 | ||||||
| 51 | 4 | 15 | my $op = qr/[#:<>+?^]/; | |||
| 52 | 4 | 11 | my $mul = qr/[0-9a-z_]/; | |||
| 53 | ||||||
| 54 | 4 | 10 | my $not_op = qr/[^#:<>+?^]/; | |||
| 55 | 4 | 10 | my $not_mul = qr/[^0-9a-z_]/; | |||
| 56 | ||||||
| 57 | 4 | 86 | while ($input =~ m/$not_op*($op)$not_mul*($mul)/g){ | |||
| 58 | ||||||
| 59 | 43 | 92 | my $this_op = $1; | |||
| 60 | 43 | 86 | my $this_mul = $2; | |||
| 61 | ||||||
| 62 | 43 | 121 | $this_op =~ y/#:<>+?^/0123456/; | |||
| 63 | ||||||
| 64 | 43 | 153 | $this_mul = 10 + (ord($this_mul) - ord('a')) if ($this_mul =~ m/[a-z]/); | |||
| 65 | 43 | 125 | $this_mul = 36 if ($this_mul eq '_'); | |||
| 66 | ||||||
| 67 | 43 | 115 | my $op_code = ($this_mul * 7) + $this_op; | |||
| 68 | ||||||
| 69 | 43 43 | 71 439 | push @{$self->{ring}}, $op_code; | |||
| 70 | } | |||||
| 71 | ||||||
| 72 | } | |||||
| 73 | ||||||
| 74 | sub load_assembly { | |||||
| 75 | 4 | 14 | my ($self, $input) = @_; | |||
| 76 | ||||||
| 77 | 4 | 13 | $self->_reset_ring(); | |||
| 78 | ||||||
| 79 | 4 | 18 | $input =~ s/#(.*?)(\r|\n|$)/$2/g; | |||
| 80 | ||||||
| 81 | 4 | 23 | my @tokens = split /[,\n\r]/, $input; | |||
| 82 | ||||||
| 83 | 4 | 14 | for my $token(@tokens){ | |||
| 84 | 16 | 71 | $token =~ s/\s*(.*?)\s*/$1/; | |||
| 85 | ||||||
| 86 | 16 | 60 | if ($token =~ m/(cut|paste|read|write|add|test|swap) (\d+)/i){ | |||
| 87 | ||||||
| 88 | 15 | 30 | my $op = $1; | |||
| 89 | 15 | 31 | my $mul = $2; | |||
| 90 | ||||||
| 91 | 15 0 0 | 130 0 0 | if ($op eq 'cut'){$op = 0;} | |||
| 92 | 3 | 8 | elsif ($op eq 'paste'){$op = 1;} | |||
| 93 | 3 | 6 | elsif ($op eq 'read'){$op = 2;} | |||
| 94 | 3 | 10 | elsif ($op eq 'write'){$op = 3;} | |||
| 95 | 3 | 6 | elsif ($op eq 'add'){$op = 4;} | |||
| 96 | 3 | 6 | elsif ($op eq 'test'){$op = 5;} | |||
| 97 | elsif ($op eq 'swap'){$op = 6;} | |||||
| 98 | ||||||
| 99 | 15 15 | 28 81 | push @{$self->{ring}}, ($mul * 7) + $op; | |||
| 100 | ||||||
| 101 | }elsif ($token =~ m/(\d+)/){ | |||||
| 102 | ||||||
| 103 | 0 0 | 0 0 | push @{$self->{ring}}, $1+0; | |||
| 104 | ||||||
| 105 | } | |||||
| 106 | } | |||||
| 107 | } | |||||
| 108 | ||||||
| 109 | sub get_linenoise { | |||||
| 110 | 36 | 102 | my ($self) = @_; | |||
| 111 | ||||||
| 112 | 36 | 147 | $self->_reset_ring(); | |||
| 113 | ||||||
| 114 | 36 | 70 | my $buffer = ''; | |||
| 115 | 36 36 | 61 125 | for my $raw(@{$self->{ring}}){ | |||
| 116 | 943 | 2157 | my $op = $raw % 7; | |||
| 117 | 943 | 2369 | my $mul = int($raw / 7); | |||
| 118 | ||||||
| 119 | 943 | 1737 | $op =~ y/0123456/#:<>+?^/; | |||
| 120 | ||||||
| 121 | 943 | 3939 | if ($mul == 36){ | |||
| 122 | 0 | 0 | $mul = '_'; | |||
| 123 | }elsif ($mul > 9){ | |||||
| 124 | 656 | 1725 | $mul = chr(ord('a') + ($mul - 10)); | |||
| 125 | } | |||||
| 126 | ||||||
| 127 | 943 | 2768 | $buffer .= $op.$mul; | |||
| 128 | } | |||||
| 129 | 36 | 151 | return $buffer; | |||
| 130 | } | |||||
| 131 | ||||||
| 132 | sub get_assembly { | |||||
| 133 | 5 | 14 | my ($self, $per_line) = @_; | |||
| 134 | ||||||
| 135 | 5 | 16 | $self->_reset_ring(); | |||
| 136 | ||||||
| 137 | 5 | 20 | $per_line = 4 unless defined $per_line; | |||
| 138 | ||||||
| 139 | 5 | 7 | my @ops; | |||
| 140 | ||||||
| 141 | 5 5 | 10 19 | for my $raw(@{$self->{ring}}){ | |||
| 142 | 25 | 55 | my $op = $raw % 7; | |||
| 143 | 25 | 60 | my $mul = int($raw / 7); | |||
| 144 | ||||||
| 145 | 25 | 305 | if ($op == 0){ | |||
| 146 | 0 | 0 | $op = 'cut'; | |||
| 147 | }elsif ($op == 1){ | |||||
| 148 | 0 | 0 | $op = 'paste'; | |||
| 149 | }elsif ($op == 2){ | |||||
| 150 | 5 | 12 | $op = 'read'; | |||
| 151 | }elsif ($op == 3){ | |||||
| 152 | 5 | 11 | $op = 'write'; | |||
| 153 | }elsif ($op == 4){ | |||||
| 154 | 5 | 10 | $op = 'add'; | |||
| 155 | }elsif ($op == 5){ | |||||
| 156 | 5 | 11 | $op = 'test'; | |||
| 157 | }elsif ($op == 6){ | |||||
| 158 | 5 | 10 | $op = 'swap'; | |||
| 159 | } | |||||
| 160 | ||||||
| 161 | 25 | 103 | push @ops, "$op $mul"; | |||
| 162 | } | |||||
| 163 | ||||||
| 164 | 5 | 11 | my $buffer = ''; | |||
| 165 | 5 | 16 | while (@ops){ | |||
| 166 | 10 | 60 | $buffer .= join(', ', splice @ops, 0, $per_line)."\n"; | |||
| 167 | } | |||||
| 168 | ||||||
| 169 | 5 | 24 | return $buffer; | |||
| 170 | } | |||||
| 171 | ||||||
| 172 | sub run { | |||||
| 173 | 0 | 0 | my ($self) = @_; | |||
| 174 | ||||||
| 175 | 0 0 | 0 0 | while(scalar(@{$self->{ring}})){ | |||
| 176 | 0 | 0 | $self->step(); | |||
| 177 | } | |||||
| 178 | } | |||||
| 179 | ||||||
| 180 | sub step { | |||||
| 181 | 30 | 192 | my ($self) = @_; | |||
| 182 | ||||||
| 183 | 30 | 85 | my ($op, $mul, $raw) = $self->_get_op(); | |||
| 184 | ||||||
| 185 | 30 30 | 50 101 | my $skip = scalar(@{$self->{stack}}) * $mul; | |||
| 186 | 30 | 51 | $skip++; | |||
| 187 | ||||||
| 188 | 30 | 90 | if ($op == 0){ | |||
| 189 | # cut | |||||
| 190 | 3 | 9 | $self->_skip($skip); | |||
| 191 | 3 | 11 | $self->_push($self->_get_oprand()); | |||
| 192 | 3 | 12 | $self->_remove_op(); | |||
| 193 | 3 | 6 | $skip--; | |||
| 194 | } | |||||
| 195 | ||||||
| 196 | 30 | 90 | if ($op == 1){ | |||
| 197 | # paste | |||||
| 198 | 1 | 3 | $self->_skip($skip); | |||
| 199 | 1 1 | 1 5 | if (scalar(@{$self->{stack}})){ | |||
| 200 | 1 | 3 | $self->_insert_op($self->_pop()); | |||
| 201 | }else{ | |||||
| 202 | 0 | 0 | $self->_insert_op($self->_get_oprand()); | |||
| 203 | } | |||||
| 204 | } | |||||
| 205 | ||||||
| 206 | 30 | 80 | if ($op == 2){ | |||
| 207 | # read | |||||
| 208 | 0 0 | 0 0 | my $in = &{$self->{sub_get}}(); | |||
| 209 | 0 | 0 | if (defined $in){ | |||
| 210 | 0 | 0 | $self->_push(ord($in) % 256); | |||
| 211 | } | |||||
| 212 | } | |||||
| 213 | ||||||
| 214 | 30 | 86 | if ($op == 3){ | |||
| 215 | # write | |||||
| 216 | 14 14 | 22 56 | if (scalar(@{$self->{stack}})){ | |||
| 217 | 14 14 | 39 44 | &{$self->{sub_put}}(chr($self->_peek())); | |||
| 218 | } | |||||
| 219 | } | |||||
| 220 | ||||||
| 221 | 30 | 125 | if ($op == 4){ | |||
| 222 | # add | |||||
| 223 | 11 11 | 18 42 | if (scalar(@{$self->{stack}})){ | |||
| 224 | 11 | 31 | $self->_skip($skip); | |||
| 225 | 11 | 28 | my $oprand = $self->_get_oprand(); | |||
| 226 | 11 | 32 | $oprand += $self->_pop(); | |||
| 227 | 11 | 37 | $self->_push($oprand % 256); | |||
| 228 | } | |||||
| 229 | } | |||||
| 230 | ||||||
| 231 | 30 | 88 | if ($op == 5){ | |||
| 232 | # test | |||||
| 233 | 0 0 | 0 0 | if (scalar(@{$self->{stack}})){ | |||
| 234 | 0 | 0 | $self->_skip($skip); | |||
| 235 | 0 | 0 | my $oprand = $self->_get_oprand(); | |||
| 236 | 0 | 0 | if ($oprand == $self->_peek()){ | |||
| 237 | 0 | 0 | $self->_pop(); | |||
| 238 | } | |||||
| 239 | } | |||||
| 240 | } | |||||
| 241 | ||||||
| 242 | 30 | 84 | if ($op == 6){ | |||
| 243 | # swap | |||||
| 244 | 1 | 4 | $self->_swap(); | |||
| 245 | } | |||||
| 246 | ||||||
| 247 | 30 | 80 | $self->_skip($skip); | |||
| 248 | } | |||||
| 249 | ||||||
| 250 | sub _skip { | |||||
| 251 | 45 | 122 | my ($self, $by) = @_; | |||
| 252 | # skip the ring pointer along by $by places | |||||
| 253 | 45 | 110 | $self->{ring_pointer} += $by; | |||
| 254 | ||||||
| 255 | 45 45 | 70 179 | my $s = scalar(@{$self->{ring}}); | |||
| 256 | 45 | 127 | if ($s == 0){ | |||
| 257 | 1 | 2 | $self->{ring_pointer} = 0; | |||
| 258 | 1 | 3 | return; | |||
| 259 | } | |||||
| 260 | ||||||
| 261 | 44 | 174 | $self->{ring_pointer} = $self->{ring_pointer} % $s; | |||
| 262 | } | |||||
| 263 | ||||||
| 264 | sub _get_op { | |||||
| 265 | 30 | 72 | my ($self) = @_; | |||
| 266 | ||||||
| 267 | 30 | 78 | my $raw = $self->_get_oprand(); | |||
| 268 | 30 | 72 | my $op = $raw % 7; | |||
| 269 | 30 | 79 | my $mul = int($raw / 7); | |||
| 270 | ||||||
| 271 | 30 | 105 | return ($op, $mul, $raw); | |||
| 272 | } | |||||
| 273 | ||||||
| 274 | sub _get_oprand { | |||||
| 275 | 44 | 106 | my ($self) = @_; | |||
| 276 | 44 44 | 64 168 | die "ARGH: The ring is empty and you're asking for an oprand!" if !scalar(@{$self->{ring}}); | |||
| 277 | 44 | 191 | return $self->{ring}->[$self->{ring_pointer}]; | |||
| 278 | } | |||||
| 279 | ||||||
| 280 | sub _push { | |||||
| 281 | 28 | 128 | my ($self, $value) = @_; | |||
| 282 | 28 28 | 97 101 | push @{$self->{stack}}, $value; | |||
| 283 | } | |||||
| 284 | ||||||
| 285 | sub _pop { | |||||
| 286 | 26 | 61 | my ($self) = @_; | |||
| 287 | 26 26 | 44 88 | return pop @{$self->{stack}}; | |||
| 288 | } | |||||
| 289 | ||||||
| 290 | sub _peek { | |||||
| 291 | 14 | 34 | my ($self) = @_; | |||
| 292 | 14 | 91 | my $data = $self->_pop(); | |||
| 293 | 14 | 41 | $self->_push($data); | |||
| 294 | 14 | 36 | return $data; | |||
| 295 | } | |||||
| 296 | ||||||
| 297 | sub _remove_op { | |||||
| 298 | 3 | 8 | my ($self) = @_; | |||
| 299 | 3 3 | 6 13 | splice @{$self->{ring}}, $self->{ring_pointer}, 1; | |||
| 300 | } | |||||
| 301 | ||||||
| 302 | sub _insert_op { | |||||
| 303 | 1 | 3 | my ($self, $value) = @_; | |||
| 304 | 1 1 | 3 5 | splice @{$self->{ring}}, $self->{ring_pointer}, 0, ($value); | |||
| 305 | } | |||||
| 306 | ||||||
| 307 | sub _reset_ring { | |||||
| 308 | 49 | 116 | my ($self) = @_; | |||
| 309 | ||||||
| 310 | 49 | 128 | my $p = $self->{ring_pointer}; | |||
| 311 | 49 49 | 69 142 | my $l = scalar(@{$self->{ring}}); | |||
| 312 | ||||||
| 313 | 49 49 | 91 276 | my @new_ring = splice(@{$self->{ring}}, $p, $l-$p); | |||
| 314 | 49 49 | 116 228 | push @new_ring, splice(@{$self->{ring}}, 0, $p); | |||
| 315 | ||||||
| 316 | 49 | 152 | $self->{ring} = \@new_ring; | |||
| 317 | 49 | 160 | $self->{ring_pointer} = 0; | |||
| 318 | } | |||||
| 319 | ||||||
| 320 | sub _swap { | |||||
| 321 | 1 | 3 | my ($self) = @_; | |||
| 322 | ||||||
| 323 | 1 1 | 2 5 | my @new_ring = @{$self->{stack}}; | |||
| 324 | ||||||
| 325 | 1 | 3 | my $p = $self->{ring_pointer}; | |||
| 326 | 1 1 | 2 4 | my $l = scalar(@{$self->{ring}}); | |||
| 327 | ||||||
| 328 | 1 1 | 2 12 | my @new_stack = splice(@{$self->{ring}}, $p, $l-$p); | |||
| 329 | 1 1 | 4 4 | push @new_stack, splice(@{$self->{ring}}, 0, $p); | |||
| 330 | ||||||
| 331 | 1 | 3 | $self->{stack} = \@new_stack; | |||
| 332 | 1 | 4 | $self->{ring} = \@new_ring; | |||
| 333 | 1 | 4 | $self->{ring_pointer} = 0; | |||
| 334 | } | |||||
| 335 | ||||||
| 336 | ||||||
| 337 | sub debug { | |||||
| 338 | 0 | my ($self) = @_; | ||||
| 339 | ||||||
| 340 | 0 0 0 | print "RING: ".join(',',@{$self->{ring}})." STACK:".join(',',@{$self->{stack}})."\n"; | ||||
| 341 | } | |||||
| 342 | ||||||
| 343 | 1; | |||||
| 344 | ||||||