//Copyright (c) 2010 Christopher E. S. King (http://chriskingconsulting.com) // //Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files // (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Xml.Linq; using System.Reflection; using System.Collections; using System.IO; using System.Diagnostics.Contracts; using King.Extensions; using King.Text; using King.Reflection; using King.Language; using King.Language.CSharp.Antlr3; using Antlr.Runtime; using Antlr.Runtime.Tree; namespace King.Language.CSharp { namespace Antlr3 { public partial class CSharpAst { public override void Recover(IIntStream input, RecognitionException re) { throw re; } } public partial class CSharpParser { private HashSet m_preProcessorSymbols; private bool m_emitPreProcessor; partial void OnCreated() { m_preProcessorSymbols = new HashSet(StringComparer.InvariantCultureIgnoreCase); } protected override object RecoverFromMismatchedToken(IIntStream input, int ttype, BitSet follow) { throw new MismatchedTokenException(ttype, input, TokenNames); } private CSharpLexer Lexer { get { return (CSharpLexer)TokenStream.TokenSource; } } private bool EmitPreProcessor { get { return m_emitPreProcessor; } set { m_emitPreProcessor = value; } } private bool SkipSection { get { return Lexer.SkipSection; } set { Lexer.SkipSection = value; } } private void Define(IToken preProcessorSymbol) { m_preProcessorSymbols.Add(preProcessorSymbol.Text); } private void Undefine(IToken preProcessorSymbol) { m_preProcessorSymbols.Remove(preProcessorSymbol.Text); } private bool IsDefined(IToken preProcessorSymbol) { return m_preProcessorSymbols.Contains(preProcessorSymbol.Text); } } public partial class CSharpLexer { internal static CSharpAstNodeType AntlrTypeToAstNodeType(int value) { return s_tokens[value]; } static CSharpLexer() { var tokens = from o in typeof(CSharpLexer).Get(isPublic: true, isStatic: true) join e in typeof(CSharpAstNodeType).Get() on o.Name equals e.Name select new { Key = (int)o.GetValue(), Value = (CSharpAstNodeType)e.GetValue() }; s_tokens = tokens.ToDictionary(o => o.Key, o => o.Value); } private static Dictionary s_tokens; private bool m_active; internal bool SkipSection { //get { return false; } get { return m_active; } set { m_active = value; } } public void skip() { Skip(); } protected override object RecoverFromMismatchedToken(IIntStream input, int ttype, BitSet follow) { throw new MismatchedTokenException(ttype, input, TokenNames); } } public partial class BugLexer { public void skip() { Skip(); } } } public enum CSharpAstNodeType { ROOT, BOOLEAN_LITERAL, CLOSE_PARENTHESIS, COMMA, CONDITIONAL_SYMBOL, DELIMITED_COMMENT, DOUBLE_AMPERSTAND, DOUBLE_BAR, DOUBLE_EQUAL, EXCLAMATION, EXCLAMATION_EQUAL, INTEGER, NEW_LINE, OPEN_PARENTHESIS, PP_DEFINE, PP_ELSE, PP_ELSE_IF, PP_END_IF, PP_END_REGION, PP_ERROR, PP_LINE, PP_LINE_DEFAULT, PP_LINE_HIDDEN, PP_IF, PP_REGION, PP_SKIPPED_CHARACTERS, PP_UNDEFINE, PP_WARNING, PRAGMA_WARNING_DISABLE, PRAGMA_WARNING_RESTORE, SINGLE_LINE_COMMENT, WHITESPACE, } public enum CSharpExpressionType { Not, OrElse, AndAlso, Equal, NotEqual, Constant, Variable, } public abstract class CSharpAst : Ast { public static IEnumerable Parse(string code, bool includePreProcessor = true, bool includeComments = true) { return Parse(code, "start", includePreProcessor, includeComments).ToEnumerable(); } internal static object Parse(string code, string rule, bool includePreProcessor = true, bool includeComments = true) { var ast = s_parser.Parse(rule, code); if (ast is CommonTree) return Construct((CommonTree)ast); return ((IEnumerable)ast).Select(o => Construct(o)); } private static CSharpAst ConstructComments(CommonTree tree, Token token) { var type = tree.Token.Type; // comment delimited if (type == CSharpLexer.DELIMITED_COMMENT) return new CSharpComment(Regex.Match(token.Text, @"^[/][*](?.*)[*][/]$").Get("comment"), true, token); // comment single-line else if (type == CSharpLexer.SINGLE_LINE_COMMENT) return new CSharpComment(Regex.Match(token.Text, @"^[/][/](?.*)$").Get("comment"), false, token); return null; } private static CSharpAst ConstructPreProcessor(CommonTree tree, IEnumerable children, Token token) { var type = tree.Token.Type; var parentType = tree.Parent == null ? 0 : tree.Parent.Type; // #define if (type == CSharpLexer.PP_DEFINE) return new CSharpPreProcessorDeclaration(true, children, token); // #undef else if (type == CSharpLexer.PP_UNDEFINE) return new CSharpPreProcessorDeclaration(false, children, token); else if (type == CSharpLexer.PP_WARNING || type == CSharpLexer.PP_ERROR) { var message = Regex.Match(token.Text, @"^#(warning|error) (?.*)$").Get("message"); // #warning if (type == CSharpLexer.PP_WARNING) return new CSharpPreProcessorWarning(message, token); // #error else if (type == CSharpLexer.PP_ERROR) return new CSharpPreProcessorError(message, token); } // #pragma warning restore else if (type == CSharpLexer.PP_PRAGMA_WARNING_RESTORE) return new CSharpPreProcessorPragma(true, children, token); // #pragma warning disable else if (type == CSharpLexer.PP_PRAGMA_WARNING_DISABLE) return new CSharpPreProcessorPragma(false, children, token); // #region else if (type == CSharpLexer.PP_REGION) return new CSharpPreProcessorRegion(Regex.Match(token.Text, @"^#region\s*(?.*)$").Get("value"), children, token); // #line default else if (type == CSharpLexer.PP_LINE_DEFAULT) return new CSharpPreProcessorLine(false, token); // #line hidden else if (type == CSharpLexer.PP_LINE_HIDDEN) return new CSharpPreProcessorLine(true, token); // #line else if (type == CSharpLexer.PP_LINE) { var line = Regex.Match(token.Text, @"^#line\s*(?\d+)\s*([""](?.*)[""])?$"); return new CSharpPreProcessorLine(line.Get("line"), line.Get("file"), token); // } else if (type == CSharpLexer.PP_CONDITIONAL) return new CSharpPreProcessorConditional(children); // #if else if (type == CSharpLexer.PP_IF) return new CSharpPreProcessorIf(children, token); // #elif else if (type == CSharpLexer.PP_ELSE_IF) return new CSharpPreProcessorElif(children, token); // #else else if (type == CSharpLexer.PP_ELSE) return new CSharpPreProcessorElse(children, token); // if-defed-out else if (type == CSharpLexer.PP_SKIPPED_CHARACTERS) return new CSharpPreProcessorSkip(token.Text, token); // pre-processor symbol else if (type == CSharpLexer.CONDITIONAL_SYMBOL) { // declaration if (parentType == CSharpLexer.PP_DEFINE || parentType == CSharpLexer.PP_UNDEFINE) return new CSharpSymbol(token.Text, token); } return null; } private static CSharpAst ConstructExpression(CommonTree tree, IEnumerable children, Token token) { var type = tree.Token.Type; // binary operator "&&" if (type == CSharpLexer.DOUBLE_AMPERSTAND) return new CSharpBinaryExpression(CSharpExpressionType.AndAlso, children, token); // binary operator "||" else if (type == CSharpLexer.DOUBLE_BAR) return new CSharpBinaryExpression(CSharpExpressionType.OrElse, children, token); // binary operator "==" else if (type == CSharpLexer.DOUBLE_EQUAL) return new CSharpBinaryExpression(CSharpExpressionType.Equal, children, token); // binary operator "!=" else if (type == CSharpLexer.EXCLAMATION_EQUAL) return new CSharpBinaryExpression(CSharpExpressionType.NotEqual, children, token); // unary operator "!" else if (type == CSharpLexer.EXCLAMATION) return new CSharpUnaryExpression(CSharpExpressionType.Not, children, token); // true/false else if (type == CSharpLexer.BOOLEAN_LITERAL) return new CSharpConstantExpression(bool.Parse(token.Text), token); // true/false else if (type == CSharpLexer.INTEGER) return new CSharpConstantExpression(int.Parse(token.Text), token); return null; } private static CSharpAst Construct(CommonTree tree) { var commonToken = (CommonToken)tree.token; var type = tree.Token.Type; var token = new Token(commonToken.Text, commonToken.Line, commonToken.CharPositionInLine, commonToken.StartIndex, commonToken.StopIndex - commonToken.StartIndex, commonToken.TokenIndex); var children = tree.GetChildren().Select(o => Construct(o)); CSharpAst node = null; // pre-processor if ((node = ConstructPreProcessor(tree, children, token)) != null) return node; // comment else if ((node = ConstructComments(tree, token)) != null) return node; // expression else if ((node = ConstructExpression(tree, children, token)) != null) return node; else if (type == CSharpLexer.CONDITIONAL_SYMBOL) return new CSharpVariableExpression(token.Text, token); else throw new NotImplementedException(); } static CSharpAst() { s_parser = new Parser( typeof(Antlr3.CSharpLexer), typeof(Antlr3.CSharpParser), typeof(Antlr3.CSharpAst)); } private static Parser s_parser; protected CSharpAst(IEnumerable children = null, Token token = null) : base(children, token) { } } public abstract class CSharpPreProcessorCode : CSharpAst { protected CSharpPreProcessorCode(IEnumerable children = null, Token token = null) : base(children, token) { } } public sealed class CSharpPreProcessorDeclaration : CSharpPreProcessorCode { private bool m_define; public CSharpPreProcessorDeclaration(bool define, IEnumerable children, Token token = null) : base(children, token) { m_define = define; } public bool IsDefine { get { return m_define; } } public string Symbol { get { return ((CSharpSymbol)Children().Single()).Symbol; } } } public sealed class CSharpPreProcessorConditional : CSharpPreProcessorCode { public CSharpPreProcessorConditional(IEnumerable children = null) : base(children) { } public CSharpPreProcessorIf If { get { return (CSharpPreProcessorIf)Children().First(); } } public IEnumerable Elifs() { return Children().Skip(1).TakeWhile(o => o is CSharpPreProcessorElif).Cast(); } public CSharpPreProcessorElse Else { get { return Children().Last() as CSharpPreProcessorElse; } } } public sealed class CSharpPreProcessorIf : CSharpPreProcessorCode { public CSharpPreProcessorIf(IEnumerable children = null, Token token = null) : base(children, token) { } public CSharpExpression Test { get { return (CSharpExpression)Children().First(); } } } public sealed class CSharpPreProcessorElif : CSharpPreProcessorCode { public CSharpPreProcessorElif(IEnumerable children = null, Token token = null) : base(children, token) { } public CSharpExpression Test { get { return (CSharpExpression)Children().First(); } } } public sealed class CSharpPreProcessorElse : CSharpPreProcessorCode { public CSharpPreProcessorElse(IEnumerable children = null, Token token = null) : base(children, token) { } } public sealed class CSharpPreProcessorSkip : CSharpPreProcessorCode { private string m_text; public CSharpPreProcessorSkip(string text, Token token = null) : base(token: token) { m_text = text; } public string Text { get { return m_text; } } } public sealed class CSharpPreProcessorPragma : CSharpPreProcessorCode { private bool m_restore; public CSharpPreProcessorPragma(bool restore, IEnumerable children = null, Token token = null) : base(children, token) { m_restore = restore; } public IEnumerable Warnings() { return Children().Cast().Select(o => (int)o.Value); } public bool Restore { get { return m_restore; } } } public sealed class CSharpPreProcessorWarning : CSharpPreProcessorCode { private string m_message; public CSharpPreProcessorWarning(string message, Token token = null) : base(token: token) { m_message = message; } public string Message { get { return m_message; } } } public sealed class CSharpPreProcessorError : CSharpPreProcessorCode { private string m_message; public CSharpPreProcessorError(string message, Token token = null) : base(token: token) { m_message = message; } public string Message { get { return m_message; } } } public sealed class CSharpPreProcessorRegion : CSharpPreProcessorCode { private string m_value; public CSharpPreProcessorRegion(string value, IEnumerable children = null, Token token = null) : base(children, token) { m_value = value; } public string Value { get { return m_value; } } } public sealed class CSharpPreProcessorLine : CSharpPreProcessorCode { private int m_line; private string m_fileName; private bool? m_hidden; public CSharpPreProcessorLine(bool hidden, Token token = null) : base(token: token) { m_hidden = hidden; m_line = -1; } public CSharpPreProcessorLine(int line, string fileName, Token token = null) : base(token: token) { m_line = line; m_fileName = fileName; } public bool Hidden { get { return m_hidden == true; } } public bool Default { get { return m_hidden == false; } } public string FileName { get { return m_fileName; } } public int NewLine { get { return m_line; } } } public sealed class CSharpSymbol : CSharpPreProcessorCode { private string m_symbol; public CSharpSymbol(string symbol, Token token = null) : base(token: token) { m_symbol = symbol; } public string Symbol { get { return m_symbol; } } } public sealed class CSharpComment : CSharpPreProcessorCode { private string m_comment; private bool? m_delimited; public CSharpComment(string comment, bool? delimited = null, Token token = null) : base(token: token) { m_comment = comment; m_delimited = delimited; } public string Value { get { return m_comment; } } public bool? Delimited { get { return m_delimited; } } } public abstract class CSharpExpression : CSharpPreProcessorCode { public static CSharpExpression Parse(string code) { return (CSharpExpression)Parse(code, "pp_expression"); } private CSharpExpressionType m_expressionType; protected CSharpExpression(CSharpExpressionType expressionType, IEnumerable children = null, Token token = null) : base(children, token) { m_expressionType = expressionType; } public CSharpExpressionType ExpressionType { get { return m_expressionType; } } } public sealed class CSharpVariableExpression : CSharpExpression { private string m_variable; public CSharpVariableExpression(string variable, Token token = null) : base(CSharpExpressionType.Variable, token: token) { m_variable = variable; } public string Symbol { get { return m_variable; } } } public sealed class CSharpConstantExpression : CSharpExpression { private object m_value; public CSharpConstantExpression(object value, Token token = null) : base(CSharpExpressionType.Constant, token: token) { m_value = value; } public object Value { get { return m_value; } } } public sealed class CSharpBinaryExpression : CSharpExpression { public CSharpBinaryExpression( CSharpExpressionType expressionType, IEnumerable children, Token token = null) : base(expressionType, children, token) { } public CSharpExpression Left { get { return (CSharpExpression)Children().First(); } } public CSharpExpression Right { get { return (CSharpExpression)Children().Second(); } } } public sealed class CSharpUnaryExpression : CSharpExpression { public CSharpUnaryExpression( CSharpExpressionType expressionType, IEnumerable children, Token token = null) : base(expressionType, children, token) { } public CSharpExpression Operand { get { return (CSharpExpression)Children().Single(); } } } }