-
Notifications
You must be signed in to change notification settings - Fork 2
Decorations
Decorations are mainly for syntax highlight. By using Sarcasm's formatter extremely advanced syntax highlight can be provided for the unparsed text. Each utoken can have a decoration which is actually a dictionary with key-value pairs. Sarcasm's formatter knows nothing about colors, fonts, etc.
In your grammar's formatter you can decorate e.g. red foreground colour on keyword utokens. This could be done e.g. by using System.Windows.Media.Color.Red, and although Sarcasm would be platform independent (since it knows nothing about e.g. ), however your formatter would be tied to WPF, therefore it would not be platform independent, thus could not be portable.
Therefore Sarcasm provide a bunch of decoration keys and types (they are almost equivalent to the styles in WPF). A decoration key is an entity that designates the target for a certain decoration, e.g. the background color, the foreground color, the font style, etc. Each decoration key has a generic type parameter (for typesafety), which determine the type of the values that can be assigned to the given key.
The predefined decoration keys can be found in the DecorationKey
class (of course, you can define your own decoration keys, if you wish):
public static class DecorationKey
{
public static DecorationKey<FontFamily> FontFamily { get; private set; }
public static DecorationKey<FontStretch> FontStretch { get; private set; }
public static DecorationKey<FontStyle> FontStyle { get; private set; }
public static DecorationKey<FontWeight> FontWeight { get; private set; }
public static DecorationKey<double> FontSize { get; private set; }
public static DecorationKey<double> FontSizeRelativePercent { get; private set; }
public static DecorationKey<TextDecoration> TextDecoration { get; private set; }
public static DecorationKey<BaselineAlignment> BaselineAlignment { get; private set; }
public static DecorationKey<Color> Foreground { get; private set; }
public static DecorationKey<Color> Background { get; private set; }
}
public enum FontWeight { Normal, Bold, Thin }
public enum FontStyle { Normal, Italic }
public enum Color : uint
{
AliceBlue = (255u << 24 | 240u << 16 | 248u << 8 | 255u),
AntiqueWhite = (255u << 24 | 250u << 16 | 235u << 8 | 215u),
Aqua = (255u << 24 | 0u << 16 | 255u << 8 | 255u),
Aquamarine = (255u << 24 | 127u << 16 | 255u << 8 | 212u),
// ...
}
// etc.
Using these decoration keys and decoration types you can keep your formatter portable.
To decorate utokens you need to override the GetDecoration
method in your formatter, and return the decoration. Beside the utoken, you also get an UnparsableAst
parameter, which is not null if the utoken is an UtokenText
(i.e. not an UtokenIndent
and not an UtokenWhitespace
). You can also use the utoken's Discriminator
property which can be set by you if you directly yields utokens in a BnfiTermConversion in your grammar, or is set by Sarcasm to a predefined value (these values can be found in the Formatter
base class):
public static Discriminator CommentContent { get; private set; }
public static Discriminator CommentStartSymbol { get; private set; }
public static Discriminator CommentEndSymbol { get; private set; }
public static Discriminator StringLiteralContent { get; private set; }
public static Discriminator StringLiteralStartSymbol { get; private set; }
public static Discriminator StringLiteralEndSymbol { get; private set; }
public static Discriminator DataLiteralContent { get; private set; }
public static Discriminator DsvLiteralTerminator { get; private set; }
public static Discriminator QuotedValueLiteralStartSymbol { get; private set; }
public static Discriminator QuotedValueLiteralEndSymbol { get; private set; }
public static Discriminator NumberLiteralContent { get; private set; }
public static Discriminator NumberLiteralBasePrefix { get; private set; }
public static Discriminator NumberLiteralTypeModifierSuffix { get; private set; }
So, let's write a crazy decoration, where give different decoration to comment contents, comment end symbols, string literal start symbols, utokens that comes from if
(which means the if
, then
, else
keywords and the parentheses of if
) and special decoration for the if
's left parenthesis, program
keyword, and different decoration for even and odd number literals:
protected override IDecoration GetDecoration(Utoken utoken, UnparsableAst target)
{
var decoration = base.GetDecoration(utoken, target);
// default decoration
decoration.Add(DecorationKey.FontFamily, FontFamily.GenericMonospace);
if (target != null)
{
if (utoken.Discriminator == CommentContent)
{
decoration
.Add(DecorationKey.Foreground, Color.Pink)
.Add(DecorationKey.TextDecoration, TextDecoration.Strikethrough)
.Add(DecorationKey.FontStyle, FontStyle.Italic)
;
}
else if (utoken.Discriminator == CommentEndSymbol)
{
decoration
.Add(DecorationKey.Foreground, Color.Blue)
.Add(DecorationKey.Background, Color.Yellow)
;
}
else if (utoken.Discriminator == StringLiteralStartSymbol)
{
decoration
.Add(DecorationKey.FontSizeRelativePercent, 2)
.Add(DecorationKey.Foreground, Color.Red)
.Add(DecorationKey.Background, Color.Blue)
;
}
else if (target.AstValue is D.If)
{
if (target.BnfTerm == B.LEFT_PAREN)
{
decoration
.Add(DecorationKey.FontWeight, FontWeight.Bold)
.Add(DecorationKey.FontSizeRelativePercent, 2)
.Add(DecorationKey.Foreground, Color.Blue)
;
}
else
{
decoration
.Add(DecorationKey.FontWeight, FontWeight.Bold)
.Add(DecorationKey.TextDecoration, TextDecoration.Underline)
;
}
}
else if (target.BnfTerm == B.PROGRAM)
{
decoration
.Add(DecorationKey.FontStyle, FontStyle.Italic)
.Add(DecorationKey.Foreground, Color.Red)
.Add(DecorationKey.Background, Color.Yellow)
.Add(DecorationKey.FontSize, 30);
}
else if (target.AstValue is INumberLiteral)
{
INumberLiteral number = (INumberLiteral)target.AstValue;
if (number.Value is int)
{
if (((int)number.Value) % 2 == 0)
decoration
.Add(DecorationKey.Foreground, Color.Red)
;
else
decoration
.Add(DecorationKey.Foreground, Color.Green)
.Add(DecorationKey.Background, Color.Yellow)
;
}
}
}
return decoration;
}
This is a short version of the "Crazy syntax highlight" that can be found in MiniPL's GrammarP. The crazy syntax highlight looks like this (this is printscreen of Mystique):
Of course, you wrote your formatter because you would like to enjoy the nice colours, fonts etc. in your application which is written for a specific platform. Thus, you need to convert these platform independent decorations to platform specific decoration. The easiest way to do this is to write a DecorationConnector which does that conversion (e.g. a DecorationConnector for WPF, and then you can use this decoration connector for every portable grammars/formatters you use in your WPF applications).
A decoration connector consists of a static DecorationKey
class and a the connector class itself. The DecorationKey
class is similar to the portable one in Sarcasm, and it defines the decoration keys available in the given platform. The connector class should implement the IReadOnlyDecoration
interface, and it does the actual conversion between the platform independent and platform dependent decorations.
Note, that some decoration keys might be missing in certain platforms, or one portable decoration key might be mapped to more platform specific decoration keys, or more portable decoration keys might be mapped to one platform specific decoration key.
E.g. DecorKeyConnector.FontStyle
for Windows Forms has the type Drawing.FontStyle
and holds the Bold
, Italic
, Underline
and Strikeout
decoration values, while in the portable decoration world these are mapped to different decoration keys (DecorationKey.FontStyle
, DecorationKey.FontWeight
, DecorationKey.TextDecoration
) and types (Styles.FontStyle
, Styles.FontWeight
, Styles.TextDecoration
).
Sarcasm has four predefined decoration connectors in separates projects: DecorationConnector.WPF, DecorationConnector.Forms, DecorationConnector.Silverlight and DecorationConnector.Android (this latter one is for Xamarin.Android).
If you are interested in the unparser's lazy evaluation, continue with Lazy Evaluation.