public void ParseRgbColorProperty() { var nodes = StyleParser.Parse(@" style withBg { BackgroundColor = rgb(10, 20, 30); } "); Assert.AreEqual(1, nodes.Count); var rootNode = ((StyleRootNode)nodes[0]); Assert.AreEqual("withBg", rootNode.identifier); Assert.AreEqual(null, rootNode.tagName); Assert.AreEqual(1, rootNode.children.Count); var property = (((PropertyNode)rootNode.children[0])); Assert.AreEqual("BackgroundColor", property.identifier); Assert.AreEqual(StyleASTNodeType.Rgb, property.children[0].type); var rgbNode = (RgbNode)property.children[0]; Assert.AreEqual(StyleASTNodeType.Rgb, rgbNode.type); Assert.AreEqual(StyleASTNodeFactory.NumericLiteralNode("10"), rgbNode.red); Assert.AreEqual(StyleASTNodeFactory.NumericLiteralNode("20"), rgbNode.green); Assert.AreEqual(StyleASTNodeFactory.NumericLiteralNode("30"), rgbNode.blue); }
private void ParseAnimationKeyFrames(AnimationRootNode rootNode) { while (tokenStream.HasMoreTokens && !AdvanceIfTokenType(StyleTokenType.BracesClose)) { string value = AssertTokenTypeAndAdvance(StyleTokenType.Number); AssertTokenTypeAndAdvance(StyleTokenType.Mod); KeyFrameNode keyFrameNode = StyleASTNodeFactory.KeyFrameNode(int.Parse(value)); while (AdvanceIfTokenType(StyleTokenType.Comma)) { keyFrameNode.keyframes.Add(int.Parse(AssertTokenTypeAndAdvance(StyleTokenType.Number))); AssertTokenTypeAndAdvance(StyleTokenType.Mod); } AssertTokenTypeAndAdvance(StyleTokenType.BracesOpen); while (tokenStream.HasMoreTokens && !AdvanceIfTokenType(StyleTokenType.BracesClose)) { ParseProperties(keyFrameNode); } rootNode.AddKeyFrameNode(keyFrameNode); } }
private void ParseStateOrAttributeGroup(StyleNodeContainer styleRootNode) { switch (tokenStream.Current.styleTokenType) { // this is the state group case StyleTokenType.Identifier: StyleStateContainer stateGroupRootNode = StyleASTNodeFactory.StateGroupRootNode(tokenStream.Current); tokenStream.Advance(); AssertTokenTypeAndAdvance(StyleTokenType.BracketClose); AssertTokenTypeAndAdvance(StyleTokenType.BracesOpen); ParseProperties(stateGroupRootNode); AssertTokenTypeAndAdvance(StyleTokenType.BracesClose); styleRootNode.AddChildNode(stateGroupRootNode); break; case StyleTokenType.AttributeSpecifier: ParseAttributeGroup(); break; default: throw new ParseException(tokenStream.Current, "Expected either a group state identifier (hover etc.)" + " or an attribute identifier (attr:...)"); } }
private StyleASTNode ParseIdentifierInParentheses() { AssertTokenTypeAndAdvance(StyleTokenType.ParenOpen); StyleASTNode identifier; switch (tokenStream.Current.styleTokenType) { case StyleTokenType.Identifier: identifier = StyleASTNodeFactory.IdentifierNode(tokenStream.Current.value); break; case StyleTokenType.At: identifier = ParseConstReference(); break; default: throw new ParseException(tokenStream.Current, "Was expecting an identifier or a reference."); } tokenStream.Advance(); // todo: add support for parameters here AssertTokenTypeAndAdvance(StyleTokenType.ParenClose); return(identifier); }
private RunNode ParseRunNode(RunCommandType cmdType, RunAction runAction) { StyleToken runCommandToken = tokenStream.Current; CommandNode command; if (AdvanceIfTokenType(StyleTokenType.Animation)) { command = StyleASTNodeFactory.AnimationCommandNode(ParseIdentifierInParentheses(), cmdType, runAction); } else if (AdvanceIfTokenType(StyleTokenType.Sound)) { command = StyleASTNodeFactory.SoundCommandNode(ParseIdentifierInParentheses(), cmdType, runAction); } else { throw new ParseException(runCommandToken, $"Only animation and sound run commands are supported. Found {runCommandToken}"); } command.WithLocation(tokenStream.Current); AssertTokenTypeAndAdvance(StyleTokenType.EndStatement); RunNode runNode = StyleASTNodeFactory.RunNode(command); runNode.WithLocation(runCommandToken); return(runNode); }
public void ParseUrl() { var nodes = StyleParser.Parse(@" style withBg { Background = url(path/to/image); } "); Assert.AreEqual(1, nodes.Count); var rootNode = ((StyleRootNode)nodes[0]); Assert.AreEqual("withBg", rootNode.identifier); Assert.AreEqual(null, rootNode.tagName); Assert.AreEqual(1, rootNode.children.Count); var property = (((PropertyNode)rootNode.children[0])); Assert.AreEqual("Background", property.identifier); Assert.AreEqual(StyleASTNodeType.Url, property.children[0].type); var urlNode = (UrlNode)property.children[0]; Assert.AreEqual(StyleASTNodeType.Url, urlNode.type); Assert.AreEqual(StyleASTNodeFactory.IdentifierNode("path/to/image"), urlNode.url); }
private void ParseAttributeGroup() { AssertTokenTypeAndAdvance(StyleTokenType.AttributeSpecifier); AssertTokenTypeAndAdvance(StyleTokenType.Colon); StyleToken attributeToken = tokenStream.Current; string attributeIdentifier = AssertTokenTypeAndAdvance(StyleTokenType.Identifier); string attributeValue = null; if (AdvanceIfTokenType(StyleTokenType.EqualSign)) { attributeValue = tokenStream.Current.value; tokenStream.Advance(); } bool invert = groupOperatorStack.Count > 0 && groupOperatorStack.Pop() == StyleOperatorType.Not; AttributeNodeContainer andAttribute = groupExpressionStack.Count > 0 ? groupExpressionStack.Pop() : null; AttributeNodeContainer attributeNodeContainer = StyleASTNodeFactory.AttributeGroupRootNode(attributeIdentifier, attributeValue, invert, andAttribute); attributeNodeContainer.WithLocation(attributeToken); groupExpressionStack.Push(attributeNodeContainer); AssertTokenTypeAndAdvance(StyleTokenType.BracketClose); }
public void CreateContextWithReferences() { LightList <StyleASTNode> nodes = new LightList <StyleASTNode>(); nodes.Add(StyleASTNodeFactory.ExportNode(StyleASTNodeFactory.ConstNode("x", StyleASTNodeFactory.ConstReferenceNode("y")))); nodes.Add(StyleASTNodeFactory.ExportNode(StyleASTNodeFactory.ConstNode("y", StyleASTNodeFactory.ConstReferenceNode("z")))); var stringValue = StyleASTNodeFactory.StringLiteralNode("you win!"); nodes.Add(StyleASTNodeFactory.ExportNode(StyleASTNodeFactory.ConstNode("z", stringValue))); var context = new StyleSheetConstantImporter(new StyleSheetImporter(null, null)).CreateContext(nodes, default); Assert.AreEqual(3, context.constants.Count); Assert.AreEqual("x", context.constants[2].name); Assert.AreEqual(stringValue, context.constants[2].value); Assert.True(context.constants[2].exported); Assert.AreEqual("y", context.constants[1].name); Assert.AreEqual(stringValue, context.constants[1].value); Assert.True(context.constants[1].exported); Assert.AreEqual("z", context.constants[0].name); Assert.AreEqual(stringValue, context.constants[0].value); Assert.True(context.constants[0].exported); Assert.AreEqual(0, context.constantsWithReferences.Count, "There should be no unresolved const left."); }
private StyleASTNode ParseLiteralOrReference(StyleTokenType literalType) { StyleToken currentToken = tokenStream.Current; if (AdvanceIfTokenType(StyleTokenType.At)) { return(ParseConstReference().WithLocation(currentToken)); } string value = AssertTokenTypeAndAdvance(literalType); switch (literalType) { case StyleTokenType.String: return(StyleASTNodeFactory.StringLiteralNode(value).WithLocation(currentToken)); case StyleTokenType.Number: return(StyleASTNodeFactory.NumericLiteralNode(value).WithLocation(currentToken)); case StyleTokenType.Boolean: return(StyleASTNodeFactory.BooleanLiteralNode(value).WithLocation(currentToken)); case StyleTokenType.Identifier: return(StyleASTNodeFactory.IdentifierNode(value).WithLocation(currentToken)); } throw new ParseException(currentToken, $"Please add support for this type: {literalType}!"); }
private void ParseAnimationOptions(AnimationRootNode rootNode) { while (tokenStream.HasMoreTokens && !AdvanceIfTokenType(StyleTokenType.BracesClose)) { StyleToken typeToken = tokenStream.Current; string optionName = AssertTokenTypeAndAdvance(StyleTokenType.Identifier).ToLower(); bool typeFound = false; for (int index = 0; index < s_AnimationOptionNames.Length; index++) { string name = s_AnimationOptionNames[index].Item1; if (name == optionName) { AssertTokenTypeAndAdvance(StyleTokenType.EqualSign); StyleToken variableToken = tokenStream.Current; AnimationOptionNode optionNode = StyleASTNodeFactory.AnimationOptionNode(s_AnimationOptionNames[index].Item2, ParsePropertyValue()); optionNode.WithLocation(variableToken); rootNode.AddOptionNode(optionNode); typeFound = true; break; } } if (!typeFound) { throw new ParseException(typeToken, $"{optionName} is not a supported animation option. Valid values are: {FormatOptionList(s_AnimationOptionNames)}\n"); } AssertTokenTypeAndAdvance(StyleTokenType.EndStatement); } }
private void ParseExportNode() { StyleToken exportToken = tokenStream.Current; tokenStream.Advance(); // export statement must be followed by const keyword // now let's find out which value we're assigning nodes.Add(StyleASTNodeFactory.ExportNode(ParseConstNode()).WithLocation(exportToken)); AdvanceIfTokenType(StyleTokenType.EndStatement); }
private void ParseSpriteSheet() { StyleToken initialStyleToken = tokenStream.Current; SpriteSheetNode rootNode = StyleASTNodeFactory.SpriteSheetNode(initialStyleToken); rootNode.WithLocation(initialStyleToken); tokenStream.Advance(); AssertTokenTypeAndAdvance(StyleTokenType.BracesOpen); SpriteSheetParseLoop(rootNode); nodes.Add(rootNode); }
private void ParseAnimation() { StyleToken initialStyleToken = tokenStream.Current; AnimationRootNode animRoot = StyleASTNodeFactory.AnimationRootNode(initialStyleToken); animRoot.WithLocation(initialStyleToken); tokenStream.Advance(); AssertTokenTypeAndAdvance(StyleTokenType.BracesOpen); AnimationParseLoop(animRoot); nodes.Add(animRoot); }
private void ParseSound() { StyleToken soundNameToken = tokenStream.Current; SoundRootNode soundRootNode = StyleASTNodeFactory.SoundRootNode(soundNameToken); soundRootNode.WithLocation(soundNameToken); tokenStream.Advance(); AssertTokenTypeAndAdvance(StyleTokenType.BracesOpen); SoundParseLoop(soundRootNode); nodes.Add(soundRootNode); }
private void ParseStyleExpression() { switch (tokenStream.Current.styleTokenType) { case StyleTokenType.Plus: StyleASTNodeFactory.OperatorNode(StyleOperatorType.Plus); break; } AssertTokenTypeAndAdvance(StyleTokenType.BracketClose); }
private StyleASTNode ParseConstReference() { AdvanceIfTokenType(StyleTokenType.At); ConstReferenceNode constReferenceNode = StyleASTNodeFactory.ConstReferenceNode(AssertTokenTypeAndAdvance(StyleTokenType.Identifier)); while (tokenStream.HasMoreTokens && AdvanceIfTokenType(StyleTokenType.Dot)) { constReferenceNode.AddChildNode(StyleASTNodeFactory.DotAccessNode(AssertTokenTypeAndAdvance(StyleTokenType.Identifier)).WithLocation(tokenStream.Previous)); } return(constReferenceNode); }
public void ImportAndUseConsts() { LightList <StyleASTNode> nodes = new LightList <StyleASTNode>(); nodes.Add(StyleASTNodeFactory.ImportNode("importedThing", "Data/Styles/ImportFromMe.style")); string basepath = Path.Combine(Application.dataPath, "..", "Packages", "UIForia", "Tests"); StyleCompileContext context = new StyleSheetConstantImporter(new StyleSheetImporter(basepath, null)).CreateContext(nodes, default); Assert.AreEqual(1, context.importedStyleConstants.Count); Assert.AreEqual(1, context.importedStyleConstants["importedThing"].Count); Assert.AreEqual("colorRed", context.importedStyleConstants["importedThing"][0].name); }
public void AssertAllStylePropertiesAreMapped() { foreach (var propId in Enum.GetValues(typeof(StylePropertyId))) { if (propId.ToString().StartsWith("__")) continue; var propertyNode = StyleASTNodeFactory.PropertyNode(propId.ToString()); // this node should fail in a compile exception if this property is mapped. propertyNode.children.Add(new StyleRootNode()); UIStyle target = new UIStyle(); try { StylePropertyMappers.MapProperty(target, propertyNode, new StyleCompileContext(default));
private StyleASTNode ParseVariableReference() { AdvanceIfTokenType(StyleTokenType.Dollar); VariableReferenceNode refNode = new VariableReferenceNode(AssertTokenTypeAndAdvance(StyleTokenType.Identifier)); while (tokenStream.HasMoreTokens && AdvanceIfTokenType(StyleTokenType.Dot)) { refNode.AddChildNode(StyleASTNodeFactory.DotAccessNode(AssertTokenTypeAndAdvance(StyleTokenType.Identifier)).WithLocation(tokenStream.Previous)); } return(refNode); }
/// <summary> /// Takes on all the things after a 'style' keyword on the root level of a style file. /// </summary> /// <exception cref="ParseException"></exception> private void ParseStyle() { string identifier = null; string tagName = null; StyleToken initialStyleToken = tokenStream.Current; switch (initialStyleToken.styleTokenType) { // <TagName> { ... } case StyleTokenType.LessThan: tokenStream.Advance(); AssertTokenType(StyleTokenType.Identifier); tagName = tokenStream.Current.value; tokenStream.Advance(); if (tokenStream.Current.styleTokenType == StyleTokenType.LessThan) { tagName += tokenStream.Current.value; tokenStream.Advance(); tagName += tokenStream.Current.value; AssertTokenTypeAndAdvance(StyleTokenType.Identifier); tagName += tokenStream.Current.value; AssertTokenTypeAndAdvance(StyleTokenType.GreaterThan); } AssertTokenTypeAndAdvance(StyleTokenType.GreaterThan); break; // styleId { ... } case StyleTokenType.Identifier: identifier = tokenStream.Current.value; tokenStream.Advance(); break; default: throw new ParseException(initialStyleToken, $"Expected style definition or tag name but found {initialStyleToken.styleTokenType}"); } StyleRootNode styleRootNode = StyleASTNodeFactory.StyleRootNode(identifier, tagName); styleRootNode.WithLocation(initialStyleToken); nodes.Add(styleRootNode); // we just read an element name or style name // now move forward and expect an open curly brace // next there should be one of those: // - property // - state // - attribute with or without boolean modifier // - expression with constants ParseStyleGroupBody(styleRootNode); }
private StyleFunctionNode ParsePropertyFunction(string identifier) { StyleFunctionNode functionNode = StyleASTNodeFactory.StyleFunctionNode(identifier); while (tokenStream.HasMoreTokens && !AdvanceIfTokenType(StyleTokenType.ParenClose)) { functionNode.AddChildNode(ParsePropertyValue()); // we just ignore the comma for now AdvanceIfTokenType(StyleTokenType.Comma); } return(functionNode); }
private void ParseSoundProperty(SoundRootNode soundRootNode) { string propertyName = AssertTokenTypeAndAdvance(StyleTokenType.Identifier); AssertTokenTypeAndAdvance(StyleTokenType.EqualSign); StyleToken variableToken = tokenStream.Current; SoundPropertyNode propertyNode = StyleASTNodeFactory.SoundPropertyNode(propertyName, ParsePropertyValue()); propertyNode.WithLocation(variableToken); soundRootNode.AddChildNode(propertyNode); AssertTokenTypeAndAdvance(StyleTokenType.EndStatement); }
private void ParseImportNode() { StyleToken importToken = tokenStream.Current; AssertTokenTypeAndAdvance(StyleTokenType.Import); string source = AssertTokenTypeAndAdvance(StyleTokenType.String); AssertTokenTypeAndAdvance(StyleTokenType.As); string alias = AssertTokenTypeAndAdvance(StyleTokenType.Identifier); AssertTokenTypeAndAdvance(StyleTokenType.EndStatement); nodes.Add(StyleASTNodeFactory.ImportNode(alias, source).WithLocation(importToken)); }
private ConstNode ParseConstNode() { StyleToken constToken = tokenStream.Current; AssertTokenTypeAndAdvance(StyleTokenType.Const); // const name string variableName = AssertTokenTypeAndAdvance(StyleTokenType.Identifier); AssertTokenTypeAndAdvance(StyleTokenType.EqualSign); ConstNode constNode = StyleASTNodeFactory.ConstNode(variableName, ParsePropertyValue()); constNode.WithLocation(constToken); return(constNode); }
public void ParsePropertyWithReference() { var nodes = StyleParser.Parse(@" style hasReferenceToBackgroundImagePath { Background = url(@pathRef); } "); Assert.AreEqual(1, nodes.Count); var property = (((PropertyNode)((StyleRootNode)nodes[0]).children[0])); Assert.AreEqual("Background", property.identifier); Assert.AreEqual(StyleASTNodeType.Url, property.children[0].type); var urlNode = (UrlNode)property.children[0]; Assert.AreEqual(StyleASTNodeType.Url, urlNode.type); Assert.AreEqual(StyleASTNodeFactory.ConstReferenceNode("pathRef"), urlNode.url); }
private UnitNode ParseUnit() { StyleToken styleToken = tokenStream.Current; string value = styleToken.value; tokenStream.Advance(); switch (styleToken.styleTokenType) { case StyleTokenType.Identifier: return(StyleASTNodeFactory.UnitNode(value)); case StyleTokenType.Mod: return(StyleASTNodeFactory.UnitNode(value)); default: throw new ParseException(styleToken, "Expected a token that looks like a unit but this didn't."); } }
private StyleASTNode ParseRgb() { AssertTokenTypeAndAdvance(StyleTokenType.Rgb); AssertTokenTypeAndAdvance(StyleTokenType.ParenOpen); StyleASTNode red = ParseLiteralOrReference(StyleTokenType.Number); AssertTokenTypeAndAdvance(StyleTokenType.Comma); StyleASTNode green = ParseLiteralOrReference(StyleTokenType.Number); AssertTokenTypeAndAdvance(StyleTokenType.Comma); StyleASTNode blue = ParseLiteralOrReference(StyleTokenType.Number); AssertTokenTypeAndAdvance(StyleTokenType.ParenClose); return(StyleASTNodeFactory.RgbNode(red, green, blue)); }
public void CreateContextWithMultipleConstants() { LightList <StyleASTNode> nodes = new LightList <StyleASTNode>(); nodes.Add(StyleASTNodeFactory.ExportNode(StyleASTNodeFactory.ConstNode("col0", StyleASTNodeFactory.ColorNode("red")))); nodes.Add(StyleASTNodeFactory.ExportNode(StyleASTNodeFactory.ConstNode("thing0", StyleASTNodeFactory.StringLiteralNode("someVal")))); nodes.Add(StyleASTNodeFactory.ExportNode(StyleASTNodeFactory.ConstNode("number", StyleASTNodeFactory.NumericLiteralNode("1")))); var context = new StyleSheetConstantImporter(new StyleSheetImporter(null, null)).CreateContext(nodes, default); Assert.AreEqual(3, context.constants.Count); Assert.AreEqual("col0", context.constants[0].name); Assert.True(context.constants[0].exported); Assert.AreEqual("thing0", context.constants[1].name); Assert.True(context.constants[1].exported); Assert.AreEqual("number", context.constants[2].name); Assert.True(context.constants[2].exported); }
public void ParseAttributeGroup() { var nodes = StyleParser.Parse(@" style hasBackgroundOnHover { [attr:attrName] { Background = url(@pathRef); } } "); Assert.AreEqual(1, nodes.Count); var attributeGroupContainer = (((AttributeNodeContainer)((StyleRootNode)nodes[0]).children[0])); Assert.AreEqual("attrName", attributeGroupContainer.identifier); var property = (PropertyNode)attributeGroupContainer.children[0]; Assert.AreEqual("Background", property.identifier); Assert.AreEqual(StyleASTNodeType.Url, property.children[0].type); var urlNode = (UrlNode)property.children[0]; Assert.AreEqual(StyleASTNodeFactory.ConstReferenceNode("pathRef"), urlNode.url); }
private StyleASTNode ParsePropertyValue() { StyleToken propertyToken = tokenStream.Current; StyleASTNode propertyValue; switch (tokenStream.Current.styleTokenType) { case StyleTokenType.Number: StyleLiteralNode value = StyleASTNodeFactory.NumericLiteralNode(tokenStream.Current.value).WithLocation(propertyToken) as StyleLiteralNode; tokenStream.Advance(); if (!IsAnyTokenType(StyleTokenType.EndStatement, StyleTokenType.Number, StyleTokenType.Comma, StyleTokenType.ParenClose)) { UnitNode unit = ParseUnit().WithLocation(tokenStream.Previous) as UnitNode; propertyValue = StyleASTNodeFactory.MeasurementNode(value, unit); } else { propertyValue = value; } break; case StyleTokenType.String: propertyValue = StyleASTNodeFactory.StringLiteralNode(tokenStream.Current.value); tokenStream.Advance(); if (tokenStream.Current == StyleTokenType.BracesOpen) { TextUtil.StringBuilder.Clear(); StyleLiteralNode n = (StyleLiteralNode)propertyValue; TextUtil.StringBuilder.Append("\""); TextUtil.StringBuilder.Append(n.rawValue); TextUtil.StringBuilder.Append("\" "); while (tokenStream.HasMoreTokens && tokenStream.Current != StyleTokenType.BracesClose) { TextUtil.StringBuilder.Append(tokenStream.Current.value); tokenStream.Advance(); } TextUtil.StringBuilder.Append(tokenStream.Current.value); tokenStream.Advance(); n.rawValue = TextUtil.StringBuilder.ToString(); TextUtil.StringBuilder.Clear(); } break; case StyleTokenType.Identifier: string identifier = tokenStream.Current.value; tokenStream.Advance(); if (tokenStream.Current.styleTokenType == StyleTokenType.ParenOpen) { tokenStream.Advance(); propertyValue = ParsePropertyFunction(identifier); } else { propertyValue = StyleASTNodeFactory.IdentifierNode(identifier); } break; case StyleTokenType.Rgba: propertyValue = ParseRgba(); break; case StyleTokenType.Rgb: propertyValue = ParseRgb(); break; case StyleTokenType.HashColor: propertyValue = StyleASTNodeFactory.ColorNode(tokenStream.Current.value); tokenStream.Advance(); break; case StyleTokenType.Url: tokenStream.Advance(); AssertTokenTypeAndAdvance(StyleTokenType.ParenOpen); StyleASTNode url; StyleASTNode spriteName = null; if (tokenStream.Current.styleTokenType == StyleTokenType.String) { url = ParseLiteralOrReference(StyleTokenType.String); } else { url = ParseLiteralOrReference(StyleTokenType.Identifier); } if (url is StyleIdentifierNode urlIdentifier) { // todo -- this doesn't handle spaces! while (tokenStream.HasMoreTokens && !AdvanceIfTokenType(StyleTokenType.ParenClose)) { // advancing tokens no matter the type. We want to concatenate all identifiers and slashes of a path again. urlIdentifier.name += tokenStream.Current.value; spriteName = ParseSpriteName(); tokenStream.Advance(); } } else if (url is StyleLiteralNode || url is ConstReferenceNode) { spriteName = ParseSpriteName(); AssertTokenTypeAndAdvance(StyleTokenType.ParenClose); } else { throw new CompileException(url, "URL could not be parsed."); } propertyValue = StyleASTNodeFactory.UrlNode(url, spriteName); break; case StyleTokenType.At: propertyValue = ParseConstReference(); break; case StyleTokenType.Dollar: propertyValue = ParseVariableReference(); break; default: throw new ParseException(propertyToken, "Expected a property value but found no valid token."); } return(propertyValue.WithLocation(propertyToken)); }