Author: jisom
Date: Tue Mar 7 17:22:45 2006
New Revision: 11820
Added:
trunk/runtime/parrot/include/hllmacros.pir (contents, props changed)
Removed:
trunk/t/op/macro.t
Modified:
trunk/MANIFEST
trunk/MANIFEST.SKIP
trunk/compilers/imcc/imcc.l
trunk/compilers/imcc/imclexer.c
trunk/docs/imcc/macros.pod
trunk/t/compilers/imcc/syn/ (props changed)
trunk/t/compilers/imcc/syn/macro.t
Log:
Add brace support for macros
Add tests in t/compilers/imcc/syn/macro.t
Delete redundant t/op/macro.t
Modify docs/imcc/macros.pod a little
Add runtime/parrot/include/hllmacros.pir
Oh, and I remembered MANIFEST
Modified: trunk/MANIFEST
==============================================================================
--- trunk/MANIFEST (original)
+++ trunk/MANIFEST Tue Mar 7 17:22:45 2006
@@ -1580,6 +1580,7 @@
LICENSES/gpl.txt [main]doc
runtime/parrot/dynext/README [devel]doc
runtime/parrot/include/DWIM.pir [devel]doc
+runtime/parrot/include/hllmacros.pir [main]
runtime/parrot/include/README [devel]doc
runtime/parrot/library/config.pir [library]
runtime/parrot/library/dumper.pir [library]
@@ -2010,7 +2011,6 @@
t/op/jitn.t []
t/op/lexicals.t []
t/op/literal.t []
-t/op/macro.t []
t/op/number.t []
t/op/random.t []
t/op/spawnw.t []
Modified: trunk/MANIFEST.SKIP
==============================================================================
--- trunk/MANIFEST.SKIP (original)
+++ trunk/MANIFEST.SKIP Tue Mar 7 17:22:45 2006
@@ -838,12 +838,12 @@
^t/compilers/imcc/reg/.*\.pir$
^t/compilers/imcc/reg/.*\.pir/
# generated from svn:ignore of 't/compilers/imcc/syn/'
+^t/compilers/imcc/syn/.*\.pasm$
+^t/compilers/imcc/syn/.*\.pasm/
^t/compilers/imcc/syn/.*\.pbc$
^t/compilers/imcc/syn/.*\.pbc/
^t/compilers/imcc/syn/.*\.pir$
^t/compilers/imcc/syn/.*\.pir/
-^t/compilers/imcc/syn/const_8\.pasm$
-^t/compilers/imcc/syn/const_8\.pasm/
# generated from svn:ignore of 't/compilers/pge/'
^t/compilers/pge/.*\.pir$
^t/compilers/pge/.*\.pir/
Modified: trunk/compilers/imcc/imcc.l
==============================================================================
--- trunk/compilers/imcc/imcc.l (original)
+++ trunk/compilers/imcc/imcc.l Tue Mar 7 17:22:45 2006
@@ -678,6 +678,33 @@
return c;
}
+static char*
+read_braced (YYSTYPE *valp, void *interp, const char *macro_name,
+ char *current)
+{
+ int c;
+ YYSTYPE val;
+ int len = strlen(current);
+ c = yylex(&val, interp);
+ int count = 0;
+ while (c != '}' || count > 0) {
+ if (c == '}') count--;
+ else if (c == '{') count++;
+ if (c <= 0)
+ IMCC_fataly(interp, E_SyntaxError,
+ "End of file reached while reading arguments in '%s'",
+ macro_name);
+ len += strlen(val.s);
+ current = realloc(current, len + 1);
+ strcat(current,val.s);
+ free(val.s);
+ c = yylex(&val,interp);
+ }
+ if (valp) *valp = val;
+ else free(val.s);
+ return current;
+}
+
static int
read_params (YYSTYPE *valp, void *interp, struct params_t *params,
const char *macro_name, int need_id)
@@ -714,6 +741,10 @@
"Parameter definition in '%s' must be IDENTIFIER",
macro_name);
}
+ else if (c == '{') {
+ current = read_braced (&val, interp, macro_name, current);
+ c = yylex_skip(&val, interp, " \n");
+ }
else {
if (!need_id || c != ' ') {
len += strlen(val.s);
Modified: trunk/compilers/imcc/imclexer.c
==============================================================================
--- trunk/compilers/imcc/imclexer.c (original)
+++ trunk/compilers/imcc/imclexer.c Tue Mar 7 17:22:45 2006
@@ -2,7 +2,7 @@
/* A lexical scanner generated by flex */
/* Scanner skeleton version:
- * $Header: /home/daffy/u0/vern/flex/RCS/flex.skl,v 2.91 96/09/10 16:58:48
vern Exp $
+ * $Header: /cvs/root/flex/flex/skel.c,v 1.1.1.1 1999/04/23 00:46:30 wsanchez
Exp $
*/
#define FLEX_SCANNER
@@ -5211,6 +5211,33 @@
return c;
}
+static char*
+read_braced (YYSTYPE *valp, void *interp, const char *macro_name,
+ char *current)
+{
+ int c;
+ YYSTYPE val;
+ int len = strlen(current);
+ c = yylex(&val, interp);
+ int count = 0;
+ while (c != '}' || count > 0) {
+ if (c == '}') count--;
+ else if (c == '{') count++;
+ if (c <= 0)
+ IMCC_fataly(interp, E_SyntaxError,
+ "End of file reached while reading arguments in '%s'",
+ macro_name);
+ len += strlen(val.s);
+ current = realloc(current, len + 1);
+ strcat(current,val.s);
+ free(val.s);
+ c = yylex(&val,interp);
+ }
+ if (valp) *valp = val;
+ else free(val.s);
+ return current;
+}
+
static int
read_params (YYSTYPE *valp, void *interp, struct params_t *params,
const char *macro_name, int need_id)
@@ -5247,6 +5274,10 @@
"Parameter definition in '%s' must be IDENTIFIER",
macro_name);
}
+ else if (c == '{') {
+ current = read_braced (&val, interp, macro_name, current);
+ c = yylex_skip(&val, interp, " \n");
+ }
else {
if (!need_id || c != ' ') {
len += strlen(val.s);
Modified: trunk/docs/imcc/macros.pod
==============================================================================
--- trunk/docs/imcc/macros.pod (original)
+++ trunk/docs/imcc/macros.pod Tue Mar 7 17:22:45 2006
@@ -137,5 +137,9 @@
.inc3(I0) # Expands to the obvious ('inc I0\n') x 3
+Using braces, C<{ }>, allows you to use commas and parenthesis inside
+parameters for a macro. See F<runtime/parrot/include/hllmacros.pir> for
+examples and possible usage.
+
=cut
Added: trunk/runtime/parrot/include/hllmacros.pir
==============================================================================
--- (empty file)
+++ trunk/runtime/parrot/include/hllmacros.pir Tue Mar 7 17:22:45 2006
@@ -0,0 +1,233 @@
+=head1 High Level Language Macros
+
+These macros are to make it easier to write readable and maintainable PIR by
+preprocessing common HLL forms into their low level PIR forms. These are
+B<not> actually high level constructs, but merely preprocessor directives.
+
+=head2 Conditionals
+
+When a conditional is labeled as a parameter, the valid syntax is limited.
+Only code snippets such as C<$P0>, C<< $I0 <= 42 >>, C<null $P0>, are allowed.
+Groupings such as C<< $I0 <= 42 || $I0 > 142 >> are not allowed, although it is
+possible to write a macro that contains two parameters representing
+conditionals.
+
+=head2 Code blocks
+
+The macro preprocessor is very simple. But it allows using braces to delimit
+code that contains commas or right parenthesis, C<)>, without causing an error.
+This allows for nesting macros to near infinite depths. And macro parameter
+that uses a comma or right parenthesis must be enclosed in braces.
+
+=head1 Macros
+
+=over 4
+
+=item C<.NL()>
+
+This is the most simple of macros. Because PIR statements are delimited by
+newlines, putting multiple statements, no matter how simple or short, is not
+allowed. Sometimes for readability you wish to have two on the same line.
+Using C<.NL()> in a line will insert a linebreak.
+
+=cut
+
+.macro NL()
+.endm
+
+=item C<.If(conditional, code)>
+
+Runs the code in C<code> if C<conditional> is true.
+
+=cut
+
+.macro If(conditional, code)
+ unless .conditional goto .$endif
+ .code
+.local $endif:
+.endm
+
+=item C<.Unless(conditional, code)>
+
+Runs the code in C<code> if C<conditional> is false.
+
+=cut
+
+.macro Unless(conditional, code)
+ if .conditional goto .$endif
+ .code
+.local $endif:
+.endm
+
+=item C<.IfElse(conditional, true, false)>
+
+Runs the code in C<true> if C<conditional> is true, otherwise runs the code in
+C<false>.
+
+=cut
+
+.macro IfElse(conditional, true, false)
+ unless .conditional goto .$else
+ .true
+ goto .$endif
+.local $else:
+ .false
+.local $endif:
+.endm
+
+=item C<.While(conditional, code)>
+
+Runs the code in C<code> as long as C<conditional> is true.
+
+=cut
+
+.macro While(conditional, code)
+.local $beginwhile:
+ unless .conditional goto .$endwhile
+ .code
+ goto .$beginwhile
+.local $endwhile:
+.endm
+
+=item C<.DoWhile(code, conditional)>
+
+Runs the code in C<code> once, and then as long as C<conditional> is true.
+
+=cut
+
+.macro DoWhile(code, conditional)
+.local $beginwhile:
+ .code
+ if .conditional goto .$beginwhile
+.endm
+
+=item C<.For(start, conditional, continue, code)>
+
+First C<start> is executed, such as C<i = 0>, and then if the C<conditional> is
+true, runs C<code>, and then the C<continue> code, such as C<inc i>.
+
+=cut
+
+.macro For(start, conditional, cont, code)
+ .start
+.local $beginfor:
+ unless .conditional goto .$endfor
+ .code
+ .cont
+ goto .$beginfor
+.local $endfor:
+.endm
+
+=item C<.Foreach(name, array, code)>
+
+A simple foreach loop. Given the aggregate C<array>, which B<must> be a
+register, it iterates through the array, putting each value into C<name> to
+work with in C<code>.
+
+=cut
+
+.macro Foreach(name, array, code)
+ .sym int local__Foreach__i, local__Foreach__k
+ local__Foreach__i = 0
+ local__Foreach__k = .array
+.local $beginforeach:
+ unless local__Foreach__i < local__Foreach__k goto .$endforeach
+ .name = .array[local__Foreach__i]
+ .code
+ inc local__Foreach__i
+ goto .$beginforeach
+.local $endforeach:
+.endm
+
+=back
+
+=head1 Examples
+
+=head2 Good morning!
+
+"Hello, world!" is so mundane, make it more fun.
+
+ .include "tm.pasm"
+ .include "hllmacros.pir"
+ .sub main :main
+ $I0 = time
+ $P0 = decodelocaltime $I0
+ $I0 = $P0[.TM_HOUR]
+ .IfElse($I0 < 12,{
+ print "Good morning!\n"
+ },{
+ .IfElse($I0 > 18,{
+ print "Good night!\n"
+ },{
+ print "Good evening!\n"
+ })
+ })
+ .end
+
+=head2 Simple looping
+
+A simple demonstration of the For loop.
+
+ .include "hllmacros.pir"
+ .sub main :main
+ print "For\n"
+ .For(i = 0 , i < 5, inc i,
+ print "\t"
+ print i
+ print "\n"
+ )
+ .end
+
+A simple demonstration of the Foreach loop.
+
+ .include "hllmacros.pir"
+ .sub main :main
+ print "Foreach\n"
+ .local pmc array, letter
+ array = new .ResizablePMCArray
+ push array, "a"
+ push array, "b"
+ push array, "c"
+ .Foreach(letter, array, {
+ print "\t"
+ print letter
+ print " "
+ .IfElse(letter == 'b',
+ print "is b\n"
+ ,
+ print "isn't b\n"
+ )
+ })
+ .end
+
+Using C<.NL()>
+
+ .local int i, j
+ .For({i = 0 .NL() j = 11}, i < j, {inc i .NL() dec j }, {
+ print i
+ print "\t"
+ print j
+ print "\n"
+ })
+
+=head1 Caveats
+
+The .Foreach macro is not nestable within itself currently. You can use other
macros inside a .Foreach loop, and the .Foreach loop can be nested inside other
macros.
+
+ .Foreach(foo, bar, {
+ .Foreach(foobar, foo, {
+ print foobar
+ })
+ })
+
+Will not run as you would expect.
+
+=begin vim
+
+vim: ts=4 expandtab
+
+=end vim
+
+=cut
+
+
Modified: trunk/t/compilers/imcc/syn/macro.t
==============================================================================
--- trunk/t/compilers/imcc/syn/macro.t (original)
+++ trunk/t/compilers/imcc/syn/macro.t Tue Mar 7 17:22:45 2006
@@ -8,7 +8,7 @@
use lib qw( . lib ../lib ../../lib );
use Test::More;
use Parrot::Config;
-use Parrot::Test tests => 23;
+use Parrot::Test tests => 29;
# macro tests
@@ -146,6 +146,60 @@
.end
CODE
+pasm_output_is(<<'CODE', 'foo', "constant defined, used in a macro call");
+.constant FOO S0
+.macro answer (bar)
+ print .bar
+.endm
+ set .FOO,"foo"
+ .answer(.FOO)
+ end
+CODE
+
+open FOO, ">macro.tempfile";
+print FOO <<'ENDF';
+ set S0, "Betelgeuse\n"
+ENDF
+close FOO;
+
+pasm_output_is(<<"CODE", <<OUTPUT, "basic include macro");
+.include "macro.tempfile"
+ print S0
+
+ set S0, "Rigel"
+.include "macro.tempfile"
+ print S0
+ end
+CODE
+Betelgeuse
+Betelgeuse
+OUTPUT
+
+open FOO, ">macro.tempfile"; # Clobber previous
+print FOO <<'ENDF';
+.macro multiply(A,B)
+ new P0, .Float
+ set P0, .A
+ new P1, .Float
+ set P1, .B
+ new P2, .Float
+ mul P2, P1, P0
+.endm
+ENDF
+close FOO;
+
+pasm_output_is(<<"CODE", <<OUTPUT, "include a file defining a macro");
+.include "macro.tempfile"
+ .multiply(12,13)
+ print P2
+ print "\\n"
+ end
+CODE
+156
+OUTPUT
+
+unlink("macro.tempfile");
+
pir_output_is( <<'CODE', <<OUTPUT, ".newid" );
.sub test :main
.macro newid(ID, CLASS)
@@ -313,3 +367,46 @@
/in macro '.M'/
OUTPUT
+pir_output_is( <<'CODE', <<OUTPUT, "braces in param" );
+.macro M(A)
+ print .A
+.endm
+.sub test :main
+ $S0 = "foo\n"
+ .M({$S0})
+ end
+.end
+CODE
+foo
+OUTPUT
+
+pir_output_is( <<'CODE', <<OUTPUT, "braces and comma, with a newline in param"
);
+.macro M(A)
+ .A
+.endm
+.sub test :main
+ $S0 = "foo\n"
+ .M({set $S0, "bar\n"
+ print $S0})
+ end
+.end
+CODE
+bar
+OUTPUT
+
+pir_output_is( <<'CODE', <<OUTPUT, "braces and parenthesis in param" );
+.macro M(A)
+ .A
+.endm
+.sub test :main
+ .M({foo()})
+ end
+.end
+
+.sub foo
+ print "foo\n"
+.end
+CODE
+foo
+OUTPUT
+