Click here to Skip to main content
15,896,063 members
Articles / Programming Languages / C#

Writing Your First Visual Studio Language Service

Rate me:
Please Sign up or sign in to vote.
4.95/5 (61 votes)
11 Dec 2009CPOL8 min read 234.1K   4K   157  
A guide to writing a language service for Visual Studio using Irony.
#region License
/* **********************************************************************************
 * This source code is subject to terms and conditions of the MIT License
 * for Irony. A copy of the license can be found in the License.txt file
 * at the root of this distribution. 
 * By using this source code in any fashion, you are agreeing to be bound by the terms of the 
 * MIT License.
 * You must not remove this notice from this software.
 * **********************************************************************************/
#endregion
//Authors: Roman Ivantsov, Philipp Serr

using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;

namespace Irony.Compiler {
  public static class TerminalFactory {

    public static StringLiteral CreateCSharpString(string name) {
      StringLiteral term = new StringLiteral(name, "\"", StringFlags.AllowsAllEscapes);
      term.AddPrefix("@", StringFlags.NoEscapes | StringFlags.AllowsLineBreak | StringFlags.AllowsDoubledQuote);
      return term;
    }
    public static StringLiteral CreateCSharpChar(string name) {
      StringLiteral term = new StringLiteral(name, "'", StringFlags.IsChar);
      return term;
    }

    public static StringLiteral CreateVbString(string name) {
      StringLiteral term = new StringLiteral(name);
      term.AddStartEnd("\"", StringFlags.NoEscapes | StringFlags.AllowsDoubledQuote);
      term.AddSuffixCodes("$", TypeCode.String);
      term.AddSuffixCodes("c", TypeCode.Char);
      return term;
    }

    public static StringLiteral CreatePythonString(string name) {
      StringLiteral term = new StringLiteral(name);
      term.AddStartEnd("'", StringFlags.AllowsAllEscapes);
      term.AddStartEnd("'''", StringFlags.AllowsAllEscapes | StringFlags.AllowsLineBreak);
      term.AddStartEnd("\"", StringFlags.AllowsAllEscapes);
      term.AddStartEnd("\"\"\"", StringFlags.AllowsAllEscapes | StringFlags.AllowsLineBreak);

      term.AddPrefix("u", StringFlags.AllowsAllEscapes);
      term.AddPrefix("r", StringFlags.NoEscapes );
      term.AddPrefix("ur", StringFlags.NoEscapes);
 
      return term;
    }

		//http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf section 9.4.4
    public static NumberLiteral CreateCSharpNumber(string name) {
      NumberLiteral term = new NumberLiteral(name);
      term.DefaultIntTypes = new TypeCode[] { TypeCode.Int32, TypeCode.UInt32, TypeCode.Int64, TypeCode.UInt64 };
      term.DefaultFloatType = TypeCode.Double;
      term.AddPrefix("0x", NumberFlags.Hex);
      term.AddSuffixCodes("u", TypeCode.UInt32, TypeCode.UInt64);
      term.AddSuffixCodes("l", TypeCode.Int64, TypeCode.UInt64);
      term.AddSuffixCodes("ul", TypeCode.UInt64);
      term.AddSuffixCodes("f", TypeCode.Single);
			term.AddSuffixCodes("d", TypeCode.Double);
      term.AddSuffixCodes("m", TypeCode.Decimal);
      return term;
    }
    //http://www.microsoft.com/downloads/details.aspx?FamilyId=6D50D709-EAA4-44D7-8AF3-E14280403E6E&displaylang=en section 2
    public static NumberLiteral CreateVbNumber(string name) {
      NumberLiteral term = new NumberLiteral(name);
      term.DefaultIntTypes = new TypeCode[] { TypeCode.Int32, TypeCode.Int64 };
      //term.DefaultFloatType = TypeCode.Double; it is default
      term.AddPrefix("&H", NumberFlags.Hex);
      term.AddPrefix("&O", NumberFlags.Octal);
      term.AddSuffixCodes("S", TypeCode.Int16);
      term.AddSuffixCodes("I", TypeCode.Int32);
      term.AddSuffixCodes("%", TypeCode.Int32);
      term.AddSuffixCodes("L", TypeCode.Int64);
      term.AddSuffixCodes("&", TypeCode.Int64);
      term.AddSuffixCodes("D", TypeCode.Decimal);
      term.AddSuffixCodes("@", TypeCode.Decimal);
      term.AddSuffixCodes("F", TypeCode.Single);
      term.AddSuffixCodes("!", TypeCode.Single);
      term.AddSuffixCodes("R", TypeCode.Double);
      term.AddSuffixCodes("#", TypeCode.Double);
      term.AddSuffixCodes("US", TypeCode.UInt16);
      term.AddSuffixCodes("UI", TypeCode.UInt32);
      term.AddSuffixCodes("UL", TypeCode.UInt64);
      return term;
    }
    //http://docs.python.org/ref/numbers.html
    public static NumberLiteral CreatePythonNumber(string name) {
      NumberLiteral term = new NumberLiteral(name, NumberFlags.AllowStartEndDot);
      //default int types are Integer (32bit) -> LongInteger (BigInt); Try Int64 before BigInt: Better performance?
      term.DefaultIntTypes = new TypeCode[] { TypeCode.Int32, TypeCode.Int64, NumberLiteral.TypeCodeBigInt };
      // term.DefaultFloatType = TypeCode.Double; -- it is default
      //float type is implementation specific, thus try decimal first (higher precision)
      //term.DefaultFloatTypes = new TypeCode[] { TypeCode.Decimal, TypeCode.Double };
      term.AddPrefix("0x", NumberFlags.Hex);
      term.AddPrefix("0", NumberFlags.Octal);
      term.AddSuffixCodes("L", TypeCode.Int64, NumberLiteral.TypeCodeBigInt);
      term.AddSuffixCodes("J", NumberLiteral.TypeCodeImaginary);
      return term;
    }

    //Note - this is incomplete implementation; need to add functionality to NumberTerminal class to support type detection based 
    // on exponent symbol. 
    // From R6RS:
    //  ... representations of number objects may be written with an exponent marker that indicates the desired precision 
    // of the inexact representation. The letters s, f, d, and l specify the use of short, single, double, and long precision, respectively. 
    public static NumberLiteral CreateSchemeNumber(string name) {
      NumberLiteral term = new NumberLiteral(name);
      term.DefaultIntTypes = new TypeCode[] { TypeCode.Int32, TypeCode.Int64, NumberLiteral.TypeCodeBigInt };
      term.DefaultFloatType = TypeCode.Double; // it is default
      term.ExponentSymbols = "sfdl";
      term.AddPrefix("#b", NumberFlags.Binary);
      term.AddPrefix("#o", NumberFlags.Octal);
      term.AddPrefix("#x", NumberFlags.Hex);
      term.AddPrefix("#d", NumberFlags.None);
      term.AddPrefix("#i", NumberFlags.None); // inexact prefix, has no effect
      term.AddPrefix("#e", NumberFlags.None); // exact prefix, has no effect
      term.AddSuffixCodes("J", NumberLiteral.TypeCodeImaginary);
      return term;
    }


    public static IdentifierTerminal CreateCSharpIdentifier(string name) {
      IdentifierTerminal id = new IdentifierTerminal(name, IdFlags.AllowsEscapes | IdFlags.CanStartWithEscape);
      id.AddPrefix("@", IdFlags.IsNotKeyword);
      //From spec:
      //Start char is "_" or letter-character, which is a Unicode character of classes Lu, Ll, Lt, Lm, Lo, or Nl 
      id.StartCharCategories.AddRange(new UnicodeCategory[] {
         UnicodeCategory.UppercaseLetter, //Ul
         UnicodeCategory.LowercaseLetter, //Ll
         UnicodeCategory.TitlecaseLetter, //Lt
         UnicodeCategory.ModifierLetter,  //Lm
         UnicodeCategory.OtherLetter,     //Lo
         UnicodeCategory.LetterNumber     //Nl
      });
      //Internal chars
      /* From spec:
      identifier-part-character: letter-character | decimal-digit-character | connecting-character |  combining-character |
          formatting-character
*/
      id.CharCategories.AddRange(id.StartCharCategories); //letter-character categories
      id.CharCategories.AddRange(new UnicodeCategory[] {
        UnicodeCategory.DecimalDigitNumber, //Nd
        UnicodeCategory.ConnectorPunctuation, //Pc
        UnicodeCategory.SpacingCombiningMark, //Mc
        UnicodeCategory.NonSpacingMark,       //Mn
        UnicodeCategory.Format                //Cf
      });
      //Chars to remove from final identifier
      id.CharsToRemoveCategories.Add(UnicodeCategory.Format);
      return id;
    }

    public static IdentifierTerminal CreatePythonIdentifier(string name) {
      IdentifierTerminal id = new IdentifierTerminal("Identifier"); //defaults are OK
      return id;
    }

    public static StringLiteral CreateSqlExtIdentifier(string name) {
      StringLiteral term = new StringLiteral(name);
      term.AddStartEnd("[", "]", StringFlags.NoEscapes);
      term.AddStartEnd("\"", StringFlags.NoEscapes);
      return term;
    }

  }//class
}//namespace

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions