NAME
Decl::Semantics - provides the framework for a set of semantic classes in a declarative framework.
VERSION
Version 0.01
SYNOPSIS
Everything in Decl
is declared using tags, and some of those tags are standard with the distribution. A set of tags is called a "semantic domain", and the standard set is called the "core semantics". The core semantics contain a lot of stuff you might not consider core, like database functionality, but the basic rule is that if I use something a lot, and I want specific support for it in the core code, it kind of needs to be in the core semantics.
This particular module (Decl::Semantics
) is a kind of template for other semantic domains, but it really doesn't do a lot except define how standard domains scan for their own tag definitions. Other semantic domains define useful utility code that can be used by tags and code working with them, but since the core domain is the language itself, there's nothing additional it needs to do.
So this is where I'm putting the tutorial, because to be absolutely honest, I can't get .pod files to link right on CPAN. Once you start reading, you'll see that this is clearly a work in progress. If you have questions or suggestions, drop me a line at the email at the end of this file.
HOW TO USE DECL
How to call a Decl program
Before we get down to brass tacks of what a Decl program looks like, you'll probably want to know how to set one up in the first place. As usual with Perl, there's more than one right way to do it.
The simplest is to invoke Decl as a source filter in a normal Perl program:
use Decl;
value count "0"
sub increment {
$^count ++;
}
do {
increment();
print "$^count\n";
}
(Ignore the blatant weirdness in that code for the moment; this is just a taste.)
If you need more control over the Perl, you can invoke Decl without the source filter.
use Decl qw(-nofilter);
$tree = Decl->new(<<EOF);
! value count "0"
! sub increment {
! ...
EOF
$tree->start();
The initial bangs aren't required, of course; they just make it easier to see where the declarative code is.
If you don't want to mess with significant indentation, you can do the whole thing with arrayrefs: (TODO: test this with coderefs)
use Decl qw(-nofilter);
$tree = Decl->new([['value count "0"'], [sub increment {...}]]);
$tree->start();
If you're using one or more semantic domains, the same things apply (see Win32::Word::Declarative for examples of invocation).
On Windows, you can define .dpl (say) as a declarative Perl extension, and then you don't even need to use Decl. (I do that myself; eventually I should build it into the installation.)
The structure of a Decl program
Declarative Perl is built of nodes. Each node consists of a first line and an optional body; the body is either bracketed, in which case it's executable code (Perl), or not bracketed, in which case it must be indented and the node will decide what to do with it. By default, the body will itself consist of a series of nodes.
The first line consists of a tag, zero or more names, parameters in parentheses, options in square brackets, a label in quotes, and an optional parser label to define what to do with the code block, if any, but right now the parser is ignored. (Eventually I'll put in Inline and you'll be able to go crazy defining code in any language you like, which will actually be pretty darned cool.)
The code in a bracketed body has no significant indentation; it's just Perl (or whatever, eventually - meaning that if it's Python it will have significant indentation, but that sort of clouds the issue, so pretend you didn't just read it).
Order of execution
The overall execution of a Decl program has two phases, the build phase and the run phase. During the build phase, tags are parsed in a depth-first order and their active structure, if any, is built at the same time. (For instance, a database connection is created as soon as the database tag is parsed.) Once everything is built, the run phase starts, consisting of the top-level node trying to run each of its children in order (and each node its own children, again depth-first), with one caveat. Some tags are declarative only, and so do not run. Some tags are code, and so always run. And some tags are ambivalent. It's the ambivalent ones that have the caveat: if an ambivalent tag is the last tag in its enclosing node (or the program as a whole), it will run; otherwise, it will consider itself declarative and it won't run.
This is to permit a default behavior that makes sense if no explicit code is included, while still providing an easy way to override it. The prime example is Win32::Word::Declarative - if the Word document stands alone in its program, it will "run" by writing the file it defines or by executing the actions defined within it. But if there is code below it, that code is considered to be the definitive action of the program, so the Word file structure will simply be taken as descriptive.
Code that appears above such a tag is assumed to be setup code that prepares whatever the native action of the ambivalent tag in question will be, perhaps doing a calculation whose result will appear in the text of the Word file defined.
Embedding Perl
Every Decl program is, of course, a Perl program. It's just more succinct. But at nearly every point along the way, it's possible to drop down into Perl and do things by hand if the Decl framework doesn't give you enough functionality. And of course, any but the most trivial things will require this.
The thing to remember is that the 'do' tag always defines a callable action. The 'sub' tag defines a subroutine for later use, just like you'd think. The 'on' tag defines an event, which is essentially a simple command in the enclosing event context. It's most commonly used for user interface programming, so if you're doing, say, reporting, you might never even see an event.
The event context in embedded Perl
Every piece of Perl code in a Decl program runs in a "context" consisting of its parent object or some ancestor of the parent. Tags know whether they're event contexts or not, and so if you ask a given tag for its context, it will respond either with a pointer to itself, or to a pointer to its parent's event context.
The reason I started calling this an event context is because I was using it to model the parts of a Wx GUI program that handle events; a button on a form has an event associated with it, but the event runs in the context of the form, so the form is the event context. But once I had something like a context to hang things on, I ended up adding variables and functions to them as well. So now they're really more than just "event contexts", but the word "context" is too broad - so they're event contexts.
Syntactic sugar when embedding Perl
My main reason for writing Decl is to save typing and repetition. The declarative structure itself replaces reams of setup code, but there are lots of shorthand abbreviations I like to use in embedded Perl snippets as well.
Magic variables
An event context has a hashref that contains named values for the context. However, you can also define getters and setters for those values that do more than just get and set values. I call these "magic variables". The tastiest application of magic variables is to bind named values to the text in input fields on a form. Now you can get and set those values and Decl (actually Wx::Decl) will automatically forward that to the input fields themselves. This allows a lot of succinctness:
use Wx::Declarative;
dialog (xsize=250, ysize=110) "Wx::Declarative dialog sample"
field celsius (size=100, x=20, y=20) "0"
button celsius (x=130, y=20) "Celsius" { $^fahrenheit = ($^celsius / 100.0) * 180 + 32; }
field fahrenheit (size=100, x=20, y=50) "32"
button fahrenheit (x=130, y=50) "Fahrenheit" { $^celsius = (($^fahrenheit - 32) / 180.0) * 100; }
Here, defining the fields "celsius" and "fahrenheit" automatically defines magic variables of the same names. Magic variables have syntactic sugar for access, so they're $^celsius
and $^fahrenheit
respectively. This is one of my favorite examples because (1) it works, (2) it's the reason I started down this path in the first place, and (3) it replaces about 80 lines of vanilla Perl setup code.
Iterators
There is syntactic sugar available for iterators. An iterator is defined by any text in the tree, by a 'data' tag, or by queries against databases.
TODO: Iterator examples
Databases
Decl
includes DBI
access for the simple reason that - while I love DBI like I love my own children - I hate all the additional code needed to query a database. I can never remember it, and that's my primary criterion for what is a good candidate for declaratization.
The simplest way to query a database is just this:
database (msaccess) "c:/translation/jobs2002.mdb"
do {
^select due, words, customer, desc from [open jobs] {{
print "-------------------\n";
print "due: $due\n";
print "words: $words\n";
print "customer: $customer\n";
print "desc: $desc\n";
}}
}
Well, I lie. The simplest way to query a database is more like this:
database (msaccess) "c:/translation/jobs2002.mdb"
do {
use Data::Dumper;
^select * from [open jobs] {{ print Dumper($row); }}
}
The ^select
construct is code-generation magic; note that it requires double brackets {{ }} to delineate its loop, just like other iterators. This is because it is actually a block with 'my' variables enclosing a loop; the closing }} terminates both the block and the loop explicitly, while you should think of the opening {{ as containing all the 'my' variables.
If your select statement has a *, then the 'my' variables will be $dbh
, $sth
, and $row
, where $row
is a hashref with the results of the current row. If you explicitly name variables, then they will all be declared as 'my' variables, and $sth-
bind_columns> will be used to bind them to the current row results. This is the fastest way to extract information from DBI
, so it's highly recommended - but as you know, the point of Decl
is to be fast to write and understand; optimization will be left as an exercise for the reader.
It's important to understand that the database
tag represents the database connection. It's established during the build phase, and you can reach it easily from Perl code like this:
database (msaccess) "c:/translation/jobs2002.mdb"
do {
my $dbh = ^('database')->dbh;
$dbh->table_info(...);
}
Everything that DBI exposes is thus available to you as well; you just don't have to set it all up.
Since the database connection is established at build time, you can also use it at build time with the 'select' tag to build structure that depends on database input. Here's an example with a Word document used as a report:
use Win32::Word::Declarative;
database (msaccess) "jobs2002.mdb"
document "invoice_report.doc"
para (align=center, size=16, bold) "Customers to invoice"
table
column (align=center)
column (align=center)
row (bold)
cell "Customer"
row (bold)
cell "Amount"
select customer, sum(value) as total from [jobs to invoice] group by customer order by total desc
row
cell "$customer"
cell "\$$total"
Just try that as succinctly with any vanilla imperative language! You could do the same with a PDF, except that PDF doesn't support tables so conveniently, and I haven't yet written code to format them. Not to mention that PDF::Declarative hasn't been published yet, but hey. (TODO: revisit this paragraph when appropriate.)
Templates
WHAT DECL STILL DOESN'T DO WELL
Error handling, especially. Generally, errors are still handled with croak or die, and in a GUI environment that's really not appropriate. Some errors are just ignored, and that's not appropriate anywhere. This alone makes Decl not quite ready for primetime. I wouldn't mind tips and pointers if you're interested.
INTERNALS
If you're just wanting to use Decl
, you can probably stop reading now.
new(), tag()
A semantic class is just a collection of utilities for its plugins. The core Semantics class doesn't really have anything at all - but as other semantic classes will subclass this, your mileage will vary. The one thing we know is that we'll want to keep track of the root.
The tag used to identify a semantic class will differ for each semantic class. It's used to register the class in the root object.
node
The node
function creates a new node by handing things off to Decl. It's not too useful in the core semantics, but of course it's inherited by the other semantic domains, where it can come in rather handy.
import, scan_plugins, file_root, our_flags
The import
function is called when the package is imported. It checks for submodules (i.e. plugins) and calls their "defines" methods to ask them what tag they claim to implement. Then it gives that back to Decl
. Most of the work is done in scan_plugins
, because import
has to execute in any subclass module so we can scan the right directory for plugins.
The scan_plugins
, file_root
, and our_flags
methods are all ways of managing subclasses that require independent existence.
start()
The start
function is called by the framework to start the application if this semantic class is the controlling class. This won't happen too often with the core semantics (except in the unit tests) but the default behavior here is to execute each callable child of the top-level application in turn.
do()
Each semantic module can accept events/commands issued to its name. They are sent to the do
method here, already parsed.
AUTHOR
Michael Roberts, <michael at vivtek.com>
BUGS
Please report any bugs or feature requests to bug-decl at rt.cpan.org
, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Decl. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
LICENSE AND COPYRIGHT
Copyright 2010 Michael Roberts.
This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.
See http://dev.perl.org/licenses/ for more information.