- Contents
- Stages
- Troubleshooting
Compilation
The Ferret compiler is also written entirely in Perl. Compilation is a multi-stage process.
Stages
1. Tokenizer
The source code is tokenized into a flat list of labels and values. This step involves very minimal error checking. In fact, the only error that can occur during this process is a failure to tokenize a certain byte or string.
=== Tokenization === PKG_DEC | {"name":"Math"} | 1.5 CLASS_DEC | {"name":"Line"} | 2.5 METHOD | {"main":1,"name":"initializer__"} | 4.33333 CLOSURE_S | | 4.66667 KEYWORD_NEED | | 5.1 VAR_SELF | "pt1" | 5.2 OP_VALUE | | 5.3 BAREWORD | "Point" | 5.4 OP_COMMA | | 5.5 VAR_SELF | "pt2" | 5.6 OP_VALUE | | 5.7 BAREWORD | "Point" | 5.8 OP_SEMI | 1 | 5.9 CLOSURE_E | | 6.5
Example of an error raised during the tokenization stage
1 + 2 @ 3
Unable to tokenize '@' at line 1.
2. Constructor
The most significant step is the construction of the document model. The tokens are processed into a hierarchical tree of elements. Elements are represented by Perl objects which may belong to several categories such as List, Node, Expression, etc. Throughout this process, the constructor is checking that grammatical rules are met and aborts compilation if it encounters nonsense.
=== Document Model === Document './test/hello20/IRC/Bot.frt' Package 'IRC' Class 'Bot' Class method 'initializer__' Instruction Need Instance variable '@addr' Bareword 'Str' Instruction Need Instance variable '@nick' Bareword 'Str' Instruction Want Instance variable '@port' Expression ('want' parameter) Number '6667' Bareword 'Num' Instruction Assignment (instance variable '@handlers') Hash [3 items] Item 0 Pair 'MODE' Instance variable '@joinChannels' Item 1 Pair 'PING' Instance variable '@pong' Item 2 Pair 'PRIVMSG' Instance variable '@handleMessage' Instruction Assignment (instance variable '@sock') Call Bareword 'Socket::TCP' Named argument list [2 items] Item 0 Pair 'address' Instance variable '@addr' Item 1 Pair 'port' Instance variable '@port' On Expression ('on' parameter) Property 'connected' Instance variable '@sock' Anonymous function Instruction Call Instance variable '@send' Single value [1 items] Item 0 Operation String 'USER ' Addition operator (+) Instance variable '@user' Addition operator (+) String ' * * :' Addition operator (+) Instance variable '@real' Instruction Call Instance variable '@send' Argument list [1 items] Item 0 Operation String 'NICK ' Addition operator (+) Instance variable '@nick' Method 'connect' Instruction Call Property 'connect' Instance variable '@sock' Argument list [0 items]
Example of an error raised during the construction stage
$x in (1, 2, 3) { say("Number: $x") }
Error: Unexpected keyword 'in' (where is 'for'?). File -> (stdin) Line -> 1 Near -> lexical variable '$x' Parent -> document '(stdin)' Exception raised by Ferret::Lexer::Constructor line 387.
3. Enforcer
After the entire document tree is constructed, a final check ensures that all grammatical rules are met. Below is an example of an element rule definition.
WantNeed => { # WantNeed must always be a direct child of an instruction. parent_must_be => 'Instruction', # WantNeed[0] # WantNeed must always be inside one of these. must_be_somewhere_inside => [ # WantNeed[1] 'Function Method', 'Argument declaration must be within a function or method' ], instruction_inside_rules => { # directly inside a method, WantNeed can ONLY contain these things. Method => { children_must_be => [ # WantNeed[2] 'InstanceVariable LexicalVariable Expression Bareword', 'Argument declaration inside method can only contain '. 'lexical or instance variables and their types' ] }, # directly inside a function, WantNeed can ONLY contain these things. Function => { children_must_be => [ # WantNeed[3] 'LexicalVariable Expression Bareword', 'Argument declaration inside function can only contain '. 'lexical variables and their types' ] }, } # end directly_inside_rules }
Example of an error raised during the enforcement stage
func add { need $x, $y $z = $x + $y say("$x + $y = $z") } need $x
Error: Unexpected need outside of a containing function. Argument declaration must be within a function or method. File -> (stdin) Line -> 7 Near -> operation Parent -> document '(stdin)' Exception raised by F::Node line 24.
4. Verifier
Following document tree construction and grammatical rule enforcement, the verifier stage raises compile-time exceptions for anything that is grammatically correct but logically incorrect. This stage includes compile-time scope and variable tracking, type and protocol conformance checks, and more.
Example of an error raised by during the verification stage
for ($a, $b) in [color: "blue", mood: "sad"] { $x $x = true }
Error: Reference to lexical variable '$x' without previous declaration. Note that '$x' is later declared in this scope on line 3. Error -> UndeclaredVariableReference File -> (stdin) Line -> 2 Element -> lexical variable '$x' Near -> instruction Parent -> body ('for' scope) Exception raised by Ferret::Lexer::Verifier line 150.
5. Compiler
At this point in the process, the document tree is fully constructed, and all error checking is complete. The object model is converted to Perl source code.
# Method event 'center' definition my $method_8 = FF::method_event_def($f, $scope, 'center', [ ], sub { my ($self, $args, $call_scope, $scope, $ret) = @_; $scope->set_property_ow($context, x => add($scope, $self->property_u('origin')->property_u('x'), mul($scope, $self->property_u('width'), num($f, 0.5)))); $scope->set_property_ow($context, y => add($scope, $self->property_u('origin')->property_u('y'), mul($scope, $self->property_u('height'), num($f, 0.5)))); return $scope->property_u('Point')->call_u([ $scope->property_u('x'), $scope->property_u('y') ], $scope); return $ret; });
6. Beautifier
Finally, the code is beautified using perltidy. Well, I wouldn't call it beautiful, but it does help when the lines begin to exceed 1,000 characters.
# Method event 'center' definition my $method_8 = FF::method_event_def( $f, $scope, 'center', [], sub { my ( $self, $args, $call_scope, $scope, $ret ) = @_; $scope->set_property_ow( $context, x => add( $scope, $self->property_u('origin')->property_u('x'), mul( $scope, $self->property_u('width'), num( $f, 0.5 ) ) ) ); $scope->set_property_ow( $context, y => add( $scope, $self->property_u('origin')->property_u('y'), mul( $scope, $self->property_u('height'), num( $f, 0.5 ) ) ) ); return $scope->property_u('Point') ->call_u( [ $scope->property_u('x'), $scope->property_u('y') ], $scope ); return $ret; } );
Troubleshooting
Here are some problematic situations you might encounter and their solutions.
Unable to tokenize
These errors are raised by the Tokenizer.
Try running ferret
with the -tv
flags. This will print the tokenization to
give you a better idea of where the error occurred.
(Un)expected elements
These errors are raised by the Constructor.
Hopefully, the error message is descriptive enough for you to find the issue. If not, it helps to know a little Perl. Ferret will tell you where the exception occurred in the compiler's Perl source code. That may help you nail it down.
If the error says it was raised by a rule, it will provide the name of the rule. Eventually, all rules will be documented, but for the time being, it may be helpful to manually check the rule definition to see what it means.
Let's say, for example, that you wrote $x = 1 1
and got an error message
which ends with Exception raised by rule Instruction[0]
. This is because there
is a rule that says instructions can only contain one element. You could then
search Rules.pm and
Constructor.pm for Instruction[0]
.
Compiler process "exited prematurely"
The Ferret compiler is multi-process and asynchronous. Put another way, the
ferret
executable is more like make
than cc
. These errors occur when a
child process exits unexpectedly, but the parent process survives it.
Try running ferret
with the -y
flag. This tells the compiler to use only
one process and one thread. Usually it will then provide a Perl error message.
None of those
Head to #k on irc.notroll.net and start complaining.