Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

YAMLParser: Some minor fixes/additions #5

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
7 changes: 7 additions & 0 deletions YAMLParser/KnownStuff.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,18 @@ public static SingleType WhatItIs(MsgFile parent, string s, string extraindent)
{
string[] pieces = s.Split('/');
string package = null;
// sometimes, a string can contain the char '/', such as the following line:
// string CONTENT_JSON = "application/json"
if (pieces.Length == 2)
{
if (s.ToLower().Contains("string") && !MsgFile.resolver.ContainsKey(pieces[0]))
goto ResolvingStep;

package = pieces[0];
s = pieces[1];
}

ResolvingStep:
SingleType st = new SingleType(package, s, extraindent);
parent.resolve(st);
WhatItIs(parent, st);
Expand Down
2 changes: 1 addition & 1 deletion YAMLParser/MsgFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ public override string ToString()
def[i] = def[i].Replace(" ", " ");
def[i] = def[i].Replace(" = ", "=");
}
GUTS = GUTS.Replace("$MYMESSAGEDEFINITION", "@\"" + def.Aggregate("", (current, d) => current + (d + "\n")).Trim('\n') + "\"");
GUTS = GUTS.Replace("$MYMESSAGEDEFINITION", "@\"" + def.Aggregate("", (current, d) => current + (d + "\n")).Trim('\n').Replace("\"", "\"\"") + "\"");
GUTS = GUTS.Replace("$MYHASHEADER", HasHeader.ToString().ToLower());
GUTS = GUTS.Replace("$MYFIELDS", GeneratedDictHelper.Length > 5 ? "{{" + GeneratedDictHelper + "}}" : "()");
GUTS = GUTS.Replace("$NULLCONSTBODY", "");
Expand Down
74 changes: 74 additions & 0 deletions YAMLParser/MsgFileLocator.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,36 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using FauxMessages;

namespace YAMLParser
{
public class MsgFileLocation
{
// Unicode categories according to https://stackoverflow.com/a/950651/4036588
private static readonly UnicodeCategory[] validFirstChars =
{
UnicodeCategory.UppercaseLetter, // Lu
UnicodeCategory.LowercaseLetter, // Ll
UnicodeCategory.TitlecaseLetter, // Lt
UnicodeCategory.ModifierLetter, // Lm
UnicodeCategory.OtherLetter // Lo
// underscore is also permitted for the first char
};
private static readonly UnicodeCategory[] otherValidChars =
{
UnicodeCategory.LetterNumber, // Nl
UnicodeCategory.NonSpacingMark, // Mn
UnicodeCategory.SpacingCombiningMark, // Mc
UnicodeCategory.DecimalDigitNumber, // Nd
UnicodeCategory.ConnectorPunctuation, // Pc
UnicodeCategory.Format // Cf
};
private static readonly UnicodeCategory[] validCSharpChars = validFirstChars.Union(otherValidChars).ToArray();

private static string[] MSG_GEN_FOLDER_NAMES =
{
"msg",
Expand All @@ -29,6 +52,10 @@ private static string getPackageName(string path)
string foldername = chunks[chunks.Length - 2];
if (MSG_GEN_FOLDER_NAMES.Contains(foldername))
foldername = chunks[chunks.Length - 3];

if (!IsValidCSharpIdentifier(foldername))
throw new ArgumentException(String.Format("'{0}' from '{1}' is not a compatible C# identifier name\n\tThe package name must conform to C# Language Specifications (refer to this StackOverflow answer: https://stackoverflow.com/a/950651/4036588)\n", foldername, path));

return foldername;
}

Expand Down Expand Up @@ -75,6 +102,37 @@ public override string ToString()
{
return string.Format("{0}.{1}", System.IO.Path.Combine(package, basename), extension);
}

public static bool IsValidCSharpIdentifier(string toTest)
{
if (toTest.Length == 0) // obviously..?
return false;

if (SingleType.IsCSharpKeyword(toTest)) // best to avoid any complications
return false;

char[] letters = toTest.ToCharArray();
char first = letters[0];

if (first != '_' && !validFirstChars.Contains(CharUnicodeInfo.GetUnicodeCategory(first)))
return false;

foreach (char c in letters)
if (!validCSharpChars.Contains(CharUnicodeInfo.GetUnicodeCategory(c)))
return false;

return true;

// TODO: fix this regex method, replace the above method with it,
// and get rid of the three UnicodeCategory arrays
// (regex method found at https://stackoverflow.com/a/1904462/4036588)

//const string start = @"(\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl})";
//const string extend = @"(\p{Mn}|\p{Mc}|\p{Nd}|\p{Pc}|\p{Cf})";
//Regex ident = new Regex(string.Format("{0}({0}|{1})*", start, extend));
//toTest = toTest.Normalize();
//return ident.IsMatch(toTest);
}
}

internal static class MsgFileLocator
Expand Down Expand Up @@ -112,6 +170,22 @@ private static void explode(List<MsgFileLocation> m, List<MsgFileLocation> s, Li
Console.WriteLine("Skipped " + (msgfiles.Length - (m.Count - mb4)) + " duplicate msgs and " + (srvfiles.Length - (s.Count - sb4)) + " duplicate srvs");
}

internal static int priority(string package)
{
switch (package)
{
case "std_msgs": return 1;
case "geometry_msgs": return 2;
case "actionlib_msgs": return 3;
default: return 9;
}
}

internal static List<MsgFileLocation> sortMessages(List<MsgFileLocation> msgs)
{
return msgs.OrderBy(m => "" + priority(m.package) + m.package + m.basename).ToList();
}

public static void findMessages(List<MsgFileLocation> msgs, List<MsgFileLocation> srvs, List<MsgFileLocation> actionFiles,
params string[] args)
{
Expand Down
20 changes: 19 additions & 1 deletion YAMLParser/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ internal class Program
static List<ActionFile> actionFiles = new List<ActionFile>();
private static ILogger Logger { get; set; }
const string DEFAULT_OUTPUT_FOLDERNAME = "Messages";
static readonly string[] required_packages = {"std_msgs", "geometry_msgs", /*"actionlib_msgs",*/ "sensor_msgs"};

public static void Main(params string[] args)
{
Expand All @@ -45,8 +46,10 @@ public static void Main(params string[] args)
return 1;
}

List<string> dirs = messageDirectories.Value().Split(',').ToList();

Program.Run(
messageDirectories.HasValue() ? messageDirectories.Values : null,
messageDirectories.HasValue() ? dirs : null,
assemblies.HasValue() ? assemblies.Values : null,
outputDirectory.HasValue() ? outputDirectory.Value() : null,
interactive.HasValue(),
Expand Down Expand Up @@ -127,6 +130,8 @@ private static void Run(List<string> messageDirs, List<string> assemblies = null
Console.WriteLine("Looking in " + d);
MsgFileLocator.findMessages(paths, pathssrv, actionFileLocations, d);
}
// sort paths by priority
paths = MsgFileLocator.sortMessages(paths);

// first pass: create all msg files (and register them in static resolver dictionary)
var baseTypes = MessageTypeRegistry.Default.GetTypeNames().ToList();
Expand Down Expand Up @@ -163,6 +168,14 @@ private static void Run(List<string> messageDirs, List<string> assemblies = null
actionFiles = actionFileParser.GenerateRosMessageClasses();
//var actionFiles = new List<ActionFile>();

if (!StdMsgsProcessed()) // may seem obvious, but needed so that all other messages can build...
{
string resolvedPkgs = String.Join(", ", MsgFile.resolver.Keys.OrderBy(x => x.ToString()).ToArray());
Console.WriteLine("Missing at least one of the following ROS packages: [\"" + String.Join("\", \"", required_packages) + "\"]. Exiting...");
Console.WriteLine("resolver's keys: [" + resolvedPkgs + "]");
return;
}

if (paths.Count + pathssrv.Count > 0)
{
MakeTempDir(outputdir);
Expand All @@ -184,6 +197,11 @@ private static void Run(List<string> messageDirs, List<string> assemblies = null
}
}

public static bool StdMsgsProcessed()
{
return required_packages.All(c => MsgFile.resolver.ContainsKey(c));
}

private static void MakeTempDir(string outputdir)
{
if (!Directory.Exists(outputdir))
Expand Down
48 changes: 35 additions & 13 deletions YAMLParser/SingleType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ namespace FauxMessages
{
public class SingleType
{
// TODO extend check to other C# keywords
private static readonly string[] CSharpKeywords = { "object", "params", "namespace", "const", "static" };
// c# keywords listed on https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/ as of August 1st, 2018
private static readonly string[] reserved_keywords = { "abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked", "class", "const", "continue", "decimal", "default", "delegate", "do", "double", "else", "enum", "event", "explicit", "extern", "false", "finally", "fixed", "float", "for", "foreach", "goto", "if", "implicit", "in", "int", "interface", "internal", "is", "lock", "long", "namespace", "new", "null", "object", "operator", "out", "override", "params", "private", "protected", "public", "readonly", "ref", "return", "sbyte", "sealed", "short", "sizeof", "stackalloc", "static", "string", "struct", "switch", "this", "throw", "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using", "using static", "virtual", "void", "volatile", "while" };
private static readonly string[] contextual_keywords = { "add", "alias", "ascending", "async", "await", "descending", "dynamic", "from", "get", "global", "group", "into", "join", "let", "nameof", "orderby", "partial", "partial", "remove", "select", "set", "value", "var", "when", "where", "where", "yield" };
private static readonly string[] CSharpKeywords = reserved_keywords.Union(contextual_keywords).ToArray();

private static bool IsCSharpKeyword(string name)
public static bool IsCSharpKeyword(string name)
{
return CSharpKeywords.Contains(name);
}
Expand Down Expand Up @@ -107,9 +109,16 @@ public void Finalize(MsgFile parent, string[] s, bool isliteral)
otherstuff = " = " + parts[1];
}

if (IsCSharpKeyword(name))
if (name == parent.Name.Split(".").Last() || !MsgFileLocation.IsValidCSharpIdentifier(name) && name.Length > 0)
{
name = "@" + name;
if (IsCSharpKeyword(name))
{
name = "@" + name;
}
else if (MsgFileLocation.IsValidCSharpIdentifier(name) && name == parent.Name.Split(".").Last())
name = "_" + name;
else
throw new ArgumentException(String.Format("Variable '{0}' from '{1}' is not a compatible C# identifier name\n\tAll variable names must conform to C# Language Specifications (refer to this StackOverflow answer: https://stackoverflow.com/a/950651/4036588)\n", name, parent.msgFileLocation.Path));
}

for (int i = 2; i < s.Length; i++)
Expand Down Expand Up @@ -151,14 +160,16 @@ public void Finalize(MsgFile parent, string[] s, bool isliteral)
string prefix = "", suffix = "";
if (isconst)
{
if (!type.Equals("string", StringComparison.OrdinalIgnoreCase))
{
// why can't strings be constants?

//if (!type.Equals("string", StringComparison.OrdinalIgnoreCase))
//{
if (KnownStuff.IsPrimitiveType(this))
prefix = "const ";
else
prefix = "static readonly ";
wantsconstructor = false;
}
//}
}

string t = KnownStuff.GetNamespacedType(this, type);
Expand Down Expand Up @@ -225,10 +236,19 @@ public void refinalize(MsgFile parent, string REALTYPE)
name = parts[0];
otherstuff = " = " + parts[1];
}
if (IsCSharpKeyword(name))

if (name == parent.Name.Split(".").Last() || !MsgFileLocation.IsValidCSharpIdentifier(name) && name.Length > 0)
{
name = "@" + name;
if (IsCSharpKeyword(name))
{
name = "@" + name;
}
else if (MsgFileLocation.IsValidCSharpIdentifier(name) && name == parent.Name.Split(".").Last())
name = "_" + name;
else
throw new ArgumentException(String.Format("Variable '{0}' from '{1}' is not a compatible C# identifier name\n\tAll variable names must conform to C# Language Specifications (refer to this StackOverflow answer: https://stackoverflow.com/a/950651/4036588)\n", name, parent.msgFileLocation.Path));
}

for (int i = 2; i < backup.Length; i++)
otherstuff += " " + backup[i];
if (otherstuff.Contains('='))
Expand Down Expand Up @@ -269,10 +289,12 @@ public void refinalize(MsgFile parent, string REALTYPE)
string prefix = "", suffix = "";
if (isconst)
{
if (!Type.Equals("string", StringComparison.OrdinalIgnoreCase))
{
// why can't strings be constants?

//if (!Type.Equals("string", StringComparison.OrdinalIgnoreCase))
//{
prefix = "const ";
}
//}
}
if (otherstuff.Contains('='))
if (wantsconstructor)
Expand Down