public virtual LNode GenerateSwitch(IPGTerminalSet[] branchSets, MSet <int> casesToInclude, LNode[] branchCode, LNode defaultBranch, LNode laVar)
        {
            Debug.Assert(branchSets.Length == branchCode.Length);

            WList <LNode> stmts = new WList <LNode>();

            for (int i = 0; i < branchSets.Length; i++)
            {
                if (casesToInclude.Contains(i))
                {
                    foreach (LNode value in GetCases(branchSets[i]))
                    {
                        stmts.Add(F.Call(S.Case, value));
                        if (stmts.Count > 65535)                         // sanity check
                        {
                            throw new InvalidOperationException("switch is too large to generate");
                        }
                    }
                    AddSwitchHandler(branchCode[i], stmts);
                }
            }

            if (!defaultBranch.IsIdNamed(S.Missing))
            {
                stmts.Add(F.Call(S.Label, F.Id(S.Default)));
                AddSwitchHandler(defaultBranch, stmts);
            }

            return(F.Call(S.Switch, (LNode)laVar, F.Braces(stmts.ToVList())));
        }
Exemple #2
0
        public override LNode GenerateMatchExpr(IPGTerminalSet set_, bool savingResult, bool recognizerMode)
        {
            var set = (PGIntSet)set_;

            LNode call;
            var   type = set.ChooseMatchType(2, 4);

            if (type != PGIntSet.Match.Set)
            {
                var args = new WList <LNode>();
                if (type == PGIntSet.Match.Ranges)
                {
                    // Use MatchRange or MatchExceptRange
                    foreach (var r in set)
                    {
                        if (!set.IsInverted || r.Lo != EOF_int || r.Hi != EOF_int)
                        {
                            args.Add((LNode)set.MakeLiteral(r.Lo));

                            args.Add((LNode)set.MakeLiteral(r.Hi));
                        }
                    }
                    var target = recognizerMode
                                                ? (set.IsInverted ? _TryMatchExceptRange : _TryMatchRange)
                                                : (set.IsInverted ? _MatchExceptRange : _MatchRange);
                    call = ApiCall(target, args);
                }
                else
                {
                    // Use Match or MatchExcept
                    foreach (var r in set)
                    {
                        for (int c = r.Lo; c <= r.Hi; c++)
                        {
                            if (!set.IsInverted || c != EOF_int)
                            {
                                args.Add((LNode)set.MakeLiteral(c));
                            }
                        }
                    }
                    var target = recognizerMode
                                                ? (set.IsInverted ? _TryMatchExcept : _TryMatch)
                                                : (set.IsInverted ? _MatchExcept : _Match);
                    call = ApiCall(target, args.ToVList());
                }
            }
            else
            {
                var setName = GenerateSetDecl(set);
                if (set.IsInverted)
                {
                    call = ApiCall(recognizerMode ? _TryMatchExcept : _MatchExcept, F.Id(setName));
                }
                else
                {
                    call = ApiCall(recognizerMode ? _TryMatch : _Match, F.Id(setName));
                }
            }
            return(call);
        }
Exemple #3
0
            LNode GetOutputAsLNode()
            {
                WList <LNode> finalOutput = _handler.ToWList();

                for (int end = _output.Count - 1; end >= 0; end--)
                {
                    var   tmp_10 = _output[end];
                    Mode  mode   = tmp_10.Item1;
                    LNode code   = tmp_10.Item2;
                    if (mode == Mode.Condition)
                    {
                        // Merge adjacent conditions into the same if-statement
                        int start = end;
                        for (; start > 0 && _output[start - 1].A == mode; start--)
                        {
                        }
                        LNode cond = _output[start].B;
                        for (int i = start + 1; i <= end; i++)
                        {
                            cond = LNode.Call(CodeSymbols.And, LNode.List(cond, _output[i].B)).SetStyle(NodeStyle.Operator);
                        }
                        end = start;

                        finalOutput = new WList <LNode> {
                            LNode.Call(CodeSymbols.If, LNode.List(cond, finalOutput.ToVList().AsLNode(S.Braces)))
                        };
                    }
                    else
                    {
                        finalOutput.Insert(0, code);
                    }
                }
                return(finalOutput.ToVList().AsLNode(S.Braces));
            }
Exemple #4
0
        public static LNode match(LNode node, IMacroContext context)
        {
            {
                LNode         input;
                VList <LNode> contents;
                if (node.Args.Count == 2 && (input = node.Args[0]) != null && node.Args[1].Calls(CodeSymbols.Braces))
                {
                    contents = node.Args[1].Args;
                    var outputs = new WList <LNode>();
                    input = MaybeAddTempVarDecl(context, input, outputs);

                    int next_i = 0;
                    for (int case_i = 0; case_i < contents.Count; case_i = next_i)
                    {
                        var @case = contents[case_i];
                        if (!IsCaseLabel(@case))
                        {
                            return(Reject(context, contents[0], "In 'match': expected 'case' statement"));
                        }
                        for (next_i = case_i + 1; next_i < contents.Count; next_i++)
                        {
                            var stmt = contents[next_i];
                            if (IsCaseLabel(stmt))
                            {
                                break;
                            }
                            if (stmt.Calls(S.Break, 0))
                            {
                                next_i++;
                                break;
                            }
                        }
                        var handler = new VList <LNode>(contents.Slice(case_i + 1, next_i - (case_i + 1)));

                        if (@case.Calls(S.Case) && @case.Args.Count > 0)
                        {
                            var codeGen = new CodeGeneratorForMatchCase(context, input, handler);
                            foreach (var pattern in @case.Args)
                            {
                                outputs.Add(codeGen.GenCodeForPattern(pattern));
                            }
                        }
                        else                                    // default:
                        // Note: the extra {braces} around the handler are rarely
                        // needed. They are added just in case the handler declares a
                        // variable and a different handler declares another variable
                        // by the same name, which is illegal unless we add braces.
                        {
                            outputs.Add(LNode.Call(CodeSymbols.Braces, LNode.List(handler)).SetStyle(NodeStyle.Statement));
                            if (next_i < contents.Count)
                            {
                                context.Sink.Error(contents[next_i], "The default branch must be the final branch in a 'match' statement.");
                            }
                        }
                    }
                    return(LNode.Call(CodeSymbols.DoWhile, LNode.List(outputs.ToVList().AsLNode(S.Braces), LNode.Literal(false))));
                }
            }
            return(null);
        }
Exemple #5
0
        public static LNode UnpackTuple(LNode node, IMacroContext context)
        {
            var a = node.Args;

            if (a.Count == 2 && a[0].CallsMin(S.Tuple, 1))
            {
                var output = new WList <LNode>();
                var tuple  = a[0].Args;
                var rhs    = a[1];

                // Avoid evaluating rhs more than once, if it doesn't look like a simple variable
                rhs = MaybeAddTempVarDecl(context, rhs, output);

                for (int i = 0; i < tuple.Count; i++)
                {
                    var itemi = F.Dot(rhs, F.Id(GSymbol.Get("Item" + (i + 1))));
                    if (tuple[i].Calls(S.Var, 2))
                    {
                        output.Add(F.Var(tuple[i].Args[0], tuple[i].Args[1], itemi));
                    }
                    else
                    {
                        output.Add(F.Call(S.Assign, tuple[i], itemi));
                    }
                }
                return(F.Call(S.Splice, output.ToVList()));
            }
            return(null);
        }
			public void Generate(Rule rule)
			{
				CGH.BeginRule(rule);
				_currentRule = rule;
				_target = new WList<LNode>();
				_laVarsNeeded = 0;
				_separatedMatchCounter = _stopLabelCounter = 0;
				_recognizerMode = rule.IsRecognizer;
				_labelsInUse.Clear();

				Visit(rule.Pred);

				if (_laVarsNeeded != 0) {
					LNode laVars = F.Call(S.Var, CGH.LAType());
					for (int i = 0; _laVarsNeeded != 0; i++, _laVarsNeeded >>= 1)
						if ((_laVarsNeeded & 1) != 0)
							laVars = laVars.PlusArg(F.Id("la" + i.ToString()));
					_target.Insert(0, laVars);
				}

				LNode method;
				if (rule.TryWrapperName != null) {
					Debug.Assert(rule.IsRecognizer);
					method = F.OnNewLine(CGH.CreateTryWrapperForRecognizer(rule));
					_classBody.SpliceAdd(method, S.Splice);
				}

				method = CGH.CreateRuleMethod(rule, _target.ToVList());
				if (!rule.IsRecognizer)
					method = F.OnNewLine(method);
				_classBody.SpliceAdd(method, S.Splice);
			}
Exemple #7
0
 [LexicalMacro("match (var) { case ...: ... }; // In LES, use a => b instead of case a: b", "Attempts to match and deconstruct an object against a \"pattern\", such as a tuple or an algebraic data type. Example:\n" + "match (obj) {  \n" + "   case is Shape(ShapeType.Circle, $size, Location: $p is Point<int>($x, $y)): \n" + "      Circle(size, x, y); \n" + "}\n\n" + "This is translated to the following C# code: \n" + "do { \n" + "   Point<int> p; \n" + "   Shape tmp1; \n" + "   if (obj is Shape) { \n" + "      var tmp1 = (Shape)obj; \n" + "      if (tmp1.Item1 == ShapeType.Circle) { \n" + "         var size = tmp1.Item2; \n" + "         var tmp2 = tmp1.Location; \n" + "         if (tmp2 is Point<int>) { \n" + "            var p = (Point<int>)tmp2; \n" + "            var x = p.Item1; \n" + "            var y = p.Item2; \n" + "            Circle(size, x, y); \n" + "            break; \n" + "         } \n" + "      }\n" + "   }\n" + "} while(false); \n" + "`break` is not expected at the end of each handler (`case` code block), but it can " + "be used to exit early from a `case`. You can associate multiple patterns with the same " + "handler using `case pattern1, pattern2:` in EC#, but please note that (due to a " + "limitation of plain C#) this causes code duplication since the handler will be repeated " + "for each pattern.")] public static LNode match(LNode node, IMacroContext context)
 {
     {
         LNode         input;
         VList <LNode> contents;
         if (node.Args.Count == 2 && (input = node.Args[0]) != null && node.Args[1].Calls(CodeSymbols.Braces))
         {
             contents = node.Args[1].Args;
             var outputs = new WList <LNode>();
             input = MaybeAddTempVarDecl(input, outputs);
             int next_i = 0;
             for (int case_i = 0; case_i < contents.Count; case_i = next_i)
             {
                 var @case = contents[case_i];
                 if (!IsCaseLabel(@case))
                 {
                     return(Reject(context, contents[0], "In 'match': expected 'case' statement"));
                 }
                 for (next_i = case_i + 1; next_i < contents.Count; next_i++)
                 {
                     var stmt = contents[next_i];
                     if (IsCaseLabel(stmt))
                     {
                         break;
                     }
                     if (stmt.Calls(S.Break, 0))
                     {
                         next_i++;
                         break;
                     }
                 }
                 var handler = new VList <LNode>(contents.Slice(case_i + 1, next_i - (case_i + 1)));
                 if (@case.Calls(S.Case) && @case.Args.Count > 0)
                 {
                     var codeGen = new CodeGeneratorForMatchCase(context, input, handler);
                     foreach (var pattern in @case.Args)
                     {
                         outputs.Add(codeGen.GenCodeForPattern(pattern));
                     }
                 }
                 else
                 {
                     outputs.Add(LNode.Call(CodeSymbols.Braces, LNode.List(handler)).SetStyle(NodeStyle.Statement));
                     if (next_i < contents.Count)
                     {
                         context.Write(Severity.Error, contents[next_i], "The default branch must be the final branch in a 'match' statement.");
                     }
                 }
             }
             return(LNode.Call(CodeSymbols.DoWhile, LNode.List(outputs.ToVList().AsLNode(S.Braces), LNode.Literal(false))));
         }
     }
     return(null);
 }
Exemple #8
0
 public static LNode match(LNode node, IMacroContext context)
 {
     {
         LNode         input;
         VList <LNode> contents;
         if (node.Args.Count == 2 && (input = node.Args[0]) != null && node.Args[1].Calls(CodeSymbols.Braces))
         {
             contents = node.Args[1].Args;
             var outputs = new WList <LNode>();
             input = MaybeAddTempVarDecl(context, input, outputs);
             int next_i = 0;
             for (int case_i = 0; case_i < contents.Count; case_i = next_i)
             {
                 var @case = contents[case_i];
                 if (!IsCaseLabel(@case))
                 {
                     return(Reject(context, contents[0], "In 'match': expected 'case' statement"));
                 }
                 for (next_i = case_i + 1; next_i < contents.Count; next_i++)
                 {
                     var stmt = contents[next_i];
                     if (IsCaseLabel(stmt))
                     {
                         break;
                     }
                     if (stmt.Calls(S.Break, 0))
                     {
                         next_i++;
                         break;
                     }
                 }
                 var handler = new VList <LNode>(contents.Slice(case_i + 1, next_i - (case_i + 1)));
                 if (@case.Calls(S.Case) && @case.Args.Count > 0)
                 {
                     var codeGen = new CodeGeneratorForMatchCase(context, input, handler);
                     foreach (var pattern in @case.Args)
                     {
                         outputs.Add(codeGen.GenCodeForPattern(pattern));
                     }
                 }
                 else
                 {
                     outputs.Add(LNode.Call(CodeSymbols.Braces, LNode.List(handler)).SetStyle(NodeStyle.Statement));
                     if (next_i < contents.Count)
                     {
                         context.Write(Severity.Error, contents[next_i], "The default branch must be the final branch in a 'match' statement.");
                     }
                 }
             }
             return(LNode.Call(CodeSymbols.DoWhile, LNode.List(outputs.ToVList().AsLNode(S.Braces), LNode.Literal(false))));
         }
     }
     return(null);
 }
Exemple #9
0
 // Restructures a VList if it has degraded severely. Time: O(Count)
 void AutoOptimize <T>(ref VList <T> v)
 {
     // Check if the chain length substantially exceeds Sqrt(v.Count)
     if ((v.BlockChainLength - 10) * (v.BlockChainLength - 10) > v.Count)
     {
         WList <T> w   = v.ToWList();
         int       end = w.Count - 1;
         w[end] = w[end];
         v      = w.ToVList();
     }
 }
Exemple #10
0
        public LNode Vars(LNode type, params LNode[] namesWithValues)
        {
            type = type ?? Missing;
            var list = new WList <LNode>()
            {
                type
            };

            list.AddRange(namesWithValues);
            return(Call(S.Var, list.ToVList()));
        }
Exemple #11
0
                      Mode = MacroMode.ProcessChildrenBefore)]   // post-normal-macro-expansion
        public static LNode with(LNode fn, IMacroContext context)
        {
            LNode braces;

            if (fn.ArgCount != 2 || !(braces = fn.Args[1]).Calls(S.Braces))
            {
                return(null);
            }

            LNode         tmp   = F.Id(NextTempName(context));
            WList <LNode> stmts = braces.Args.ToWList();

            stmts = stmts.SmartSelect(stmt =>
                                      stmt.ReplaceRecursive(expr => {
                if (expr.Calls(S.Dot, 1))
                {
                    return(expr.WithArgs(new VList <LNode>(tmp, expr.Args.Last)));
                }
                else if (expr.IsIdNamed("#"))
                {
                    return(tmp);
                }
                return(null);
            }));

            stmts.Insert(0, F.Var(null, tmp.Name, fn.Args[0]));
            if (IsExpressionContext(context))
            {
                stmts.Add(tmp);
                return(F.Call("#runSequence", stmts.ToVList()));
            }
            else
            {
                return(F.Braces(stmts.ToVList()));
            }
        }
Exemple #12
0
            public void Generate(Rule rule)
            {
                CGH.BeginRule(rule);
                _currentRule           = rule;
                _target                = new WList <LNode>();
                _laVarsNeeded          = 0;
                _separatedMatchCounter = _stopLabelCounter = 0;
                _recognizerMode        = rule.IsRecognizer;
                _labelsInUse.Clear();

                Visit(rule.Pred);

                if (_laVarsNeeded != 0)
                {
                    LNode laVars = F.Call(S.Var, CGH.LAType());
                    for (int i = 0; _laVarsNeeded != 0; i++, _laVarsNeeded >>= 1)
                    {
                        if ((_laVarsNeeded & 1) != 0)
                        {
                            laVars = laVars.PlusArg(F.Id("la" + i.ToString()));
                        }
                    }
                    _target.Insert(0, laVars);
                }

                LNode method;

                if (rule.TryWrapperName != null)
                {
                    Debug.Assert(rule.IsRecognizer);
                    method = F.OnNewLine(CGH.CreateTryWrapperForRecognizer(rule));
                    _classBody.SpliceAdd(method, S.Splice);
                }

                method = CGH.CreateRuleMethod(rule, _target.ToVList());
                if (!rule.IsRecognizer)
                {
                    method = F.OnNewLine(method);
                }
                _classBody.SpliceAdd(method, S.Splice);
            }
Exemple #13
0
		[LexicalMacro("match (var) { case ...: ... }; // In LES, use a => b instead of case a: b", "Attempts to match and deconstruct an object against a \"pattern\", such as a tuple or an algebraic data type. Example:\n" + "match (obj) {  \n" + "   case is Shape(ShapeType.Circle, $size, Location: $p is Point<int>($x, $y)): \n" + "      Circle(size, x, y); \n" + "}\n\n" + "This is translated to the following C# code: \n" + "do { \n" + "   Point<int> p; \n" + "   Shape tmp1; \n" + "   if (obj is Shape) { \n" + "      var tmp1 = (Shape)obj; \n" + "      if (tmp1.Item1 == ShapeType.Circle) { \n" + "         var size = tmp1.Item2; \n" + "         var tmp2 = tmp1.Location; \n" + "         if (tmp2 is Point<int>) { \n" + "            var p = (Point<int>)tmp2; \n" + "            var x = p.Item1; \n" + "            var y = p.Item2; \n" + "            Circle(size, x, y); \n" + "            break; \n" + "         } \n" + "      }\n" + "   }\n" + "} while(false); \n" + "`break` is not expected at the end of each handler (`case` code block), but it can " + "be used to exit early from a `case`. You can associate multiple patterns with the same " + "handler using `case pattern1, pattern2:` in EC#, but please note that (due to a " + "limitation of plain C#) this causes code duplication since the handler will be repeated " + "for each pattern.")] public static LNode match(LNode node, IMacroContext context)
		{
			{
				LNode input;
				VList<LNode> contents;
				if (node.Args.Count == 2 && (input = node.Args[0]) != null && node.Args[1].Calls(CodeSymbols.Braces)) {
					contents = node.Args[1].Args;
					var outputs = new WList<LNode>();
					input = MaybeAddTempVarDecl(input, outputs);
					int next_i = 0;
					for (int case_i = 0; case_i < contents.Count; case_i = next_i) {
						var @case = contents[case_i];
						if (!IsCaseLabel(@case))
							return Reject(context, contents[0], "In 'match': expected 'case' statement");
						for (next_i = case_i + 1; next_i < contents.Count; next_i++) {
							var stmt = contents[next_i];
							if (IsCaseLabel(stmt))
								break;
							if (stmt.Calls(S.Break, 0)) {
								next_i++;
								break;
							}
						}
						var handler = new VList<LNode>(contents.Slice(case_i + 1, next_i - (case_i + 1)));
						if (@case.Calls(S.Case) && @case.Args.Count > 0) {
							var codeGen = new CodeGeneratorForMatchCase(context, input, handler);
							foreach (var pattern in @case.Args)
								outputs.Add(codeGen.GenCodeForPattern(pattern));
						} else {
							outputs.Add(LNode.Call(CodeSymbols.Braces, LNode.List(handler)).SetStyle(NodeStyle.Statement));
							if (next_i < contents.Count)
								context.Write(Severity.Error, contents[next_i], "The default branch must be the final branch in a 'match' statement.");
						}
					}
					return LNode.Call(CodeSymbols.DoWhile, LNode.List(outputs.ToVList().AsLNode(S.Braces), LNode.Literal(false)));
				}
			}
			return null;
		}
Exemple #14
0
                      Mode = MacroMode.ProcessChildrenBefore)]   // post-normal-macro-expansion
        public static LNode with(LNode fn, IMessageSink sink)
        {
            LNode braces;

            if (fn.ArgCount != 2 || !(braces = fn.Args[1]).Calls(S.Braces))
            {
                return(null);
            }

            LNode         tmp   = F.Id(NextTempName());
            WList <LNode> stmts = braces.Args.ToWList();

            stmts = stmts.SmartSelect(stmt =>
                                      stmt.ReplaceRecursive(expr => {
                if (expr.Calls(S.Dot, 1))
                {
                    return(expr.WithArgs(new VList <LNode>(tmp, expr.Args.Last)));
                }
                return(null);
            }));
            stmts.Insert(0, F.Var(null, tmp.Name, fn.Args[0]));
            return(F.Braces(stmts.ToVList()));
        }
Exemple #15
0
        public virtual LNode GenerateSwitch(IPGTerminalSet[] branchSets, LNode[] branchCode, MSet <int> casesToInclude, LNode defaultBranch, LNode laVar)
        {
            Debug.Assert(branchSets.Length == branchCode.Length);
            Debug.Assert(casesToInclude.Count <= branchCode.Length);

            WList <LNode> stmts = new WList <LNode>();

            for (int i = 0; i < branchCode.Length; i++)
            {
                if (casesToInclude.Contains(i))
                {
                    int index = -1;
                    foreach (LNode value in GetCases(branchSets[i]))
                    {
                        var label = F.Call(S.Case, value);
                        if (++index > 0 && (index % 4) != 0)                         // write 4 cases per line
                        {
                            label = label.PlusAttr(F.Id(S.TriviaAppendStatement));
                        }
                        stmts.Add(label);
                        if (stmts.Count > 65535)                         // sanity check
                        {
                            throw new InvalidOperationException("switch is too large to generate");
                        }
                    }
                    AddSwitchHandler(branchCode[i], stmts);
                }
            }

            if (!defaultBranch.IsIdNamed(GSymbol.Empty))
            {
                stmts.Add(F.Call(S.Label, F.Id(S.Default)));
                AddSwitchHandler(defaultBranch, stmts);
            }

            return(F.Call(S.Switch, (LNode)laVar, F.Braces(stmts.ToVList())));
        }
Exemple #16
0
        public static LNode matchCode(LNode node, IMacroContext context)
        {
            if (node.AttrNamed(S.Static) != null)
            {
                return(null);                   // this case is handled by static_matchCode macro
            }
            var       args_body = context.GetArgsAndBody(false);
            LNodeList args = args_body.Item1, body = args_body.Item2;

            if (args.Count != 1 || body.Count < 1)
            {
                return(null);
            }
            var cases = GetCases(body, context.Sink);

            if (cases.IsEmpty)
            {
                return(null);
            }

            var output = new WList <LNode>();
            var var    = MaybeAddTempVarDecl(context, args[0], output);

            var ifClauses = new List <Pair <LNode, LNode> >();
            var cmc       = new CodeMatchContext {
                Context = context
            };

            foreach (var @case in cases)
            {
                cmc.ThenClause.Clear();
                // e.g. case [$(..._)] Foo($x + 1, $y) =>
                //      LNode x, y, tmp9;
                //      if (var.Calls((Symbol) "Foo", 2) && (tmp9 = var.Args[0]).Calls(CodeSymbols.Plus, 2)
                //          && (x = tmp9.Args[0]) != null // this will never be null, but we want to put it the assignment in the 'if' statement
                //          && 1.Equals(tmp9.Args[1].Value) && (y = var.Args[1]) != null) { ... }
                LNode testExpr = null;
                if (@case.Key.Count > 0)
                {
                    if (cmc.IsMultiCase = @case.Key.Count > 1)
                    {
                        cmc.UsageCounters.Clear();
                        testExpr = @case.Key.Aggregate((LNode)null, (test, pattern) => {
                            test = LNode.MergeBinary(test, cmc.MakeTopTestExpr(pattern, var), S.Or);
                            return(test);
                        });
                        foreach (var pair in cmc.UsageCounters.Where(p => p.Value < @case.Key.Count))
                        {
                            if (cmc.NodeVars.ContainsKey(pair.Key))
                            {
                                cmc.NodeVars[pair.Key] = true;
                            }
                            if (cmc.ListVars.ContainsKey(pair.Key))
                            {
                                cmc.ListVars[pair.Key] = true;
                            }
                        }
                    }
                    else
                    {
                        testExpr = cmc.MakeTopTestExpr(@case.Key[0], var);
                    }
                }
                var handler = F.Braces(@case.Value);
                if (cmc.ThenClause.Count > 0)
                {
                    handler = LNode.MergeLists(F.Braces(cmc.ThenClause), handler, S.Braces);
                }
                ifClauses.Add(Pair.Create(testExpr, handler));
            }

            LNode ifStmt = null;

            for (int i = ifClauses.Count - 1; i >= 0; i--)
            {
                if (ifClauses[i].Item1 == null)
                {
                    if (ifStmt == null)
                    {
                        ifStmt = ifClauses[i].Item2;
                    }
                    else
                    {
                        context.Sink.Error(node, "The default case must appear last, and there can be only one.");
                    }
                }
                else
                {
                    if (ifStmt == null)
                    {
                        ifStmt = F.Call(S.If, ifClauses[i].Item1, ifClauses[i].Item2);
                    }
                    else
                    {
                        ifStmt = F.Call(S.If, ifClauses[i].Item1, ifClauses[i].Item2, ifStmt);
                    }
                }
            }

            if (cmc.NodeVars.Count > 0)
            {
                output.Add(F.Call(S.Var, ListExt.Single(F.Id("LNode")).Concat(
                                      cmc.NodeVars.OrderBy(v => v.Key.Name).Select(kvp => kvp.Value ? F.Call(S.Assign, F.Id(kvp.Key), F.Null) : F.Id(kvp.Key)))));
            }
            if (cmc.ListVars.Count > 0)
            {
                LNode type = LNode.Id((Symbol)"LNodeList");
                output.Add(F.Call(S.Var, ListExt.Single(type).Concat(
                                      cmc.ListVars.OrderBy(v => v.Key.Name).Select(kvp => kvp.Value ? LNode.Call(CodeSymbols.Assign, LNode.List(F.Id(kvp.Key), LNode.Call(CodeSymbols.Default, LNode.List(type)))).SetStyle(NodeStyle.Operator) : F.Id(kvp.Key)))));
            }
            if (output.Count == 0)
            {
                return(ifStmt.IncludingTriviaFrom(node));
            }
            else
            {
                output.Add(ifStmt);
                return(F.Braces(output.ToVList()).IncludingTriviaFrom(node));
            }
        }
			protected LNode GeneratePredictionTreeCode(PredictionTree tree, Pair<LNode, string>[] matchingCode, ref Symbol haveLoop)
			{
				var braces = F.Braces();

				Debug.Assert(tree.Children.Count >= 1);
				var alts = (Alts)_currentPred;

				if (tree.Children.Count == 1)
					return GetPredictionSubtreeCode(tree.Children[0], matchingCode, ref haveLoop);

				// From the prediction table, we can generate either an if-else chain:
				//
				//   if (la0 >= '0' && la0 <= '7') sub_tree_1();
				//   else if (la0 == '-') sub_tree_2();
				//   else break;
				//
				// or a switch statement:
				//
				//   switch(la0) {
				//   case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
				//     sub_tree_1();
				//     break;
				//   case '-':
				//     sub_tree_2();
				//     break;
				//   default:
				//     goto breakfor;
				//   }
				//
				// Assertion levels always need an if-else chain; lookahead levels 
				// consider the complexity of switch vs if and decide which is most
				// appropriate. Generally "if" is slower, but a switch may require
				// too many labels since it doesn't support ranges like "la0 >= 'a'
				// && la0 <= 'z'".
				//
				// This class makes if-else chains directly (using IPGTerminalSet.
				// GenerateTest() to generate the test expressions), but the code 
				// snippet generator (CSG) is used to generate switch statements 
				// because the required code may be more complex.
				//
				// We may or may not be generating code inside a for(;;) loop. If we 
				// decide to generate a switch() statement, one of the branches will 
				// usually need to break out of the for loop, but "break" can only
				// break out of the switch(). In that case, add "stop:" after the
				// switch() and use "goto stop" instead of "break".

				WList<LNode> block = new WList<LNode>();
				LNode laVar = null;
				MSet<int> switchCases = new MSet<int>();
				IPGTerminalSet[] branchSets = null;
				bool should = false;

				if (tree.UsesLA()) {
					laVar = F.Id("la" + tree.Lookahead.ToString());

					if (!tree.IsAssertionLevel) {
						IPGTerminalSet covered = CGH.EmptySet;
						branchSets = tree.Children.Select(branch => {
							var set = branch.Set.Subtract(covered);
							covered = covered.Union(branch.Set);
							return set;
						}).ToArray();

						should = CGH.ShouldGenerateSwitch(branchSets, switchCases, tree.Children.Last.IsErrorBranch);
						if (!should)
							switchCases.Clear();
						else if (should && haveLoop == S.For)
							// Can't "break" out of the for-loop when there is a nested switch,
							haveLoop = GSymbol.Get(NextStopLabel()); // so use "goto stop".
					}
				}

				LNode[] branchCode = new LNode[tree.Children.Count];
				for (int i = 0; i < tree.Children.Count; i++)
					if (tree.Children[i].IsErrorBranch) {
						if (alts.ErrorBranch != null && alts.ErrorBranch != DefaultErrorBranch.Value) {
							Debug.Assert(matchingCode.Length == alts.Arms.Count + 1);
							branchCode[i] = matchingCode[alts.Arms.Count].A;
						} else
							branchCode[i] = CGH.ErrorBranch(tree.TotalCoverage, tree.Lookahead);
					} else
						branchCode[i] = GetPredictionSubtreeCode(tree.Children[i], matchingCode, ref haveLoop);

				var code = GenerateIfElseChain(tree, branchCode, ref laVar, switchCases);
				if (laVar != null) {
					block.Insert(0, F.Assign(laVar, CGH.LA(tree.Lookahead)));
					_laVarsNeeded |= 1ul << tree.Lookahead;
				} else if (should)
					laVar = CGH.LA(tree.Lookahead);

				if (should) {
					Debug.Assert(switchCases.Count != 0);
					code = CGH.GenerateSwitch(branchSets, switchCases, branchCode, code, laVar);
				}

				block.Add(code);
				return F.Braces(block.ToVList());
			}
Exemple #18
0
        public static LNode unroll(LNode var, VList <LNode> cases, LNode body, IMessageSink sink)
        {
            // Maps identifiers => replacements. The integer counts how many times replacement occurred.
            var replacements = InternalList <Triplet <Symbol, LNode, int> > .Empty;

            if (var.IsId && !var.HasPAttrs())
            {
                replacements.Add(Pair.Create(var.Name, (LNode)LNode.Missing, 0));
            }
            else
            {
                var vars = var.Args;
                if ((var.Calls(S.Tuple) || var.Calls(S.Braces)) && vars.All(a => a.IsId && !a.HasPAttrs()))
                {
                    replacements = new Triplet <Symbol, LNode, int> [vars.Count].AsInternalList();
                    for (int i = 0; i < vars.Count; i++)
                    {
                        replacements.InternalArray[i].A = vars[i].Name;

                        // Check for duplicate names
                        for (int j = 0; j < i; j++)
                        {
                            if (replacements[i].A == replacements[j].A && replacements[i].A.Name != "_")
                            {
                                sink.Error(vars[i], "Duplicate name in the left-hand tuple");                                 // non-fatal
                            }
                        }
                    }
                }
                else
                {
                    return(Reject(sink, var, "The left-hand side of 'in' should be a simple identifier or a tuple of simple identifiers."));
                }
            }

            UnrollCtx ctx = new UnrollCtx {
                Replacements = replacements
            };
            WList <LNode> output = new WList <LNode>();
            int iteration        = 0;
            foreach (LNode replacement in cases)
            {
                iteration++;
                bool tuple = replacement.Calls(S.Tuple) || replacement.Calls(S.Braces);
                int  count = tuple ? replacement.ArgCount : 1;
                if (replacements.Count != count)
                {
                    sink.Error(replacement, "iteration {0}: Expected {1} replacement items, got {2}", iteration, replacements.Count, count);
                    if (count < replacements.Count)
                    {
                        continue;                         // too few
                    }
                }
                for (int i = 0; i < replacements.Count; i++)
                {
                    replacements.InternalArray[i].B = tuple ? replacement.Args[i] : replacement;
                }

                if (body.Calls(S.Braces))
                {
                    foreach (LNode stmt in body.Args)
                    {
                        output.Add(ctx.Replace(stmt).Value);
                    }
                }
                else
                {
                    output.Add(ctx.Replace(body).Value);
                }
            }

            foreach (var r in replacements)
            {
                if (r.C == 0 && !r.A.Name.StartsWith("_"))
                {
                    sink.Write(Severity.Warning, var, "Replacement variable '{0}' was never used", r.A);
                }
            }

            return(body.With(S.Splice, output.ToVList()));
        }
Exemple #19
0
            // GENERATED CODE EXAMPLE: The methods in this region generate
            // the for(;;) loop in this example and everything inside it, except
            // the calls to Match() which are generated by Visit(TerminalPred).
            // The generated code uses "goto" and "match" blocks in some cases
            // to avoid code duplication. This occurs when the matching code
            // requires multiple statements AND appears more than once in the
            // prediction tree. Otherwise, matching is done "inline" during
            // prediction. We generate a for(;;) loop for (...)*, and in certain
            // cases, we generates a do...while(false) loop for (...)?.
            //
            // rule Foo @{ (('a'|'A') 'A')* 'a'..'z' 'a'..'z' };
            // public void Foo()
            // {
            //     int la0, la1;
            //     for (;;) {
            //         la0 = LA(0);
            //         if (la0 == 'a') {
            //             la1 = LA(1);
            //             if (la1 == 'A')
            //                 goto match1;
            //             else
            //                 break;
            //         } else if (la0 == 'A')
            //             goto match1;
            //         else
            //             break;
            //         match1:
            //         {
            //             Match('A', 'a');
            //             Match('A');
            //         }
            //     }
            //     MatchRange('a', 'z');
            //     MatchRange('a', 'z');
            // }

            private void GenerateCodeForAlts(Alts alts, Dictionary <int, int> timesUsed, PredictionTree tree)
            {
                bool needError = LLPG.NeedsErrorBranch(tree, alts);

                if (!needError && alts.ErrorBranch != null)
                {
                    LLPG.Output(Warning, alts, "The error branch will not be used because the other alternatives are exhaustive (cover all cases)");
                }
                bool userDefinedError = needError && !_recognizerMode && alts.ErrorBranch != null && alts.ErrorBranch != DefaultErrorBranch.Value;

                // Generate matching code for each arm. the "string" in each pair
                // becomes non-null if the matching code for that branch needs to be
                // split out (separated) from the prediction tree because it appears
                // multiple times in the tree. The string is the goto-label name.
                Pair <LNode, string>[] matchingCode = new Pair <LNode, string> [alts.Arms.Count + (userDefinedError ? 1: 0)];
                MSet <int>             unreachable  = new MSet <int>();
                int separateCount = 0;

                for (int i = 0; i < alts.Arms.Count; i++)
                {
                    if (!timesUsed.ContainsKey(i))
                    {
                        unreachable.Add(i);
                        continue;
                    }

                    var codeForThisArm = new WList <LNode>();
                    VisitWithNewTarget(alts.Arms[i], codeForThisArm);

                    matchingCode[i].A = F.Braces(codeForThisArm.ToVList());
                    if (timesUsed[i] > 1 && !SimpleEnoughToRepeat(matchingCode[i].A))
                    {
                        separateCount++;
                        matchingCode[i].B = alts.Arms[i].ChooseGotoLabel()
                                            ?? "match" + (i + 1).ToString();
                    }
                }

                // Add matching code for the error branch, if present. Note: the
                // default error branch, which is produced by IPGCodeGenHelper.
                // ErrorBranch() is handled differently: default error code can
                // differ at each error point in the prediction tree. Therefore
                // we generate it later, on-demand.
                if (userDefinedError)
                {
                    int i            = alts.Arms.Count;
                    var errorHandler = new WList <LNode>();
                    VisitWithNewTarget(alts.ErrorBranch, errorHandler);
                    matchingCode[i].A = F.Braces(errorHandler.ToVList());
                    if (timesUsed[ErrorAlt] > 1 && !SimpleEnoughToRepeat(matchingCode[i].A))
                    {
                        matchingCode[i].B = "error";
                        separateCount++;
                    }
                }

                // Print unreachability warnings
                if (unreachable.Count == 1)
                {
                    LLPG.Output(Warning, alts, string.Format("Branch {{{0}}} is unreachable.", alts.AltName(unreachable.First())));
                }
                else if (unreachable.Count > 1)
                {
                    LLPG.Output(Warning, alts, string.Format("Branches {{{0}}} are unreachable.", unreachable.Select(i => alts.AltName(i)).Join(", ")));
                }
                if (!timesUsed.ContainsKey(ExitAlt) && alts.Mode != LoopMode.None)
                {
                    LLPG.Output(Warning, alts, "Infinite loop. The exit branch is unreachable.");
                }

                Symbol loopType = null;

                // Choose a loop type for (...)* or (...)?:
                if (alts.Mode == LoopMode.Star)
                {
                    loopType = S.For;
                }
                else if (alts.Mode == LoopMode.Opt)
                {
                    if (alts.HasErrorBranch(LLPG) || alts.NonExitDefaultArmRequested())
                    {
                        loopType = S.DoWhile;
                    }
                }

                // If the code for an arm is nontrivial and appears multiple times
                // in the prediction table, it will have to be split out into a
                // labeled block and reached via "goto". I'd rather just do a goto
                // from inside one "if" statement to inside another, but in C#
                // (unlike in CIL, and unlike in C) that is prohibited :(
                DeduplicateLabels(matchingCode);
                var extraMatching = GenerateExtraMatchingCode(matchingCode, separateCount, ref loopType);

                if (separateCount != 0)
                {
                    loopType = loopType ?? S.DoWhile;
                }

                Symbol breakMode = loopType;                 // used to request a "goto" label in addition to the loop
                LNode  code      = GeneratePredictionTreeCode(tree, matchingCode, ref breakMode);

                // Add break/continue between prediction tree and extra matching code,
                // if necessary.
                if (extraMatching.Count != 0 && CodeGenHelperBase.EndMayBeReachable(code))
                {
                    loopType = loopType ?? S.DoWhile;
                    extraMatching.Insert(0, GetContinueStmt(loopType));
                }

                if (!extraMatching.IsEmpty)
                {
                    code = LNode.MergeLists(code, F.Braces(extraMatching), S.Braces);
                }

                if (loopType == S.For)
                {
                    // (...)* => for (;;) {}
                    code = F.Call(S.For, F.List(), F.Missing, F.List(), code);
                }
                else if (loopType == S.DoWhile)
                {
                    // (...)? becomes "do {...} while(false);" IF the exit branch is NOT the default.
                    // If the exit branch is the default, then no loop and no "break" is needed.
                    code = F.Call(S.DoWhile, code, F.@false);
                }
                if (breakMode != loopType && breakMode != null)
                {
                    // Add "stop:" label (plus extra ";" for C# compatibility, in
                    // case the label ends the block in which it is located.)
                    var stopLabel = F.Call(S.Label, F.Id(breakMode))
                                    .PlusTrailingTrivia(F.Trivia(S.TriviaRawText, ";"));
                    code = LNode.MergeLists(code, stopLabel, S.Braces);
                }

                int oldCount = _target.Count;

                _target.SpliceAdd(code, S.Braces);

                // Add comment before code
                if (LLPG.AddComments)
                {
                    var pos     = alts.Basis.Range.Start;
                    var comment = F.Trivia(S.TriviaSLComment, string.Format(" Line {0}: {1}", pos.Line, alts.ToString()));
                    if (_target.Count > oldCount)
                    {
                        _target[oldCount] = _target[oldCount].PlusAttr(comment);
                    }
                }
            }
		public override LNode GenerateMatchExpr(IPGTerminalSet set_, bool savingResult, bool recognizerMode)
		{
			var set = (PGIntSet)set_;

			LNode call;
			var type = set.ChooseMatchType(2, 4);
			if (type != PGIntSet.Match.Set) {
				var args = new WList<LNode>();
				if (type == PGIntSet.Match.Ranges) {
					// Use MatchRange or MatchExceptRange
					foreach (var r in set) {
						if (!set.IsInverted || r.Lo != EOF_int || r.Hi != EOF_int) {
							args.Add((LNode)set.MakeLiteral(r.Lo));
							args.Add((LNode)set.MakeLiteral(r.Hi));
						}
					}
					var target = recognizerMode
						? (set.IsInverted ? _TryMatchExceptRange : _TryMatchRange)
						: (set.IsInverted ? _MatchExceptRange : _MatchRange);
					call = ApiCall(target, args);
				} else {
					// Use Match or MatchExcept
					foreach (var r in set) {
						for (int c = r.Lo; c <= r.Hi; c++) {
							if (!set.IsInverted || c != EOF_int)
								args.Add((LNode)set.MakeLiteral(c));
						}
					}
					var target = recognizerMode
						? (set.IsInverted ? _TryMatchExcept : _TryMatch)
						: (set.IsInverted ? _MatchExcept : _Match);
					call = ApiCall(target, args.ToVList());
				}
			} else {
				var setName = GenerateSetDecl(set);
				if (set.IsInverted)
					call = ApiCall(recognizerMode ? _TryMatchExcept : _MatchExcept, F.Id(setName));
				else
					call = ApiCall(recognizerMode ? _TryMatch : _Match, F.Id(setName));
			}
			return call;
		}
Exemple #21
0
 public static LNodeList ToLNodeList(this WList <LNode> list) => new LNodeList(list.ToVList());
Exemple #22
0
        public static LNode @try(LNode node, IMessageSink sink)
        {
            if (!node.IsCall)
            {
                return(null);
            }

            // try(code, catch, Exception::e, handler, catch, ..., finally, handler)
            // ...becomes...
            // #try(#{ stmt1; stmt2; ... }, #catch(#var(Exception, e), handler), #finally(handler))
            LNode         finallyCode = null;
            WList <LNode> clauses     = new WList <LNode>();
            var           parts       = node.Args;

            for (int i = parts.Count - 2; i >= 1; i -= 2)
            {
                var p = parts[i];
                if (p.IsIdNamed(_finally))
                {
                    if (clauses.Count != 0 || finallyCode != null)
                    {
                        sink.Write(Severity.Error, p, "The «finally» clause must come last, there can only be one of them.");
                    }
                    finallyCode = parts[i + 1];
                }
                else if (p.Name == _catch)
                {
                    if (p.ArgCount > 0)
                    {
                        if (p.ArgCount > 1)
                        {
                            sink.Write(Severity.Error, p, "Expected catch() to take one argument.");
                        }
                        // This is a normal catch clause
                        clauses.Insert(0, F.Call(S.Catch, p.Args[0], F.Missing, parts[i + 1]));
                    }
                    else
                    {
                        // This is a catch-all clause (the type argument is missing)
                        if (clauses.Count != 0)
                        {
                            sink.Write(Severity.Error, p, "The catch-all clause must be the last «catch» clause.");
                        }
                        clauses.Add(F.Call(S.Catch, F.Missing, F.Missing, parts[i + 1]));
                    }
                }
                else if (i > 1 && parts[i - 1].IsIdNamed(_catch))
                {
                    // This is a normal catch clause
                    clauses.Insert(0, F.Call(S.Catch, AutoRemoveParens(p), F.Missing, parts[i + 1]));
                    i--;
                }
                else
                {
                    return(Reject(sink, p, "Expected «catch» or «finally» clause here. Clause is missing or malformed."));
                }
                if (i == 2)
                {
                    return(Reject(sink, parts[1], "Expected «catch» or «finally» clause here. Clause is missing or malformed."));
                }
            }
            if (clauses.Count == 0 && finallyCode == null)
            {
                Debug.Assert(node.ArgCount <= 1);
                return(Reject(sink, node, "Missing «catch, Type, Code» or «finally, Code» clause"));
            }
            if (finallyCode != null)
            {
                clauses.Add(F.Call(S.Finally, finallyCode));
            }
            clauses.Insert(0, node.Args[0]);
            return(node.With(S.Try, clauses.ToVList()));
        }
Exemple #23
0
        public static LNode @var(LNode node, IMessageSink sink)
        {
            var parts = node.Args;

            if (parts.Count == 0)
            {
                return(Reject(sink, node, "A variable definition must have the form var(Name::Type), var(Name = value), or var(Name::Type = value)"));
            }
            if (parts[0].IsId)
            {
                return(null);                // e.g. this is true for "static readonly x::Foo"
            }
            WList <LNode> varStmts = null;
            LNode         varStmt  = null;

            for (int i = 0; i < parts.Count; i++)
            {
                LNode part = parts[i], type = null, init = null;
                if (part.Calls(S.Assign, 2))
                {
                    init = part.Args[1];
                    part = part.Args[0];
                }
                if (part.Calls(S.ColonColon, 2))
                {
                    type = part.Args[1];
                    part = part.Args[0];
                }
                if (init == null && part.Calls(S.Assign, 2))
                {
                    init = part.Args[1];
                    part = part.Args[0];
                }
                if (!part.IsId)
                {
                    return(Reject(sink, part, "Expected a simple variable name here"));
                }
                if (type != null && !IsComplexId(type))
                {
                    return(Reject(sink, type, "Expected a type name here"));
                }
                type = type ?? F.Missing;

                var nameAndInit = init == null ? part : F.Call(S.Assign, part, init);
                if (varStmt != null && varStmt.Args[0].Equals(type))
                {
                    // same type used again, e.g. (var x::int y::int) => (#var int x y)
                    varStmt = varStmt.WithArgs(varStmt.Args.Add(nameAndInit));
                }
                else
                {
                    // first item (var x::int => #var int x) or type changed (var a::A b::B => #var A a; #var B b)
                    if (varStmt != null)
                    {
                        varStmts = varStmts ?? new WList <LNode>();
                        varStmts.Add(varStmt);
                    }
                    varStmt = node.With(S.Var, type, nameAndInit);
                }
            }

            // Return a single statement or a list of them if necessary
            if (varStmts != null)
            {
                varStmts.Add(varStmt);
                return(F.Call(S.Splice, varStmts.ToVList()));
            }
            else
            {
                return(varStmt);
            }
        }
Exemple #24
0
		[LexicalMacro("matchCode (var) { case ...: ... }; // In LES, use a => b instead of case a: b", "Attempts to match and deconstruct a Loyc tree against a series of cases with patterns, e.g. " + "`case $a + $b:` expects a tree that calls `+` with two parameters, placed in new variables called a and b. " + "`break` is not required or recognized at the end of each case's handler (code block). " + "Use `$(...x)` to gather zero or more parameters into a list `x`. " + "Use `case pattern1, pattern2:` in EC# to handle multiple cases with the same handler.")] public static LNode matchCode(LNode node, IMacroContext context)
		{
			var args_body = context.GetArgsAndBody(true);
			VList<LNode> args = args_body.Item1, body = args_body.Item2;
			if (args.Count != 1 || body.Count < 1)
				return null;
			var cases = GetCases(body, context.Sink);
			if (cases.IsEmpty)
				return null;
			var output = new WList<LNode>();
			var @var = MaybeAddTempVarDecl(args[0], output);
			var ifClauses = new List<Pair<LNode,LNode>>();
			var cmc = new CodeMatchContext { 
				Context = context
			};
			foreach (var @case in cases) {
				cmc.ThenClause.Clear();
				LNode testExpr = null;
				if (@case.Key.Count > 0) {
					if (cmc.IsMultiCase = @case.Key.Count > 1) {
						cmc.UsageCounters.Clear();
						testExpr = @case.Key.Aggregate((LNode) null, (test, pattern) => {
							test = LNode.MergeBinary(test, cmc.MakeTopTestExpr(pattern, @var), S.Or);
							return test;
						});
						foreach (var pair in cmc.UsageCounters.Where(p => p.Value < @case.Key.Count)) {
							if (cmc.NodeVars.ContainsKey(pair.Key))
								cmc.NodeVars[pair.Key] = true;
							if (cmc.ListVars.ContainsKey(pair.Key))
								cmc.ListVars[pair.Key] = true;
						}
					} else
						testExpr = cmc.MakeTopTestExpr(@case.Key[0], @var);
				}
				var handler = @case.Value;
				if (cmc.ThenClause.Count > 0)
					handler = LNode.MergeLists(F.Braces(cmc.ThenClause), handler, S.Braces);
				ifClauses.Add(Pair.Create(testExpr, handler));
			}
			LNode ifStmt = null;
			for (int i = ifClauses.Count - 1; i >= 0; i--) {
				if (ifClauses[i].Item1 == null) {
					if (ifStmt == null)
						ifStmt = ifClauses[i].Item2;
					else
						context.Sink.Write(Severity.Error, node, "The default case must appear last, and there can be only one.");
				} else {
					if (ifStmt == null)
						ifStmt = F.Call(S.If, ifClauses[i].Item1, ifClauses[i].Item2);
					else
						ifStmt = F.Call(S.If, ifClauses[i].Item1, ifClauses[i].Item2, ifStmt);
				}
			}
			if (cmc.NodeVars.Count > 0)
				output.Add(F.Call(S.Var, ListExt.Single(F.Id("LNode")).Concat(cmc.NodeVars.OrderBy(v => v.Key.Name).Select(kvp => kvp.Value ? F.Call(S.Assign, F.Id(kvp.Key), F.Null) : F.Id(kvp.Key)))));
			if (cmc.ListVars.Count > 0) {
				LNode type = LNode.Call(CodeSymbols.Of, LNode.List(LNode.Id((Symbol) "VList"), LNode.Id((Symbol) "LNode")));
				output.Add(F.Call(S.Var, ListExt.Single(type).Concat(cmc.ListVars.OrderBy(v => v.Key.Name).Select(kvp => kvp.Value ? LNode.Call(CodeSymbols.Assign, LNode.List(F.Id(kvp.Key), LNode.Call(CodeSymbols.Default, LNode.List(type)))).SetStyle(NodeStyle.Operator) : F.Id(kvp.Key)))));
			}
			if (output.Count == 0)
				return ifStmt;
			else {
				output.Add(ifStmt);
				return F.Braces(output.ToVList());
			}
		}
Exemple #25
0
		public virtual LNode GenerateSwitch(IPGTerminalSet[] branchSets, MSet<int> casesToInclude, LNode[] branchCode, LNode defaultBranch, LNode laVar)
		{
			Debug.Assert(branchSets.Length == branchCode.Length);

			WList<LNode> stmts = new WList<LNode>();
			for (int i = 0; i < branchSets.Length; i++)
			{
				if (casesToInclude.Contains(i))
				{
					foreach (LNode value in GetCases(branchSets[i]))
					{
						stmts.Add(F.Call(S.Case, value));
						if (stmts.Count > 65535) // sanity check
							throw new InvalidOperationException("switch is too large to generate");
					}
					AddSwitchHandler(branchCode[i], stmts);
				}
			}

			if (!defaultBranch.IsIdNamed(S.Missing))
			{
				stmts.Add(F.Call(S.Label, F.Id(S.Default)));
				AddSwitchHandler(defaultBranch, stmts);
			}

			return F.Call(S.Switch, (LNode)laVar, F.Braces(stmts.ToVList()));
		}
Exemple #26
0
		public static LNode unroll(LNode var, LNode cases, LNode body, IMessageSink sink)
		{
			if (!cases.Calls(S.Tuple) && !cases.Calls(S.Braces))
				return Reject(sink, cases, "unroll: the right-hand side of 'in' should be a tuple");

			// Maps identifiers => replacements. The integer counts how many times replacement occurred.
			var replacements = InternalList<Triplet<Symbol, LNode, int>>.Empty;
			if (var.IsId && !var.HasPAttrs()) {
				replacements.Add(Pair.Create(var.Name, (LNode)LNode.Missing, 0));
			} else {
				var vars = var.Args;
				if ((var.Calls(S.Tuple) || var.Calls(S.Braces)) && vars.All(a => a.IsId && !a.HasPAttrs())) {
					replacements = new Triplet<Symbol, LNode, int>[vars.Count].AsInternalList();
					for (int i = 0; i < vars.Count; i++) {
						replacements.InternalArray[i].A = vars[i].Name;
						
						// Check for duplicate names
						for (int j = 0; j < i; j++)
							if (replacements[i].A == replacements[j].A && replacements[i].A.Name != "_")
								sink.Write(Severity.Error, vars[i], "unroll: duplicate name in the left-hand tuple"); // non-fatal
					}
				} else
					return Reject(sink, cases, "unroll: the left-hand side of 'in' should be a simple identifier or a tuple of simple identifiers.");
			}

			UnrollCtx ctx = new UnrollCtx { Replacements = replacements };
			WList<LNode> output = new WList<LNode>();
			int iteration = 0;
			foreach (LNode replacement in cases.Args)
			{
				iteration++;
				bool tuple = replacement.Calls(S.Tuple) || replacement.Calls(S.Braces);
				int count = tuple ? replacement.ArgCount : 1;
				if (replacements.Count != count)
				{
					sink.Write(Severity.Error, replacement, "unroll, iteration {0}: Expected {1} replacement items, got {2}", iteration, replacements.Count, count);
					if (count < replacements.Count)
						continue; // too few
				}
				for (int i = 0; i < replacements.Count; i++)
					replacements.InternalArray[i].B = tuple ? replacement.Args[i] : replacement;

				if (body.Calls(S.Braces)) {
					foreach (LNode stmt in body.Args)
						output.Add(ctx.Replace(stmt).Value);
				} else
					output.Add(ctx.Replace(body).Value);
			}

			foreach (var r in replacements)
				if (r.C == 0 && !r.A.Name.StartsWith("_"))
					sink.Write(Severity.Warning, var, "Replacement variable '{0}' was never used", r.A);
			
			return body.With(S.Splice, output.ToVList());
		}
Exemple #27
0
            protected LNode GeneratePredictionTreeCode(PredictionTree tree, Pair <LNode, string>[] matchingCode, ref Symbol haveLoop)
            {
                var braces = F.Braces();

                Debug.Assert(tree.Children.Count >= 1);
                var alts = (Alts)_currentPred;

                if (tree.Children.Count == 1)
                {
                    return(GetPredictionSubtreeCode(tree.Children[0], matchingCode, ref haveLoop));
                }

                // From the prediction table, we can generate either an if-else chain:
                //
                //   if (la0 >= '0' && la0 <= '7') sub_tree_1();
                //   else if (la0 == '-') sub_tree_2();
                //   else break;
                //
                // or a switch statement:
                //
                //   switch(la0) {
                //   case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
                //     sub_tree_1();
                //     break;
                //   case '-':
                //     sub_tree_2();
                //     break;
                //   default:
                //     goto breakfor;
                //   }
                //
                // Assertion levels always need an if-else chain; lookahead levels
                // consider the complexity of switch vs if and decide which is most
                // appropriate. Generally "if" is slower, but a switch may require
                // too many labels since it doesn't support ranges like "la0 >= 'a'
                // && la0 <= 'z'".
                //
                // This class makes if-else chains directly (using IPGTerminalSet.
                // GenerateTest() to generate the test expressions), but the code
                // generation helper (CGH) is used to generate switch statements
                // because the required code may be more complex.
                //
                // We may or may not be generating code inside a for(;;) loop. If we
                // decide to generate a switch() statement, one of the branches will
                // usually need to break out of the for loop, but "break" can only
                // break out of the switch(). In that case, add "stop:" after the
                // switch() and use "goto stop" instead of "break".

                WList <LNode> block       = new WList <LNode>();
                LNode         laVar       = null;
                MSet <int>    switchCases = new MSet <int>();

                IPGTerminalSet[] branchSets = null;
                bool             should     = false;

                if (tree.UsesLA())
                {
                    laVar = F.Id("la" + tree.Lookahead.ToString());

                    if (!tree.IsAssertionLevel)
                    {
                        IPGTerminalSet covered = CGH.EmptySet;
                        branchSets = tree.Children.Select(branch => {
                            var set = branch.Set.Subtract(covered);
                            covered = covered.Union(branch.Set);
                            return(set);
                        }).ToArray();

                        should = CGH.ShouldGenerateSwitch(branchSets, switchCases, tree.Children.Last.IsErrorBranch);
                        if (!should)
                        {
                            switchCases.Clear();
                        }
                        else if (should && haveLoop == S.For)
                        {
                            // Can't "break" out of the for-loop when there is a nested switch,
                            haveLoop = GSymbol.Get(NextStopLabel());                             // so use "goto stop".
                        }
                    }
                }

                LNode[] branchCode = new LNode[tree.Children.Count];
                for (int i = 0; i < tree.Children.Count; i++)
                {
                    if (tree.Children[i].IsErrorBranch)
                    {
                        if (_recognizerMode)
                        {
                            branchCode[i] = F.Call(S.Return, F.False);
                        }
                        else if (alts.ErrorBranch != null && alts.ErrorBranch != DefaultErrorBranch.Value)
                        {
                            Debug.Assert(matchingCode.Length == alts.Arms.Count + 1);
                            branchCode[i] = matchingCode[alts.Arms.Count].A;
                        }
                        else
                        {
                            branchCode[i] = CGH.ErrorBranch(tree.TotalCoverage, tree.Lookahead);
                        }
                    }
                    else
                    {
                        branchCode[i] = GetPredictionSubtreeCode(tree.Children[i], matchingCode, ref haveLoop);
                    }
                }

                var code = GenerateIfElseChain(tree, branchCode, ref laVar, switchCases);

                if (laVar != null)
                {
                    block.Insert(0, F.Assign(laVar, CGH.LA(tree.Lookahead)));
                    _laVarsNeeded |= 1ul << tree.Lookahead;
                }
                else if (should)
                {
                    laVar = CGH.LA(tree.Lookahead);
                }

                if (should)
                {
                    Debug.Assert(switchCases.Count != 0);
                    code = CGH.GenerateSwitch(branchSets, branchCode, switchCases, code ?? F.Missing, laVar);
                }

                block.Add(code);
                return(F.Braces(block.ToVList()));
            }
Exemple #28
0
		public LNode Vars(LNode type, params LNode[] namesWithValues)
		{
			type = type ?? Missing;
			var list = new WList<LNode>() { type };
			list.AddRange(namesWithValues);
			return Call(S.Var, list.ToVList());
		}
Exemple #29
0
 public void ScanClassBody(VList <LNode> body)
 {
     foreach (var stmt in body)
     {
         int i;
         {
             LNode         altName;
             VList <LNode> attrs, childBody = default(VList <LNode>), parts, rest;
             if ((attrs = stmt.Attrs).IsEmpty | true && stmt.Calls(CodeSymbols.Fn, 3) && stmt.Args[0].IsIdNamed((Symbol)"alt") && (altName = stmt.Args[1]) != null && stmt.Args[2].Calls(CodeSymbols.AltList) && (parts = stmt.Args[2].Args).IsEmpty | true || (attrs = stmt.Attrs).IsEmpty | true && stmt.Calls(CodeSymbols.Fn, 4) && stmt.Args[0].IsIdNamed((Symbol)"alt") && (altName = stmt.Args[1]) != null && stmt.Args[2].Calls(CodeSymbols.AltList) && (parts = stmt.Args[2].Args).IsEmpty | true && stmt.Args[3].Calls(CodeSymbols.Braces) && (childBody = stmt.Args[3].Args).IsEmpty | true)
             {
                 LNode genericAltName = altName;
                 if (altName.CallsMin(CodeSymbols.Of, 1))
                 {
                 }
                 else if (_genericArgs.Count > 0)
                 {
                     genericAltName = LNode.Call(CodeSymbols.Of, LNode.List().Add(altName).AddRange(_genericArgs.ToVList())).SetStyle(NodeStyle.Operator);
                 }
                 var child = new AltType(attrs, genericAltName, LNode.List(), this);
                 child.AddParts(parts);
                 child.ScanClassBody(childBody);
                 _children.Add(child);
             }
             else if ((attrs = stmt.Attrs).IsEmpty | true && (i = attrs.IndexWhere(a => a.IsIdNamed(__alt))) > -1 && stmt.CallsMin(CodeSymbols.Constructor, 3) && stmt.Args[1].IsIdNamed((Symbol)"#this") && stmt.Args[2].Calls(CodeSymbols.AltList) && (rest = new VList <LNode>(stmt.Args.Slice(3))).IsEmpty | true && rest.Count <= 1)
             {
                 parts = stmt.Args[2].Args;
                 attrs.RemoveAt(i);
                 _constructorAttrs.AddRange(attrs);
                 if (rest.Count > 0 && rest[0].Calls(S.Braces))
                 {
                     _extraConstrLogic.AddRange(rest[0].Args);
                 }
                 AddParts(parts);
             }
             else
             {
                 _classBody.Add(stmt);
             }
         }
     }
 }
Exemple #30
0
		public static LNode match(LNode node, IMacroContext context)
		{
			{
				LNode input;
				VList<LNode> contents;
				if (node.Args.Count == 2 && (input = node.Args[0]) != null && node.Args[1].Calls(CodeSymbols.Braces)) {
					contents = node.Args[1].Args;
					var outputs = new WList<LNode>();
					input = MaybeAddTempVarDecl(context, input, outputs);
				
					int next_i = 0;
					for (int case_i = 0; case_i < contents.Count; case_i = next_i) {
						var @case = contents[case_i];
						if (!IsCaseLabel(@case))
							return Reject(context, contents[0], "In 'match': expected 'case' statement");
						for (next_i = case_i + 1; next_i < contents.Count; next_i++) {
							var stmt = contents[next_i];
							if (IsCaseLabel(stmt))
								break;
							if (stmt.Calls(S.Break, 0)) {
								next_i++;
								break;
							}
						}
						var handler = new VList<LNode>(contents.Slice(case_i + 1, next_i - (case_i + 1)));
					
						if (@case.Calls(S.Case) && @case.Args.Count > 0) {
							var codeGen = new CodeGeneratorForMatchCase(context, input, handler);
							foreach (var pattern in @case.Args)
								outputs.Add(codeGen.GenCodeForPattern(pattern));
						} else {	// default:
							// Note: the extra {braces} around the handler are rarely 
							// needed. They are added just in case the handler declares a 
							// variable and a different handler declares another variable 
							// by the same name, which is illegal unless we add braces.
							outputs.Add(LNode.Call(CodeSymbols.Braces, LNode.List(handler)).SetStyle(NodeStyle.Statement));
							if (next_i < contents.Count)
								context.Sink.Error(contents[next_i], "The default branch must be the final branch in a 'match' statement.");
						}
					}
					return LNode.Call(CodeSymbols.DoWhile, LNode.List(outputs.ToVList().AsLNode(S.Braces), LNode.Literal(false)));
				}
			}
			return null;
		}
Exemple #31
0
		public static LNode matchCode(LNode node, IMacroContext context)
		{
			if (node.AttrNamed(S.Static) != null)
				return null;	// this case is handled by static_matchCode macro
			var args_body = context.GetArgsAndBody(false);
			VList<LNode> args = args_body.Item1, body = args_body.Item2;
			if (args.Count != 1 || body.Count < 1)
				return null;
			var cases = GetCases(body, context.Sink);
			if (cases.IsEmpty)
				return null;
		
			var output = new WList<LNode>();
			var @var = MaybeAddTempVarDecl(context, args[0], output);
		
			var ifClauses = new List<Pair<LNode, LNode>>();
			var cmc = new CodeMatchContext { 
				Context = context
			};
		
			foreach (var @case in cases)
			{
				cmc.ThenClause.Clear();
				// e.g. case [$(..._)] Foo($x + 1, $y) => 
				//      LNode x, y, tmp9; 
				//      if (var.Calls((Symbol) "Foo", 2) && (tmp9 = var.Args[0]).Calls(CodeSymbols.Plus, 2)
				//          && (x = tmp9.Args[0]) != null // this will never be null, but we want to put it the assignment in the 'if' statement
				//          && 1.Equals(tmp9.Args[1].Value) && (y = var.Args[1]) != null) { ... }
				LNode testExpr = null;
				if (@case.Key.Count > 0) {
					if (cmc.IsMultiCase = @case.Key.Count > 1) {
						cmc.UsageCounters.Clear();
						testExpr = @case.Key.Aggregate((LNode) null, (test, pattern) => {
							test = LNode.MergeBinary(test, cmc.MakeTopTestExpr(pattern, @var), S.Or);
							return test;
						});
						foreach (var pair in cmc.UsageCounters.Where(p => p.Value < @case.Key.Count)) {
							if (cmc.NodeVars.ContainsKey(pair.Key))
								cmc.NodeVars[pair.Key] = true;
							if (cmc.ListVars.ContainsKey(pair.Key))
								cmc.ListVars[pair.Key] = true;
						}
					} else
						testExpr = cmc.MakeTopTestExpr(@case.Key[0], @var);
				}
				var handler = @case.Value.AsLNode(S.Braces);
				if (cmc.ThenClause.Count > 0)
					handler = LNode.MergeLists(F.Braces(cmc.ThenClause), handler, S.Braces);
				ifClauses.Add(Pair.Create(testExpr, handler));
			}
		
			LNode ifStmt = null;
			for (int i = ifClauses.Count - 1; i >= 0; i--)
			{
				if (ifClauses[i].Item1 == null) {
					if (ifStmt == null)
						ifStmt = ifClauses[i].Item2;
					else
						context.Sink.Error(node, "The default case must appear last, and there can be only one.");
				} else {
					if (ifStmt == null)
						ifStmt = F.Call(S.If, ifClauses[i].Item1, ifClauses[i].Item2);
					else
						ifStmt = F.Call(S.If, ifClauses[i].Item1, ifClauses[i].Item2, ifStmt);
				}
			}
		
			if (cmc.NodeVars.Count > 0)
				output.Add(F.Call(S.Var, ListExt.Single(F.Id("LNode")).Concat(
				cmc.NodeVars.OrderBy(v => v.Key.Name).Select(kvp => kvp.Value ? F.Call(S.Assign, F.Id(kvp.Key), F.Null) : F.Id(kvp.Key)))));
			if (cmc.ListVars.Count > 0) {
				LNode type = LNode.Call(CodeSymbols.Of, LNode.List(LNode.Id((Symbol) "VList"), LNode.Id((Symbol) "LNode"))).SetStyle(NodeStyle.Operator);
				output.Add(F.Call(S.Var, ListExt.Single(type).Concat(
				cmc.ListVars.OrderBy(v => v.Key.Name).Select(kvp => kvp.Value ? LNode.Call(CodeSymbols.Assign, LNode.List(F.Id(kvp.Key), LNode.Call(CodeSymbols.Default, LNode.List(type)))).SetStyle(NodeStyle.Operator) : F.Id(kvp.Key)))));
			}
			if (output.Count == 0)
				return ifStmt;
			else {
				output.Add(ifStmt);
				return F.Braces(output.ToVList());
			}
		}
Exemple #32
0
        [LexicalMacro("matchCode (var) { case ...: ... }; // In LES, use a => b instead of case a: b", "Attempts to match and deconstruct a Loyc tree against a series of cases with patterns, e.g. " + "`case $a + $b:` expects a tree that calls `+` with two parameters, placed in new variables called a and b. " + "`break` is not required or recognized at the end of each case's handler (code block). " + "Use `$(...x)` to gather zero or more parameters into a list `x`. " + "Use `case pattern1, pattern2:` in EC# to handle multiple cases with the same handler.")] public static LNode matchCode(LNode node, IMacroContext context)
        {
            var           args_body = context.GetArgsAndBody(true);
            VList <LNode> args = args_body.Item1, body = args_body.Item2;

            if (args.Count != 1 || body.Count < 1)
            {
                return(null);
            }
            var cases  = GetCases(body, context.Sink);

            if (cases.IsEmpty)
            {
                return(null);
            }
            var output = new WList <LNode>();
            var @var   = MaybeAddTempVarDecl(args[0], output);
            var ifClauses = new List <Pair <LNode, LNode> >();
            var cmc    = new CodeMatchContext {
                Context = context
            };

            foreach (var @case in cases)
            {
                cmc.ThenClause.Clear();
                LNode testExpr = null;
                if (@case.Key.Count > 0)
                {
                    if (cmc.IsMultiCase = @case.Key.Count > 1)
                    {
                        cmc.UsageCounters.Clear();
                        testExpr = @case.Key.Aggregate((LNode)null, (test, pattern) => {
                            test = LNode.MergeBinary(test, cmc.MakeTopTestExpr(pattern, @var), S.Or);
                            return(test);
                        });
                        foreach (var pair in cmc.UsageCounters.Where(p => p.Value < @case.Key.Count))
                        {
                            if (cmc.NodeVars.ContainsKey(pair.Key))
                            {
                                cmc.NodeVars[pair.Key] = true;
                            }
                            if (cmc.ListVars.ContainsKey(pair.Key))
                            {
                                cmc.ListVars[pair.Key] = true;
                            }
                        }
                    }
                    else
                    {
                        testExpr = cmc.MakeTopTestExpr(@case.Key[0], @var);
                    }
                }
                var handler = @case.Value;
                if (cmc.ThenClause.Count > 0)
                {
                    handler = LNode.MergeLists(F.Braces(cmc.ThenClause), handler, S.Braces);
                }
                ifClauses.Add(Pair.Create(testExpr, handler));
            }
            LNode ifStmt = null;

            for (int i = ifClauses.Count - 1; i >= 0; i--)
            {
                if (ifClauses[i].Item1 == null)
                {
                    if (ifStmt == null)
                    {
                        ifStmt = ifClauses[i].Item2;
                    }
                    else
                    {
                        context.Sink.Write(Severity.Error, node, "The default case must appear last, and there can be only one.");
                    }
                }
                else
                {
                    if (ifStmt == null)
                    {
                        ifStmt = F.Call(S.If, ifClauses[i].Item1, ifClauses[i].Item2);
                    }
                    else
                    {
                        ifStmt = F.Call(S.If, ifClauses[i].Item1, ifClauses[i].Item2, ifStmt);
                    }
                }
            }
            if (cmc.NodeVars.Count > 0)
            {
                output.Add(F.Call(S.Var, ListExt.Single(F.Id("LNode")).Concat(cmc.NodeVars.OrderBy(v => v.Key.Name).Select(kvp => kvp.Value ? F.Call(S.Assign, F.Id(kvp.Key), F.Null) : F.Id(kvp.Key)))));
            }
            if (cmc.ListVars.Count > 0)
            {
                LNode type = LNode.Call(CodeSymbols.Of, LNode.List(LNode.Id((Symbol)"VList"), LNode.Id((Symbol)"LNode")));
                output.Add(F.Call(S.Var, ListExt.Single(type).Concat(cmc.ListVars.OrderBy(v => v.Key.Name).Select(kvp => kvp.Value ? LNode.Call(CodeSymbols.Assign, LNode.List(F.Id(kvp.Key), LNode.Call(CodeSymbols.Default, LNode.List(type)))).SetStyle(NodeStyle.Operator) : F.Id(kvp.Key)))));
            }
            if (output.Count == 0)
            {
                return(ifStmt);
            }
            else
            {
                output.Add(ifStmt);
                return(F.Braces(output.ToVList()));
            }
        }
			// GENERATED CODE EXAMPLE: The methods in this region generate
			// the for(;;) loop in this example and everything inside it, except
			// the calls to Match() which are generated by Visit(TerminalPred).
			// The generated code uses "goto" and "match" blocks in some cases
			// to avoid code duplication. This occurs when the matching code 
			// requires multiple statements AND appears more than once in the 
			// prediction tree. Otherwise, matching is done "inline" during 
			// prediction. We generate a for(;;) loop for (...)*, and in certain 
			// cases, we generates a do...while(false) loop for (...)?.
			//
			// rule Foo @{ (('a'|'A') 'A')* 'a'..'z' 'a'..'z' };
			// public void Foo()
			// {
			//     int la0, la1;
			//     for (;;) {
			//         la0 = LA(0);
			//         if (la0 == 'a') {
			//             la1 = LA(1);
			//             if (la1 == 'A')
			//                 goto match1;
			//             else
			//                 break;
			//         } else if (la0 == 'A')
			//             goto match1;
			//         else
			//             break;
			//         match1:
			//         {
			//             Match('A', 'a');
			//             Match('A');
			//         }
			//     }
			//     MatchRange('a', 'z');
			//     MatchRange('a', 'z');
			// }

			private void GenerateCodeForAlts(Alts alts, Dictionary<int, int> timesUsed, PredictionTree tree)
			{
				bool needError = LLPG.NeedsErrorBranch(tree, alts);
				if (!needError && alts.ErrorBranch != null)
					LLPG.Output(Warning, alts, "The error branch will not be used because the other alternatives are exhaustive (cover all cases)");
				bool userDefinedError = needError && alts.ErrorBranch != null && alts.ErrorBranch != DefaultErrorBranch.Value;

				// Generate matching code for each arm. the "string" in each pair 
				// becomes non-null if the matching code for that branch needs to be
				// split out (separated) from the prediction tree because it appears
				// multiple times in the tree. The string is the goto-label name.
				Pair<LNode, string>[] matchingCode = new Pair<LNode, string>[alts.Arms.Count + (userDefinedError ? 1 : 0)];
				MSet<int> unreachable = new MSet<int>();
				int separateCount = 0;
				for (int i = 0; i < alts.Arms.Count; i++) {
					if (!timesUsed.ContainsKey(i)) {
						unreachable.Add(i);
						continue;
					}

					var codeForThisArm = new WList<LNode>();
					VisitWithNewTarget(alts.Arms[i], codeForThisArm);

					matchingCode[i].A = F.Braces(codeForThisArm.ToVList());
					if (timesUsed[i] > 1 && !SimpleEnoughToRepeat(matchingCode[i].A)) {
						separateCount++;
						matchingCode[i].B = alts.Arms[i].ChooseGotoLabel() 
							?? "match" + (i + 1).ToString();
					}
				}

				// Add matching code for the error branch, if present. Note: the
				// default error branch, which is produced by IPGCodeGenHelper.
				// ErrorBranch() is handled differently: default error code can 
				// differ at each error point in the prediction tree. Therefore 
				// we generate it later, on-demand.
				if (userDefinedError) {
					int i = alts.Arms.Count;
					var errorHandler = new WList<LNode>();
					VisitWithNewTarget(alts.ErrorBranch, errorHandler);
					matchingCode[i].A = F.Braces(errorHandler.ToVList());
					if (timesUsed[ErrorAlt] > 1 && !SimpleEnoughToRepeat(matchingCode[i].A)) {
						matchingCode[i].B = "error";
						separateCount++;
					}
				}

				// Print unreachability warnings 
				if (unreachable.Count == 1)
					LLPG.Output(Warning, alts, string.Format("Branch {{{0}}} is unreachable.", alts.AltName(unreachable.First())));
				else if (unreachable.Count > 1)
					LLPG.Output(Warning, alts, string.Format("Branches {{{0}}} are unreachable.", unreachable.Select(i => alts.AltName(i)).Join(", ")));
				if (!timesUsed.ContainsKey(ExitAlt) && alts.Mode != LoopMode.None)
					LLPG.Output(Warning, alts, "Infinite loop. The exit branch is unreachable.");

				Symbol loopType = null;

				// Choose a loop type for (...)* or (...)?:
				if (alts.Mode == LoopMode.Star)
					loopType = S.For;
				else if (alts.Mode == LoopMode.Opt) {
					if (alts.HasErrorBranch(LLPG) || alts.NonExitDefaultArmRequested())
						loopType = S.DoWhile;
				}

				// If the code for an arm is nontrivial and appears multiple times 
				// in the prediction table, it will have to be split out into a 
				// labeled block and reached via "goto". I'd rather just do a goto
				// from inside one "if" statement to inside another, but in C# 
				// (unlike in CIL, and unlike in C) that is prohibited :(
				DeduplicateLabels(matchingCode);
				var extraMatching = GenerateExtraMatchingCode(matchingCode, separateCount, ref loopType);
				if (separateCount != 0)
					loopType = loopType ?? S.DoWhile;

				Symbol breakMode = loopType; // used to request a "goto" label in addition to the loop
				LNode code = GeneratePredictionTreeCode(tree, matchingCode, ref breakMode);

				// Add break/continue between prediction tree and extra matching code,
				// if necessary.
				if (extraMatching.Count != 0 && CodeGenHelperBase.EndMayBeReachable(code)) {
					loopType = loopType ?? S.DoWhile;
					extraMatching.Insert(0, GetContinueStmt(loopType));
				}

				if (!extraMatching.IsEmpty)
					code = LNode.MergeLists(code, F.Braces(extraMatching), S.Braces);

				if (loopType == S.For) {
					// (...)* => for (;;) {}
					code = F.Call(S.For, F.List(), F.Missing, F.List(), code);
				} else if (loopType == S.DoWhile) {
					// (...)? becomes "do {...} while(false);" IF the exit branch is NOT the default.
					// If the exit branch is the default, then no loop and no "break" is needed.
					code = F.Call(S.DoWhile, code, F.@false);
				}
				if (breakMode != loopType && breakMode != null) {
					// Add "stop:" label (plus extra ";" for C# compatibility, in 
					// case the label ends the block in which it is located.)
					var stopLabel = F.Call(S.Label, F.Id(breakMode))
									 .PlusTrailingTrivia(F.Trivia(S.TriviaRawText, ";"));
					code = LNode.MergeLists(code, stopLabel, S.Braces);
				}

				int oldCount = _target.Count;
				_target.SpliceAdd(code, S.Braces);
				
				// Add comment before code
				if (LLPG.AddComments) {
					var pos = alts.Basis.Range.Start;
					var comment = F.Trivia(S.TriviaSLComment, string.Format(" Line {0}: {1}", pos.Line, alts.ToString()));
					if (_target.Count > oldCount)
						_target[oldCount] = _target[oldCount].PlusAttr(comment);
				}
			}
Exemple #34
0
		public static LNode @try(LNode node, IMessageSink sink)
		{
			if (!node.IsCall)
				return null;

			// try(code, catch, Exception::e, handler, catch, ..., finally, handler)
			// ...becomes...
			// #try(#{ stmt1; stmt2; ... }, #catch(#var(Exception, e), handler), #finally(handler))
			LNode finallyCode = null;
			WList<LNode> clauses = new WList<LNode>();
			var parts = node.Args;
			
			for (int i = parts.Count-2; i >= 1; i -= 2)
			{
				var p = parts[i];
				if (p.IsIdNamed(_finally)) {
					if (clauses.Count != 0 || finallyCode != null)
						sink.Write(Severity.Error, p, "The «finally» clause must come last, there can only be one of them.");
					finallyCode = parts[i+1];
				} else if (p.Name == _catch) {
					if (p.ArgCount > 0) {
						if (p.ArgCount > 1)
							sink.Write(Severity.Error, p, "Expected catch() to take one argument.");
						// This is a normal catch clause
						clauses.Insert(0, F.Call(S.Catch, p.Args[0], F.Missing, parts[i + 1]));
					} else {
						// This is a catch-all clause (the type argument is missing)
						if (clauses.Count != 0)
							sink.Write(Severity.Error, p, "The catch-all clause must be the last «catch» clause.");
						clauses.Add(F.Call(S.Catch, F.Missing, F.Missing, parts[i + 1]));
					}
				} else if (i > 1 && parts[i-1].IsIdNamed(_catch)) {
					// This is a normal catch clause
					clauses.Insert(0, F.Call(S.Catch, AutoRemoveParens(p), F.Missing, parts[i+1]));
					i--;
				} else {
					return Reject(sink, p, "Expected «catch» or «finally» clause here. Clause is missing or malformed.");
				}
				if (i == 2)
					return Reject(sink, parts[1], "Expected «catch» or «finally» clause here. Clause is missing or malformed.");
			}
			if (clauses.Count == 0 && finallyCode == null) {
				Debug.Assert(node.ArgCount <= 1);
				return Reject(sink, node, "Missing «catch, Type, Code» or «finally, Code» clause");
			}
			if (finallyCode != null)
				clauses.Add(F.Call(S.Finally, finallyCode));
			clauses.Insert(0, node.Args[0]);
			return node.With(S.Try, clauses.ToVList());
		}
Exemple #35
0
        public static LNode matchCode(LNode node, IMacroContext context)
        {
            var           args_body = context.GetArgsAndBody(true);
            VList <LNode> args = args_body.Item1, body = args_body.Item2;

            if (args.Count != 1 || body.Count < 1)
            {
                return(null);
            }
            var cases  = GetCases(body, context.Sink);

            if (cases.IsEmpty)
            {
                return(null);
            }
            var output = new WList <LNode>();
            var @var   = MaybeAddTempVarDecl(context, args[0], output);
            var ifClauses = new List <Pair <LNode, LNode> >();
            var cmc    = new CodeMatchContext {
                Context = context
            };

            foreach (var @case in cases)
            {
                cmc.ThenClause.Clear();
                LNode testExpr = null;
                if (@case.Key.Count > 0)
                {
                    if (cmc.IsMultiCase = @case.Key.Count > 1)
                    {
                        cmc.UsageCounters.Clear();
                        testExpr = @case.Key.Aggregate((LNode)null, (test, pattern) => {
                            test = LNode.MergeBinary(test, cmc.MakeTopTestExpr(pattern, @var), S.Or);
                            return(test);
                        });
                        foreach (var pair in cmc.UsageCounters.Where(p => p.Value < @case.Key.Count))
                        {
                            if (cmc.NodeVars.ContainsKey(pair.Key))
                            {
                                cmc.NodeVars[pair.Key] = true;
                            }
                            if (cmc.ListVars.ContainsKey(pair.Key))
                            {
                                cmc.ListVars[pair.Key] = true;
                            }
                        }
                    }
                    else
                    {
                        testExpr = cmc.MakeTopTestExpr(@case.Key[0], @var);
                    }
                }
                var handler = @case.Value;
                if (cmc.ThenClause.Count > 0)
                {
                    handler = LNode.MergeLists(F.Braces(cmc.ThenClause), handler, S.Braces);
                }
                ifClauses.Add(Pair.Create(testExpr, handler));
            }
            LNode ifStmt = null;

            for (int i = ifClauses.Count - 1; i >= 0; i--)
            {
                if (ifClauses[i].Item1 == null)
                {
                    if (ifStmt == null)
                    {
                        ifStmt = ifClauses[i].Item2;
                    }
                    else
                    {
                        context.Sink.Write(Severity.Error, node, "The default case must appear last, and there can be only one.");
                    }
                }
                else
                {
                    if (ifStmt == null)
                    {
                        ifStmt = F.Call(S.If, ifClauses[i].Item1, ifClauses[i].Item2);
                    }
                    else
                    {
                        ifStmt = F.Call(S.If, ifClauses[i].Item1, ifClauses[i].Item2, ifStmt);
                    }
                }
            }
            if (cmc.NodeVars.Count > 0)
            {
                output.Add(F.Call(S.Var, ListExt.Single(F.Id("LNode")).Concat(cmc.NodeVars.OrderBy(v => v.Key.Name).Select(kvp => kvp.Value ? F.Call(S.Assign, F.Id(kvp.Key), F.Null) : F.Id(kvp.Key)))));
            }
            if (cmc.ListVars.Count > 0)
            {
                LNode type = LNode.Call(CodeSymbols.Of, LNode.List(LNode.Id((Symbol)"VList"), LNode.Id((Symbol)"LNode")));
                output.Add(F.Call(S.Var, ListExt.Single(type).Concat(cmc.ListVars.OrderBy(v => v.Key.Name).Select(kvp => kvp.Value ? LNode.Call(CodeSymbols.Assign, LNode.List(F.Id(kvp.Key), LNode.Call(CodeSymbols.Default, LNode.List(type)))).SetStyle(NodeStyle.Operator) : F.Id(kvp.Key)))));
            }
            if (output.Count == 0)
            {
                return(ifStmt);
            }
            else
            {
                output.Add(ifStmt);
                return(F.Braces(output.ToVList()));
            }
        }
Exemple #36
0
		public static LNode UnpackTuple(LNode node, IMessageSink sink)
		{
			var a = node.Args;
			if (a.Count == 2 && a[0].CallsMin(S.Tuple, 1)) {
				var output = new WList<LNode>();
				var tuple = a[0].Args;
				var rhs = a[1];
				
				// Avoid evaluating rhs more than once, if it doesn't look like a simple variable
				rhs = MaybeAddTempVarDecl(rhs, output);

				for (int i = 0; i < tuple.Count; i++) {
					var itemi = F.Dot(rhs, F.Id(GSymbol.Get("Item" + (i + 1))));
					if (tuple[i].Calls(S.Var, 2))
						output.Add(F.Var(tuple[i].Args[0], tuple[i].Args[1], itemi));
					else
						output.Add(F.Call(S.Assign, tuple[i], itemi));
				}
				return F.Call(S.Splice, output.ToVList());
			}
			return null;
		}