Greets,
I've finished a provisional implementation of the project I
originally proposed as "Configurator". Turns out the name
"Configurator", which I suspected would be relatively rare -- it is a
bastardization after all -- is just ridiculously common. So, new
name: Charmonizer. (Which I pronounce charm-uh-nizer, rather than
see-harmonizer.) 8 Google hits to 13,700,000, and more descriptive
of what it does anyway.
The implementation uses a domain-specific language, as per Dave's
suggestion. The syntax is quite crude, so that a heavy-duty
recursive descent parser won't be needed. For the language
description, see the README file, near the bottom of the patch.
Comments? Commit it then refine, or hold off?
I've haven't prepared any unit tests, since I wanted to get this out
of the lab, but we'll definitely need a battery for each interpreter
implementation.
Marvin Humphrey
Rectangular Research
http://www.rectangular.com/
Index: charmonizer/interpreters/perl/Charmonizer.pm
===================================================================
--- charmonizer/interpreters/perl/Charmonizer.pm (revision 0)
+++ charmonizer/interpreters/perl/Charmonizer.pm (revision 0)
@@ -0,0 +1,399 @@
+package Charmonizer;
+use strict;
+use warnings;
+
+our $VERSION = 0.01;
+
+use Carp;
+use Config;
+use File::Spec::Functions qw( rel2abs );
+
+our %class_defaults = (
+ modules => [],
+ out_path => undef,
+ compiler => $Config{cc},
+ charmonizer_fh => undef,
+ clean_up => 1,
+);
+
+sub new {
+ my $class = shift;
+ $class = ref($class) || $class;
+ my $self = bless {%class_defaults}, $class;
+
+ # verify params
+ while (@_) {
+ my ( $var, $val ) = ( shift, shift );
+ confess "Unrecognized param: '$var'"
+ unless exists $class_defaults{$var};
+ $self->{$var} = $val;
+ }
+ confess "Missing required parameter 'out_path'"
+ unless defined $self->{out_path};
+ confess "No modules supplied" unless @{ $self->{modules} };
+
+ # test for a working C compiler, without which all hope is lost
+ open( my $charmonizer_fh, '>', '_charmonize.c' )
+ or confess("Can't open '_charmonize.c': $!");
+ print $charmonizer_fh "int main(){ return 0; }";
+ close $charmonizer_fh or confess("Can't close '_charmonize.c':
$!");
+ system( $self->{compiler}, '-o', '_charmonize', '_charmonize.c' )
+ and confess("Test compilation failed");
+
+ # reopen _charmonize.c, clobbering previous test content
+ open( $charmonizer_fh, '>', '_charmonize.c' )
+ or confess("Can't open '_charmonize.c': $!");
+ $self->{charmonizer_fh} = $charmonizer_fh;
+
+ # parse and prepare all modules.
+ my @modules;
+ for my $module_path ( @{ $self->{modules} } ) {
+ push @modules,
+ Charmonizer::Module->new(
+ charmonizer_fh => $charmonizer_fh,
+ path => $module_path,
+ compiler => $self->{compiler},
+ );
+ }
+ $self->{modules} = [EMAIL PROTECTED];
+
+ return $self;
+}
+
+sub run {
+ my $self = shift;
+ my $charmonizer_fh = $self->{charmonizer_fh};
+
+ # write _charmonize.c
+ $_->run for @{ $self->{modules} };
+ close $charmonizer_fh or confess("Can't close '_charmonize.c':
$!");
+
+ # compile _charmonize.c and capture its output
+ system( $Config{cc}, '-o', '_charmonize', '_charmonize.c' )
+ and confess("_charmonize.c failed to compile");
+ my $command = rel2abs('_charmonize');
+ open( my $out_fh, '>', $self->{out_path} )
+ or confess("Can't open '$self->{out_path}': $!");
+ print $out_fh `$command`;
+
+ # clean up
+ for ( '_charmonize.c', '_charmonize' ) {
+ next unless -e $_;
+ unlink $_ or confess("Couldn't unlink '$_': $!");
+ }
+}
+
+package Charmonizer::Module;
+use strict;
+use warnings;
+
+use Carp;
+use File::Spec::Functions qw( rel2abs );
+
+my $end_of_line = qr/(?:\015\012|\012|\015|\z)/;
+my $identifier = qr/\b[a-zA-Z_][a-zA-Z0-9_]*\b/;
+my $start_quote = qr/CH_quote/;
+my $end_quote = qr/CH_end_quote/;
+my $start_comment = qr#/\*#;
+my $end_comment = qr#\*/#;
+
+sub new {
+ my $unused = shift;
+ my $self = bless {
+ tree => [], # more of a dowel, actually (no branches)
+ code => undef,
+ @_
+ },
+ __PACKAGE__;
+
+ # if code wasn't supplied, get it from a file
+ if ( !defined $self->{code} ) {
+ open( my $module_fh, '<', $self->{path} )
+ or confess("Can't open '$self->{path}': $!");
+ $self->{code} = do { local $/; <$module_fh> };
+ close $module_fh or confess("Can't close '$self->{path}': $!");
+ }
+
+ # parse now, rather than when run() is called.
+ $self->_parse;
+
+ return $self;
+}
+
+# Parse the source, checking for syntax errors.
+sub _parse {
+ my $self = shift;
+ my $tree = $self->{tree};
+ my $code = $self->_strip_comments( $self->{code} );
+ my %inside;
+ my $func_nick;
+ my $args = {};
+ my ( $param, $value ) = ( '', '' );
+ my $line_number = 0;
+
+ # consume copy of source line by line
+LINE: while ( length $code and $code =~ s/(.*?$end_of_line)//s ) {
+ my $line = $1;
+ $line_number++;
+
+ # append the line to a value if we're inside a quoted string.
+ if ( $inside{quote} ) {
+ if ( $line =~ s/^\s*CH_end_quote// ) {
+ $inside{quote} = 0;
+ $args->{$param} = $value;
+ ( $param, $value ) = ( '', '' );
+ }
+ else {
+ $value .= $line;
+ }
+ next LINE;
+ }
+
+ if ( $inside{function} ) {
+ # if we've reached the end of a function, add a node to
the tree
+ if ( $line =~ s/^\s*CH_end_$func_nick// ) {
+ push @$tree,
+ {
+ function => $inside{function},
+ args => $args,
+ };
+ $inside{function} = 0;
+ undef $func_nick;
+ $args = {};
+ }
+ # add a named parameter
+ elsif ( $line =~ s/^\s*($identifier)\s+(.*?)\s*$// ) {
+ ( $param, $value ) = ( $1, $2 );
+ # check for start of quoted string
+ if ( $value =~ s/^$start_quote// ) {
+ $inside{quote} = 1;
+ # check for quote close
+ if ( $value =~ s/$end_quote\s*$// ) {
+ $inside{quote} = 0;
+ $args->{$param} = $value;
+ }
+ else {
+ next LINE;
+ }
+ }
+ # no quoted string, so add the param
+ else {
+ $args->{$param} = $value;
+ }
+ }
+ }
+
+ # start a function
+ if ( $line =~ s/\s*(CH_(\w+))// ) {
+ # don't allow nested functions
+ if ( $inside{function} ) {
+ confess( "Parse error: already inside $inside
{function} "
+ . "at line $line_number" );
+ }
+ $inside{function} = $1;
+ $func_nick = $2;
+ }
+
+ # if there's anything other than whitespace left, it's a
syntax error
+ if ( $line =~ /\S/ ) {
+ confess("Syntax error at $self->{path} line $line_number");
+ }
+ }
+
+ # Require all modules to name their api version
+ if ( $tree->[0]{function} ne "CH_api_version" ) {
+ confess("$self->{path} doesn't begin with CH_api_version");
+ }
+}
+
+# Remove comments from source, but preserve newlines (and thus line
numbers).
+sub _strip_comments {
+ my ( $self, $code ) = @_;
+ my $new_code = '';
+ my $line_number = 1;
+
+ for ($code) {
+ while ( length($_) ) {
+ if (s/^(.*?)($start_quote|$start_comment)//s) {
+ # append everything up to the quote/comment
+ my ( $preceding_code, $delimiter ) = ( $1, $2 );
+ $new_code .= $preceding_code;
+ $line_number += $self->_count_newlines
($preceding_code);
+
+ if ( $delimiter eq 'CH_quote' ) {
+ # it's a quote, so include the quote and the
delimiters
+ $new_code .= $delimiter;
+ s/^(.*?$end_quote)//s
+ or confess(
+ "Unterminated quote starting at $self->
{path} "
+ . "line $line_number" );
+ $new_code .= $1;
+ }
+ else {
+ # it's a comment, so strip it
+ s/^(.*?$end_comment)//s
+ or confess(
+ "Unterminated comment starting at $self->
{path} "
+ . "line $line_number" );
+ $line_number += $self->_count_newlines($1);
+ }
+ }
+ # once we've passed the last comment/quote, concat the rest
+ else {
+ $new_code .= $_;
+ $_ = '';
+ }
+ }
+ }
+
+ return $new_code;
+}
+
+sub run {
+ my $self = shift;
+
+ # process nodes linearly
+ for my $node ( @{ $self->{tree} } ) {
+ my $func = $node->{function};
+ $self->$func( $node->{args} );
+ }
+}
+
+# Helper function for determining line number.
+sub _count_newlines {
+ my $code = shift;
+ my $num_newlines = 0;
+ while ( length $code and $code =~ s/.*?$end_of_line//g ) {
+ $num_newlines++;
+ }
+ return $num_newlines;
+}
+
+sub CH_api_version {
+ my ( $self, $args ) = @_;
+ if ( int( $args->{version} ) > int($Charmonizer::VERSION) ) {
+ confess( "File '$self->{path}' version $args->{api_version} "
+ . "incompatible with Charmonizer version
$Charmonizer::VERSION"
+ );
+ }
+}
+
+sub CH_append_raw {
+ my ( $self, $args ) = @_;
+ my $charmonizer_fh = $self->{charmonizer_fh};
+ confess( "Missing required argument 'string' at $self->{path} "
+ . "line $args->{line_number}" )
+ unless defined $args->{string};
+ print $charmonizer_fh $args->{string};
+}
+
+sub CH_append_output {
+ my ( $self, $args ) = @_;
+ my $charmonizer_fh = $self->{charmonizer_fh};
+
+ # print the test code to a probe C file
+ open( my $probe_fh, '>', '_ch_probe.c' )
+ or confess("Can't open '_ch_probe.c': $!");
+ print $probe_fh $args->{source};
+ close $probe_fh or confess("Can't close '_ch_probe.c': $!");
+
+ # suppress error messages, since we know some of these will fail.
+ open( ERRCOPY, ">&STDERR" ) or confess $!;
+ close STDERR or confess $!;
+ my $succeeded
+ = system( $self->{compiler}, '-o', '_ch_probe',
'_ch_probe.c' ) == 0;
+ open( STDERR, ">&ERRCOPY" ) or confess $!;
+ close ERRCOPY or confess $!;
+
+ # append the output of the compiled program to the
+ if ($succeeded) {
+ my $command = rel2abs('_ch_probe');
+ print $charmonizer_fh `$command`;
+ }
+
+ # clean up probe files
+ for ( '_ch_probe', '_ch_probe.c' ) {
+ next unless -e $_;
+ unlink $_ or confess "Can't unlink file '$_': $!";
+ }
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Charmonizer - Write portable C header files.
+
+=head1 SYNOPSIS
+
+ my $charmonizer = Charmonizer->new(
+ modules => [
+ 'modules/start.charm',
+ 'modules/integers.charm',
+ 'modules/end.charm',
+ ],
+ out_path => 'charmonized.h',
+ );
+ $charmonizer->run;
+
+=head1 DESCRIPTION
+
+The Charmonizer class supplies an interpreter for the Charmonizer
language,
+which is used to write portable C header files. There are only two
methods,
+because all the functionality is in the Charmonizer language itself.
+
+=head1 METHODS
+
+=head2 new
+
+Constructor. Takes hash-style labeled parameters.
+
+=over
+
+=item *
+
+B<modules> -- an arrayref, where each element in the array is a
filepath
+pointing to a Charmonizer module file (typically ending in ".charm").
+
+=item *
+
+B<out_path> -- indicates where the final output from the Charmonizer
program
+should end up.
+
+=item *
+
+B<compiler> -- The command used to invoke the C compiler on your
system. The
+default is C<$Config{cc}> -- see the docs for L<Config>.
+
+=back
+
+=head2 run
+
+Run the Charmonizer program, generating the header file.
+
+=head1 AUTHOR
+
+Marvin Humphrey, E<lt>marvin at rectangular dot comE<gt>
+
+=head1 COPYRIGHT AND LICENSE
+
+ /**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
software
+ * distributed under the License is distributed on an "AS IS"
BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+=cut
+
Index: charmonizer/modules/end.charm
===================================================================
--- charmonizer/modules/end.charm (revision 0)
+++ charmonizer/modules/end.charm (revision 0)
@@ -0,0 +1,11 @@
+CH_api_version
+ version 0
+CH_end_api_version
+
+CH_append_raw
+ string CH_quote
+
+ return 0;
+}
+ CH_end_quote
+CH_end_append_raw
Index: charmonizer/modules/start.charm
===================================================================
--- charmonizer/modules/start.charm (revision 0)
+++ charmonizer/modules/start.charm (revision 0)
@@ -0,0 +1,19 @@
+CH_api_version
+ version 0
+CH_end_api_version
+
+CH_append_raw
+ string CH_quote
+
+#include <stdio.h>
+
+#define TYPEDEF_PREFIX "lucy_"
+#define CONSTANT_PREFIX "LUCY_"
+#define MACRO_PREFIX "Lucy_"
+
+int main()
+{
+ printf("/* Header file auto-generated by Charmonizer. */\n\n");
+
+ CH_end_quote
+CH_end_append_raw
Index: charmonizer/modules/integers.charm
===================================================================
--- charmonizer/modules/integers.charm (revision 0)
+++ charmonizer/modules/integers.charm (revision 0)
@@ -0,0 +1,110 @@
+CH_api_version
+ version 0
+CH_end_api_version
+
+/* Check for inttypes.h. */
+CH_append_output
+ source CH_quote
+ #include <stdio.h>
+ #include <inttypes.h>
+ int main()
+ {
+ printf("#define HAS_INTTYPES_H 1\n");
+ return 0;
+ }
+ CH_end_quote
+CH_end_append_output
+
+
+/* Check for long longs. */
+CH_append_output
+ source CH_quote
+ #include <stdio.h>
+ int main()
+ {
+ long long foo = 4;
+ printf("#define HAS_LONG_LONG 1\n");
+ printf("#define SIZEOF_LONG_LONG %d\n", (int)sizeof(long
long));
+ return 0;
+ }
+ CH_end_quote
+CH_end_append_output
+
+
+/* Record sizeof() for several common integer types. */
+CH_append_output
+ source CH_quote
+ #include <stdio.h>
+ int main () {
+ printf("#define SIZEOF_CHAR %d\n", (int)sizeof(char));
+ printf("#define SIZEOF_SHORT %d\n", (int)sizeof(short));
+ printf("#define SIZEOF_INT %d\n", (int)sizeof(int));
+ printf("#define SIZEOF_LONG %d\n", (int)sizeof(long));
+ printf("#define SIZEOF_PTR %d\n", (int)sizeof(void *));
+ return 0;
+ }
+ CH_end_quote
+CH_end_append_output
+
+
+CH_append_raw
+ string CH_quote
+
+/* 8 bit integers */
+#if SIZEOF_CHAR == 1
+ printf("typedef char %s" "i8_t;\n", TYPEDEF_PREFIX);
+ printf("typedef unsigned char %s" "u8_t;\n", TYPEDEF_PREFIX);
+#else
+ #error "No 8-bit integer type available"
+#endif
+
+/* 16-bit integers */
+#if SIZEOF_SHORT == 2
+ printf("typedef short %s" "i16_t;\n", TYPEDEF_PREFIX);
+ printf("typedef unsigned short %s" "u16_t;\n", TYPEDEF_PREFIX);
+#else
+ #error "No 16-bit integer type available"
+#endif
+
+/* 32-bit integers */
+#if SIZEOF_INT == 4
+ printf("typedef int %s" "i32_t;\n", TYPEDEF_PREFIX);
+ printf("typedef unsigned int %s" "u32_t;\n", TYPEDEF_PREFIX);
+#elif SIZEOF_LONG == 4
+ printf("typedef long %s" "i32_t;\n", TYPEDEF_PREFIX);
+ printf("typedef unsigned long %s" "u32_t;\n", TYPEDEF_PREFIX);
+#else
+ #error "No 32-bit integer type available"
+#endif
+
+ /* 64-bit integers */
+#if SIZEOF_LONG == 8
+ printf("typedef long %s" "i64_t;\n", TYPEDEF_PREFIX);
+ printf("typedef unsigned long %s" "u64_t;\n", TYPEDEF_PREFIX);
+#elif defined HAS_LONG_LONG
+ #if SIZEOF_LONG_LONG == 8
+ printf("typedef long long %s" "i64_t;\n", TYPEDEF_PREFIX);
+ printf("typedef unsigned long long %s" "u64_t;\n", TYPEDEF_PREFIX);
+ #else
+ #error "No 64-bit integer type available"
+ #endif
+#endif
+
+/* pointers */
+#if SIZEOF_PTR == SIZEOF_INT
+ printf("typedef int %s" "iptr_t;\n", TYPEDEF_PREFIX);
+ printf("typedef unsigned int %s" "uptr_t;\n", TYPEDEF_PREFIX);
+#elif SIZEOF_PTR == SIZEOF_LONG
+ printf("typedef long %s" "ptr_t;\n", TYPEDEF_PREFIX);
+ printf("typedef unsigned long %s" "uptr_t;\n", TYPEDEF_PREFIX);
+#elif defined HAS_LONG_LONG
+ #if SIZEOF_PTR == SIZEOF_LONG_LONG
+ printf("typedef long long %s" "iptr_t;\n", TYPEDEF_PREFIX);
+ printf("typedef unsigned long long %s" "uptr_t;\n",
TYPEDEF_PREFIX);
+ #else
+ #error "Couldn't find integer type with same size as pointer"
+ #endif
+#endif
+
+ CH_end_quote
+CH_end_append_raw
Index: charmonizer/README
===================================================================
--- charmonizer/README (revision 0)
+++ charmonizer/README (revision 0)
@@ -0,0 +1,178 @@
+NAME
+
+ Charmonizer - Mini-language targeted at writing portable C headers.
+
+OVERVIEW
+
+ Charmonizer is a tool for writing portable C header files. It is
+ implemented using a small domain-specific language, also called
+ "Charmonizer", and a "native" interpreter, written in a language
such as
+ Perl or Ruby.
+
+ The Charmonizer language is neither succinct, nor flexible, nor
powerful,
+ because the project's primary design specs did not require it to
be any of
+ those things:
+
+ 1. It must write portable C header files.
+ 2. It must require only a C compiler and the native build
enviroment.
+ 3. It must be easy to write a robust, reliable Charmonizer
interpreter.
+
+ Charmonizer source code consists of a linear series of function
calls.
+ There are no conditionals, no loops, and no variables. Of
course it's
+ challenging to get much done without conditionals or variables, but
+ Charmonizer is able to rely upon C's preprocessor for those.
+
+ Charmonizer doesn't require complex functionality because
basically all
+ it needs to do is attempt compilation of miniature C programs
and capture
+ output from them when they build successfully. If the C
preprocessor had
+ exception handling along the lines of Perl's "eval $string"
functionality
+ (ha ha), Charmonizer-the-language wouldn't be necessary.
+
+PROGRAM STRUCTURE
+
+ Each run of Charmonizer follows this outline:
+
+ I. Initialize a temporary target file named _charmonize.c
+ II. Process a series of .charm files, appending
_charmonize.c as
+ they indicate.
+ III. Compile _charmonize.c, run it, and capture its output
to a new
+ file: the portable C header file.
+
+ Each of these steps requires a Charmonizer interpreter. Consult
the
+ documentation of your favorite among them for details.
+
+CHARMONIZER SOURCE FILES
+
+ Charmonizer source files, as mentioned above, consist of a
linear series of
+ function calls. The first function called must always be
CH_api_version.
+
+ The preferred file extension is '.charm'.
+
+LANGUAGE SYNTAX
+
+ Functions names begin with 'CH_', and all function calls must be
+ terminated with a corresponding close directive beginning with
'CH_end_'.
+ For instance, if Charmonizer had a CH_print function, here's how it
+ might work:
+
+ CH_print
+ string Hello, world.
+ CH_end_print
+
+ However, Charmonizer doesn't have a CH_print function, because it
+ doesn't need one.
+
+ Files are parsed line by line. Leading whitespace at the front
of any
+ line gets ignored (unless it is part of a quoted string), so
Charmonizer
+ files can be sensibly indented.
+
+ Each line falls into one of five categories:
+
+ * blank
+ * start of a function
+ * end of a function
+ * name-value parameter pair
+ * part of a multi-line quoted string
+
+ Quoted strings begin with 'CH_quote' and are terminated by
+ 'CH_end_quote'. The strings are completely literal -- no
interpolation,
+ and no escapes. (If for some odd reason you want to embed the text
+ "CH_quote" within a string, you're out of luck.)
+
+ CH_append_raw
+ string CH_quote
+ #define TYPEDEF_PREFIX "lucy_"
+ CH_end_quote
+ CH_end_append_raw
+
+ C-style multi-line comments delimited by '/*' and '*/' are allowed.
+ Comment delimiters within quoted strings are treated as literal
text, not
+ comments.
+
+ /* This is a valid comment. */
+ CH_append_raw
+ string CH_quote
+ /* This comment will end up in _charmonize.c */
+ /* This un-terminated comment will mess up _charmonize.c,
+ * but it's not invalid Charmonizer syntax, as it's
+ * within a quoted string.
+ CH_end_quote
+ CH_end_append_raw /* This is another valid comment. */
+
+ All functions accept only named parameters in the form of key-
value pairs.
+ Parameter names must consist of only pure ASCII alphanumerics
plus the
+ underscore, and cannot start with a digit.
+
+ CH_do_something
+ an-illegal-parameter oops
+ 4nother_illegal_parameter 4RGH!
+ a_valid_parameter Ah! Just right. /* value is "Ah! Just
right." */
+ CH_end_do_something
+
+ The value of the parameter starts with the first non-whitespace
character
+ following the key and is terminated immediately following the last
+ non-whitespace character in the line (not including comments,
which are
+ stripped beforehand). In the event you need a value with
leading or
+ trailing whitespace (e.g. a terminating newline, which won't
ordinarily be
+ there) use the CH_quote mechanism.
+
+FUNCTIONS
+
+ CH_append_raw
+
+ CH_append_raw
+ string CH_quote
+ #define CONSTANT_PREFIX "LUCY_"
+ CH_end_quote
+ CH_end_append_raw
+
+ Append verbatim contents of the specified string to the
target file.
+
+
+ CH_append_output
+
+ CH_append_output
+ source CH_quote
+ #include <stdio.h>
+ int main()
+ {
+ printf("#define SIZEOF_LONG %d\n", (int)sizeof
(long));
+ return 0;
+ }
+ CH_end_quote
+ CH_end_append_output
+
+ Compile the supplied source code and append its output to
the target
+ file. If the code fails to compile, do nothing.
+
+ CH_api_version
+
+ CH_api_version
+ api_version 0.01
+ CH_end_api_version
+
+ Indicate the version of Charmonizer that a .charm file
conforms to.
+
+AUTHOR
+
+ Marvin Humphrey, <marvin at rectangular dot com>
+
+COPYRIGHT AND LICENSE
+
+ /**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
software
+ * distributed under the License is distributed on an "AS IS"
BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+
Index: Build.PL
===================================================================
--- Build.PL (revision 0)
+++ Build.PL (revision 0)
@@ -0,0 +1,40 @@
+use strict;
+use warnings;
+use 5.008003;
+use Module::Build;
+
+use lib 'charmonizer/interpreters/perl';
+use Charmonizer;
+
+my $charmonizer = Charmonizer->new(
+ modules => [
+ 'charmonizer/modules/start.charm',
+ 'charmonizer/modules/integers.charm',
+ 'charmonizer/modules/end.charm',
+ ],
+ out_path => 'lucyport.h'
+);
+
+$charmonizer->run;
+
+# TODO: Write Lucy. :) Then we can install it.
+
+__END__
+
+
+ /**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
software
+ * distributed under the License is distributed on an "AS IS"
BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+