The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

zoiddevel - Development documentation for zoid

DESCRIPTION

Debugging

If you switch on the global debug bit and the verbose bit both your input and debug information are echoed to STDERR. Thus by piping both STDOUT and STDERR to a file you get a detailed log of what is happening.

To set both bits type:

 zoid> set debug
 zoid> set verbose

or start zoid with:

 $ zoid -vD

You can also get debug information from just one module by setting a global variable $DEBUG in the module's namespace.

If you set the debug variable to a number higher than 1, you get a stack trace of that number of frames for each exception thrown using the error() method.

Structure

class diagram

   Zoidberg::Shell
         /|\            Zoidberg::Contractor
          |                 /|\   /|\
          |__________________|     |
               |                   |
               |            Zoidberg::Job
            Zoidberg              /|\
                                   |
                                   |
                            Zoidberg::Job::Builtin

All other classes stand on their own (except perhaps for some internal helper classes).

Zoidberg::Shell is an interface class to be used in perl scripts like zoidrc. Zoidberg inheriting from it in order to make this interface also available through the $shell object in the eval scope.

The Zoidberg::Contractor class contains the code to manage Jobs. Because the Job class also inherits from it sub-jobs are possible.

object structure

   main object  class Zoidberg
     |
     |___ {eval}          Zoidberg::Eval object
     |___ {stringparser}  Zoidberg::StringParser object
     |
     |___ {jobs}          Array with Job objects
     |      |_ items of class Zoidberg::Job
     |
     |___ {settings}      Settings and config hashes
     |___ {objects}       Plugin objects       - tied Zoidberg::PluginHash
     |___ {contexts}      Custom contexts      - tied Zoidberg::DispatchTable
     |___ {events}        Event code refs      - tied Zoidberg::DispatchTable
     |___ {commands}      Command code refs    - tied Zoidberg::DispatchTable
     |___ {aliases}       Alias definitions

FIXME explanation

Process flow

       start
         |
         |        incomplete input - readmore
         |        .----------------+----------------------.
         |        |                A                      |
         |        V                |                      |
         V        string           |           tree       |
   input loop     -------->  parse string      ------>  execute tree
   Z:main_loop()             Z:shell_string()    ,--->  Z:C:shell_list()
   readline                                      |        | 
         A                                       |        | logic chunk
         |                                       |        V
  new    |                                next   |     Z:parse_block()
  input  |                                chunk  |     (per block)
         |                                       |        |
         |                                       |        V
         '---------------------------------------+---  execute job
                                                       Z:J:exec()
  Z:   = Zoidberg::
  Z:C: = Zoidberg::Contractor::
  Z:J: = Zoidberg::Job::

The parse tree going to shell_list() is a simple parse tree consisting of scalars and scalar references (see FIXME below). This tree is processed in logic chunks, corresponding to one job each. The blocks references in a logic chunk are parsed by parse_block() just before executing. This is where the context is determined and things like expansions and subtitutions are done.

Parse tree

FIXME tell about the blocks used in lots of hooks

FIXME renice this stuff

This part describes the form of a parse tree as used between the various Zoidberg objects.

Example

 # Commandline input:
 
 $ ls -al | perl{ while (<STDIN>) { print $_ }  }abc && echo done
 
 # Would be parsed to:
 
 [ 
        [{context => 'SH'}, qw/ls -al/],
        [{context => 'PERL', opts => 'abc'}, q{ while (<STDIN>) { print $_ } } ],
        'AND',
        [{context => 'SH'}, qw/echo done/]
 ]
 
 
 
 # Commandline input:
 
 $ cd .. && ls -al ; cp dus ~/tmp/ &
 
 # Would be parsed to:
 
 [
        [{context => 'CMD'}, qw/cd ../],
        'AND',
        [{context => 'SH'}, qw/ls -al/],
        'EOS',  # End Of Statement
        [{context => 'SH'}, qw{cp dus ~/tmp/}],
        'BGS'   # BackGround Statement
 ]
  
 # FIXME an example with redirections

Basics

A parse tree is an array consisting of blocks and tokens. A block can be any kind of code and is stored in a nested array. Blocks directly following each other are supposed to be a pipeline. A token is a delimiter between blocks.

The first field of a block is a hash which contains information about the block, all other field in a block make up the content. The most important information about a block is the context, which tells the parser how to execute the block. You are free to store all kinds of specific information in this first field, but some key names are reserved.

FIXME reserved meta fields

Pseudo parse trees

These are forms that can be used with the shell() function provided by Zoidberg::Shell. Just as by the real parse tree blocks of code are references and tokens are plain scalars. A block that is a scalar reference will be split into words and parsed completely (although still expected to be one block). A block that is an array reference will be considered to be completely parsed if the first element is a hash reference and the context defined, else it is considered a word list, possibly with meta data defined in the first element.

        # for example "ls -al | perl{ while (<STDIN>) { print $_ }  }abc && echo done"
        # can be executed by calling :
        shell(
                [qw/ls -al/],
                \'perl{ while (<STDIN>) { print $_ }  }abc',
                'AND'
                [{context => 'SH'}, qw/echo done/]
        );

Using this kind of pseudo trees only makes sense if you are lazy or you don't know exactly what the command is but you have some clues.

Settings

Notice that every plugin has it's own config hash in the settings hash, it is suggested that settings only affecting one plugin are placed in this sub-hash. This rule is broken for some settings required by the posix specification of the set built-in.

FIXME list these posix settings somwhere

Below are listed advanced settings only needed for development. Common settings can be found in zoiduser.

_no_redirection

Disable builtin parsing for redirections. Probably needed if you want to do this yourself.

_no_env

Disable builtin parsing for local environment settings. Probably needed if you want to do this yourself.

_no_hardcoded_context

Disable hard-coded contexts like PERL, SH and CMD. This might be useful to make for example a restricted shell.

debug

Turn on _all_ debug messages.

Events

The hash called 'events' in the main object is a tied hash of the class Zoidberg::DispatchTable. This hash contains code refs to be called when certain events happen, thus providing a hook mechanism.

Events of two types are used, first there is the 'broadcast' type in which case all code refs on the stack are called; second there is the 'call' type, in this case only the ref on top of the stack is called and it's return value is passed on to the caller.

broadcast events

FIXME more events

beat

Called every idle second when using Term::ReadLine::Zoid. Used for asynchronous stuff.

newpwd

Called if $ENV{PWD} has changed. The old pwd can be found in $ENV{OLDPWD}.

prompt

This event is called from the main loop just before respawning the prompt. It can be used to update routines.

cmd

This event is called after the input prompt with the commandline as the first argument.

envupdate

This event is called after the spawning of every job. Like 'prompt' it can be used for update routines; since 'envupdate' is called more often then 'prompt', use 'prompt' if it makes no difference, 'envupdate' should only be used for atomic updates.

exit

Called just before round_up(), used as replacement for .bash_logout.

call events

Events of the 'call' type used by Zoidberg include:

readline

This method is used to get interactive input, by default this event would be handled by the ReadLine plugin or an alternative input plugin. The boolean $broken signifies that the previous input wasn't complete and thus more input is requested.

readmore

This typically would do a readline with $PS2 as prompt instead of $PS1, although if you use the ReadLine plugin with Term::ReadLine::Zoid it would open the original buffer with an empty line appended.

Plugins

PluginConf

A plugin configuration file can use the following keys:

module

The class to bless the plugin object in.

load_on_init

Boolean to force the plugin to be loaded during shell initialisation. By default plugins are loaded on first use to keep initialisation as fast as possible.

config

Hash with plugin specific config stuff. For plugins that inherit from Zoidberg::Fish this will automatically become the {config} attribute.

commands

Hash linking command names to subroutines. See Zoidberg::DispatchTable for the string format used in this hash.

export

Array with commands automatically linking to a subroutine of the same name in the plugin object.

events

Like commands but used for events.

import

Like export but used for events.

Custom contexts

Typically you'll want to set a custom context from a plugin. To do this you set the "load_on_init" setting in the "PluginConf" and then setup the context parser and handlers routines from the plugin's init() routine, using the add_context() method in Zoidberg::Fish.

A context config hash can contain the following routines:

FIXME more verbose description

word_list BLOCK

If you want your context to work with words you should have this hook to recognize and list commands. On wantarray it should return a list of possible completions for the word in $$block[1]; else it should check whether the word is a recognized command and return true on success. In both cases it is also allowed to return a block ref, this is for the more advanced options.

parser BLOCK

Here you can set options like for example fork_job or no_words for your context.

handler BLOCK

This is the part where your command gets executed.

intel BLOCK

Hook used by the Intel plugin for custom completion.

filter BLOCK

Hook to filter blocks before they are parsed, this can be used for example for custom redirection code.

Be aware that the a block might be filtered twice with the same meta hash, the second time should reset any meta fields set by the first time.

"Hello World" plugin

Here follows an example to create a simple plugin with the "hello_world" builtin.

First create a dir ~/.zoid/plugins/HelloWorld.

Next create a module, for example :

  package HelloWorldZoid;

  # Zoidberg::Fish is the base class for plugins
  use Zoidberg::Fish;
  our @ISA = qw/Zoidberg::Fish/;

  # Zoidberg::Utils provides the output method
  use Zoidberg::Utils;

  # no need for a constructor, bootstrap with init
  sub init { $$_[0]{config}{string} ||= "Hello world !" }

  # and in this sub we actually print the string
  sub hello_world { output $$_[0]{config}{string} }

  # this will be called when the plugin is unloaded
  sub round_up { output "someone help me" }

  1; # keep require happy

Save the module as ~/.zoid/plugins/HelloWorld/HelloWorldFish.pm.

Then create a config file, this is just a perl script returning a config hash.

  {
    module => q/HelloWorldFish/,
    config => { # This hash will be accessible as $shell->{settings}{HelloWorld}
      string => q/Hello cruel world !/
    },
    export => ['hello_world'],
  }

Save the config file as ~/.zoid/plugins/HelloWorld/PluginConf.pl.

After restarting zoid, you should have a builtin command "hello_world" that prints the string "Hello cruel world !", and an object called "HelloWorld". Of course you guessed already that you can control the string that will be printed from the config file, and also that it defaults to "Hello world !". On run-time this string is available under $shell->{settings}{HelloWorld}{string}, it can be changed at any time.

ENVIRONMENT

The following environment variables are used be Zoidberg.

ZOIDPID

Contains the process id of a parent zoidberg shell, intended to be used for an IPC mechanism.

ZOIDREF

In forked child processes this variable contains a stringyfied version of the current Zoidberg object in charge. The parent process has a global hash %Zoidberg::OBJECTS which maps these strings to the original references. It is intended that an IPC mechanism uses this hash to convert strings back to references.

This value should correspond to the object stored in the global $Zoidberg::CURRENT at the time of forking.

In secondary scripts it is better to use Zoidberg::Shell->current().

TODO this mechanism seems to have some problems

ZOIDCMD

Contains the string that caused the current command to be executed. Can be used by a command to inspect its arguments as they were before path expansions etc. .

SEE ALSO

perl(1), http://zoidberg.sourceforge.net

1 POD Error

The following errors were encountered while parsing the POD:

Around line 445:

You forgot a '=back' before '=head1'