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
TheTokens::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 keywordOPER
matches any sequence of non-alphanum and non block-forming characters. Note that both "+","++","=++","*=&" will be seen as valid OPERators.SLST
matches an opening parentheseLIST
matches a completed list (pointing towards a specific code sequence).THIS
matches the current tagged-keywordTRAN
matches a translated item (from a previous hook application)
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