public static Annotation Create(string text, FilePosition filePosition) { if (string.IsNullOrWhiteSpace(text)) throw new ArgumentException("text is empty."); Match match = wordWithWhitespaceRegex.Match(text.Trim()); string command = match.Groups["word"].Value; switch (command) { case "@name": return new NameAnnotation(text, filePosition); case "@text": return new TextAnnotation(text, filePosition); case "@const": return new ConstantAnnotation(text, filePosition); case "@flag": return new FlagAnnotation(text, filePosition); case "@attr": return new AttributeAnnotation(text, filePosition); case "@in": return new InParameterAnnotation(text, filePosition); case "@opt": return new OptionalInParameterAnnotation(text, filePosition); case "@out": return new OutParameterAnnotation(text, filePosition); case "@overload": return new OverloadAnnotation(text, filePosition); default: return new UnknownAnnotation(text, filePosition); } }
public void Parse(DirectoryInfo moaiSourceDirectory, PathFormat messagePathFormat) { // Check that the input directory looks like the Moai src directory if (!moaiSourceDirectory.GetDirectoryInfo("moai-core").Exists) { throw new ApplicationException(string.Format("Path '{0}' does not appear to be the 'src' directory of a Moai source copy.", moaiSourceDirectory)); } // Initialize type list with primitive types typesByName = new Dictionary<string, MoaiType>(); var primitiveTypeNames = new[] { "nil", "boolean", "number", "string", "userdata", "function", "thread", "table" }; foreach (string primitiveTypeName in primitiveTypeNames) { typesByName[primitiveTypeName] = new MoaiType { Name = primitiveTypeName, IsPrimitive = true }; } // Parse Moai types and store them by type name log.Info("Parsing Moai types."); ParseMoaiCodeFiles(moaiSourceDirectory, messagePathFormat); // MOAILuaObject is not documented, probably because it would mess up // the Doxygen-generated documentation. Use dummy code instead. log.Info("Adding hard-coded documentation for MoaiLuaObject base class."); FilePosition dummyFilePosition = new FilePosition(new FileInfo("MoaiLuaObject dummy code"), new DirectoryInfo("."), messagePathFormat); ParseMoaiFile(MoaiLuaObject.DummyCode, dummyFilePosition); // Make sure every class directly or indirectly inherits from MOAILuaObject MoaiType moaiLuaObjectType = GetOrCreateType("MOAILuaObject", null); foreach (MoaiType type in typesByName.Values) { if (!(type.AncestorTypes.Contains(moaiLuaObjectType)) && type != moaiLuaObjectType) { type.BaseTypes.Add(moaiLuaObjectType); } } // Check if we have information on all referenced classes IEnumerable<MoaiType> typesReferencedInDocumentation = typesByName.Values .Where(type => type.DocumentationReferences.Any()); foreach (MoaiType type in typesReferencedInDocumentation.ToArray()) { WarnIfSpeculative(type); } log.Info("Creating compact method signatures."); foreach (MoaiType type in typesByName.Values) { foreach (MoaiMethod method in type.Members.OfType<MoaiMethod>()) { if (!method.Overloads.Any()) { log.WarnFormat("No method documentation found. [{0}]", method.MethodPosition); continue; } try { method.InParameterSignature = GetCompactSignature(method.Overloads.Select(overload => overload.InParameters.ToArray())); method.OutParameterSignature = GetCompactSignature(method.Overloads.Select(overload => overload.OutParameters.ToArray())); } catch (Exception e) { log.WarnFormat("Error determining method signature. {0} [{1}]", e.Message, method.MethodPosition); } } } }
protected Annotation(string text, FilePosition filePosition) { if (string.IsNullOrWhiteSpace(text)) throw new ArgumentException("text is empty."); IEnumerable<Match> matches = wordWithWhitespaceRegex.Matches(text.Trim()).Cast<Match>(); Elements = matches .Select(match => match.Groups["word"].Value) .ToArray(); WhitespaceAfterElements = matches .Select(match => match.Groups["whitespace"].Value) .ToArray(); FilePosition = filePosition; }
private MoaiType GetOrCreateType(string typeName, FilePosition documentationPosition) { MoaiType result = typesByName.ContainsKey(typeName) ? typesByName[typeName] : typesByName[typeName] = new MoaiType { Name = typeName }; if (documentationPosition != null) { result.DocumentationReferences.Add(documentationPosition); } return result; }
public MethodPosition(FilePosition filePosition, string typeName, string nativeMethodName) : base(filePosition, typeName) { NativeMethodName = nativeMethodName; }
public TypePosition(FilePosition filePosition, string typeName) : base(filePosition.FileInfo, filePosition.RootDirectory, filePosition.MessagePathFormat) { TypeName = typeName; }
public UnknownAnnotation(string text, FilePosition filePosition) : base(text, filePosition) { }
public ConstantAnnotation(string text, FilePosition filePosition) : base(text, filePosition) { }
private void ParseMoaiFile(string code, FilePosition filePosition) { // Find all documentation blocks var matches = documentationRegex.Matches(code); // Parse documentation blocks foreach (Match match in matches) { // A documentation block may be attached to a type or to a method. string typeName = match.Groups["className"].Value; FilePosition documentationPosition = match.Groups["methodName"].Success ? new MethodPosition(filePosition, typeName, match.Groups["methodName"].Value) : new TypePosition(filePosition, typeName); // Parse annotations, filtering out unknown ones Annotation[] annotations = match.Groups["annotation"].Captures .Cast<Capture>() .Select(capture => Annotation.Create(capture.Value, documentationPosition)) .ToArray(); foreach (var unknownAnnotation in annotations.OfType<UnknownAnnotation>()) { log.WarnFormat("Unknown annotation command '{0}'. [{1}]", unknownAnnotation.Command, documentationPosition); } annotations = annotations .Where(annotation => !(annotation is UnknownAnnotation)) .ToArray(); // Parse annotation block MoaiType type = GetOrCreateType(typeName, documentationPosition); if (documentationPosition is MethodPosition) { // The documentation was attached to a method definition ParseMethodDocumentation(type, annotations, (MethodPosition) documentationPosition); } else { // The documentation was attached to a type definition // Get base type names, ignoring all template classes MoaiType[] baseTypes = match.Groups["baseClassName"].Captures .Cast<Capture>() .Where(capture => !capture.Value.Contains("<")) .Select(capture => GetOrCreateType(capture.Value, null)) .ToArray(); var typePosition = (TypePosition) documentationPosition; type.TypePosition = typePosition; ParseTypeDocumentation(type, annotations, baseTypes, typePosition); } } }
public OverloadAnnotation(string text, FilePosition filePosition) : base(text, filePosition) { }
public OutParameterAnnotation(string text, FilePosition filePosition) : base(text, filePosition) { }
public OptionalInParameterAnnotation(string text, FilePosition filePosition) : base(text, filePosition) { if (Name == null) { log.WarnFormat("{0} annotation with type '{1}' is missing its name (2nd word). [{2}]", Command, Type, filePosition); } // Let's not insist on a description for well-named parameters. }
public FlagAnnotation(string text, FilePosition filePosition) : base(text, filePosition) { }
protected FieldAnnotation(string text, FilePosition filePosition) : base(text, filePosition) { if (Name == null) { log.WarnFormat("{0} annotation is missing its name (1st word). [{1}]", Command, filePosition); } if (Description == null) { log.WarnFormat("{0} annotation '{1}' is missing its description (2nd word+). [{2}]", Command, Name, filePosition); } }
private void ParseMoaiCodeFile(FileInfo codeFile, FilePosition filePosition) { string code = File.ReadAllText(codeFile.FullName); ParseMoaiFile(code, filePosition); }
private void ParseMoaiCodeFiles(DirectoryInfo moaiSourceDirectory, PathFormat messagePathFormat) { IEnumerable<FileInfo> codeFiles = Directory .EnumerateFiles(moaiSourceDirectory.FullName, "*.*", SearchOption.AllDirectories) .Where(name => name.EndsWith(".cpp") || name.EndsWith(".h")) .Select(name => new FileInfo(name)); foreach (var codeFile in codeFiles) { FilePosition filePosition = new FilePosition(codeFile, moaiSourceDirectory, messagePathFormat); ParseMoaiCodeFile(codeFile, filePosition); } }
protected ParameterAnnotation(string text, FilePosition filePosition) : base(text, filePosition) { if (Type == null) { log.WarnFormat("{0} annotation is missing its type (1st word). [{1}]", Command, filePosition); } // Not all parameter annotations require a type or name. // Let the derived classes decide. if (Description != null && WhitespaceAfterElements[2] == " ") { // There is only a single space before the description log.WarnFormat( "{0} annotation has only a single space between its name ('{1}') and its description ('{2}'). This often indicates that the description is not self-contained. [{3}]", Command, Name, Description.GetExcerpt(), filePosition); } }
public TextAnnotation(string text, FilePosition filePosition) : base(text, filePosition) { if (Value == null) { log.WarnFormat("{0} annotation is missing its value. [{1}]", Command, filePosition); } }
public AttributeAnnotation(string text, FilePosition filePosition) : base(text, filePosition) { }