/// <summary> /// Finds sets recursively of type "pop" or "eol_pop". /// </summary> private static List <KeyValuePair <string, IroVariable> > FindPops(IroSet set, IroSet contexts) { //Loop over set members, for each include find pops too. var pops = new List <KeyValuePair <string, IroVariable> >(); foreach (var member in set) { if (member.Value is IroSet) { //Is it a pop or eol_pop? var setMem = ((IroSet)member.Value); if (setMem.SetType == "pop" || setMem.SetType == "eol_pop") { pops.Add(member); } } //Is it an include? if (member.Value is IroInclude) { var include = ((IroInclude)member.Value); string includeCtx = include.Value; //Try and find context to evaluate. if (!contexts.ContainsKey(includeCtx) || !(contexts[includeCtx] is IroSet)) { Error.Compile("Include statement references context '" + includeCtx + "', but it does not exist."); return(null); } var ctx = (IroSet)contexts[includeCtx]; //Find pops in there, add range. pops.AddRange(FindPops(ctx, contexts)); } } //Return list. return(pops); }
/// <summary> /// Parses a single inline push context member in Iro. /// </summary> private static ContextMember ParseInlinePush(IroSet ilp, IroSet contexts) { //Find the required elements 'regex', 'styles' and 'pop'. if (!ilp.ContainsKey("regex") || !ilp.ContainsKey("styles")) { Error.Compile("Required attribute is missing from inline push (must have members 'regex', 'styles')."); return(null); } if (!ilp.ContainsSetOfType("pop") && !ilp.ContainsSetOfType("eol_pop")) { //No pops or eol_pops here. Try to find one in children. var popsFound = FindPops(ilp, contexts); if (popsFound.Count == 0) { Error.Compile("Inline push patterns must have a 'pop' or 'eol_pop' set to know when to end the state."); return(null); } if (popsFound.Count > 1) { Error.Compile("Inline push patterns can only contain one 'pop' or 'eol_pop' rule. It appears multiple 'pop's are imported from other contexts."); return(null); } //Add the relevant set to the ILP. ilp.Add(popsFound[0]); } if (ilp.ContainsSetOfType("pop") && ilp.ContainsSetOfType("eol_pop")) { Error.Compile("Inline push patterns cannot hav both a 'pop' and an 'eol_pop', you must use one or the other."); return(null); } //Verify their types. string regex; if (!(ilp["regex"] is IroRegex)) { //Is it a constant yet to be converted? if ((ilp["regex"] is IroReference)) { //Attempt to get the constant. IroVariable constant = GetConstant(((IroReference)ilp["regex"]).Value, VariableType.Regex); if (constant is IroRegex) { regex = ((IroRegex)constant).StringValue; } else { regex = ((IroValue)constant).Value; } } else { Error.Compile("Inline push attribute 'regex' must be a regex value."); return(null); } } if (!(ilp["styles"] is IroList)) { Error.Compile("Inline push attribute 'styles' must be an array value."); return(null); } if (ilp.ContainsSetOfType("pop") && !(ilp.GetFirstSetOfType("pop") is IroSet)) { Error.Compile("Pop attributes must be a set."); return(null); } if (ilp.ContainsSetOfType("eol_pop") && !(ilp.GetFirstSetOfType("eol_pop") is IroSet)) { Error.Compile("End of line pop attributes must be a set."); return(null); } //Get out the regex and style values. regex = ((IroRegex)ilp["regex"]).StringValue; List <string> styles = new List <string>(); foreach (var style in ((IroList)ilp["styles"])) { //Is the value a name? if (!(style is IroValue)) { Error.CompileWarning("Failed to add pattern style for pattern with regex '" + regex + "', array member is not a value."); continue; } //Get the name out and add it. styles.Add(((IroValue)style).Value); } //Generate the pop (if it's there). List <string> popStyles = new List <string>(); string popRegex; if (ilp.ContainsSetOfType("pop")) { //Parse the 'pop'. var pop = ilp.GetFirstSetOfType("pop"); if (!pop.ContainsKey("regex")) { Error.Compile("Inline push 'pop' messages must contain a 'regex' property."); return(null); } if (!(pop["regex"] is IroValue) && !(pop["regex"] is IroRegex)) { Error.Compile("Inline push 'pop' messages must contain a 'regex' property of type 'regex'."); } if (!pop.ContainsKey("styles") || pop["styles"].Type != VariableType.Array) { Error.Compile("Inline push 'pop' messages must have a 'styles' attribute of type 'array'."); return(null); } //get regex if (pop["regex"] is IroReference) { //It's a constant, uh oh. Get it. var const_ = GetConstant(((IroReference)pop["regex"]).Value, VariableType.Regex); if (const_ is IroRegex) { popRegex = ((IroRegex)const_).StringValue; } else { popRegex = ((IroValue)const_).Value; } } else { popRegex = ((IroRegex)pop["regex"]).StringValue; } //styles foreach (var style in ((IroList)pop["styles"])) { if (!(style is IroValue)) { Error.CompileWarning("Failed to add 'pop' style for pattern with regex '" + regex + "', array member is not a value."); continue; } //Get the name out and add it. popStyles.Add(((IroValue)style).Value); } } else { //eol_pop popStyles = styles; popRegex = "(\\n|\\r\\n)"; } //Get all patterns and includes out. var patterns = ilp.Where(x => x.Value is IroSet && ((IroSet)x.Value).SetType == "pattern") .Select(x => x.Value) .Cast <IroSet>(); var includes = ilp.Where(x => x.Value is IroInclude) .Select(x => x.Value) .Cast <IroValue>(); var values = ilp.Where(x => x.Value.Type == VariableType.Value); var ctxMems = new List <ContextMember>(); //Parse them into the context list. foreach (var pattern in patterns) { ctxMems.Add(ParsePattern(pattern)); } foreach (var include in includes) { ctxMems.Add(new ContextMember() { Data = include.Value, Type = ContextMemberType.Include }); } foreach (var value in values) { //Ignore the horrible spaghetti, first parameter is getting out the string value from the KeyValuePair. //Second is getting out the name. var ctxVal = AddContextValue(((IroValue)value.Value).Value, value.Key); if (ctxVal != null) { ctxMems.Add(ctxVal); } } //Create the module and return it. return(new InlinePushContextMember() { Data = regex, Styles = styles, PopData = popRegex, PopStyles = popStyles, Patterns = ctxMems, Type = ContextMemberType.InlinePush }); }
/// <summary> /// Parses a single pattern from a context. /// </summary> private static ContextMember ParsePattern(IroSet value) { //Find the mandatory "styles" and "regex" properties. if (!value.ContainsKey("regex") || !value.ContainsKey("styles")) { Error.Compile("Pattern missing a required attribute (must have 'styles' and 'regex')."); return(null); } //Valid types? string regex; if (value["styles"].Type != VariableType.Array) { Error.Compile("Pattern 'styles' attribute must be an array."); return(null); } if (value["regex"].Type != VariableType.Regex) { if (value["regex"].Type == VariableType.Reference) { var constant = GetConstant(((IroReference)value["regex"]).Value, VariableType.Regex); if (constant is IroValue) { regex = ((IroValue)constant).Value; } else { regex = ((IroRegex)constant).StringValue; } } else { Error.Compile("Pattern 'regex' attribute must be a regex value."); return(null); } } else { regex = ((IroRegex)value["regex"]).StringValue; } //Get them out. List <string> styles = new List <string>(); foreach (var style in ((IroList)value["styles"])) { //Is the value a name? if (!(style is IroValue)) { Error.CompileWarning("Failed to add pattern style for pattern with regex '" + regex + "', array member is not a value."); continue; } //Get the name out and add it. styles.Add(((IroValue)style).Value); } //Create a pattern. return(new PatternContextMember() { Data = regex, Styles = styles, Type = ContextMemberType.Pattern }); }