Tuesday, June 18, 2024

CodeMonkey

context: January 2005 is 4 months earlier than the latest web.archive of Clicker website. It is still lacking important (?) pages such as CodeMonkeyInternals (restructured from the current page ?) or ResourceCmPlugin explaining another thing code monkey was used for.
The CodeMonkey is a [Tool] for code rewriting that is due for Clicker 0.8.20 It will helps writing KDS/IDL code, accessing koLib templates (foreach on koList, cursors, etc), defining koLib & memory classes and much more.

Mechanics of CodeMonkey

The CodeMonkey core is a Perl script that performs input tokenization and manage context stacks (each 'block' has its own context). It comes with a helper package Tokens::Filter.pm that allows one to easily perform high-level pattern matching against operators, etc. Each CodeMonkeyPlugin can be requested separately via the @plugin "name.pl" in the source file.

Rules to write CodeMonkeyPlugin~s

All the 'top-level' code is for initialization

Among other things, it should insert _keyword hooks_ in $context->{hooks} to have plugin functions called when a keyword is encountered. You can also register _terminators_, that is, plugin functions to be called when the input file is completed.

# a skeleton CodeMonkey plugin

use Tokens::Filter;
print STDERR "using skeleton plugin\n";
$context->{hooks}->{skeleton}=['SKELETON',\&myHookFunction];
push @{$context->{terminators}},['SKELETON',\&myTerminator];
true;

sub myHookFunction {
}
sub myTerminator {
}

Hooks receive $mode, $tagname, $codeA, $codeB

The $mode parameter tells you the context where the keyword has been seen. It could be an 'INSTR' or a 'BLOCK'. $tagname returns you the first word that was associated with the hook (here 'SKELETON'). This can be used when multiple similar keywords wish to use the same function but still differenciate the cases.

$codeA and $codeB contains what you need to pass Tokens::Filter to analyze the code that triggered the hook.


sub myHookFunction {
  my ($mode, $tagname)=@_;
  my $code=new Tokens::Filter(@_[2,3]);

  # write your stuff here
}

$context and blocks

Each block construct has its own $context information. Thus when your hook is registered with $mode eq 'BLOCK', you can register local hooks in $context->{hooks}, etc. The "header" of the block (e.g. all the text between the previous instruction/block and the { symbol will used for the "block name" (after tags are processed). A hook can discard the name's output by setting a true value in $context->{naked}

$context->{upper} chains towards the upper-level context information.

controlling INSTRuctions

The hook can decide whether or not a terminating ';' should be written after the instruction's translation by returning a _true_ or _false_ value.

matching text

The Tokens::Filter package can be used to check high-level patterns in the 'running code'. The function to use is $code->match(<offset>, <pattern>). The <pattern> is a list of item types describing what we expect to be on the input:
  • WORD matches an identifier that is not a registered keyword.
  • LITT matches a litteral (e.g. a string/character)
  • TAGG matches any identified keyword
  • OPER matches any sequence of non-alphanum and non block-forming characters. Note that both "+","++","=++","*=&" will be seen as valid OPERators.
  • SLST matches an opening parenthese
  • LIST matches a completed list (pointing towards a specific code sequence).
  • THIS matches the current tagged-keyword
  • TRAN matches a translated item (from a previous hook application)
The type can be followed by a ":<value>" string further restricting possible matches For instance qw(THIS WORD OPER:~= LIST) will match an code sequence like skeleton var=(any thing can be here). The <offset> argument of $code->match tells the relative position of THIS in the pattern.

# let's try to check if we have the expected "skeleton var = (...)" framework.
sub myHookFunction {
  my ($mode, $tagname)=@_;
  my $code=new Tokens::Filter(@_[2,3]);

  # 0 is the position of "THIS" in the list ...
  if ($code->match(0, qw(THIS WORD OPER:~= LIST))) {
     # here we know.
  } else {
     die "unexpected use of 'skeleton'".$code->show;
  }
}

getting/replacing text

Once a pattern has been matched, you can get the text of the different items using $code->content. Each item in the pattern is numbered from _0_ to _N_ and you can retrieve its value with $code->content(i). For instance,

  # retrieve the variable name and the content of the list.
  # note that we get the list content as a raw string.
  # "skeleton myVar = (a,b,c,d,e)" ==> $variable_name eq 'myVar' &&
  #   $list_content eq '(a,b,c,d,e)'
  my $variable_name=$code->content(1);
  my $list_content=$code->content(3);
As the purpose of CodeMonkey is to _rewrite_ text, we could like to replace the actual text by something else. That is (again) done with $code->content() giving the new value as an additionnal argument:

  # rewrite it as "int* myVar[]=..."
  $code->content(0,'int*');
  $code->content(3,'[]=');
Of course, you may use PERL's power for more complex operations, for instance if i wish to have all the items in the $list_content translated so that we extract their address and make an array out of it, it simply means

#  remove parenthesis
$list_content=~ s/^\((.*)\)$/$1/;
#  split the content, assuming a flat list
@list_content= split /,/,$list_content;
@list_content= map { '&'.$_ } @list_content;
$code->content(2,"{".join(',',@list_content)."}");

#now we have "int* myVar[]={&a,&b,&c,&d,&e};

Date: Fri, 14 Jan 2005 06:52:43 -0800 Mime-Version: 1.0 (Produced by PhpWiki 1.3.9) Content-Type: application/x-phpwiki; pagename=CodeMonkey; flags=""; author=PypeClicker; version=3; lastmodified=1105714363; author_id=PypeClicker; markup=2; summary=more%20like%20a%20tutorial; hits=124; charset=iso-8859-1 Content-Transfer-Encoding: binary

No comments:

Post a Comment