PageRenderTime 7ms CodeModel.GetById 3ms app.highlight 20ms RepoModel.GetById 1ms app.codeStats 0ms

/Runtime/Samples/sympl/csharp/ETGen.cs

http://github.com/IronLanguages/main
C# | 730 lines | 578 code | 64 blank | 88 comment | 113 complexity | f4c2cd9752eb25e73ffe9651e18f0032 MD5 | raw file
  1using System;
  2using System.Dynamic;
  3using System.Linq.Expressions;
  4using System.Linq;
  5using System.Collections.Generic;
  6
  7namespace SymplSample {
  8
  9    internal static class ETGen {
 10
 11        public static Expression AnalyzeExpr(SymplExpr expr, AnalysisScope scope) {
 12            if (expr is SymplImportExpr) {
 13                return AnalyzeImportExpr((SymplImportExpr)expr, scope);
 14            } else if (expr is SymplFunCallExpr) {
 15                return AnalyzeFunCallExpr((SymplFunCallExpr)expr, scope);
 16            } else if (expr is SymplDefunExpr) {
 17                return AnalyzeDefunExpr((SymplDefunExpr)expr, scope);
 18            } else if (expr is SymplLambdaExpr) {
 19                return AnalyzeLambdaExpr((SymplLambdaExpr)expr, scope);
 20            } else if (expr is SymplIdExpr) {
 21                return AnalyzeIdExpr((SymplIdExpr)expr, scope);
 22            } else if (expr is SymplQuoteExpr) {
 23                return AnalyzeQuoteExpr((SymplQuoteExpr)expr, scope);
 24            } else if (expr is SymplLiteralExpr) {
 25                return Expression.Constant(((SymplLiteralExpr)expr).Value);
 26            } else if (expr is SymplAssignExpr) {
 27                return AnalyzeAssignExpr((SymplAssignExpr)expr, scope);
 28            } else if (expr is SymplLetStarExpr) {
 29                return AnalyzeLetStarExpr((SymplLetStarExpr)expr, scope);
 30            } else if (expr is SymplBlockExpr) {
 31                return AnalyzeBlockExpr((SymplBlockExpr)expr, scope);
 32            } else if (expr is SymplEqExpr) {
 33                return AnalyzeEqExpr((SymplEqExpr)expr, scope);
 34            } else if (expr is SymplConsExpr) {
 35                return AnalyzeConsExpr((SymplConsExpr)expr, scope);
 36            } else if (expr is SymplListCallExpr) {
 37                return AnalyzeListCallExpr((SymplListCallExpr)expr, scope);
 38            } else if (expr is SymplIfExpr) {
 39                return AnalyzeIfExpr((SymplIfExpr)expr, scope);
 40            } else if (expr is SymplDottedExpr) {
 41                return AnalyzeDottedExpr((SymplDottedExpr)expr, scope);
 42            } else if (expr is SymplNewExpr) {
 43                return AnalyzeNewExpr((SymplNewExpr)expr, scope);
 44            } else if (expr is SymplLoopExpr) {
 45                return AnalyzeLoopExpr((SymplLoopExpr)expr, scope);
 46            } else if (expr is SymplBreakExpr) {
 47                return AnalyzeBreakExpr((SymplBreakExpr)expr, scope);
 48            } else if (expr is SymplEltExpr) {
 49                return AnalyzeEltExpr((SymplEltExpr)expr, scope);
 50            } else if (expr is SymplBinaryExpr) {
 51                return AnalyzeBinaryExpr((SymplBinaryExpr)expr, scope);
 52            } else if (expr is SymplUnaryExpr) {
 53                return AnalyzeUnaryExpr((SymplUnaryExpr)expr, scope);
 54            } else {
 55                throw new InvalidOperationException(
 56                    "Internal: no expression to analyze.");
 57            }
 58        }
 59
 60        public static Expression AnalyzeImportExpr(SymplImportExpr expr,
 61                                                    AnalysisScope scope) {
 62            if (!scope.IsModule) {
 63                throw new InvalidOperationException(
 64                    "Import expression must be a top level expression.");
 65            }
 66            return Expression.Call(
 67                typeof(RuntimeHelpers).GetMethod("SymplImport"),
 68                scope.RuntimeExpr,
 69                scope.ModuleExpr,
 70                Expression.Constant(expr.NamespaceExpr.Select(id => id.Name)
 71                                                      .ToArray()),
 72                Expression.Constant(expr.MemberNames.Select(id => id.Name)
 73                                                      .ToArray()),
 74                Expression.Constant(expr.Renames.Select(id => id.Name)
 75                                                      .ToArray()));
 76        }
 77
 78        public static DynamicExpression AnalyzeDefunExpr(SymplDefunExpr expr,
 79                                                          AnalysisScope scope) {
 80            if (!scope.IsModule) {
 81                throw new InvalidOperationException(
 82                    "Use Defmethod or Lambda when not defining top-level function.");
 83            }
 84            return Expression.Dynamic(
 85                       scope.GetRuntime().GetSetMemberBinder(expr.Name),
 86                       typeof(object),
 87                       scope.ModuleExpr,
 88                       AnalyzeLambdaDef(expr.Params, expr.Body, scope,
 89                                        "defun " + expr.Name));
 90        }
 91
 92        public static LambdaExpression AnalyzeLambdaExpr(SymplLambdaExpr expr,
 93                                                         AnalysisScope scope) {
 94            return (LambdaExpression)AnalyzeLambdaDef(expr.Params, expr.Body,
 95                                                      scope, "lambda");
 96        }
 97
 98        private static Expression AnalyzeLambdaDef
 99                (IdOrKeywordToken[] parms, SymplExpr[] body,
100                 AnalysisScope scope, string description) {
101            var funscope = new AnalysisScope(scope, description);
102            funscope.IsLambda = true;  // needed for return support.
103            var paramsInOrder = new List<ParameterExpression>();
104            foreach (var p in parms) {
105                var pe = Expression.Parameter(typeof(object), p.Name);
106                paramsInOrder.Add(pe);
107                funscope.Names[p.Name.ToLower()] = pe;
108            }
109            // No need to add fun name to module scope since recursive call just looks
110            // up global name late bound.  For lambdas,to get the effect of flet to
111            // support recursion, bind a variable to nil and then set it to a lambda.
112            // Then the lambda's body can refer to the let bound var in its def.
113            var bodyexprs = new List<Expression>();
114            foreach (var e in body) {
115                bodyexprs.Add(AnalyzeExpr(e, funscope));
116            }
117            // Set up the Type arg array for the delegate type.  Must include
118            // the return type as the last Type, which is object for Sympl defs.
119            var funcTypeArgs = new List<Type>();
120            for (int i = 0; i < parms.Length + 1; i++) {
121                funcTypeArgs.Add(typeof(object));
122            }
123            return Expression.Lambda(
124                       Expression.GetFuncType(funcTypeArgs.ToArray()),
125                       Expression.Block(bodyexprs),
126                       paramsInOrder);
127        }
128
129
130		// Returns a dynamic InvokeMember or Invoke expression, depending on the
131		// Function expression.
132		//
133        public static DynamicExpression AnalyzeFunCallExpr(
134                SymplFunCallExpr expr, AnalysisScope scope) {
135            if (expr.Function is SymplDottedExpr) {
136                SymplDottedExpr dottedExpr = (SymplDottedExpr)expr.Function;
137                Expression objExpr;
138                int length = dottedExpr.Exprs.Length;
139                if (length > 1) {
140                    objExpr = AnalyzeDottedExpr(
141                        // create a new dot expression for the object that doesn't
142                        // include the last part
143                        new SymplDottedExpr(
144                               dottedExpr.ObjectExpr, 
145                               RuntimeHelpers.RemoveLast(dottedExpr.Exprs)),
146                        scope
147                    );
148                } else {
149                    objExpr = AnalyzeExpr(dottedExpr.ObjectExpr, scope);
150                }
151                List<Expression> args = new List<Expression>();
152                args.Add(objExpr);
153                args.AddRange(expr.Arguments.Select(a => AnalyzeExpr(a, scope)));
154
155                // last expr must be an id
156                var lastExpr = (SymplIdExpr)(dottedExpr.Exprs.Last());
157                return Expression.Dynamic(
158                    scope.GetRuntime().GetInvokeMemberBinder(
159                        new InvokeMemberBinderKey(
160                            lastExpr.IdToken.Name,
161                            new CallInfo(expr.Arguments.Length))),
162                    typeof(object),
163                    args
164                );
165            } else {
166                var fun = AnalyzeExpr(expr.Function, scope);
167                List<Expression> args = new List<Expression>();
168                args.Add(fun);
169                args.AddRange(expr.Arguments.Select(a => AnalyzeExpr(a, scope)));
170                // Use DynExpr so that I don't always have to have a delegate to call,
171                // such as what happens with IPy interop.
172                return Expression.Dynamic(
173                    scope.GetRuntime()
174                         .GetInvokeBinder(new CallInfo(expr.Arguments.Length)),
175                    typeof(object),
176                    args
177                );
178            }
179        }
180
181		// Returns a chain of GetMember and InvokeMember dynamic expressions for
182		// the dotted expr.
183		//
184        public static Expression AnalyzeDottedExpr(SymplDottedExpr expr,
185                                                    AnalysisScope scope) {
186            var curExpr = AnalyzeExpr(expr.ObjectExpr, scope);
187            foreach (var e in expr.Exprs) {
188                if (e is SymplIdExpr) {
189                    curExpr = Expression.Dynamic(
190                        scope.GetRuntime()
191                             .GetGetMemberBinder(((SymplIdExpr)e).IdToken.Name),
192                        typeof(object),
193                        curExpr
194                    );
195                } else if (e is SymplFunCallExpr) {
196                    var call = (SymplFunCallExpr)e;
197                    List<Expression> args = new List<Expression>();
198                    args.Add(curExpr);
199                    args.AddRange(call.Arguments.Select(a => AnalyzeExpr(a, scope)));
200
201                    curExpr = Expression.Dynamic(
202                        // Dotted exprs must be simple invoke members, a.b.(c ...) 
203                        scope.GetRuntime().GetInvokeMemberBinder(
204                            new InvokeMemberBinderKey(
205                                ((SymplIdExpr)call.Function).IdToken.Name,
206                                new CallInfo(call.Arguments.Length))),
207                        typeof(object),
208                        args
209                    );
210                } else {
211                    throw new InvalidOperationException(
212                        "Internal: dotted must be IDs or Funs.");
213                }
214            }
215            return curExpr;
216        }
217
218		// AnalyzeAssignExpr handles IDs, indexing, and member sets.  IDs are either
219		// lexical or dynamic exprs on the module scope.  Everything
220		// else is dynamic.
221		//
222        public static Expression AnalyzeAssignExpr(SymplAssignExpr expr,
223                                                    AnalysisScope scope) {
224            if (expr.Location is SymplIdExpr) {
225                var idExpr = (SymplIdExpr)(expr.Location);
226                var lhs = AnalyzeExpr(expr.Location, scope);
227                var val = AnalyzeExpr(expr.Value, scope);
228                var param = FindIdDef(idExpr.IdToken.Name, scope);
229                if (param != null) {
230                    // Assign returns value stored.
231                    return Expression.Assign(
232                               lhs,
233                               Expression.Convert(val, param.Type));
234                } else {
235                    var tmp = Expression.Parameter(typeof(object),
236                                                   "assignTmpForRes");
237                    // Ensure stored value is returned.  Had some erroneous
238                    // MOs come through here and left the code for example.
239                    return Expression.Block(
240                       new[] { tmp },
241                       Expression.Assign(
242                           tmp,
243                           Expression.Convert(val, typeof(object))),
244                       Expression.Dynamic(
245                           scope.GetRuntime()
246                                .GetSetMemberBinder(idExpr.IdToken.Name),
247                           typeof(object),
248                           scope.GetModuleExpr(),
249                           tmp),
250                       tmp);
251                }
252            } else if (expr.Location is SymplEltExpr) {
253                var eltExpr = (SymplEltExpr)(expr.Location);
254                var args = new List<Expression>();
255                args.Add(AnalyzeExpr(eltExpr.ObjectExpr, scope));
256                args.AddRange(eltExpr.Indexes.Select(e => AnalyzeExpr(e, scope)));
257                args.Add(AnalyzeExpr(expr.Value, scope));
258                // Trusting MO convention to return stored values.
259                return Expression.Dynamic(
260                           scope.GetRuntime().GetSetIndexBinder(
261                                   new CallInfo(eltExpr.Indexes.Length)), 
262                           typeof(object),
263                           args);
264            } else if (expr.Location is SymplDottedExpr) {
265                // For now, one dot only.  Later, pick oflast dotted member
266                // access (like AnalyzeFunctionCall), and use a temp and block.
267                var dottedExpr = (SymplDottedExpr)(expr.Location);
268                if (dottedExpr.Exprs.Length > 1) {
269                    throw new InvalidOperationException(
270                        "Don't support assigning with more than simple dotted " +
271                        "expression, o.foo.");
272                }
273                if (!(dottedExpr.Exprs[0] is SymplIdExpr)) {
274                    throw new InvalidOperationException(
275                        "Only support unindexed field or property when assigning " +
276                        "dotted expression location.");
277                }
278                var id = (SymplIdExpr)(dottedExpr.Exprs[0]);
279                // Trusting MOs convention to return stored values.
280                return Expression.Dynamic(
281                           scope.GetRuntime().GetSetMemberBinder(id.IdToken.Name),
282                           typeof(object),
283                           AnalyzeExpr(dottedExpr.ObjectExpr, scope),
284                           AnalyzeExpr(expr.Value, scope)
285                );
286            }
287
288            throw new InvalidOperationException("Invalid left hand side type.");
289        }
290
291		// Return an Expression for referencing the ID.  If we find the name in the
292		// scope chain, then we just return the stored ParamExpr.  Otherwise, the
293		// reference is a dynamic member lookup on the root scope, a module object.
294		//
295        public static Expression AnalyzeIdExpr(SymplIdExpr expr,
296                                                AnalysisScope scope) {
297            if (expr.IdToken.IsKeywordToken) {
298                if (expr.IdToken == KeywordToken.Nil)
299                    return Expression.Constant(null, typeof(object));
300                else if (expr.IdToken == KeywordToken.True)
301                    return Expression.Constant(true);
302                else if (expr.IdToken == KeywordToken.False)
303                    return Expression.Constant(false);
304                else
305                    throw new InvalidOperationException(
306                        "Internal: unrecognized keyword literal constant.");
307            } else {
308                var param = FindIdDef(expr.IdToken.Name, scope);
309                if (param != null) {
310                    return param;
311                } else {
312                    return Expression.Dynamic(
313                       scope.GetRuntime().GetGetMemberBinder(expr.IdToken.Name),
314                       typeof(object),
315                       scope.GetModuleExpr()
316                    );
317                }
318            }
319        }
320
321        // _findIdDef returns the ParameterExpr for the name by searching the scopes,
322        // or it returns None.
323        //
324        private static Expression FindIdDef(string name, AnalysisScope scope) {
325            var curscope = scope;
326            name = name.ToLower();
327            ParameterExpression res;
328            while (curscope != null && !curscope.IsModule) {
329                if (curscope.Names.TryGetValue(name, out res)) {
330                    return res;
331                } else {
332                    curscope = curscope.Parent;
333                }
334            }
335
336            if (scope == null) {
337                throw new InvalidOperationException(
338                    "Got bad AnalysisScope chain with no module at end.");
339            }
340
341            return null;
342        }
343
344		// AnalyzeLetStar returns a Block with vars, each initialized in the order
345		// they appear.  Each var's init expr can refer to vars initialized before it.
346		// The Block's body is the Let*'s body.
347		//
348        public static Expression AnalyzeLetStarExpr(SymplLetStarExpr expr,
349                                                      AnalysisScope scope) {
350            var letscope = new AnalysisScope(scope, "let*");
351            // Analyze bindings.
352            List<Expression> inits = new List<Expression>();
353            List<ParameterExpression> varsInOrder = new List<ParameterExpression>();
354            foreach (var b in expr.Bindings) {
355                // Need richer logic for mvbind
356                var v = Expression.Parameter(typeof(object), b.Variable.Name);
357                varsInOrder.Add(v);
358                inits.Add(
359                    Expression.Assign(
360                        v,
361                        Expression.Convert(AnalyzeExpr(b.Value, letscope), v.Type))
362                );
363                // Add var to scope after analyzing init value so that init value
364                // references to the same ID do not bind to his uninitialized var.
365                letscope.Names[b.Variable.Name.ToLower()] = v;
366            }
367            List<Expression> body = new List<Expression>();
368            foreach (var e in expr.Body) {
369                body.Add(AnalyzeExpr(e, letscope));
370            }
371            // Order of vars to BlockExpr don't matter semantically, but may as well
372            // keep them in the order the programmer specified in case they look at the
373            // Expr Trees in the debugger or for meta-programming.
374            inits.AddRange(body);
375            return Expression.Block(typeof(object), varsInOrder.ToArray(), inits);
376        }
377
378        // AnalyzeBlockExpr returns a Block with the body exprs.
379        //
380        public static Expression AnalyzeBlockExpr(SymplBlockExpr expr,
381                                                    AnalysisScope scope) {
382            List<Expression> body = new List<Expression>();
383            foreach (var e in expr.Body) {
384                body.Add(AnalyzeExpr(e, scope));
385            }
386            return Expression.Block(typeof(object), body);
387        }
388
389        // AnalyzeQuoteExpr converts a list, literal, or id expr to a runtime quoted
390        // literal and returns the Constant expression for it.
391        //
392        public static Expression AnalyzeQuoteExpr(SymplQuoteExpr expr,
393                                                    AnalysisScope scope) {
394            return Expression.Constant(MakeQuoteConstant(
395                                           expr.Expr, scope.GetRuntime()));
396        }
397
398        private static object MakeQuoteConstant(object expr, Sympl symplRuntime) {
399            if (expr is SymplListExpr) {
400                SymplListExpr listexpr = (SymplListExpr)expr;
401                int len = listexpr.Elements.Length;
402                var exprs = new object[len];
403                for (int i = 0; i < len; i++) {
404                    exprs[i] = MakeQuoteConstant(listexpr.Elements[i], symplRuntime);
405                }
406                return Cons._List(exprs);
407            } else if (expr is IdOrKeywordToken) {
408                return symplRuntime.MakeSymbol(((IdOrKeywordToken)expr).Name);
409            } else if (expr is LiteralToken) {
410                return ((LiteralToken)expr).Value;
411            } else {
412                throw new InvalidOperationException(
413                    "Internal: quoted list has -- " + expr.ToString());
414            }
415        }
416
417        public static Expression AnalyzeEqExpr (SymplEqExpr expr,
418                                                AnalysisScope scope) {
419            var mi = typeof(RuntimeHelpers).GetMethod("SymplEq");
420            return Expression.Call(mi, Expression.Convert(
421                                           AnalyzeExpr(expr.Left, scope),
422                                           typeof(object)),
423                                   Expression.Convert(
424                                       AnalyzeExpr(expr.Right, scope),
425                                       typeof(object)));
426        }
427            
428        public static Expression AnalyzeConsExpr (SymplConsExpr expr,
429                                                  AnalysisScope scope) {
430            var mi = typeof(RuntimeHelpers).GetMethod("MakeCons");
431            return Expression.Call(mi, Expression.Convert(
432                                           AnalyzeExpr(expr.Left, scope),
433                                           typeof(object)),
434                                   Expression.Convert(
435                                       AnalyzeExpr(expr.Right, scope),
436                                       typeof(object)));
437        }
438
439        public static Expression AnalyzeListCallExpr (SymplListCallExpr expr,
440                                                      AnalysisScope scope) {
441            var mi = typeof(Cons).GetMethod("_List");
442            int len = expr.Elements.Length;
443            var args = new Expression[len];
444            for (int i = 0; i < len; i++) {
445                args[i] = Expression.Convert(AnalyzeExpr(expr.Elements[i], scope),
446                                             typeof(object));
447            }
448            return Expression.Call(mi, Expression
449                                           .NewArrayInit(typeof(object), args));
450        }
451
452        public static Expression AnalyzeIfExpr (SymplIfExpr expr,
453                                                AnalysisScope scope) {
454            Expression alt = null;
455            if (expr.Alternative != null) {
456                alt = AnalyzeExpr(expr.Alternative, scope);
457            } else {
458                alt = Expression.Constant(false);
459            }
460            return Expression.Condition(
461                       WrapBooleanTest(AnalyzeExpr(expr.Test, scope)),
462                       Expression.Convert(AnalyzeExpr(expr.Consequent, scope),
463                                             typeof(object)),
464                       Expression.Convert(alt, typeof(object)));
465        }
466        
467        private static Expression WrapBooleanTest (Expression expr) {
468            var tmp = Expression.Parameter(typeof(object), "testtmp");
469            return Expression.Block(
470                new ParameterExpression[] { tmp },
471                new Expression[] 
472                        {Expression.Assign(tmp, Expression
473                                                  .Convert(expr, typeof(object))),
474                         Expression.Condition(
475                             Expression.TypeIs(tmp, typeof(bool)), 
476                             Expression.Convert(tmp, typeof(bool)),
477                             Expression.NotEqual(
478                                tmp, 
479                                Expression.Constant(null, typeof(object))))});
480        }
481
482        public static Expression AnalyzeLoopExpr (SymplLoopExpr expr,
483                                                  AnalysisScope scope) {
484            var loopscope = new AnalysisScope(scope, "loop ");
485            loopscope.IsLoop = true; // needed for break and continue
486            loopscope.LoopBreak = Expression.Label(typeof(object), "loop break");
487            int len = expr.Body.Length;
488            var body = new Expression[len];
489            for (int i = 0; i < len; i++) {
490                body[i] = AnalyzeExpr(expr.Body[i], loopscope);
491            }
492            return Expression.Loop(Expression.Block(typeof(object), body), 
493                                   loopscope.LoopBreak);
494        }
495
496        public static Expression AnalyzeBreakExpr (SymplBreakExpr expr,
497                                        AnalysisScope scope) {
498            var loopscope = _findFirstLoop(scope);
499            if (loopscope == null)
500                throw new InvalidOperationException(
501                               "Call to Break not inside loop.");
502            Expression value;
503            if (expr.Value == null)
504                value = Expression.Constant(null, typeof(object));
505            else
506                // Ok if value jumps to break label.
507                value = AnalyzeExpr(expr.Value, loopscope);
508            // Need final type=object arg because the Goto is in a value returning
509            // position, and the Break factory doesn't set the GotoExpr.Type property
510            // to the type of the LoopBreak label target's type.  For example, removing
511            // this would cause the Convert to object for an If branch to throw because
512            // the Goto is void without this last arg.
513            return Expression.Break(loopscope.LoopBreak, value, typeof(object));
514        }
515
516        // _findFirstLoop returns the first loop AnalysisScope or None.
517        //
518        private static AnalysisScope _findFirstLoop (AnalysisScope scope) {
519            var curscope = scope;
520            while (curscope != null) {
521                if (curscope.IsLoop)
522                    return curscope;
523                else
524                    curscope = curscope.Parent;
525            }
526            return null;
527        }
528
529    
530        public static Expression AnalyzeNewExpr(SymplNewExpr expr,
531                                                AnalysisScope scope) {
532
533            List<Expression> args = new List<Expression>();
534            args.Add(AnalyzeExpr(expr.Type, scope));
535            args.AddRange(expr.Arguments.Select(a => AnalyzeExpr(a, scope)));
536
537            return Expression.Dynamic(
538                scope.GetRuntime().GetCreateInstanceBinder(
539                                     new CallInfo(expr.Arguments.Length)),
540                typeof(object),
541                args
542            );
543        }
544
545        public static Expression AnalyzeBinaryExpr(SymplBinaryExpr expr,
546                                                   AnalysisScope scope) {
547
548            // The language has the following special logic to handle And and Or
549            // x And y == if x then y
550            // x Or y == if x then x else (if y then y)
551            if (expr.Operation == ExpressionType.And) {
552                return AnalyzeIfExpr(
553                    new SymplIfExpr(
554                        expr.Left, expr.Right, null),
555                    scope); 
556            } else if (expr.Operation == ExpressionType.Or) {
557                // Use (LetStar (tmp expr) (if tmp tmp)) to represent (if expr expr)
558                // to remore duplicate evaluation.
559                // So x Or y is translated into
560                // (Let* (tmp1 x) 
561                //    (If tmp1 tmp1  
562                //       (Let* (tmp2 y) (If tmp2 tmp2))))
563                //           
564
565                IdOrKeywordToken tmp2 = new IdOrKeywordToken(
566                    // Real implementation needs to ensure unique ID in scope chain.
567                    "__tmpLetVariable2");
568                var tmpExpr2 = new SymplIdExpr(tmp2);
569                var binding2 = new LetBinding(tmp2, expr.Right); ;
570                var ifExpr2 = new SymplIfExpr(
571                    tmpExpr2, tmpExpr2, null);
572                var letExpr2 = new SymplLetStarExpr(
573                        new[] { binding2 },
574                        new[] { ifExpr2 });
575
576                IdOrKeywordToken tmp1 = new IdOrKeywordToken(
577                    // Real implementation needs to ensure unique ID in scope chain.
578                    "__tmpLetVariable1");
579                var tmpExpr1 = new SymplIdExpr(tmp1);
580                LetBinding binding1 = new LetBinding(tmp1, expr.Left); ;
581                SymplExpr ifExpr1 = new SymplIfExpr(
582                    tmpExpr1, tmpExpr1, letExpr2);
583                return AnalyzeLetStarExpr(
584                    new SymplLetStarExpr(
585                        new[] { binding1 },
586                        new[] { ifExpr1 }
587                    ),
588                    scope
589                );
590            }
591
592            return Expression.Dynamic(
593                scope.GetRuntime().GetBinaryOperationBinder(expr.Operation),
594                typeof(object),
595                AnalyzeExpr(expr.Left, scope),
596                AnalyzeExpr(expr.Right, scope)
597            );
598        }
599
600        public static Expression AnalyzeUnaryExpr(SymplUnaryExpr expr,
601                                                  AnalysisScope scope) {
602            
603            if (expr.Operation == ExpressionType.Not) {
604                return Expression.Not(WrapBooleanTest(AnalyzeExpr(expr.Operand,
605                                                                   scope)));
606            }
607            // Example purposes only, we should never get here since we only have Not.
608            return Expression.Dynamic(
609                scope.GetRuntime().GetUnaryOperationBinder(expr.Operation),
610                typeof(object),
611                AnalyzeExpr(expr.Operand, scope)
612            );
613        }
614
615        // AnalyzeEltExpr returns and Expression for accessing an element of an
616        // aggregate structure.  This also works for .NET objs with indexer Item
617        // properties.  We handle analyzing Elt for assignment in AnalyzeAssignExpr.
618        //
619        public static Expression AnalyzeEltExpr(SymplEltExpr expr,
620                                                AnalysisScope scope) {
621
622            List<Expression> args = new List<Expression>();
623            args.Add(AnalyzeExpr(expr.ObjectExpr, scope));
624            args.AddRange(expr.Indexes.Select(e => AnalyzeExpr(e, scope)));
625
626            return Expression.Dynamic(
627                scope.GetRuntime().GetGetIndexBinder(
628                                      new CallInfo(expr.Indexes.Length)),
629                typeof(object),
630                args
631            );
632        }
633
634    } // ETGen
635
636
637    // AnalysisScope holds identifier information so that we can do name binding
638    // during analysis.  It manages a map from names to ParameterExprs so ET
639    // definition locations and reference locations can alias the same variable.
640    //
641    // These chain from inner most BlockExprs, through LambdaExprs, to the root
642    // which models a file or top-level expression.  The root has non-None
643    // ModuleExpr and RuntimeExpr, which are ParameterExprs.
644    //
645    internal class AnalysisScope {
646
647        private AnalysisScope _parent;
648        private string _name;
649        // Need runtime for interning Symbol constants at code gen time.
650        private Sympl _runtime;
651        private ParameterExpression _runtimeParam;
652        private ParameterExpression _moduleParam;
653        // Need IsLambda when support return to find tightest closing fun.
654        private bool _isLambda = false;
655        private bool _isLoop = false;
656        private LabelTarget _loopBreak = null;
657        //private LabelTarget _continueBreak = null;
658        private Dictionary<string, ParameterExpression> _names;
659
660        public AnalysisScope(AnalysisScope parent, string name)
661            : this(parent, name, null, null, null) { }
662
663        public AnalysisScope(AnalysisScope parent,
664							  string name,
665							  Sympl runtime,
666							  ParameterExpression runtimeParam,
667							  ParameterExpression moduleParam) {
668            _parent = parent;
669            _name = name;
670            _runtime = runtime;
671            _runtimeParam = runtimeParam;
672            _moduleParam = moduleParam;
673
674            _names = new Dictionary<string, ParameterExpression>();
675            _isLambda = false;
676        }
677
678        public AnalysisScope Parent { get { return _parent; } }
679
680        public ParameterExpression ModuleExpr { get { return _moduleParam; } }
681
682        public ParameterExpression RuntimeExpr { get { return _runtimeParam; } }
683
684        public Sympl Runtime { get { return _runtime; } }
685
686        public bool IsModule { get { return _moduleParam != null; } }
687
688        public bool IsLambda { 
689            get { return _isLambda; } 
690            set { _isLambda = value; }
691        }
692
693        public bool IsLoop { 
694            get { return _isLoop; } 
695            set { _isLoop = value; }
696        }
697        
698        public LabelTarget LoopBreak { 
699            get { return _loopBreak; } 
700            set { _loopBreak = value; }
701        }
702        
703        public LabelTarget LoopContinue { 
704            get { return _loopBreak; } 
705            set { _loopBreak = value; }
706        }
707
708        public Dictionary<string, ParameterExpression> Names { 
709            get { return _names; } 
710            set { _names = value; }
711        }
712
713        public ParameterExpression GetModuleExpr() {
714            var curScope = this;
715            while (!curScope.IsModule) {
716                curScope = curScope.Parent;
717            }
718            return curScope.ModuleExpr;
719        }
720
721        public Sympl GetRuntime() {
722            var curScope = this;
723            while (curScope.Runtime == null) {
724                curScope = curScope.Parent;
725            }
726            return curScope.Runtime;
727        }
728    } //AnalysisScope
729
730} // Namespace