Esempio n. 1
0
        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);
                    }
                }
            }
        }
Esempio n. 2
0
        private void WarnIfSpeculative(MoaiType type)
        {
            if (type.Name == "...") return;

            if (type.Name.EndsWith("...")) {
                type = GetOrCreateType(type.Name.Substring(0, type.Name.Length - 3), null);
            }

            if (!type.IsDocumented && !type.IsPrimitive) {
                // Make an educated guess as to what type was meant.
                var commonProposals = new Dictionary<string, string> {
                    { "bool", "boolean" },
                    { "num", "number" },
                    { "int", "number" },
                    { "integer", "number" },
                    { "float", "number" },
                    { "double", "number" }
                };
                string nameProposal;
                if (commonProposals.ContainsKey(type.Name)) {
                    nameProposal = commonProposals[type.Name];
                } else {
                    nameProposal = typesByName
                        .Where(pair => pair.Value.IsDocumented || pair.Value.IsPrimitive)
                        .Select(pair => pair.Key)
                        .MinBy(name => Levenshtein.Distance(name, type.Name));
                    if (Levenshtein.Similarity(nameProposal, type.Name) < 0.6) {
                        nameProposal = null;
                    }
                }

                StringBuilder message = new StringBuilder();
                message.AppendFormat("Documentation mentions missing or undocumented type '{0}'.", type.Name);
                if (nameProposal != null) {
                    message.AppendFormat(" Should this be '{0}'?", nameProposal);
                }
                message.AppendLine();
                foreach (FilePosition referencingFilePosition in type.DocumentationReferences) {
                    message.AppendFormat("> {0}", referencingFilePosition);
                    message.AppendLine();
                }
                log.WarnFormat(message.ToString());
            }
        }
Esempio n. 3
0
        private void ParseTypeDocumentation(MoaiType type, Annotation[] annotations, MoaiType[] baseTypes, TypePosition typePosition)
        {
            // Check that there is a single @name annotation
            int nameAnnotationCount = annotations.OfType<NameAnnotation>().Count();
            if (nameAnnotationCount == 0) {
                log.WarnFormat("Missing @name annotation. [{0}].", typePosition);
            } else if (nameAnnotationCount > 1) {
                log.WarnFormat("Multiple @name annotations. [{0}]", typePosition);
            }

            // Check that there is a single @text annotation
            int textAnnotationCount = annotations.OfType<TextAnnotation>().Count();
            if (textAnnotationCount == 0) {
                log.WarnFormat("Missing @text annotation. [{0}]", typePosition);
            } else if (textAnnotationCount > 1) {
                log.WarnFormat("Multiple @text annotations. [{0}]", typePosition);
            }

            // Store base types
            type.BaseTypes.AddRange(baseTypes);

            // Parse annotations
            foreach (var annotation in annotations) {
                if (annotation is NameAnnotation) {
                    // Nothing to do. Name is already set. Just make sure the annotation is correct.
                    var nameAnnotation = (NameAnnotation) annotation;
                    if (nameAnnotation.Value != type.Name) {
                        log.WarnFormat("@name annotation has inconsistent value '{0}'. [{1}]", nameAnnotation.Value, typePosition);
                    }
                } else if (annotation is TextAnnotation) {
                    // Set type description
                    type.Description = ((TextAnnotation) annotation).Value;
                } else if (annotation is FieldAnnotation) {
                    // Add field (constant, flag, or attribute)
                    var fieldAnnotation = (FieldAnnotation) annotation;
                    MoaiField field = (annotation is ConstantAnnotation) ? new MoaiConstant()
                        : (annotation is FlagAnnotation) ? (MoaiField) new MoaiFlag()
                        : new MoaiAttribute();
                    field.OwningType = type;
                    field.Name = fieldAnnotation.Name;
                    field.Description = fieldAnnotation.Description;
                    type.Members.Add(field);
                } else {
                    log.WarnFormat("Unexpected {0} annotation. [{1}]", annotation.Command, typePosition);
                }
            }
        }
Esempio n. 4
0
        private void ParseMethodDocumentation(MoaiType type, Annotation[] annotations, MethodPosition methodPosition)
        {
            // Check that there is a single @name annotation and that it isn't a duplicate. Otherwise exit.
            int nameAnnotationCount = annotations.OfType<NameAnnotation>().Count();
            if (nameAnnotationCount == 0) {
                log.WarnFormat("Missing @name annotation. [{0}]", methodPosition);
                return;
            }
            if (nameAnnotationCount > 1) {
                log.WarnFormat("Multiple @name annotations. [{0}]", methodPosition);
            }
            var nameAnnotation = annotations.OfType<NameAnnotation>().Single();
            if (type.Members.Any(member => member.Name == nameAnnotation.Value)) {
                log.WarnFormat("Multiple members with name '{0}'. [{1}]", nameAnnotation.Value, methodPosition);
                return;
            }

            // Check that @name annotation sticks to convention
            if (!methodPosition.NativeMethodName.StartsWith("_")) {
                log.WarnFormat(
                    "Unexpected C++ method name '{0}'. By convention, the name of a Lua method implementation shold start with an underscore. [{1}]",
                    methodPosition.NativeMethodName, methodPosition);
            }
            string expectedName = methodPosition.NativeMethodName.Substring(1);
            if (nameAnnotation.Value != expectedName) {
                log.WarnFormat(
                    "@name annotation has unexpected value '{0}'. By convention expected '{1}'. [{2}]",
                    nameAnnotation.Value, expectedName, methodPosition);
            }

            // Check that there is a single @text annotation
            int textAnnotationCount = annotations.OfType<TextAnnotation>().Count();
            if (textAnnotationCount == 0) {
                log.WarnFormat("Missing @text annotation. [{0}]", methodPosition);
            } else if (textAnnotationCount > 1) {
                log.WarnFormat("Multiple @text annotations. [{0}]", methodPosition);
            }

            // Check that there is at least one @out annotation
            if (!annotations.OfType<OutParameterAnnotation>().Any()) {
                log.WarnFormat(
                    "Missing @out annotation. Even for void methods, an @out annotation with type nil is expected. [{0}]",
                    methodPosition);
            }

            // Parse annotations
            // Guess if the method is static
            bool isStatic = annotations
                .OfType<InParameterAnnotation>()
                .All(param => param.Name != "self");
            var method = new MoaiMethod {
                MethodPosition = methodPosition,
                Name = nameAnnotation.Value,
                OwningType = type,
                IsStatic = isStatic
            };
            type.Members.Add(method);
            MoaiMethodOverload currentOverload = null;
            foreach (var annotation in annotations) {
                if (annotation is NameAnnotation) {
                    // Nothing to do - name has already been set.
                } else if (annotation is TextAnnotation) {
                    // Set method description
                    method.Description = ((TextAnnotation) annotation).Value;
                } else if (annotation is ParameterAnnotation) {
                    if (currentOverload == null) {
                        currentOverload = new MoaiMethodOverload { OwningMethod = method };
                        method.Overloads.Add(currentOverload);
                    }
                    var parameterAnnotation = (ParameterAnnotation) annotation;
                    string paramName = parameterAnnotation.Name;
                    if (annotation is InParameterAnnotation | annotation is OptionalInParameterAnnotation) {
                        // Add input parameter
                        if (currentOverload.InParameters.Any(param => param.Name == paramName)) {
                            log.WarnFormat("Multiple '{0}' params for single overload. [{1}]", paramName, methodPosition);
                        }
                        var inParameter = new MoaiInParameter {
                            Name = paramName,
                            Description = parameterAnnotation.Description,
                            Type = GetOrCreateType(parameterAnnotation.Type, methodPosition),
                            IsOptional = annotation is OptionalInParameterAnnotation
                        };
                        currentOverload.InParameters.Add(inParameter);
                    } else {
                        // Add output parameter
                        var outParameter = new MoaiOutParameter {
                            Name = paramName,
                            Type = GetOrCreateType(parameterAnnotation.Type, methodPosition),
                            Description = parameterAnnotation.Description
                        };
                        currentOverload.OutParameters.Add(outParameter);
                    }
                } else if (annotation is OverloadAnnotation) {
                    // Let the next parameter annotation start a new override
                    currentOverload = null;
                } else {
                    log.WarnFormat("Unexpected {0} annotation. [{1}]", annotation.Command, methodPosition);
                }
            }
        }
Esempio n. 5
0
 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;
 }
Esempio n. 6
0
        private LuaTable CreateMemberListTable(MoaiType type)
        {
            var memberListTable = new LuaTable();

            memberListTable.Add(new LuaComment("Direct members"));
            IEnumerable<MoaiTypeMember> directMembers = type.Members
                .OrderBy(member => member.GetType().Name) // MoaiAttribute, then MoaiConstant, MoaiFlag, MoaiMethod
                .ThenBy(member => member.Name);
            foreach (var member in directMembers) {
                memberListTable.Add(member.Name, CreateMemberTable((dynamic) member));
            }

            if (type.InheritedMembers.Any()) {
                memberListTable.Add(new LuaComment("Inherited members", blankLineBefore: true));
                var inheritedMembers = type.InheritedMembers
                    .OrderBy(member => member.GetType().Name)
                    .ThenBy(member => member.Name);
                foreach (var member in inheritedMembers) {
                    memberListTable.Add(member.Name, CreateMemberTable((dynamic) member));
                }
            }

            return memberListTable;
        }
Esempio n. 7
0
 private LuaTable CreateTypeTable(MoaiType type)
 {
     return new LuaTable {
         { "type", "class" },
         { "description", ConvertString(type.Description) },
         { "childs", CreateMemberListTable(type) }
     };
 }