[svn] r6459: nemerle/trunk: macros/Late.n ncc/Makefile ncc/testsuite/positive/late.n

nazgul svnadmin at nemerle.org
Fri Jul 21 20:38:16 CEST 2006


Log:
Add Late Bound macros

Author: nazgul
Date: Fri Jul 21 20:38:01 2006
New Revision: 6459

Added:
   nemerle/trunk/macros/Late.n   (contents, props changed)
   nemerle/trunk/ncc/testsuite/positive/late.n   (contents, props changed)
Modified:
   nemerle/trunk/ncc/Makefile

Added: nemerle/trunk/macros/Late.n
==============================================================================
--- (empty file)
+++ nemerle/trunk/macros/Late.n	Fri Jul 21 20:38:01 2006
@@ -0,0 +1,354 @@
+//
+// Late Binding Macro for Nemerle
+// Copyright (c) 2006, Snaury (snaury at gmail.com)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above copyright notice,
+//       this list of conditions and the following disclaimer in the documentation
+//       and/or other materials provided with the distribution.
+//     * Neither the name of the author nor the names of its contributors may be
+//       used to endorse or promote products derived from this software without
+//       specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#pragma indent
+
+namespace Nemerle.Late
+
+  using Nemerle.IO
+  using Nemerle.Compiler
+  using Nemerle.Compiler.Parsetree
+  using BF = System.Reflection.BindingFlags
+  using PM = System.Reflection.ParameterModifier
+
+  [Record] \
+  public class LateMacro
+    env : GlobalEnv
+  
+    /// This class holds info used when passing it to Type.InvokeMember
+    public class CallInfo
+      public mutable name : string
+      public mutable args : list[PExpr]
+      public mutable byref : list[bool]
+      public mutable isout : list[bool]
+      public mutable names : list[PExpr]
+
+      public this(name : string, args : list[PExpr], tail : list[PExpr] = null)
+        this.name = name
+        this.args = args
+        transform()
+        unless(tail == null)
+          this.args = this.args + tail
+
+      transform() : void
+        def transform_named()
+          def loop(args, named_args = [], named_names = [], unnamed_args = [])
+            match(args)
+              | [] => (named_args.Rev() + unnamed_args.Rev(), named_names.Rev())
+              | arg :: args => match(arg)
+                | <[ $(id : name) = $val ]> => loop(args, val :: named_args, <[ $(id.Id : string) ]> :: named_names, unnamed_args)
+                | _ => loop(args, named_args, named_names, arg :: unnamed_args)
+          (args, names) = loop(args)
+        def transform_byref()
+          def loop(args, _args = [], _byref = [], _isout = [])
+            match(args)
+              | [] => (_args.Rev(), _byref.Rev(), _isout.Rev())
+              | arg :: args => match(arg)
+                | <[ out $arg ]> => loop(args, arg :: _args, true :: _byref, true :: _isout)
+                | <[ ref $arg ]> => loop(args, arg :: _args, true :: _byref, false :: _isout)
+                | _ => loop(args, arg :: _args, false :: _byref, false :: _isout)
+          (args, byref, isout) = loop(args)
+        transform_named()
+        transform_byref()
+
+    public variant LateCall
+      | SetProperty { loc: Location; callinfo: CallInfo }
+      | GetProperty { loc: Location; callinfo: CallInfo }
+      | Method { loc: Location; callinfo: CallInfo }
+
+    /// returns expression that does a single late invoke
+    /// expr_name is used to hold expr
+    public static invoke(expr_name : Name, expr : PExpr, loc : Location, info : CallInfo, flags : BF, ignore_result = false) : PExpr
+      def array_literal(ty, args, exclude = [])
+        def transform(args, exclude, r = [])
+          match(args)
+            | [] => r.Rev()
+            | arg :: args => match(exclude)
+              | x :: exclude when x => transform(args, exclude, <[ null : object ]> :: r)
+              | _ :: exclude \
+              | exclude => transform(args, exclude, PExpr.TypeEnforcement(arg.Location, arg, ty) :: r)
+        if(args.Length != 0)
+          PExpr.Array(loc, <[ $(1 : int) ]>, PExpr.ListLiteral(loc, transform(args, exclude)))
+        else
+          <[ null ]>
+
+      def pms_literal(numargs, byrefs)
+        def pms_needed = need:
+          foreach(x in byrefs) when(x)
+            need(true)
+          false
+        if(pms_needed)
+          def pms = Macros.NewSymbol()
+          def init(byrefs, i = 0, r = [])
+            match(byrefs)
+              | x :: byrefs when (x && i < numargs) => init(byrefs, i + 1, PExpr.Assign(loc, PExpr.Indexer(loc, <[ $(pms : name) ]>, [<[ $(i : int)]>]), <[ true ]>) :: r)
+              | _ => r.Rev()
+          PExpr.Array(loc, <[ $(1 : int) ]>, PExpr.ListLiteral(loc, [PExpr.Sequence(loc, [<[ def $(pms : name) = PM($(numargs : int)) ]>, PExpr.Sequence(loc, init(byrefs)), <[ $(pms : name) ]>])]))
+        else
+          <[ null ]>
+
+      def name_expr = <[ $(info.name : string) ]>
+      def flags_expr = PExpr.Literal(Literal.FromObject(flags))
+      def args_expr = array_literal(<[ object ]>, info.args, info.isout) // FIXME: do we really need to pass info.isout here?
+      def pms_expr = pms_literal(info.args.Length, info.byref)
+      def names_expr = array_literal(<[ string ]>, info.names)
+      def call_expr(args_expr)
+        PExpr.Sequence(loc, [PExpr.Assign(loc, <[ $(expr_name : name) ]>, expr),
+                             <[ $(expr_name : name).GetType().InvokeMember($name_expr, $flags_expr, null, $(expr_name : name), $args_expr, $pms_expr, null, $names_expr) ]>])
+      def need_copyback = need:
+        foreach(x in info.byref) when(x)
+          need(true)
+        false
+      if(need_copyback)
+        def args_name = Macros.NewSymbol()
+        def init = PExpr.Define(loc, <[ $(args_name : name) ]>, args_expr)
+        def call = call_expr(<[ $(args_name : name) ]>)
+        def copyback(args, byref, i = 0, r = [])
+          match(args)
+            | arg :: args => match(byref)
+              | x :: byref when x => copyback(args, byref, i + 1, PExpr.Assign(arg.Location, arg, PExpr.Indexer(loc, <[ $(args_name : name) ]>, [<[ $(i : int) ]>])) :: r)
+              | _ :: byref => copyback(args, byref, i + 1, r)
+              | _ => r.Rev()
+            | _ => r.Rev()
+        def copyback = PExpr.Sequence(loc, copyback(info.args, info.byref))
+        if(ignore_result)
+          PExpr.Sequence(loc, [init, PExpr.Assign(loc, PExpr.Wildcard(), call), copyback])
+        else
+          def result_name = Macros.NewSymbol()
+          PExpr.Sequence(loc, [init, PExpr.Define(loc, <[ $(result_name : name) ]>, call), copyback, <[ $(result_name : name) ]>])
+      else
+        def call = call_expr(args_expr)
+        if(ignore_result)
+          PExpr.Sequence(loc, [PExpr.Assign(loc, PExpr.Wildcard(), call)])
+        else
+          call
+
+    /// returns expression that does all late invokes in chain
+    /// expr_name is used to hold expr with each invoke
+    public static build(expr_name : Name, expr : PExpr, chain : list[LateCall]) : PExpr
+      def loop(expr, chain)
+        match(chain)
+          | null => expr
+          | [] => expr
+          | x :: xs => match(x)
+            | null => loop(expr, xs)
+            | LateCall.SetProperty(loc, info) =>
+              loop(invoke(expr_name, expr, loc, info, if(info.args.Length > 1) BF.SetProperty else BF.SetField %| BF.SetProperty, true), xs)
+            | LateCall.GetProperty(loc, info) =>
+              loop(invoke(expr_name, expr, loc, info, if(info.args.Length > 0) BF.GetProperty else BF.GetField %| BF.GetProperty), xs)
+            | LateCall.Method(loc, info) =>
+              loop(invoke(expr_name, expr, loc, info, BF.InvokeMethod), xs)
+      loop(expr, chain)
+
+    /// scans single expression and returns (expr, list[LateCall])
+    /// returns (expr, []) if nothing in expression can be late bound
+    public static scan(expr : PExpr) : PExpr * list[LateCall]
+      def loop(expr, r = [])
+        def can_be_late(expr)
+          | PExpr.This => false
+          | PExpr.Base => false
+          | _ when Macros.IsTypeName(expr) => false
+          | _ => true
+        match(expr)
+          | null => (expr, r)
+          | <[ $rest . $(id : name) [.. $args] = $value ]> when can_be_late(rest) \
+          | <[ $rest . $(id : name) = $value ]> when can_be_late(rest) with (args = []) \
+          | <[ $rest . [.. $args] = $value ]> when can_be_late(rest) with(id = Name("")) \
+          | <[ $rest [.. $args] = $value ]> when can_be_late(rest) with(id = Name("")) =>
+            when(id.Id == "" && args.Length == 0)
+              Message.FatalError(expr.Location, "default indexer must have parameters")
+            loop(rest, LateCall.SetProperty(expr.Location, CallInfo(id.Id, args, [value])) :: r)
+          | <[ $rest . $(id : name) [.. $args] ]> when can_be_late(rest) \
+          | <[ $rest . $(id : name) ]> when can_be_late(rest) with(args = []) \
+          | <[ $rest . [.. $args] ]> when can_be_late(rest) with(id = Name("")) \
+          | <[ $rest [.. $args] ]> when can_be_late(rest) with(id = Name("")) =>
+            when(id.Id == "" && args.Length == 0)
+              Message.FatalError(expr.Location, "default indexer must have parameters")
+            loop(rest, LateCall.GetProperty(expr.Location, CallInfo(id.Id, args)) :: r)
+          | <[ $rest . $(id : name) (.. $args) ]> when can_be_late(rest) =>
+            loop(rest, LateCall.Method(expr.Location, CallInfo(id.Id, args)) :: r)
+          | _ => (expr, r)
+      loop(expr)
+
+    /// transform any first class lateexpr by transformation rules
+    /// sets expr' and returns true if expr is first class lateexpr
+    /// returns false otherwise
+    /// note: if deep is true then any subexpr in expr' is sent to transform(expr, true)
+    public latebound(expr : PExpr, expr' : out PExpr, deep = true) : bool
+      def (expr, chain) = scan(expr)
+      if(chain is [])
+        false
+      else
+        def expr = if(deep)
+          def deeptransform(expr)
+            transform(expr, true)
+          def expr = deeptransform(expr)
+          def loop(chain)
+            | x :: chain =>
+              match(x)
+                | LateCall.SetProperty(_, info) \
+                | LateCall.GetProperty(_, info) \
+                | LateCall.Method(_, info) =>
+                  info.args = info.args.Map(deeptransform)
+              loop(chain)
+            | _ => ()
+          loop(chain)
+          expr
+        else
+          expr
+        def expr_name = Macros.NewSymbol()
+        expr' = PExpr.Sequence(expr.Location, [PExpr.DefMutable(expr.Location, <[ $(expr_name : name) ]>, null), build(expr_name, expr, chain)])
+        true
+
+    /// transforms any lateexpr in expr by lateexpr transformation rules
+    /// if deep is true, then also transforms any subexpr in any lateexpr
+    /// note: mostly based on traverse in typing/Macros.n
+    /// too bad that traverse can't be used here directly
+    public transform(expr : PExpr, deep = true) : PExpr
+      def loc = expr.Location
+      def recurse(expr)
+        transform(expr, deep)
+      def recurse_fun(f)
+        def recurse_funparm(p)
+          def recurse_attr(attr)
+            | <[ System.ComponentModel.DefaultValueAttribute($e) ]> =>
+              <[ System.ComponentModel.DefaultValueAttribute($(recurse(e))) ]>
+            | _ => attr
+          Fun_parm(p.Location, p.name, p.ty, Modifiers(p.modifiers.mods, p.modifiers.custom_attrs.Map(recurse_attr)))
+        Function_decl(Fun_header(f.header.Location, f.header.typarms, f.header.name, f.header.ret_type, f.header.parms.Map(recurse_funparm)), recurse(f.body))
+      mutable expr'
+      def result = match(expr)
+        | null => null
+        | _ when latebound(expr, out expr', deep) => expr'
+        | PExpr.Wildcard \
+        | PExpr.Void => expr
+        | PExpr.As(pat, name) => PExpr.As(loc, recurse(pat), name)
+        | PExpr.Is(pat, ty) => PExpr.Is(loc, recurse(pat), ty)
+        | PExpr.Where(name, fields) => PExpr.Where(loc, recurse(name), recurse(fields))
+        | PExpr.Match(mexpr, cases) =>
+          def recurse_case(c)
+            def recurse_guard(g)
+              | PExpr.Call(<[ $("when" : dyn) ]> as w, [pat, expr]) =>
+                PExpr.Call(g.Location, w, [pat, recurse(expr)])
+              | _ => g
+            MatchCase(c.patterns.Map(recurse_guard), recurse(c.body), c.disable_warnings)
+          PExpr.Match(loc, recurse(mexpr), cases.Map(recurse_case))
+        | PExpr.Ref => expr
+        | PExpr.Member(obj, mem) => 
+          if (Macros.IsTypeName (obj))
+            expr
+          else
+            PExpr.Member(loc, recurse(obj), mem)
+        | PExpr.Call(func, parms) => 
+          def ismacro = match (Util.QidOfExpr (func))
+            | Some ((id, name)) =>
+              def ctx = name.GetEnv (env)
+              match (ctx.LookupMacro (id))
+                | Some (_) => true
+                | None => false
+            | None => false
+          if (ismacro)
+            PExpr.Call(loc, func, parms.Map(recurse))
+          else
+            PExpr.Call(loc, recurse(func), parms.Map(recurse))
+        | PExpr.GenericSpecifier(func, parms) => PExpr.GenericSpecifier(loc, recurse(func), parms.Map(recurse))
+        | PExpr.ListLiteral(elems) => PExpr.ListLiteral(loc, elems.Map(recurse))
+        | PExpr.Assign(target, source) => PExpr.Assign(loc, recurse(target), recurse(source))
+        | PExpr.DefMutable(name, val) => PExpr.DefMutable(loc, name, recurse(val))
+        | PExpr.Define(name, val) => PExpr.Define(loc, name, recurse(val))
+        | PExpr.DefFunctions(funs) => PExpr.DefFunctions(loc, funs.Map(recurse_fun))
+        | PExpr.Lambda(decl) => PExpr.Lambda(loc, recurse_fun(decl))
+        | PExpr.Throw(expr) =>
+          PExpr.Throw(loc, recurse(expr))
+        | PExpr.TryWith(body, ex, ty, handler) =>
+          PExpr.TryWith(loc, recurse(body), ex, ty, recurse(handler))
+        | PExpr.TryFinally(body, handler) =>
+          PExpr.TryFinally(loc, recurse(body), recurse(handler))
+        | PExpr.Literal \
+        | PExpr.This \
+        | PExpr.Base \
+        | PExpr.Typeof => expr
+        | PExpr.TypeConversion(expr, ty) => PExpr.TypeConversion(loc, recurse(expr), ty)
+        | PExpr.TypeEnforcement(expr, ty) => PExpr.TypeEnforcement(loc, recurse(expr), ty)
+        | PExpr.Sequence(seq) => PExpr.Sequence(loc, seq.Map(recurse))
+        | PExpr.Tuple(args) => PExpr.Tuple(loc, args.Map(recurse))
+        | PExpr.Array(rank, args) => PExpr.Array(loc, recurse(rank), recurse(args))
+        | PExpr.EmptyArray(sizes) => PExpr.EmptyArray(loc, sizes.Map(recurse))
+        | PExpr.Indexer(obj, args) => PExpr.Indexer(loc, recurse(obj), args.Map(recurse))
+        | PExpr.ParmByRef \
+        | PExpr.ParmOut \
+        | PExpr.Error => expr
+        | PExpr.MacroCall(name, ns, parms) =>
+          match(ns.Value)
+            | NamespaceTree.TypeInfoCache.MacroCall(m) when (false
+                                                             || m is late_macroMacro
+                                                             || m is late_parens_macroMacro
+                                                             || m is nolate_macroMacro
+                                                             || m is nolate_parens_macroMacro
+                                                            ) =>
+              expr
+            | _ =>
+              def recurse_parm(parm)
+                | SyntaxElement.Expression(expr) =>
+                  SyntaxElement.Expression(recurse(expr))
+                | _ => parm
+              PExpr.MacroCall(loc, name, ns, parms.Map(recurse_parm))
+        | PExpr.Quoted(quot) =>
+          def inner = match(quot)
+            | SyntaxElement.Expression(expr) => SyntaxElement.Expression(recurse(expr))
+            | _ => quot // perhaps I shouldn't be lazy here, what if something new is added to the compiler?
+          PExpr.Quoted(loc, inner)
+        | PExpr.Spliced(expr) => PExpr.Spliced(loc, recurse(expr))
+        | PExpr.ToComplete => expr
+        | PExpr.Ellipsis(expr) => PExpr.Ellipsis(loc, recurse(expr))
+        | PExpr.Typed \
+        | PExpr.TypedPattern \
+        | PExpr.TypedType => expr
+      result.loc = loc // TODO: rewrite everything up there passing loc to constructors
+      result
+
+  macro late_parens_macro(expr) \
+  syntax("late", "(", expr, ")")
+    def result = LateMacro (Macros.ImplicitCTX().Env).transform(expr)
+    // [DEBUG] printf("result: %s\n", result.ToString())
+    result
+
+  macro late_macro(expr) \
+  syntax("late", expr)
+    def result = LateMacro (Macros.ImplicitCTX().Env).transform(expr)
+    // [DEBUG] printf("result: %s\n", result.ToString())
+    result
+
+  macro nolate_parens_macro(expr) \
+  syntax("nolate", "(", expr, ")")
+    expr
+
+  macro nolate_macro(expr) \
+  syntax("nolate", expr)
+    expr

Modified: nemerle/trunk/ncc/Makefile
==============================================================================
--- nemerle/trunk/ncc/Makefile	(original)
+++ nemerle/trunk/ncc/Makefile	Fri Jul 21 20:38:01 2006
@@ -165,6 +165,7 @@
 	../macros/Data.n \
 	../macros/DesignPatterns.n \
 	../macros/AssemblyInfo.n \
+	../macros/Late.n \
 
 ############################################################
 # OUTPUT

Added: nemerle/trunk/ncc/testsuite/positive/late.n
==============================================================================
--- (empty file)
+++ nemerle/trunk/ncc/testsuite/positive/late.n	Fri Jul 21 20:38:01 2006
@@ -0,0 +1,26 @@
+#pragma indent
+
+using Nemerle.Late
+
+public class Foo 
+  public Run () : void
+    System.Console.WriteLine ("Foo running")
+
+public class Bar
+  public Run () : void
+    System.Console.WriteLine ("Bar running")
+    
+def justRun (x : object) 
+  late x.Run ()
+  
+_ = justRun (Foo ())
+_ = justRun (Bar ())
+
+/*
+OPTIONS: -i
+
+BEGIN-OUTPUT
+Foo running
+Bar running
+END-OUTPUT
+*/
\ No newline at end of file



More information about the svn mailing list