2 # Copyright (C) 2007-2009, Parrot Foundation.
7 create_language.pl -- create initial files for a new language
11 % perl tools/dev/create_language.pl Xyz [path]
15 This script populates a directory with files for building a
16 new language translator in Parrot. The first argument is the
17 name of the language to be built. The C<path> argument
18 says where to populate the directory, if not given then the
19 lowercase version of the language name is used.
21 For a language 'Xyz', this script will create the following
22 files and directories in C<path>:
28 build/gen_builtins_pir.pl
31 src/pct/grammar-oper.pg
37 Any files that already exist are skipped, so this script can
38 be used to repopulate a language directory with omitted files.
40 If all goes well, after creating the language shell one can simply
41 change to the language directory and type
45 to verify that the new language compiles and configures properly.
56 die "usage: $0 language [path]\n";
59 ## determine the language we're trying to build
61 my $lclang = lc $lang;
62 my $uclang = uc $lang;
64 ## the name and revision of the script, for use in the generated README
66 my $rev = '$Revision$';
67 $rev =~ s/^\D*(\d+)\D*$/r$1/;
69 ## get the path from the command line, or if not supplied then
70 ## use languages/$lclang.
71 my $path = $ARGV[1] || $lclang;
73 ## now loop through the file information (see below), substituting
74 ## any instances of @lang@, @lclang@, @UCLANG@, and @Id@ with
75 ## the language name or the svn id tag. If the line has the form
76 ## __filepath__, then start a new file.
81 s{\@lclang\@} {$lclang}ig;
82 s{\@UCLANG\@} {$uclang}ig;
84 s{\@script\@} {$script}ig;
86 if (/^__(.*)__$/) { start_new_file
("$path/$1"); }
87 elsif ($fh) { print $fh $_; }
89 ## close the last file
94 Your new language has been created in the $path directory.
95 To do an initial build and test of the language:
108 ## this function closes any previous opened file, and determines
109 ## if we're creating a new file. It also calls C<mkpath> to
110 ## create any needed parent subdirectories.
113 if ($fh) { close $fh; undef $fh; }
114 if (-e
$filepath) { print "skipping $filepath\n"; return; }
115 my ($volume, $dir, $base) = File
::Spec
->splitpath($filepath);
116 my $filedir = File
::Spec
->catpath($volume, $dir);
117 unless (-d
$filedir) {
118 print "creating $filedir\n";
119 mkpath
( [ $filedir ], 0, 0777 );
121 print "creating $filepath\n";
122 open $fh, '>', $filepath;
127 ### The section below contains the text of the files to be created.
128 ### The name of the file to be created is given as C<__filepath__>,
129 ### and all subsequent lines up to the next C<__filepath__> are
130 ### placed in the file (performing substitutions on @lang@, @lclang@,
131 ### @UCLANG@, and @Id@ as appropriate).
135 Language
'@lang@' was created with
@script@
, @rev@
.
146 GetOptions
(\
%options, 'help!', 'parrot-config=s',
147 'gen-parrot!', 'gen-parrot-option=s@');
149 # Print help if it's requested
150 if ($options{'help'}) {
155 # Update/generate parrot build if needed
156 if ($options{'gen-parrot'}) {
157 my @opts = @
{ $options{'gen-parrot-option'} || [] };
158 my @command = ($^X
, "build/gen_parrot.pl", @opts);
160 print "Generating Parrot ...\n";
161 print "@command\n\n";
165 # Get a list of parrot-configs to invoke.
166 my @parrot_config_exe = qw(
172 if ($options{'parrot-config'} && $options{'parrot-config'} ne '1') {
173 @parrot_config_exe = ($options{'parrot-config'});
176 # Get configuration information from parrot_config
177 my %config = read_parrot_config
(@parrot_config_exe);
180 Unable to locate parrot_config.
181 To automatically checkout (svn) and build a copy of parrot,
182 try re-running Configure.pl with the '--gen-parrot' option.
183 Or, use the '--parrot-config' option to explicitly specify
184 the location of parrot_config.
188 # Create the Makefile using the information we just got
189 create_makefile
(%config);
191 my $make = $config{'make'};
194 You can now use '$make' to build @lang@.
195 After that, you can use '$make test' to run some local tests.
203 sub read_parrot_config
{
204 my @parrot_config_exe = @_;
206 for my $exe (@parrot_config_exe) {
208 if (open my $PARROT_CONFIG, '-|', "$exe --dump") {
209 print "\nReading configuration information from $exe ...\n";
210 while (<$PARROT_CONFIG>) {
211 if (/(\w+) => '(.*)'/) { $config{$1} = $2 }
213 close $PARROT_CONFIG;
221 # Generate a Makefile from a configuration
222 sub create_makefile
{
225 my $maketext = slurp
( 'build/Makefile.in' );
227 $config{'win32_libparrot_copy'} = $^O
eq 'MSWin32' ?
'copy $(BUILD_DIR)\libparrot.dll .' : '';
228 $maketext =~ s/@(\w+)@/$config{$1}/g;
229 if ($^O
eq 'MSWin32') {
231 $maketext =~ s{/}{\\}g;
232 # wildcards (for clean rules) need an additional backslash, see Rakudo RT #65006
233 $maketext =~ s{\\\*}{\\\\*}g;
234 # use forward slashes again for HTTP URLs
235 $maketext =~ s{http:\S+}{ do {my $t = $&; $t =~ s'\\'/'g; $t} }eg
;
238 my $outfile = 'Makefile';
239 print "\nCreating $outfile ...\n";
240 open(my $MAKEOUT, '>', $outfile) ||
241 die "Unable to write $outfile\n";
242 print {$MAKEOUT} $maketext;
243 close $MAKEOUT or die $!;
249 my $filename = shift;
251 open my $fh, '<', $filename or die "Unable to read $filename\n";
253 my $maketext = <$fh>;
260 # Print some help text.
263 Configure.pl - @lang@ Configure
266 --help Show this text
267 --parrot-config=(config)
268 Use configuration information from config
269 --gen-parrot Download and build a copy of Parrot to use
270 --gen-parrot-option='--option=value'
271 Set parrot config option when using --gen-parrot
279 # cperl-indent-level: 4
282 # vim: expandtab shiftwidth=4:
284 __build
/PARROT_REVISION__
286 __build
/Makefile
.in__
289 # arguments we want to run parrot with
292 # values from parrot_config
293 BUILD_DIR
= @build_dir@
294 LOAD_EXT
= @load_ext@
303 PARROT_DYNEXT
= $(BUILD_DIR
)/runtime/parrot
/dynext
304 PERL6GRAMMAR
= $(BUILD_DIR
)/runtime/parrot
/library/PGE
/Perl6Grammar
.pbc
305 NQP
= $(BUILD_DIR
)/compilers/nqp
/nqp
.pbc
306 PCT
= $(BUILD_DIR
)/runtime/parrot
/library/PCT
.pbc
310 OPS_FILE
= src
/ops/@lclang@
.ops
312 # Setup some commands
313 PARROT
= $(BUILD_DIR
)/parrot
$(EXE
)
314 CAT
= $(PERL
) -MExtUtils
::Command
-e cat
315 BUILD_DYNPMC
= $(PERL
) $(BUILD_DIR
)/tools/build
/dynpmc
.pl
316 BUILD_DYNOPS
= $(PERL
) $(BUILD_DIR
)/tools/build
/dynoplibs
.pl
317 PBC_TO_EXE
= $(BUILD_DIR
)/pbc_to_exe
$(EXE
)
319 SOURCES
= @lclang@
.pir \
320 src
/gen_grammar
.pir \
321 src
/gen_actions
.pir \
322 src
/gen_builtins
.pir \
326 src
/builtins/say
.pir \
329 # PMC_SOURCES = $(PMC_DIR)/@lclang@.pmc
330 # @uclang@_GROUP = $(PMC_DIR)/@lclang@_group$(LOAD_EXT)
345 $(PMC_DIR
)/*$(LOAD_EXT
) \
348 $(PMC_DIR
)/*.manifest \
351 $(PMC_DIR
)/objectref
.pmc \
355 $(OPSDIR
)/*$(LOAD_EXT
) \
357 HARNESS
= $(PERL
) t
/harness
--keep
-exit-code
--icu
=$(HAS_ICU
)
358 HARNESS_JOBS
= $(HARNESS
) --jobs
363 installable
: installable_
@lclang@
$(EXE
)
365 ## targets for building a standalone executable
366 @lclang@
$(EXE
): @lclang@
.pbc
367 $(PBC_TO_EXE
) @lclang@
.pbc
368 @win32_libparrot_copy@
370 installable_
@lclang@
$(EXE
): @lclang@
.pbc
371 $(PBC_TO_EXE
) @lclang@
.pbc
--install
374 @lclang@
.pbc
: Makefile
$(PARROT
) $(SOURCES
) $(BUILTINS_PIR
)
375 $(PARROT
) $(PARROT_ARGS
) -o
@lclang@
.pbc
@lclang@
.pir
377 src
/gen_grammar.pir: $(PARROT) $(PERL6GRAMMAR) src/pct
/grammar.pg src/pct
/grammar
-oper
.pg
378 $(PARROT
) $(PARROT_ARGS
) $(PERL6GRAMMAR
) \
379 --output
=src
/gen_grammar
.pir \
380 src
/pct/grammar
.pg src
/pct/grammar
-oper
.pg
382 src
/gen_actions.pir: $(PARROT) $(NQP) $(PCT) src/pct
/actions
.pm
383 $(PARROT
) $(PARROT_ARGS
) $(NQP
) --output
=src
/gen_actions
.pir \
384 --encoding
=fixed_8
--target
=pir src
/pct/actions
.pm
386 src
/gen_builtins.pir: Makefile build/gen_builtins_pir
.pl
387 $(PERL
) build
/gen_builtins_pir.pl $(BUILTINS_PIR) > src/gen_builtins
.pir
389 $(@uclang@_GROUP): Makefile
$(PARROT
) $(PMC_SOURCES
)
390 cd
$(PMC_DIR
) && $(BUILD_DYNPMC
) generate
$(PMCS
)
391 cd
$(PMC_DIR
) && $(BUILD_DYNPMC
) compile
$(PMCS
)
392 cd
$(PMC_DIR
) && $(BUILD_DYNPMC
) linklibs
$(PMCS
)
393 cd
$(PMC_DIR
) && $(BUILD_DYNPMC
) copy
--destination
=$(PARROT_DYNEXT
) $(PMCS
)
395 src
/ops/@lclang@_ops$(LOAD_EXT
) : $(PARROT
) $(OPS_FILE
)
396 @cd $(OPSDIR
) && $(BUILD_DYNOPS
) generate
$(OPSLIB
)
397 @cd $(OPSDIR
) && $(BUILD_DYNOPS
) compile
$(OPSLIB
)
398 @cd $(OPSDIR
) && $(BUILD_DYNOPS
) linklibs
$(OPSLIB
)
399 @cd $(OPSDIR
) && $(BUILD_DYNOPS
) copy
"--destination=$(PARROT_DYNEXT)" $(OPSLIB
)
401 ## local copy of Parrot
402 parrot
: parrot
/parrot_config build/PARROT_REVISION
403 $(PERL
) build
/gen_parrot
.pl
405 parrot
/parrot_config
:
406 @echo "Don't see parrot/parrot_config."
412 t
/*.t t/*/*.t t/*/*/*.t
: all Test
.pir
413 @
$(HARNESS_WITH_FUDGE
) --verbosity
=1 $@
422 $(RM_F
) src
/utils/Makefile Makefile
427 ## miscellaneous targets
428 # a listing of all targets meant to be called by users
431 @echo "Following targets are available for the user:"
433 @echo " all: @lclang@.exe"
434 @echo " This is the default."
437 @echo " test: Run Rakudo's sanity tests."
440 @echo " clean: Basic cleaning up."
441 @echo " distclean: Removes also anything built, in theory."
442 @echo " realclean: Removes also files generated by 'Configure.pl'."
443 @echo " testclean: Clean up test results."
446 @echo " help: Print this help message."
449 Makefile
: build
/Makefile
.in
451 @echo "warning: Makefile is out of date... re-run Configure.pl"
455 echo MANIFEST
>MANIFEST
456 git ls
-files
| $(PERL
) -ne '/^\./ || print' >>MANIFEST
459 [ -n
"$(VERSION)" ] || ( echo
"\nTry 'make release VERSION=yyyymm'\n\n"; exit 1 )
460 [ -d
@lclang@
-$(VERSION
) ] || ln
-s
. @lclang@
-$(VERSION
)
461 $(PERL
) -ne 'print "@lclang@-$(VERSION)/$$_"' MANIFEST
| \
462 tar
-zcv
-T
- -f
@lclang@
-$(VERSION
).tar
.gz
463 rm
@lclang@
-$(VERSION
)
465 __build
/gen_builtins_pir
.pl__
474 print <<"END_PRELUDE";
475 # This file automatically generated by $0.
479 foreach my $file (@files) {
480 print ".include '$file'\n";
484 __build/gen_parrot.pl__
489 gen_parrot.pl - script to obtain and build Parrot
493 perl gen_parrot.pl [--parrot --configure=options]
497 Maintains an appropriate copy of Parrot in the parrot/ subdirectory.
498 The revision of Parrot to be used in the build is given by the
499 build/PARROT_REVISION file.
507 # Work out slash character to use.
508 my $slash = $^O eq 'MSWin32' ? '\\' : '/';
510 ## determine what revision of Parrot we require
511 open my $REQ, "build/PARROT_REVISION"
512 || die "cannot open build/PARROT_REVISION\n";
513 my $required = 0+<$REQ>;
518 if (open my $REV, '-|', "parrot${slash}parrot_config revision") {
519 my $revision = 0+<$REV>;
521 if ($revision >= $required) {
522 print "Parrot r$revision already available (r$required required)\n";
528 print "Checking out Parrot r$required via svn...\n";
529 system(qw(svn checkout -r), $required , qw(https://svn.parrot.org/parrot/trunk parrot));
534 ## If we have a Makefile from a previous build, do a 'make realclean'
536 my %config = read_parrot_config
();
537 my $make = $config{'make'};
539 print "\nPerforming '$make realclean' ...\n";
540 system($make, "realclean");
544 print "\nConfiguring Parrot ...\n";
545 my @config_command = ($^X
, 'Configure.pl', @ARGV);
546 print "@config_command\n";
547 system @config_command;
549 print "\nBuilding Parrot ...\n";
550 my %config = read_parrot_config
();
551 my $make = $config{'make'} or exit(1);
554 sub read_parrot_config
{
556 if (open my $CFG, "config_lib.pasm") {
558 if (/P0\["(.*?)"], "(.*?)"/) { $config{$1} = $2 }
568 @lclang@.pir - A @lang@ compiler.
572 This is the base file for the @lang@ compiler.
574 This file includes the parsing and grammar rules from
575 the src/ directory, loads the relevant PGE libraries,
576 and registers the compiler under the name '@lang@'.
584 Creates the @lang@ compiler using a C<PCT::HLLCompiler>
591 .namespace
[ '@lang@';'Compiler' ]
593 .loadlib
'@lclang@_group'
595 .sub '' :anon
:load
:init
596 load_bytecode
'PCT.pbc'
597 .local pmc parrotns
, hllns
, exports
598 parrotns
= get_root_namespace
['parrot']
599 hllns
= get_hll_namespace
600 exports
= split ' ', 'PAST PCT PGE'
601 parrotns
.'export_to'(hllns
, exports
)
604 .include
'src/gen_grammar.pir'
605 .include
'src/gen_actions.pir'
607 .sub 'onload' :anon
:load
:init
608 $P0 = get_hll_global
['PCT'], 'HLLCompiler'
610 $P1.'language'('@lclang@')
611 $P0 = get_hll_namespace
['@lang@';'Grammar']
612 $P1.'parsegrammar'($P0)
613 $P0 = get_hll_namespace
['@lang@';'Grammar';'Actions']
614 $P1.'parseactions'($P0)
617 =item main(args :slurpy) :main
619 Start compilation by passing any command line C<args>
620 to the @lang@ compiler.
627 $P0 = compreg
'@lclang@'
628 $P1 = $P0.'command_line'(args
)
631 .include
'src/gen_builtins.pir'
641 # vim: expandtab shiftwidth=4 ft=pir:
643 __src
/pct/grammar
.pg__
648 This is the grammar for @lang@ written as a sequence of Perl 6 rules.
652 grammar @lang@::Grammar is PCT::Grammar;
656 [ $ || <panic: 'Syntax error'> ]
660 ## this <ws> rule treats # as "comment to eol"
661 ## you may want to replace it with something appropriate
664 [ '#' \N* \n? | \s+ ]*
668 'say' <expression> [ ',' <expression> ]* ';'
673 | <integer> {*} #= integer
674 | <quote> {*} #= quote
677 token integer { \d+ {*} }
680 [ \' <string_literal: '\'' > \' | \" <string_literal: '"' > \" ]
686 | <value> {*} #= value
689 rule expression is optable { ... }
691 __src/pct/grammar-oper.pg__
694 ## expressions and operators
695 proto 'term:' is precedence('=') is parsed(&term) { ... }
697 ## multiplicative operators
698 proto infix:<*> is looser(term:) is pirop('mul') { ... }
699 proto infix:</> is equiv(infix:<*>) is pirop('div') { ... }
701 ## additive operators
702 proto infix:<+> is looser(infix:<*>) is pirop('add') { ... }
703 proto infix:<-> is equiv(infix:<+>) is pirop('sub') { ... }
705 __src/pct/actions.pm__
710 @lang@::Grammar::Actions - ast transformations for @lang@
712 This file contains the methods that are used by the parse grammar
713 to build the PAST representation of an @lang@ program.
714 Each method below corresponds to a rule in F<src/parser/grammar.pg>,
715 and is invoked at the point where C<{*}> appears in the rule,
716 with the current match object as the first argument. If the
717 line containing C<{*}> also has a C<#= key> comment, then the
718 value of the comment is passed as the second argument to the method.
722 class @lang@::Grammar::Actions;
725 my $past := PAST::Block.new( :blocktype('declaration'), :node( $/ ), :hll('@lang@') );
727 $past.push( $_.ast );
733 method statement($/) {
734 my $past := PAST::Op.new( :name('say'), :pasttype('call'), :node( $/ ) );
736 $past.push( $_.ast );
742 ## This is one of the more complex transformations, because
743 ## our grammar is using the operator precedence parser here.
744 ## As each node in the expression tree is reduced by the
745 ## parser, it invokes this method with the operator node as
746 ## the match object and a $key of 'reduce'. We then build
747 ## a PAST::Op node using the information provided by the
748 ## operator node. (Any traits for the node are held in $<top>.)
749 ## Finally, when the entire expression is parsed, this method
750 ## is invoked with the expression in $<expr> and a $key of 'end'.
751 method expression($/, $key) {
756 my $past := PAST::Op.new( :name($<type>),
757 :pasttype($<top><pasttype>),
758 :pirop($<top><pirop>),
759 :lvalue($<top><lvalue>),
763 $past.push( $_.ast );
771 ## Like 'statement' above, the $key has been set to let us know
772 ## which term subrule was matched.
773 method term($/, $key) {
778 method value($/, $key) {
784 make PAST::Val.new( :value( ~$/ ), :returns('Integer'), :node($/) );
789 make PAST::Val.new( :value( $<string_literal>.ast ), :node($/) );
795 # cperl-indent-level: 4
798 # vim: expandtab shiftwidth=4:
800 __src/builtins/say.pir__
805 say.pir -- simple implementation of a say function
812 .param pmc args
:slurpy
816 unless it
goto iter_end
830 # vim: expandtab shiftwidth=4 ft=pir:
842 use Getopt
::Long
qw(:config pass_through);
844 $ENV{'HARNESS_PERL'} = './@lclang@';
846 $Test::Harness
::switches
= '';
849 'tests-from-file=s' => \
my $list_file,
850 'verbosity=i' => \
$Test::Harness
::verbose
,
851 'jobs:3' => \
my $jobs,
852 'icu:1' => \
my $do_icu,
855 my @pass_through_options = grep m/^--?[^-]/, @ARGV;
856 my @files = grep m/^[^-]/, @ARGV;
858 my $slash = $^O
eq 'MSWin32' ?
'\\' : '/';
861 open(my $f, '<', $list_file)
862 or die "Can't open file '$list_file' for reading: $!";
867 my ($fn, $flags) = split /\s+#\s*/;
868 next if ($flags && ($flags =~ m/icu/) && !$do_icu);
869 $fn = "t/spec/$fn" unless $fn =~ m/^t\Q$slash\Espec\Q$slash\E/;
870 $fn =~ s{/}{$slash}g;
875 warn "Missing test file: $fn\n";
881 my @tfiles = map { all_in
($_) } sort @files;
883 if (eval { require TAP
::Harness
; 1 }) {
884 my %harness_options = (
885 exec => ['./@lclang@'],
886 verbosity
=> 0+$Test::Harness
::verbose
,
889 TAP
::Harness
->new( \
%harness_options )->runtests(@tfiles);
895 # adapted to return only files ending in '.t'
899 return $start unless -d
$start;
901 my @skip = ( File
::Spec
->updir, File
::Spec
->curdir, qw( .svn CVS .git ) );
902 my %skip = map {($_,1)} @skip;
906 if ( opendir( my $dh, $start ) ) {
907 my @files = sort readdir $dh;
908 closedir $dh or die $!;
909 for my $file ( @files ) {
910 next if $skip{$file};
912 my $currfile = File
::Spec
->catfile( $start, $file );
913 if ( -d
$currfile ) {
914 push( @hits, all_in
( $currfile ) );
917 push( @hits, $currfile ) if $currfile =~ /\.t$/;
929 # This just checks that the basic parsing and call to builtin say() works.
941 # cperl-indent-level: 4
944 # vim: expandtab shiftwidth=4: