Skip to content

Commit

Permalink
Add plain push handling: c272/push-handling (#10)
Browse files Browse the repository at this point in the history
Add plain push handling to all compiler levels (#9).
  • Loading branch information
c272 committed Feb 1, 2023
2 parents ad1884a + c8a8180 commit e777cb3
Show file tree
Hide file tree
Showing 8 changed files with 502 additions and 124 deletions.
23 changes: 21 additions & 2 deletions Compiler/AceCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ public CompileResult Compile(IroPrecompileData data)
//Add the header.
text.AppendLine("/*");
text.AppendLine("* To try in Ace editor, copy and paste into the mode creator");
text.AppendLine("*here : http://ace.c9.io/tool/mode_creator.html");
text.AppendLine("* here: http://ace.c9.io/tool/mode_creator.html");
text.AppendLine("* Generated using iro4cli!");
text.AppendLine("*/");
text.AppendLine();
text.AppendLine("define(function(require, exports, module) {");
Expand Down Expand Up @@ -119,7 +120,7 @@ private void AddMember(ContextMember member, string ctxName, ref StringBuilder t
text.AppendLine("{");
text.AppendLine("\"token\": \"" + patternStyles[0].AceScope + "\",");
//Replace normal '\' with '\\', since it's inside another string.
text.AppendLine("\"regex\": \"" + pattern.Data.Replace("\\", "\\\\") + "\"");
text.AppendLine("\"regex\": \"" + pattern.Data.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"");

//Close pattern.
text.AppendLine("},");
Expand Down Expand Up @@ -159,6 +160,24 @@ private void AddMember(ContextMember member, string ctxName, ref StringBuilder t
text.AppendLine("},");

}
else if (member.Type == ContextMemberType.Push)
{
//Inline push pattern.
var push = ((PushContextMember)member);
var ilpStyles = GetPatternStyles(push.Styles, data);

//Get the push data.
text.AppendLine("{");
text.AppendLine("\"token\": \"" + ilpStyles[0].AceScope + "\",");
//Replace normal '\' with '\\', since it's inside another string.
text.AppendLine("\"regex\": \"" + push.Data.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\",");

//Add push for target context.
text.AppendLine("\"push\": \"" + push.TargetContext + "\"");

//Close pattern.
text.AppendLine("},");
}
else if (member.Type == ContextMemberType.Include)
{
//Include.
Expand Down
62 changes: 49 additions & 13 deletions Compiler/AtomCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,6 @@ private void AddMember(ContextMember member, IroPrecompileData data, ref StringB
var pattern = ((PatternContextMember)member);
var styles = GetPatternStyles(pattern.Styles, data);

//Check if the groups match.
if (!Compiler.GroupsMatch(pattern.Data, styles.Count))
{
Error.Compile("Amount of capture groups does not line up with the amount of assigned styles.");
return;
}

//Add to text.
text.AppendLine("{");
//Make sure to replace backslashes and quotes.
Expand Down Expand Up @@ -221,15 +214,58 @@ private void AddMember(ContextMember member, IroPrecompileData data, ref StringB
//Patterns done, pop condition & styles.
var popStyles = GetPatternStyles(ilp.PopStyles, data);

//Patterns match up with context groups?
if (!Compiler.GroupsMatch(ilp.PopData, popStyles.Count))
//Okay, add pop data.
text.AppendLine("'end': '" + ilp.PopData.Replace("\\", "\\\\").Replace("'", "\\'") + "'");
text.AppendLine("'endCaptures': {");
for (int i = 0; i < popStyles.Count; i++)
{
text.AppendLine("'" + (i + 1) + "': {");
text.AppendLine("'name': '" + popStyles[i].TextmateScope + "." + data.Name + "'");
text.AppendLine("}");
}
text.AppendLine("}");

//Close whole ILP.
text.AppendLine("}");
}
else if (member.Type == ContextMemberType.Push)
{
//Push pattern.
var push = ((PushContextMember)member);
var styles = GetPatternStyles(push.Styles, data);

//Add push styles.
text.AppendLine("{");
text.AppendLine("'begin': '" + push.Data.Replace("\\", "\\\\").Replace("'", "\\'") + "'");
text.AppendLine("'beginCaptures': {");
for (int i=0; i<styles.Count; i++)
{
text.AppendLine("'" + (i + 1) + "': {");
text.AppendLine("'name': '" + styles[i].TextmateScope + "." + data.Name + "'");
text.AppendLine("}");
}
text.AppendLine("}");

//Get the context for the pop.
var targetCtx = data.Contexts.Find(x => x.Name == push.TargetContext);
if (targetCtx == null)
{
Error.Compile("Mismatch between capture groups and number of styles for pop with regex '" + ilp.PopData + "'.");
Error.Compile($"Could not find target context '{push.TargetContext}' for push. Does it exist?");
return;
}

//Okay, add pop data.
text.AppendLine("'end': '" + ilp.PopData.Replace("\\", "\\\\").Replace("'", "\\'") + "'");
//Does a pop condition exist within this context?
var cond = targetCtx.Members.Find(x => x.Type == ContextMemberType.Pop);
if (cond == null)
{
Error.Compile($"No pop condition provided in pushed context '{push.TargetContext}'.");
return;
}
var popCondition = (PopContextMember)cond;

//Patterns done, pop condition & styles.
var popStyles = GetPatternStyles(popCondition.Styles, data);
text.AppendLine("'end': '" + popCondition.Data.Replace("\\", "\\\\").Replace("'", "\\'") + "'");
text.AppendLine("'endCaptures': {");
for (int i = 0; i < popStyles.Count; i++)
{
Expand All @@ -239,7 +275,7 @@ private void AddMember(ContextMember member, IroPrecompileData data, ref StringB
}
text.AppendLine("}");

//Close whole ILP.
//Close whole push.
text.AppendLine("}");
}
else if (member.Type == ContextMemberType.Include)
Expand Down
219 changes: 176 additions & 43 deletions Compiler/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,45 +15,6 @@ public static class Compiler
//The variables currently being compiled.
public static Dictionary<string, IroVariable> Variables;

/// <summary>
/// Checks whether the amount of groups from a regex string is the same as expected.
/// </summary>
public static bool GroupsMatch(string data, int expectedGroups)
{
//Get groups out, ignoring escape chars.
int realGroups = 0;
bool ignoreNext = false, inCharSet = false;
for (int i=0; i<data.Length; i++)
{
//Get current character.
char c = data[i];

//Ignore escape chars.
if (c == '\\') { ignoreNext = true; continue; }
if (ignoreNext) { ignoreNext = false; continue; }

//Ignore character sets.
if (c == '[') { inCharSet = true; }
if (c == ']') { inCharSet = false; }
if (inCharSet) { continue; }

//If there's an open bracket, this might be a group.
if (c == '(')
{
//Is this a lookahead/lookbehind?
if (i<data.Length-1 && data[i+1] == '?')
{
//Yes, ignore.
continue;
}
realGroups++;
}
}

//Return equality.
return (expectedGroups == realGroups);
}

/// <summary>
/// Compiles a set of Algo variables given targets.
/// </summary>
Expand Down Expand Up @@ -322,10 +283,14 @@ private static IroContext ProcessContext(string contextName, IroSet context, Iro
iroCtx.Members.Add(ParsePattern((IroSet)value));
break;
case "pop":
//Pop rules are never directly parsed, only used as a result of inline_push or push.
iroCtx.Members.Add(ParsePop((IroSet)value));
break;
case "push":
//todo: add push rules
iroCtx.Members.Add(ParsePush((IroSet)value, contexts));
break;

//We have no idea what this is.
default:
throw new NotImplementedException();
}
}
Expand Down Expand Up @@ -517,6 +482,10 @@ private static ContextMember ParseInlinePush(IroSet ilp, IroSet contexts)
Error.Compile("Inline push attribute 'regex' must be a regex value.");
return null;
}
} else
{
//Just a normal regex, get out the string value.
regex = ((IroRegex)ilp["regex"]).StringValue;
}
if (!(ilp["styles"] is IroList))
{
Expand All @@ -534,8 +503,7 @@ private static ContextMember ParseInlinePush(IroSet ilp, IroSet contexts)
return null;
}

//Get out the regex and style values.
regex = ((IroRegex)ilp["regex"]).StringValue;
//Get out the style values.
List<string> styles = new List<string>();
foreach (var style in ((IroList)ilp["styles"]))
{
Expand Down Expand Up @@ -660,6 +628,171 @@ private static ContextMember ParseInlinePush(IroSet ilp, IroSet contexts)
};
}

/// <summary>
/// Parses a single push context member in Iro.
/// </summary>
private static ContextMember ParsePush(IroSet push, IroSet contexts)
{
//Ensure the push has required fields 'regex', 'styles' and 'context'.
if (!push.ContainsKey("regex") || !push.ContainsKey("styles")
|| !push.ContainsKey("context"))
{
Error.Compile("Required attribute is missing from push (must have members 'regex', 'styles', 'context').");
return null;
}

//Ensure types of required fields are correct.
string regex = null;
if (!(push["regex"] is IroRegex))
{
//Is it a constant yet to be converted?
if ((push["regex"] is IroReference))
{
//Attempt to get the constant.
IroVariable constant = GetConstant(((IroReference)push["regex"]).Value, VariableType.Regex);
if (constant is IroRegex)
{
regex = ((IroRegex)constant).StringValue;
}
else
{
regex = ((IroValue)constant).Value;
}
}
else
{
Error.Compile("Push attribute 'regex' must be a regex value.");
return null;
}
}
else
{
//Just a normal regex, get out the string value.
regex = ((IroRegex)push["regex"]).StringValue;
}
if (!(push["styles"] is IroList))
{
Error.Compile("Push attribute 'styles' must be an array value.");
return null;
}
if (!(push["context"] is IroList) || ((IroList)push["context"]).Count() != 1)
{
Error.Compile("Push attribute 'context' must be an array value of length 1.");
return null;
}

//Parse out the styles.
List<string> styles = new List<string>();
foreach (var style in ((IroList)push["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);
}

//Ensure that the target context value is of a valid type.
IroVariable context_var = ((IroList)push["context"]).ElementAt(0);
if (context_var.Type != VariableType.Value)
{
Error.Compile("Context items within 'push' must be a value type.");
return null;
}

//Ensure that the target context exists within our context list.
var context_name = ((IroValue)context_var).Value;
if (!contexts.ContainsKey(context_name))
{
Error.Compile($"Referenced context in push '{context_name}' not found in contexts list.");
return null;
}

//Everything seems valid, create our push context!
return new PushContextMember()
{
Data = regex,
TargetContext = context_name,
Styles = styles,
Type = ContextMemberType.Push,
};
}

/// <summary>
/// Parses a single pop context member in Iro.
/// </summary>
private static ContextMember ParsePop(IroSet pop)
{
//Ensure the pop has required fields 'regex', 'styles'.
if (!pop.ContainsKey("regex") || !pop.ContainsKey("styles"))
{
Error.Compile("Required attribute is missing from pop (must have members 'regex', 'styles').");
return null;
}

//Ensure types of required fields are correct.
string regex = null;
if (!(pop["regex"] is IroRegex))
{
//Is it a constant yet to be converted?
if ((pop["regex"] is IroReference))
{
//Attempt to get the constant.
IroVariable constant = GetConstant(((IroReference)pop["regex"]).Value, VariableType.Regex);
if (constant is IroRegex)
{
regex = ((IroRegex)constant).StringValue;
}
else
{
regex = ((IroValue)constant).Value;
}
}
else
{
Error.Compile("Push attribute 'regex' must be a regex value.");
return null;
}
}
else
{
//Just a normal regex, get out the string value.
regex = ((IroRegex)pop["regex"]).StringValue;
}
if (!(pop["styles"] is IroList))
{
Error.Compile("Push attribute 'styles' must be an array value.");
return null;
}

//Parse out the styles.
List<string> styles = new List<string>();
foreach (var style in ((IroList)pop["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 the pop.
return new PopContextMember()
{
Data = regex,
Styles = styles,
Type = ContextMemberType.Pop
};
}

/// <summary>
/// Finds sets recursively of type "pop" or "eol_pop".
/// </summary>
Expand Down
Loading

0 comments on commit e777cb3

Please sign in to comment.