NAME
create_language.pl -- create initial files for a new language
SYNOPSIS
% perl tools/dev/create_language.pl Xyz [path]
DESCRIPTION
This script populates a directory with files for building a new language translator in Parrot. The first argument is the name of the language to be built. The path
argument says where to populate the directory, if not given then the lowercase version of the language name is used.
For a language 'Xyz', this script will create the following files and directories in path
:
README
Configure.pl
build/Makefile.in
build/gen_parrot.pl
src/Xyz.pir
src/Xyz/Grammar.pm
src/Xyz/Actions.pm
src/Xyz/Compiler.pm
src/Xyz/Runtime.pm
src/gen/.gitignore
t/harness
t/00-sanity.t
Any files that already exist are skipped, so this script can be used to repopulate a language directory with omitted files.
If all goes well, after creating the language shell one can simply change to the language directory and type
$ perl Configure.pl [--gen-parrot]
$ make
$ make test
to verify that the new language compiles and configures properly.
@lang@
This is @lang@, a compiler for the Parrot virtual machine.
Build requirements (installing from source)
For building @lang@ you need at least a C compiler, a make
utility, and Perl 5.8 or newer. To automatically obtain and build Parrot you also need a git client.
Building and invoking @lang@
We generally recommend downloading @lang@ directly from [XXX: fill in this information for @lang@].
Once you have a copy of @lang@, build it as follows:
$ cd @lclang@
$ perl Configure.pl --gen-parrot
$ make
This will create a "@lclang@" or "@lclang@.exe" executable in the current directory. Programs can then be run from the build directory using a command like:
$ ./@lclang@ <source>
The --gen-parrot
option above tells Configure.pl to automatically download and build the most appropriate version of Parrot into a local "parrot/" subdirectory, install that Parrot into the "parrot_install/" subdirectory, and use that for building @lang@. It's okay to use the --gen-parrot
option on later invocations of Configure.pl; the configure system will re-build Parrot only if a newer version is needed for whatever version of @lang@ you're working with.
You can use --parrot-config=/path/to/parrot_config
instead of --gen-parrot
to use an already installed Parrot for building @lang@. This installed Parrot must include its development environment; typically this is done via Parrot's make install
target or by installing prebuilt parrot-devel
and/or libparrot-dev
packages. The version of the already installed Parrot must satisfy a minimum specified by @lang@ -- Configure.pl will verify this for you.
Once built, @lang@'s make install
target will install @lang@ and its libraries into the Parrot installation that was used to create it. Until this step is performed, the "@lclang@" executable created by make
above can only be reliably run from the root of @lang@'s build directory. After make install
is performed, the installed executable can be run from any directory (as long as the Parrot installation that was used to create it remains intact).
If the @lang@ compiler is invoked without an explicit script to run, it enters a small interactive mode that allows statements to be executed from the command line. Each line entered is treated as a separate compilation unit, however (which means that subroutines are preserved after they are defined, but variables are not).
Running the test suite
Entering make test
will run a test suite that comes bundled with @lang@. This is a simple suite of tests, designed to make sure that the compiler is basically working and that it's capable of running a simple test harness.
If you want to run the tests in parallel, you need to install a fairly recent version of the Perl 5 module Test::Harness (3.16 works for sure).
Where to get help or answers to questions
Reporting bugs
Submitting patches
How the compiler works
See docs/compiler_overview.pod.
AUTHOR
TITLE
gen_parrot.pl - script to obtain and build Parrot for Rakudo
SYNOPSIS
perl gen_parrot.pl [--parrot --configure=options]
DESCRIPTION
Maintains an appropriate copy of Parrot in the parrot/ subdirectory. The revision of Parrot to be used in the build is given by the build/PARROT_REVISION file.
grammar @lang@::Grammar is HLL::Grammar;
token TOP { <statementlist> [ $ || <.panic: "Syntax error"> ] }
## Lexer items
# This <ws> rule treats # as "comment to eol". token ws { <!ww> [ '#' \N* \n? | \s+ ]* }
## Statements
rule statementlist { [ <statement> | <?> ] ** ';' }
rule statement { | <statement_control> | <EXPR> }
proto token statement_control { <...> } rule statement_control:sym<say> { <sym> [ <EXPR> ] ** ',' } rule statement_control:sym<print> { <sym> [ <EXPR> ] ** ',' }
## Terms
token term:sym<integer> { <integer> } token term:sym<quote> { <quote> }
proto token quote { <...> } token quote:sym<'> { <?[']> <quote_EXPR: ':q'> } token quote:sym<"> { <?["]> <quote_EXPR: ':qq'> }
## Operators
INIT { @lang@::Grammar.O(':prec<u>, :assoc<left>', '%multiplicative'); @lang@::Grammar.O(':prec<t>, :assoc<left>', '%additive'); }
token circumfix:sym<( )> { '(' <.ws> <EXPR> ')' }
token infix:sym<*> { <sym> <O('%multiplicative, :pirop<mul>')> } token infix:sym</> { <sym> <O('%multiplicative, :pirop<div>')> }
token infix:sym<+> { <sym> <O('%additive, :pirop<add>')> } token infix:sym<-> { <sym> <O('%additive, :pirop<sub>')> } __src/@lang@/Actions.pm__ class @lang@::Actions is HLL::Actions;
method TOP($/) { make PAST::Block.new( $<statementlist>.ast , :hll<@lclang@>, :node($/) ); }
method statementlist($/) { my $past := PAST::Stmts.new( :node($/) ); for $<statement> { $past.push( $_.ast ); } make $past; }
method statement($/) { make $<statement_control> ?? $<statement_control>.ast !! $<EXPR>.ast; }
method statement_control:sym<say>($/) { my $past := PAST::Op.new( :name<say>, :pasttype<call>, :node($/) ); for $<EXPR> { $past.push( $_.ast ); } make $past; }
method statement_control:sym<print>($/) { my $past := PAST::Op.new( :name<print>, :pasttype<call>, :node($/) ); for $<EXPR> { $past.push( $_.ast ); } make $past; }
method term:sym<integer>($/) { make $<integer>.ast; } method term:sym<quote>($/) { make $<quote>.ast; }
method quote:sym<'>($/) { make $<quote_EXPR>.ast; } method quote:sym<">($/) { make $<quote_EXPR>.ast; }
method circumfix:sym<( )>($/) { make $<EXPR>.ast; }
__src/@lang@/Compiler.pm__ class @lang@::Compiler is HLL::Compiler;
INIT { @lang@::Compiler.language('@lang@'); @lang@::Compiler.parsegrammar(@lang@::Grammar); @lang@::Compiler.parseactions(@lang@::Actions); } __src/@lang@/Runtime.pm__ # language-specific runtime functions go here
sub print(*@args) { pir::print(pir::join('', @args)); 1; }
sub say(*@args) { pir::say(pir::join('', @args)); 1; } __src/gen/.gitignore__ * __t/harness__ #! perl
use strict; use warnings;
use FindBin; use File::Spec; use Getopt::Long qw(:config pass_through);
$ENV{'HARNESS_PERL'} = './@lclang@'; use Test::Harness; $Test::Harness::switches = '';
GetOptions( 'tests-from-file=s' => \my $list_file, 'verbosity=i' => \$Test::Harness::verbose, 'jobs:3' => \my $jobs, 'icu:1' => \my $do_icu, );
my @pass_through_options = grep m/^--?[^-]/, @ARGV; my @files = grep m/^[^-]/, @ARGV;
my $slash = $^O eq 'MSWin32' ? '\\' : '/';
if ($list_file) { open(my $f, '<', $list_file) or die "Can't open file '$list_file' for reading: $!"; while (<$f>) { next if m/^\s*#/; next unless m/\S/; chomp; my ($fn, $flags) = split /\s+#\s*/; next if ($flags && ($flags =~ m/icu/) && !$do_icu); $fn = "t/spec/$fn" unless $fn =~ m/^t\Q$slash\Espec\Q$slash\E/; $fn =~ s{/}{$slash}g; if ( -r $fn ) { push @files, $fn; } else { warn "Missing test file: $fn\n"; } } close $f or die $!; }
my @tfiles = map { all_in($_) } sort @files;
if (eval { require TAP::Harness; 1 }) { my %harness_options = ( exec => ['./@lclang@'], verbosity => 0+$Test::Harness::verbose, jobs => $jobs || 1, ); TAP::Harness->new( \%harness_options )->runtests(@tfiles); } else { runtests(@tfiles); }
# adapted to return only files ending in '.t' sub all_in { my $start = shift;
return $start unless -d $start;
my @skip = ( File::Spec->updir, File::Spec->curdir, qw( .svn CVS .git ) );
my %skip = map {($_,1)} @skip;
my @hits = ();
if ( opendir( my $dh, $start ) ) {
my @files = sort readdir $dh;
closedir $dh or die $!;
for my $file ( @files ) {
next if $skip{$file};
my $currfile = File::Spec->catfile( $start, $file );
if ( -d $currfile ) {
push( @hits, all_in( $currfile ) );
}
else {
push( @hits, $currfile ) if $currfile =~ /\.t$/;
}
}
}
else {
warn "$start: $!\n";
}
return @hits;
}
__t/00-sanity.t__ # This just checks that the basic parsing and call to builtin say() works. say '1..4'; say 'ok 1'; say 'ok ', 2; say 'ok ', 2 + 1; say 'ok', ' ', 4; __DATA__
# Local Variables: # mode: cperl # cperl-indent-level: 4 # fill-column: 100 # End: # vim: expandtab shiftwidth=4: