// Parser.cs // // Author: // Aaron Bockover // // Copyright 2013 Xamarin, Inc. using System; using System.IO; using System.Linq; using System.Reflection; using System.Collections; using System.Collections.Generic; using System.Linq.Expressions; namespace Xamarin.Pmcs { public class Parser { readonly Func lowestPrecedenceOpParser; public BinOpParser Multiplicitive { get; set; } public BinOpParser Additive { get; set; } public BinOpParser Shift { get; set; } public BinOpParser Relational { get; set; } public BinOpParser Equality { get; set; } public BinOpParser LogicalAnd { get; set; } public BinOpParser LogicalExclusiveOr { get; set; } public BinOpParser LogicalOr { get; set; } public BinOpParser AndAlso { get; set; } public BinOpParser OrElse { get; set; } public BinOpParser Assign { get; set; } public Parser () { // Factor // ^-- Primary // ^-- Unary Multiplicitive = new BinOpParser ("Multiplicive", this, Unary) { new BinOpParserEntry (TokenType.Multiply, Expression.Multiply), new BinOpParserEntry (TokenType.Divide, Expression.Divide), new BinOpParserEntry (TokenType.Modulo, Expression.Modulo) }; Additive = new BinOpParser ("Additive", this, Multiplicitive) { new BinOpParserEntry (TokenType.Add, Expression.Add (l, r)), new BinOpParserEntry (TokenType.Subtract, Expression.Subtract), }; Shift = new BinOpParser ("Shift", this, Additive) { new BinOpParserEntry (TokenType.LeftShift, Expression.LeftShift), new BinOpParserEntry (TokenType.RightShift, Expression.RightShift) }; Relational = new BinOpParser ("Relational", this, Shift) { new BinOpParserEntry (TokenType.LessThan, Expression.LessThan), new BinOpParserEntry (TokenType.GreaterThan, Expression.GreaterThan), new BinOpParserEntry (TokenType.LessThanOrEqual, Expression.LessThanOrEqual), new BinOpParserEntry (TokenType.GreaterThanOrEqual, Expression.GreaterThanOrEqual), new BinOpParserEntry (TokenType.Is, (l, r) => Expression.TypeIs (l, (Type)((ConstantExpression)r).Value)), new BinOpParserEntry (TokenType.As, (l, r) => Expression.TypeAs (l, (Type)((ConstantExpression)r).Value)), new BinOpParserEntry (TokenType.In, (l, r) => Expression.Call (enumerable_Contains.MakeGenericMethod (l.Type), r, l)) }; Equality = new BinOpParser ("Equality", this, Relational) { new BinOpParserEntry (TokenType.Equal, Expression.Equal), new BinOpParserEntry (TokenType.NotEqual, Expression.NotEqual) }; LogicalAnd = new BinOpParser ("LogicalAnd", this, Equality) { new BinOpParserEntry (TokenType.And, Expression.And) }; LogicalExclusiveOr = new BinOpParser ("LogicalExclusiveOr", this, LogicalAnd) { new BinOpParserEntry (TokenType.ExclusiveOr, Expression.ExclusiveOr) }; LogicalOr = new BinOpParser ("LogicalOr", this, LogicalExclusiveOr) { new BinOpParserEntry (TokenType.Or, Expression.Or) }; AndAlso = new BinOpParser ("ConditionalAnd", this, LogicalOr) { new BinOpParserEntry (TokenType.AndAlso, Expression.AndAlso) }; OrElse = new BinOpParser ("ConditionalOr", this, AndAlso) { new BinOpParserEntry (TokenType.OrElse, Expression.OrElse) }; Func conditional = () => { try { var expr = OrElse.Descend (); if (current.Type == TokenType.QuestionMark) { Read (); var trueExpr = Expr (); Match (TokenType.Colon); expr = Expression.Condition (expr, trueExpr, Expr ()); } return expr; } finally { trace.Leave (); } }; var assign = new BinOpParser ("Assign", this, conditional) { new BinOpParserEntry (TokenType.Assign, Expression.Assign), new BinOpParserEntry (TokenType.MultiplyAssign, Expression.MultiplyAssign), new BinOpParserEntry (TokenType.DivideAssign, Expression.DivideAssign), new BinOpParserEntry (TokenType.ModuloAssign, Expression.ModuloAssign), new BinOpParserEntry (TokenType.AddAssign, Expression.AddAssign), new BinOpParserEntry (TokenType.SubtractAssign, Expression.SubtractAssign), new BinOpParserEntry (TokenType.LeftShiftAssign, Expression.LeftShiftAssign), new BinOpParserEntry (TokenType.RightShiftAssign, Expression.RightShiftAssign), new BinOpParserEntry (TokenType.AndAssign, Expression.AndAssign), new BinOpParserEntry (TokenType.ExclusiveOrAssign, Expression.ExclusiveOrAssign), new BinOpParserEntry (TokenType.OrAssign, Expression.OrAssign), }; lowestPrecedenceOpParser = assign.Descend; } IEnumerable Concat (IEnumerable a, IEnumerable b) { if (a == null && b == null) return null; else if (a == null) return b; else if (b == null) return a; return a.Concat (b); } public Expression Parse (Tokenizer tokenizer, IEnumerable parameters = null, IEnumerable variables = null, IEnumerable preamble = null) { return Expression.Lambda (Parse (tokenizer, parameters, variables, preamble), parameters); } public BlockExpression Parse (Tokenizer tokenizer, IEnumerable parameters = null, IEnumerable variables = null, IEnumerable preamble = null) { return null; } Token Read () { tokenizer.Scan (); current = tokenizer.Current; return current; } void Match (TokenType type, bool read = true) { if (current.Type != type) throw new SyntaxException (current, type); if (read) Read (); } Expression Expr () { try { trace.Enter ("Expr"); return lowestPrecedenceOpParser (); } finally { trace.Leave (); } } Expression Factor () { try { trace.Enter ("Factor"); Expression expr = null; switch (current.Type) { case TokenType.Constant: expr = current.Expression; Read (); return expr; case TokenType.Identifier: return Resolve (); case TokenType.LeftParen: Read (); expr = Expr (); Match (TokenType.RightParen); return expr; default: throw new SyntaxException (current); } } finally { trace.Leave (); } } Expression Primary () { try { trace.Enter ("Primary"); var expr = Factor (); while (current.Type == TokenType.Period) { switch (current.Type) { case TokenType.Period: Read (); expr = Resolve (expr); break; } } return expr; } finally { trace.Leave (); } } Expression Unary () { try { trace.Enter ("Unary"); switch (current.Type) { case TokenType.Subtract: Read (); return Expression.Negate (Unary ()); case TokenType.Add: Read (); return Expression.UnaryPlus (Unary ()); case TokenType.Not: Read (); return Expression.Not (Unary ()); case TokenType.OnesComplement: Read (); return Expression.OnesComplement (Unary ()); case TokenType.Increment: Read (); return Expression.PostIncrementAssign (Unary ()); case TokenType.Decrement: Read (); return Expression.PostDecrementAssign (Unary ()); case TokenType.LeftParen: if (!current.Flags.HasFlag (TokenFlags.MaybeCast)) return Primary (); Read (); // a left paren flagged as a possible cast is guaranteed // by the tokenizer to be followed by an identifier, // but just in case... Match (TokenType.Identifier, read: false); Type type = null; var typeExpr = Resolve () as ConstantExpression; if (typeExpr == null || (type = typeExpr.Value as Type) == null) { // the tokenizer guarantees that this will be a factor or an error var expr = Factor (); Match (TokenType.RightParen); return expr; } Match (TokenType.RightParen); return Expression.Convert (Primary (), type); default: return Primary (); } } finally { trace.Leave (); } } public struct BinOpParserEntry { public TokenType Type; public Func Generator; public BinOpParserEntry (TokenType op, Func generator) { Type = op; Generator = generator; } } public class BinOpParser : IEnumerable { List operators = new List (); Func descender; Parser parser; string name; public BinOpParser (string name, Parser parser, Func descender) { this.name = name; this.parser = parser; this.descender = descender; } public BinOpParser (string name, Parser parser, BinOpParser descender) { this.name = name; this.parser = parser; this.descender = descender.Descend; } public void Add (BinOpParserEntry op) { operators.Add (op); } public bool IsMatch (TokenType op, out BinOpParserEntry entry) { entry = default (BinOpParserEntry); foreach (var e in operators) { if (e.Type == op) { entry = e; return true; } } return false; } public void Swizzle (TokenType op, Func, Func> swizzler) { for (int i = 0; i < operators.Count; i++) { if (operators [i].Type == op) { operators [i] = new BinOpParserEntry { Type = op, Generator = swizzler (operators [i].Generator) }; break; } } } public IEnumerator GetEnumerator () { return operators.GetEnumerator (); } IEnumerator IEnumerable.GetEnumerator () { return operators.GetEnumerator (); } public Expression Descend () { try { parser.trace.Enter (name); var expr = descender (); BinOpParserEntry op; while (IsMatch (parser.current.Type, out op)) { parser.Read (); expr = op.Generator (expr, descender ()); } return expr; } finally { parser.trace.Leave (); } } } class Trace { Parser parser; Stack depth = new Stack (); public TextWriter Writer { get; set; } public Trace (Parser parser, TextWriter writer = null) { this.parser = parser; Writer = writer; } void Indent (int level) { if (Writer != null) Writer.Write (String.Empty.PadRight (level * 2)); } public void Enter (string name) { if (Writer != null) { Indent (depth.Count); depth.Push (name); Writer.WriteLine ("Enter {0} @ {1}", name, parser.current); } } public void Leave () { if (Writer != null) { var name = depth.Pop (); Indent (depth.Count); Writer.WriteLine ("Leave {0} @ {1}", name, parser.current); } } } } }