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
+

Reply via email to