Skip to content

Commit

Permalink
added XBNF validation
Browse files Browse the repository at this point in the history
  • Loading branch information
codewitch-honey-crisis committed Aug 22, 2019
1 parent 79eee23 commit c99f681
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 9 deletions.
30 changes: 25 additions & 5 deletions pckedit/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ void _AddMessage(XbnfMessage msg, string filename)
lvi.ImageIndex = 2;
break;
}
messages.Items.Add(lvi);
messages.BeginInvoke(new Action<ListViewItem>(_AddLVItem), lvi);
}
void _AddMessage(ExpectingException ex, string filename)
{
Expand All @@ -209,8 +209,8 @@ void _AddMessage(ExpectingException ex, string filename)


lvi.ImageIndex = 2;
messages.Items.Add(lvi);

messages.BeginInvoke(new Action<ListViewItem>(_AddLVItem), lvi);
}
void _AddMessage(Exception ex, string filename)
{
Expand All @@ -226,7 +226,7 @@ void _AddMessage(Exception ex, string filename)

lvi.ImageIndex = 2;

messages.Items.Add(lvi);
messages.BeginInvoke(new Action<ListViewItem>(_AddLVItem), lvi);
}
private void RemoveTextEditor(TextEditorControl editor)
{
Expand Down Expand Up @@ -866,6 +866,15 @@ string _XbnfToPck(string input,string fname=null, Progress form=null)
_AddMessage(ex, fname);
return null;
}
var hasErrors = false;
foreach(var msg in xbnf.TryValidate())
{
if (msg.ErrorLevel == XbnfErrorLevel.Error)
hasErrors = true;
_AddMessage(msg, fname);
}
if (hasErrors)
return null;
var sb = new StringBuilder();
if(null!=form)
form.WriteLog("Transforming XBNF to PCK...");
Expand Down Expand Up @@ -895,6 +904,8 @@ async void _HandleCreatePckSpec(object sender, EventArgs e)
await Task.Run(()=>{
input = _XbnfToPck(input, fname, prog);
});
if (null == input)
break;
goto case ".pck";
case ".pck":
await Task.Run(() =>
Expand Down Expand Up @@ -958,6 +969,8 @@ await Task.Run(() =>
{
input = _XbnfToPck(input, _GetFilename(fileTabs.SelectedTab), prog);
});
if (null == input)
break;
goto case ".pck";
case ".pck":
await Task.Run(() => {
Expand Down Expand Up @@ -1026,6 +1039,8 @@ async void _HandleCreateLalr1ParserClass(object sender, EventArgs e)
{
case ".xbnf":
input = _XbnfToPck(input, fname, prog);
if (null == input)
break;
goto case ".pck";
case ".pck":
await Task.Run(()=>{
Expand Down Expand Up @@ -1098,7 +1113,8 @@ async void _HandleCreateTokenizerClass(object sender, EventArgs e)
{
case ".xbnf":
input = _XbnfToPck(input, _GetFilename(fileTabs.SelectedTab), prog);

if (null == input)
break;
goto case ".pck";
case ".pck":
await Task.Run(() => {
Expand Down Expand Up @@ -1217,6 +1233,8 @@ await Task.Run(() =>
{
case ".xbnf":
input = _XbnfToPck(input, fname, prog);
if (null == input)
break;
goto case ".pck";
case ".pck":
var cfg = CfgDocument.Parse(input);
Expand Down Expand Up @@ -1284,6 +1302,8 @@ await Task.Run(() => {
{
case ".xbnf":
input = _XbnfToPck(input, fname, prog);
if (null == input)
break;
goto case ".pck";
case ".pck":
var cfg = CfgDocument.Parse(input);
Expand Down
10 changes: 6 additions & 4 deletions scratch/xbnf.xbnf
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ production= identifier [ "<" attributes ">" ] "=" expressions ";";
//
// expressions
//
expressions= expression { orExpression };
expression= { symbol };
expressions= concatExpression { orExpression };
concatExpression= { symbol };
symbol<collapsed>= literal | regex | identifier |
subexpression |
optionalExpression |
repeatExpression;
orExpression = "|" expression;
orExpression = "|" concatExpression;
subexpression<collapsed>= "(" expressions ")";
optionalExpression= "[" expressions "]";
repeatExpression= "{" expressions (repeatZero | repeatOne);
Expand Down Expand Up @@ -62,7 +62,9 @@ lineComment<hidden,color="green">= '//[^\n]*[\n]';
blockComment<hidden,blockEnd="*/",color="green">= "/*";

// defining these isn't required, but gives
// us better constant names
// us better constant names and an opportunity to
// remove them from the tree.
//
// double quotes denote a literal. the escapes are nearly the
// same as C#. \U and \x are not supported
or<collapsed>="|";
Expand Down
154 changes: 154 additions & 0 deletions xbnf/XbnfDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,81 @@ namespace Pck
{
public class XbnfDocument : IEquatable<XbnfDocument>, ICloneable
{
public XbnfProduction StartProduction {
get {
var ic = Productions.Count;
var firstNT = -1;
for (var i = 0;i<ic;++i)
{
var prod = Productions[i];
var hi = prod.Attributes.IndexOf("start");
if(-1<hi)
{
var o = prod.Attributes[i].Value;
if (o is bool && (bool)o)
return prod;
}
if (-1 == firstNT && !prod.IsTerminal)
firstNT = i;
}
if (-1!=firstNT)
return Productions[firstNT];
return null;
}
set {
if (null!=value && !Productions.Contains(value))
throw new InvalidOperationException(string.Concat("The production \"",value.Name,"\" is not the grammar."));
for (int ic = Productions.Count, i = 0; i < ic; ++i)
{
if (null != value && Productions[i] == value)
{
var prod = Productions[i];
prod.Attributes.Remove("start");
prod.Attributes.Add(new XbnfAttribute("start", true));
}
else
{
var prod = Productions[i];
var hi = prod.Attributes.IndexOf("start");
if (-1 < hi)
prod.Attributes.RemoveAt(hi);
}
}
}
}
public XbnfProductionList Productions { get; } = new XbnfProductionList();
public IList<XbnfMessage> TryValidate(IList<XbnfMessage> result = null)
{
if (null == result)
result = new List<XbnfMessage>();
var refCounts = new Dictionary<string, int>(EqualityComparer<string>.Default);

foreach (var prod in Productions)
refCounts.Add(prod.Name, 0);
foreach (var prod in Productions)
{
_ValidateExpression(prod.Expression, refCounts, result);
}
foreach (var rc in refCounts)
{
if (0 == rc.Value)
{
var prod = Productions[rc.Key];
object o;
var i = prod.Attributes.IndexOf("hidden");
var isHidden = false;
if (-1<i)
{
o = prod.Attributes[i].Value;
isHidden = (o is bool && (bool)o);
}
if (!isHidden && !Equals(rc.Key, StartProduction.Name))
result.Add(new XbnfMessage(XbnfErrorLevel.Warning, -1, string.Concat("Unreferenced production \"", rc.Key, "\""),
prod.Line, prod.Column, prod.Position));
}
}
return result;
}
public XbnfDocument Clone()
{
var result = new XbnfDocument();
Expand Down Expand Up @@ -83,5 +157,85 @@ public override int GetHashCode()
return !lhs.Equals(rhs);
}
#endregion
void _ValidateExpression(XbnfExpression expr, IDictionary<string, int> refCounts, IList<XbnfMessage> messages)
{
var l = expr as XbnfLiteralExpression;
if (null != l)
{

string id = null;
for(int ic = Productions.Count,i=0;i<ic;++i)
{
var ll = Productions[i].Expression as XbnfLiteralExpression;
if(ll==l)
{
id = Productions[i].Name;
break;
}
}
// don't count itself. only things just like itself
if (!string.IsNullOrEmpty(id) && !ReferenceEquals(Productions[id].Expression, l))
refCounts[id] += 1;
}

var r = expr as XbnfRefExpression;
if (null != r)
{
int rc;
if (null == r.Symbol)
{
messages.Add(
new XbnfMessage(
XbnfErrorLevel.Error, -1,
"Null reference expression",
expr.Line, expr.Column, expr.Position));
return;
}
if (!refCounts.TryGetValue(r.Symbol, out rc))
{
messages.Add(
new XbnfMessage(
XbnfErrorLevel.Error, -1,
string.Concat(
"Reference to undefined symbol \"",
r.Symbol,
"\""),
expr.Line, expr.Column, expr.Position));
return;
}
refCounts[r.Symbol] = rc + 1;
return;
}
var b = expr as XbnfBinaryExpression;
if (null != b)
{
if (null == b.Left && null == b.Right)
{
messages.Add(
new XbnfMessage(
XbnfErrorLevel.Warning, -1,
"Nil expression",
expr.Line, expr.Column, expr.Position));
return;
}
_ValidateExpression(b.Left, refCounts, messages);
_ValidateExpression(b.Right, refCounts, messages);
return;
}
var u = expr as XbnfUnaryExpression;
if (null != u)
{
if (null == u.Expression)
{
messages.Add(
new XbnfMessage(
XbnfErrorLevel.Warning, -1,
"Nil expression",
expr.Line, expr.Column, expr.Position));
return;
}
_ValidateExpression(u.Expression, refCounts, messages);
}
}
}
}

0 comments on commit c99f681

Please sign in to comment.