Kategorien
Allgemein

Einfacher Interpreter einer einfachen Sprache

Nachdem Jörg mir ein paar Perl-Bücher geliehen hatte und schon seit längerer Zeit das „Hello World!“ Plakat auf unserem stillen Örtchen hängt, musste ich mich einfach an einen Brainfuck Interpreter versuchen.

Und hier ist das Ergebnis:

#!/usr/bin/perl
{
  package BF;
  use strict;
  use warnings;

  my @REGISTERS=('PP', 'MP', 'LOOP');
  my %OPERATORS=(
      '+' => sub {
          my ($self) = @_;
          return if $self->{_registers}{'LOOP'} < 0;
          $self->{_memory}[$self->{_registers}{'MP'}]++;
          $self->{_registers}{'PP'}++;
      },
      '-' => sub {
          my ($self) = @_;
          return if $self->{_registers}{'LOOP'} < 0;
          $self->{_memory}[$self->{_registers}{'MP'}]--;
          $self->{_registers}{'PP'}++;
      },
      '.' => sub {
          my ($self) = @_;
          return if $self->{_registers}{'LOOP'} < 0;
          push @{ $self->{_output} }, $self->{_memory}[$self->{_registers}{'MP'}];
          $self->{_registers}{'PP'}++;
      },
      '>' => sub {
          my ($self) = @_;
          return if $self->{_registers}{'LOOP'} < 0;
          $self->{_registers}{'MP'}++;
          $self->{_registers}{'PP'}++;
      },
      '<' => sub {
          my ($self) = @_;
          return if $self->{_registers}{'LOOP'} < 0;
          $self->{_registers}{'MP'}--;
          $self->{_registers}{'PP'}++;
      },
      '[' => sub {
          my ($self) = @_;
          if ($self->{_memory}[$self->{_registers}{'MP'}] > 0) {
              push @{ $self->{_stack} }, $self->{_registers}{'PP'};
          } else {
              $self->{_registers}{'LOOP'}--;
          }
          $self->{_registers}{'PP'}++;
          return;
      },
      ']' => sub {
          my ($self) = @_;
          my $loop_stacktart = pop @{ $self->{_stack} };
          if ($self->{_memory}[$self->{_registers}{'MP'}]) {
              $self->{_registers}{'PP'} = $loop_stacktart;
          } else {
              $self->{_registers}{'PP'}++;
          }
          $self->{_registers}{'LOOP'}++;
      },
  );

  sub new {
      my $class = shift;
      my $data = {
          _registers => { map { $_ => 0 } @REGISTERS },
          _memory => [],
          _stack => [],
          _output => [],
      };
      bless $data, $class;
  };

  sub run {
      my $self = shift;
      while ($self->{_registers}{PP} < $self->{_max_program_size}) {
          my $op_char = $self->{_program}[$self->{_registers}{PP}];
          my $op = $OPERATORS{$op_char};
          $op->($self);
      }
  }

  sub set_memory {
      my ($self, $memory) = @_;
      $self->{_program} = [ split //, $memory ];
      $self->{_max_program_size} = @{ $self->{_program} };
  }

  sub output {
      my $self = shift;
      return join ',', @{ $self->{_output} };
  }

  sub char_output {
      my $self = shift;
      return join '', map { chr($_) } @{ $self->{_output} };
  }
}

package main;
use strict;
use warnings;

use Test::More qw(no_plan);

my %test_data = (
    '++.' => '2',
    '++-.' => '1',
    '++.>+.<+.' => '2,1,3',
    '+++.[-.].' => '3,2,1,0,0',
    '++.[->++[.-]<]' => '2,2,1,2,1',
    '++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.' => '72,101,108,108,111,32,87,111,114,108,100,33,10',
);
while (my ($prog, $exptected_output) = each %test_data) {
    my $bf = BF->new();
    $bf->set_memory($prog);
    $bf->run();
    is($bf->output(), $exptected_output, $prog);
}

my $bf = BF->new();
$bf->set_memory('++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.');
$bf->run();
is("Hello World!n", $bf->char_output(), "Hello World!");

Eine tolle sinnlose Beschäftigung.

Und wen es interessiert: Das Plakat zum Programm.

Kategorien
Allgemein

Standardkodierung von JSP Seiten

Wird eine JSP Seite in ein Servlet übersetzt und dann auch noch von einem Browser aus aufgerufen, so sind etwa vier Kodierungen durchlaufen.

Zuerst wird aus einer JSP Seite eine Java Klasse erzeugt. Hierfür findet eine Konvertierung von ISO-8859-1 nach UTF-8 statt. Will man eine andere Kodierung in der JSP Seite pflegen, so muss das mit einer pageEncoding Anweisung angegeben werden.

Dann wird aus der Java Klasse der JavaVM Bytecode erzeugt. Hier findet – so die Java Klasse in UTF-8 kodiert ist – keine konvertierung statt. In der JavaVM ist dann alles in Unicode.

Nun kommt der spannende Augenblick und ein Browser greift auf das übersetzt JSP-Servlet zu. Die Kodierung der HTTP-Anfrage des Browsers wird genutzt um die Daten der Anfrage vom Servlet-Container in Unicode zu wandeln.

Für die Reise zurück wandelt eine JSP Seite dann den Datenstrom – so nichts anderes vorgegeben – wieder in ISO-8859-1 um.

Um das ganze noch ein wenig spannender zu gestalten, ist die Standard-Kodierung von JSP Dokumenten – also XML-basierte JSP’s – natürlich UTF-8.

Seltsam also, das normalerweise so wenig Kodierungs-Probleme auftreten.

Kategorien
Allgemein

Loggen in Shell Programmen

In Java wird log4j genutzt, in Perl gibt es Log::Log4perl, in Python ist zwar keine 4 im Namen, aber logging tut es ja auch. Für JavaScript gibt es log4js und in Bash gibt es natürlich log4sh. Aber wie immer, kann man das auch einfach selber machen.

Zum Beispiel mit einer einfachen log Funktion:

#!/usr/bin/bash
LOG_DEBUG=0
LOG_INFO=1
LOG_WARN=2
LOG_ERROR=3
LOG_CRITICAL=4
export LOG_DEBUG LOG_INFO LOG_WARN LOG_ERROR LOG_CRITICAL

export LOG_LEVEL=$LOG_WARN
export LOG_FILE="$HOME/simple_log"

function log() {
    local level=shift
    local message=$*

    if [ "$level" -ge "$LOG_LEVEL" ]
    then
        echo "$(date) $message" >> "$LOG_FILE"
    fi
}

Ist dieses Shell Skript in einer Datei – sagen wir simple_log.sh – gespeichert, so kann es in einem anderen Shell-Skript wie folgt genutzt werden:

#!/usr/bin/bash
. simple_log.sh

log $LOG_DEBUG "Am Anfang des Skriptes"
log $LOG_INFO "Und ende."

Aber ehrlich: log4sh ist genauso einfach zu benutzen und kann viel mehr.

Kategorien
Allgemein

ByteBlog wieder da

Nach dem Crash wollte Quills nicht mehr starten.

Sobald Quills als Produkt installiert wurde, kam auf der Startseite von ByteWurf nur noch eine ewig lange Fehlermeldung, dass ein „traversable adapter“ für weblogs nicht gefunden werden konnte.

Das Zope den Adapter nicht finden konnte, lag daran, dass die Module für quills – wie app und core – gar nicht erst geladen wurden, da setuptools nicht installiert war.

Nun mussten nur noch die Workflow Zustände im Code von „published“ auf den ByteWurf Zustand gebracht werden. Und schon können alle wieder bloggen.

Kategorien
Allgemein

Refcards

Wenn man sich immer mal wieder fragt, wie hiess noch mal der Befehl, oder die welche Tastenkombination war die richtige?

Auf http://refcards.com/ gibt es jede Menge etwa A4 grosser PDF Dateien zu unterschiedlichen Themen, wie perl, javascript oder auch Firefox.

Da lohnt es sich auf jeden Fall mal vorbeizuschauen.

Kategorien
Allgemein

Gefilterte Listen

sind keine Neuigkeit und daher auch schon von anderen Implementiert.

Ich wollte aus einer Liste von Elementen nur ganz bestimmte Elemente holen und über diese direkt iterieren. Da die Elemente immer wieder nach unterschiedlichen Kriterien gefiltert werden sollten, dachte ich mir dass ich das ja verallgemeinern könnte. Eventuell mit einem Filter Interface:

interface Filter<T> {
   boolean isCase(T element);
}

Und einer allgemeinen Filterfunktion:

List<T> filterList(List<T> listOfElements, Filter<T> filter) {
   List<T> filteredElements = new ArrayList<T>();
   for (T element: listOfElements) {
      if (filter.isCase(element)) {
         filteredElements.add(element);
      }
   }
   return filteredElements;
}

Dann habe ich einfach mal filter und list und java in google eingegeben und bin (natürlich) über andere Leute mit demselben Problem und ähnlichen Ansätzen gestossen.

Bei google-collections und bei den jakarta-commons (mit generics). Dort werden allerdings auch direkt schon Iteratoren bereitgestellt, die filtern können und vieles mehr.

Also auch diesmal muss man das Rad nicht neu erfinden. Schade 🙂

Bei google-collections heissen die Filter im übrigen Predicates.

Kategorien
Allgemein

Einfach Windows installieren

In dem Fall hilft das Tool nLite weiter. Mit diesem kann ein fehlender Treiber in die Boot-CD eingebaut werden. Das funktioniert wohl zumindest mit Windows 2000 und XP.

Vor allem dann ist dies nötig, wenn die neue Hardware kein Disketten Laufwerk hat, auf dem Windows Zusatztreiber erkennen würde. USB Sticks sind dem System scheinbar nicht gut genug.

Kategorien
Allgemein

Prototypenärger

Warum Prototypen nur in den seltensten Fällen sinn machen.

Nachdem ich vor einiger Zeit schon mal über einen Sprachwechselfehler in einem meiner Perl Skripte gestossen bin.

Ich hatte eine Funktion wie folgt deklariert:

sub func1() {
    my $var = shift;
    ...
}

Und der Perl Compiler mäkelte irgendwas vonwegen too many arguments… Klar das () weist Perl an, das diese Funktion keine Parameter bekommen darf. Also das () entfernt und alles war wieder gut.

Objekte und Prototypen

Dann bin ich jetzt über eine Code Stelle gestossen, die folgendermassen als Methode in einem Objekt deklariert war:

sub method1( $ ) {
    my $self = shift;
    ....
    my $var = shift;
    ...
}

Da wollte der Autor wohl festhalten, dass diese Methode nur einen Parameter annimmt, und hat das $var=shift später übersehen, oder hat sich gedacht, $self=shift würde vorher ausgewertet werden. Es ist aber so, dass in Methodenaufrufen die Protoypen ignoriert werden.

Als Funktion aufgerufen macht die method1() aber auch nicht das was sie soll, da $self mit hoher Wahrscheinlichkeit nicht den richtigen Inhlat hat.

Praktisch sind sie aber doch

Zum Beispiel um eine Funktion wie grep nachzubauen:

sub my_grep(&@) {
    my ($grepper, @list) = @_;
    my @result = ();
    foreach (@list) {
        push @result, $_
          if ($grepper->());
    }
    return @result;
}

Diese Funktion kann dann wie das eingebaute grep benutzt werden:

my @odd = my_grep { $_[0] % 2 } ( 1 .. 20 );
Kategorien
Allgemein

Mit neuen Zope Versionen wird alles besser

Im Grunde war die Migration der Kino-Seiten auf die neue Zope Version 2.10 ein Kinderspiel…

Aber dann kam die Kleinigkeit mit den Berechtigungen. Aber das war ja schnell umschifft. Doch dann kam wieder eine seltsame Fehlermeldung über irgendeinen AquireWrapper oder so.

Nach kurzem suchen habe ich dann des Rätsels Ursprung gefunden. Verwendet man in dtml-in ein sort und sortiert damit SQL Results und haben diese doppelte gleiche Einträge, so verschluckt sich der Sortieralgorithmus. Das Problem ist wohl schon länger bekannt, aber bisher hat sich noch niemand bereit gefunden, die vorgeschlagenen Lösungswege in Zope zu übernehmen.

Nun ja, wir konnten uns helfen, indem wir die SQL Abfrage zum Sortieren genutzt haben und das sort aus dem dtml-in rausgenommen haben.

Kategorien
Allgemein

Aliasing in Perl

Da nutzt man seit Jahren Perl, und dann so was…

In Perl werden Schleifenvariablen über Arrays (und Hashes) als Aliase genutzt. Verändert man diese, so verändert man die Inhalt in den Arrays.

#!/usr/bin/perl
use warnings;
use strict;

use Test::More qw(no_plan);

my @names = qw( Felix Urte Sven );
for my $name (@names) {
    $name =~ s/e//;
}

my @new_names = qw( Flix Urt Svn );
is_deeply( @names, @new_names );

Bisher ist mir das nie aufgefallen. Hoffentlich habe ich tatsächlich nirgendwo solche Nebeneffekte produziert.

Oioioioi…