/// <summary> /// Gets a value indicating whether this type is POD (plain old data). /// </summary> public bool IsPod(Builder.BuildData buildData, ApiTypeInfo caller) { var apiType = BindingsGenerator.FindApiTypeInfo(buildData, this, caller); if (apiType != null) { if (!apiType.IsInited) { apiType.Init(buildData); } return(apiType.IsPod); } if (IsPtr || IsRef) { return(true); } // Hardcoded cases if (Type == "String" || Type == "Array") { return(false); } // Fallback to default return(true); }
public override void AddChild(ApiTypeInfo apiTypeInfo) { if (apiTypeInfo.Namespace == null) { apiTypeInfo.Namespace = Namespace; } base.AddChild(apiTypeInfo); }
/// <summary> /// Gets a value indicating whether this type is POD (plain old data). /// </summary> public bool IsPod(Builder.BuildData buildData, ApiTypeInfo caller) { // Fixed array in C++ is converted into managed array in C# by default (char Data[100] -> char[] Data) if (Type.IsArray && !NoArray) { return(false); } return(Type.IsPod(buildData, caller)); }
public override void AddChild(ApiTypeInfo apiTypeInfo) { if (apiTypeInfo is EnumInfo) { base.AddChild(apiTypeInfo); } else { throw new NotSupportedException("Structures in API can have only enums as sub-types."); } }
private static void ProcessAndValidate(BuildData buildData, ApiTypeInfo apiTypeInfo) { if (apiTypeInfo is ClassInfo classInfo) { ProcessAndValidate(buildData, classInfo); } else if (apiTypeInfo is StructureInfo structureInfo) { ProcessAndValidate(buildData, structureInfo); } foreach (var child in apiTypeInfo.Children) { ProcessAndValidate(buildData, child); } }
private static ApiTypeInfo FindApiTypeInfoInner(TypeInfo typeInfo, ApiTypeInfo parent) { foreach (var child in parent.Children) { if (child.Name == typeInfo.Type) { return(child); } var result = FindApiTypeInfoInner(typeInfo, child); if (result != null) { return(result); } } return(null); }
private static ApiTypeInfo FindApiTypeInfoInner(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo parent) { ApiTypeInfo result = null; foreach (var child in parent.Children) { if (child.Name == typeInfo.Type) { result = child; // Special case when searching for template types (use instantiated template if input type has generic arguments) if (child is ClassStructInfo classStructInfo && classStructInfo.IsTemplate && typeInfo.GenericArgs != null) { // Support instantiated template type with instantiated arguments (eg. 'typedef Vector3Base<Real> Vector3' where 'typedef float Real') var inflatedType = typeInfo.Inflate(buildData, child); // Find any typedef which introduced this type (eg. 'typedef Vector3Base<float> Float3') result = FindTypedef(buildData, inflatedType, parent.ParentModule); if (result == TypedefInfo.Current) { result = child; // Use template type for the current typedef } else if (result == null) { continue; // Invalid template match } } result.EnsureInited(buildData); if (result is TypedefInfo typedef) { result = typedef.Typedef; } break; } result = FindApiTypeInfoInner(buildData, typeInfo, child); if (result != null) { break; } } return(result); }
private static ApiTypeInfo FindTypedef(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo parent) { ApiTypeInfo result = null; foreach (var child in parent.Children) { if (child is TypedefInfo typedef && typedef.Type.Equals(typeInfo)) { result = child; break; } result = FindTypedef(buildData, typeInfo, child); if (result != null) { break; } } return(result); }
/// <summary> /// Inflates the type with typedefs for generic arguments. /// </summary> public TypeInfo Inflate(Builder.BuildData buildData, ApiTypeInfo caller) { if (GenericArgs != null && GenericArgs.Count != 0) { var inflated = new TypeInfo(this); for (int i = 0; i < inflated.GenericArgs.Count; i++) { var arg = new TypeInfo(inflated.GenericArgs[i]); var argType = BindingsGenerator.FindApiTypeInfo(buildData, arg, caller); if (argType != null) { arg.Type = argType.Name; arg.GenericArgs = null; } inflated.GenericArgs[i] = arg; } return(inflated); } return(this); }
public string GetFullNameNative(Builder.BuildData buildData, ApiTypeInfo caller) { var type = BindingsGenerator.FindApiTypeInfo(buildData, this, caller); if (type == null) { return(ToString()); } var sb = new StringBuilder(64); if (IsConst) { sb.Append("const "); } sb.Append(type.FullNameNative); if (GenericArgs != null) { sb.Append('<'); for (var i = 0; i < GenericArgs.Count; i++) { if (i != 0) { sb.Append(", "); } sb.Append(GenericArgs[i]); } sb.Append('>'); } if (IsPtr) { sb.Append('*'); } if (IsRef) { sb.Append('&'); } return(sb.ToString()); }
public void LeaveScope() { ScopeTypeStack.Pop(); ScopeInfo = ScopeTypeStack.Peek(); CurrentAccessLevel = ScopeAccessStack.Pop(); }
public void EnterScope(ApiTypeInfo type) { ScopeAccessStack.Push(CurrentAccessLevel); ScopeTypeStack.Push(type); ScopeInfo = ScopeTypeStack.Peek(); }
private static ModuleInfo ParseModuleInner(BuildData buildData, Module module, BuildOptions moduleOptions = null) { if (buildData.ModulesInfo.TryGetValue(module, out var moduleInfo)) { return(moduleInfo); } // Setup bindings module info descriptor moduleInfo = new ModuleInfo { Module = module, Name = module.BinaryModuleName, Namespace = string.Empty, Children = new List <ApiTypeInfo>(), }; if (string.IsNullOrEmpty(moduleInfo.Name)) { throw new Exception("Module name cannot be empty."); } buildData.ModulesInfo.Add(module, moduleInfo); // Skip for modules that cannot have API bindings if (module is ThirdPartyModule || !module.BuildNativeCode) { return(moduleInfo); } if (moduleOptions == null) { throw new Exception($"Cannot parse module {module.Name} without options."); } // Collect all header files that can have public symbols for API var headerFiles = moduleOptions.SourceFiles.Where(x => x.EndsWith(".h")).ToList(); // Skip if no header files to process if (headerFiles.Count == 0) { return(moduleInfo); } // Find and load files with API tags string[] headerFilesContents = null; //using (new ProfileEventScope("LoadHeaderFiles")) { var anyApi = false; for (int i = 0; i < headerFiles.Count; i++) { // Skip scripting types definitions file if (headerFiles[i].Replace('\\', '/').EndsWith("Engine/Core/Config.h", StringComparison.Ordinal) || headerFiles[i].EndsWith("EditorContextAPI.h", StringComparison.Ordinal)) { continue; } // Check if file contains any valid API tag var contents = File.ReadAllText(headerFiles[i]); for (int j = 0; j < ApiTokens.SearchTags.Length; j++) { if (contents.Contains(ApiTokens.SearchTags[j])) { if (headerFilesContents == null) { headerFilesContents = new string[headerFiles.Count]; } headerFilesContents[i] = contents; anyApi = true; break; } } } if (!anyApi) { return(moduleInfo); } } // Skip if none of the headers was modified after last time generated C++ file was edited // TODO: skip parsing if module has API cache file -> then load it from disk /*if (!forceGenerate) * { * var lastGenerateTime = File.GetLastWriteTime(bindings.GeneratedCppFilePath); * var anyModified = false; * for (int i = 0; i < headerFiles.Count; i++) * { * if (File.GetLastWriteTime(headerFiles[i]) > lastGenerateTime) * { * anyModified = true; * break; * } * } * * if (!anyModified) * return; * }*/ Log.Verbose($"Parsing API bindings for {module.Name} ({moduleInfo.Name})"); // Process all header files to generate the module API code reflection var context = new ParsingContext { CurrentAccessLevel = AccessLevel.Public, ScopeTypeStack = new Stack <ApiTypeInfo>(), ScopeAccessStack = new Stack <AccessLevel>(), PreprocessorDefines = new Dictionary <string, string>(), }; for (int i = 0; i < headerFiles.Count; i++) { if (headerFilesContents[i] == null) { continue; } var fileInfo = new FileInfo { Parent = null, Children = new List <ApiTypeInfo>(), Name = headerFiles[i], Namespace = moduleInfo.Name, }; moduleInfo.AddChild(fileInfo); try { // Tokenize the source var tokenizer = new Tokenizer(); tokenizer.Tokenize(headerFilesContents[i]); // Init the context context.Tokenizer = tokenizer; context.File = fileInfo; context.ScopeInfo = null; context.ScopeTypeStack.Clear(); context.ScopeAccessStack.Clear(); context.PreprocessorDefines.Clear(); context.EnterScope(fileInfo); // Process the source code ApiTypeInfo scopeType = null; Token prevToken = null; while (true) { // Move to the next token var token = tokenizer.NextToken(); if (token == null) { continue; } if (token.Type == TokenType.EndOfFile) { break; } // Parse API_.. tags in source code if (token.Type == TokenType.Identifier && token.Value.StartsWith("API_", StringComparison.Ordinal)) { if (string.Equals(token.Value, ApiTokens.Class, StringComparison.Ordinal)) { if (!(context.ScopeInfo is FileInfo)) { throw new NotImplementedException("TODO: add support for nested classes in scripting API"); } var classInfo = ParseClass(ref context); scopeType = classInfo; context.ScopeInfo.AddChild(scopeType); context.CurrentAccessLevel = AccessLevel.Public; } else if (string.Equals(token.Value, ApiTokens.Property, StringComparison.Ordinal)) { var propertyInfo = ParseProperty(ref context); } else if (string.Equals(token.Value, ApiTokens.Function, StringComparison.Ordinal)) { var functionInfo = ParseFunction(ref context); if (context.ScopeInfo is ClassInfo classInfo) { classInfo.Functions.Add(functionInfo); } else if (context.ScopeInfo is StructureInfo structureInfo) { structureInfo.Functions.Add(functionInfo); } else { throw new Exception($"Not supported free-function {functionInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class to use API bindings for it."); } } else if (string.Equals(token.Value, ApiTokens.Enum, StringComparison.Ordinal)) { var enumInfo = ParseEnum(ref context); context.ScopeInfo.AddChild(enumInfo); } else if (string.Equals(token.Value, ApiTokens.Struct, StringComparison.Ordinal)) { var structureInfo = ParseStructure(ref context); scopeType = structureInfo; context.ScopeInfo.AddChild(scopeType); context.CurrentAccessLevel = AccessLevel.Public; } else if (string.Equals(token.Value, ApiTokens.Field, StringComparison.Ordinal)) { var fieldInfo = ParseField(ref context); var scopeInfo = context.ValidScopeInfoFromStack; if (scopeInfo is ClassInfo classInfo) { classInfo.Fields.Add(fieldInfo); } else if (scopeInfo is StructureInfo structureInfo) { structureInfo.Fields.Add(fieldInfo); } else { throw new Exception($"Not supported location for field {fieldInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class or structure to use API bindings for it."); } } else if (string.Equals(token.Value, ApiTokens.Event, StringComparison.Ordinal)) { var eventInfo = ParseEvent(ref context); var scopeInfo = context.ValidScopeInfoFromStack; if (scopeInfo is ClassInfo classInfo) { classInfo.Events.Add(eventInfo); } else { throw new Exception($"Not supported location for event {eventInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class to use API bindings for it."); } } else if (string.Equals(token.Value, ApiTokens.InjectCppCode, StringComparison.Ordinal)) { var injectCppCodeInfo = ParseInjectCppCode(ref context); fileInfo.AddChild(injectCppCodeInfo); } else if (string.Equals(token.Value, ApiTokens.AutoSerialization, StringComparison.Ordinal)) { if (context.ScopeInfo is ClassInfo classInfo) { classInfo.IsAutoSerialization = true; } else if (context.ScopeInfo is StructureInfo structureInfo) { structureInfo.IsAutoSerialization = true; } else { throw new Exception($"Not supported location for {ApiTokens.AutoSerialization} at line {tokenizer.CurrentLine}. Place it in the class or structure that uses API bindings."); } } } // Track access level inside class if (context.ScopeInfo != null && token.Type == TokenType.Colon && prevToken != null && prevToken.Type == TokenType.Identifier) { if (string.Equals(prevToken.Value, "public", StringComparison.Ordinal)) { context.CurrentAccessLevel = AccessLevel.Public; } else if (string.Equals(prevToken.Value, "protected", StringComparison.Ordinal)) { context.CurrentAccessLevel = AccessLevel.Protected; } else if (string.Equals(prevToken.Value, "private", StringComparison.Ordinal)) { context.CurrentAccessLevel = AccessLevel.Private; } } // Handle preprocessor blocks if (token.Type == TokenType.Preprocessor) { token = tokenizer.NextToken(); switch (token.Value) { case "define": { token = tokenizer.NextToken(); var name = token.Value; var value = string.Empty; token = tokenizer.NextToken(true); while (token.Type != TokenType.Newline) { value += token.Value; token = tokenizer.NextToken(true); } value = value.Trim(); context.PreprocessorDefines[name] = value; break; } case "if": { // Parse condition var condition = string.Empty; token = tokenizer.NextToken(true); while (token.Type != TokenType.Newline) { condition += token.Value; token = tokenizer.NextToken(true); } // Replace contents with defines condition = condition.Trim(); condition = ReplacePreProcessorDefines(condition, context.PreprocessorDefines.Keys); condition = ReplacePreProcessorDefines(condition, moduleOptions.PublicDefinitions); condition = ReplacePreProcessorDefines(condition, moduleOptions.PrivateDefinitions); condition = ReplacePreProcessorDefines(condition, moduleOptions.CompileEnv.PreprocessorDefinitions); condition = condition.Replace("false", "0"); condition = condition.Replace("true", "1"); // Check condition // TODO: support expressions in preprocessor defines in API headers? if (condition != "1") { // Skip chunk of code ParsePreprocessorIf(fileInfo, tokenizer, ref token); } break; } case "ifdef": { // Parse condition var define = string.Empty; token = tokenizer.NextToken(true); while (token.Type != TokenType.Newline) { define += token.Value; token = tokenizer.NextToken(true); } // Check condition define = define.Trim(); if (!context.PreprocessorDefines.ContainsKey(define) && !moduleOptions.CompileEnv.PreprocessorDefinitions.Contains(define)) { ParsePreprocessorIf(fileInfo, tokenizer, ref token); } break; } } } // Scope tracking if (token.Type == TokenType.LeftCurlyBrace) { context.ScopeTypeStack.Push(scopeType); context.ScopeInfo = context.ScopeTypeStack.Peek(); scopeType = null; } else if (token.Type == TokenType.RightCurlyBrace) { context.ScopeTypeStack.Pop(); if (context.ScopeTypeStack.Count == 0) { throw new Exception($"Mismatch of the {{}} braces pair in file '{fileInfo.Name}' at line {tokenizer.CurrentLine}."); } context.ScopeInfo = context.ScopeTypeStack.Peek(); if (context.ScopeInfo is FileInfo) { context.CurrentAccessLevel = AccessLevel.Public; } } prevToken = token; } } catch (Exception ex) { Log.Error($"Failed to parse '{fileInfo.Name}' file to generate bindings."); Log.Exception(ex); throw; } } // Initialize API moduleInfo.Init(buildData); return(moduleInfo); }
/// <summary> /// Finds the API type information. /// </summary> /// <param name="buildData">The build data.</param> /// <param name="typeInfo">The type information.</param> /// <param name="caller">The calling type. It's parent module types and references are used to find the given API type.</param> /// <returns>The found API type inf oor null.</returns> public static ApiTypeInfo FindApiTypeInfo(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller) { if (typeInfo == null) { return(null); } var result = FindApiTypeInfoInner(typeInfo, caller); if (result != null) { return(result); } if (buildData.TypeCache.TryGetValue(typeInfo, out result)) { return(result); } // Find across in-build types if (InBuildTypes.TryGetValue(typeInfo, out result)) { return(result); } // Find across all loaded modules for this build foreach (var e in buildData.ModulesInfo) { result = FindApiTypeInfoInner(typeInfo, e.Value); if (result != null) { buildData.TypeCache.Add(typeInfo, result); return(result); } } // Check if using nested typename if (typeInfo.Type.Contains("::")) { var nesting = typeInfo.Type.Split(new[] { "::" }, StringSplitOptions.None); result = FindApiTypeInfo(buildData, new TypeInfo { Type = nesting[0], }, caller); for (int i = 1; i < nesting.Length; i++) { if (result == null) { return(null); } result = FindApiTypeInfoInner(new TypeInfo { Type = nesting[i], }, result); } return(result); } //buildData.TypeCache.Add(typeInfo, null); //Log.Warning("Unknown API type: " + typeInfo); return(null); }
/// <summary> /// Checks if use reference when passing values of this type to via scripting API methods. /// </summary> /// <param name="buildData">The build data.</param> /// <param name="typeInfo">The value type information.</param> /// <param name="caller">The calling type. It's parent module types and references are used to find the given API type.</param> /// <returns>True if use reference when passing the values of this type, otherwise false.</returns> public static bool UsePassByReference(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller) { // Skip for pointers if (typeInfo.IsPtr) { return(false); } // Skip for strings if ((typeInfo.Type == "String" || typeInfo.Type == "StringView" || typeInfo.Type == "StringAnsi" || typeInfo.Type == "StringAnsiView") && typeInfo.GenericArgs == null) { return(false); } // Skip for collections if ((typeInfo.Type == "Array" || typeInfo.Type == "Span" || typeInfo.Type == "Dictionary" || typeInfo.Type == "HashSet") && typeInfo.GenericArgs != null) { return(false); } // Skip for BytesContainer if (typeInfo.Type == "BytesContainer" && typeInfo.GenericArgs == null) { return(false); } // Skip for Variant if (typeInfo.Type == "Variant" && typeInfo.GenericArgs == null) { return(false); } // Skip for VariantType if (typeInfo.Type == "VariantType" && typeInfo.GenericArgs == null) { return(false); } // Find API type info var apiType = FindApiTypeInfo(buildData, typeInfo, caller); if (apiType != null) { // Skip for scripting objects if (apiType.IsScriptingObject) { return(false); } // Skip for classes if (apiType.IsClass) { return(false); } // Force for structures if (apiType.IsStruct) { return(true); } } // True for references if (typeInfo.IsRef) { return(true); } // Force for in-build types if (InBuildRefTypes.Contains(typeInfo.Type)) { return(true); } return(false); }
public virtual void AddChild(ApiTypeInfo apiTypeInfo) { apiTypeInfo.Parent = this; Children.Add(apiTypeInfo); }
public override void Init(Builder.BuildData buildData) { base.Init(buildData); // Remove previous typedef (if any) if (Typedef == null) { foreach (var e in Parent.Children) { if (e != this && e.Name == Name) { Typedef = e; break; } } } if (Typedef != null) { Typedef.Parent = null; Parent.Children.Remove(Typedef); Typedef = null; } // Find typedef type var current = Current; Current = this; var apiTypeInfo = BindingsGenerator.FindApiTypeInfo(buildData, Type, Parent); Current = current; if (apiTypeInfo == null) { throw new Exception(string.Format("Unknown type '{0}' for typedef '{1}'.", Type, Name)); } apiTypeInfo.EnsureInited(buildData); TypeInfo = apiTypeInfo; // Alias type without introducing any new type if (IsAlias || apiTypeInfo is LangType) { Typedef = apiTypeInfo; return; } try { // Duplicate type var typedef = (ApiTypeInfo)apiTypeInfo.Clone(); typedef.Instigator = this; typedef.NativeName = NativeName ?? Name; typedef.Name = Name; typedef.Namespace = Namespace; if (!string.IsNullOrEmpty(Attributes)) { if (string.IsNullOrEmpty(typedef.Attributes)) { typedef.Attributes = Attributes; } else { typedef.Attributes += ',' + Attributes; } } if (Comment != null && Comment.Length != 0) { typedef.Comment = Comment; } typedef.IsInBuild |= IsInBuild; typedef.IsDeprecated |= IsDeprecated; if (typedef is ClassStructInfo typedefClassStruct && typedefClassStruct.IsTemplate) { // Inflate template type typedefClassStruct.IsTemplate = false; if (typedefClassStruct is ClassInfo typedefClass) { foreach (var fieldInfo in typedefClass.Fields) { InflateType(buildData, typedefClassStruct, ref fieldInfo.Type); } } else if (typedefClassStruct is StructureInfo typedefStruct) { foreach (var fieldInfo in typedefStruct.Fields) { InflateType(buildData, typedefStruct, ref fieldInfo.Type); } } } // Add to the hierarchy typedef.Parent = Parent; Parent.Children.Add(typedef); typedef.EnsureInited(buildData); Typedef = typedef; } catch (Exception) { Log.Error($"Failed to typedef '{Type}' as '{Name}'."); throw; } }
private static void ParseModuleInnerAsync(ModuleInfo moduleInfo, BuildOptions moduleOptions, List <string> headerFiles, int workIndex) { // Find and load files with API tags bool hasApi = false; string headerFileContents = File.ReadAllText(headerFiles[workIndex]); for (int j = 0; j < ApiTokens.SearchTags.Length; j++) { if (headerFileContents.Contains(ApiTokens.SearchTags[j])) { hasApi = true; break; } } if (!hasApi) { return; } // Process header file to generate the module API code reflection var fileInfo = new FileInfo { Parent = null, Name = headerFiles[workIndex], Namespace = moduleInfo.Name, }; lock (moduleInfo) { moduleInfo.AddChild(fileInfo); } try { // Tokenize the source var tokenizer = new Tokenizer(); tokenizer.Tokenize(headerFileContents); // Init the context var context = new ParsingContext { File = fileInfo, Tokenizer = tokenizer, ScopeInfo = null, CurrentAccessLevel = AccessLevel.Public, ScopeTypeStack = new Stack <ApiTypeInfo>(), ScopeAccessStack = new Stack <AccessLevel>(), PreprocessorDefines = new Dictionary <string, string>(), }; context.PreprocessorDefines.Add("FLAX_BUILD_BINDINGS", "1"); context.EnterScope(fileInfo); // Process the source code ApiTypeInfo scopeType = null; Token prevToken = null; while (true) { // Move to the next token var token = tokenizer.NextToken(); if (token == null) { continue; } if (token.Type == TokenType.EndOfFile) { break; } // Parse API_.. tags in source code if (token.Type == TokenType.Identifier && token.Value.StartsWith("API_", StringComparison.Ordinal)) { if (string.Equals(token.Value, ApiTokens.Class, StringComparison.Ordinal)) { if (!(context.ScopeInfo is FileInfo)) { throw new NotImplementedException("TODO: add support for nested classes in scripting API"); } var classInfo = ParseClass(ref context); scopeType = classInfo; context.ScopeInfo.AddChild(scopeType); context.CurrentAccessLevel = AccessLevel.Public; } else if (string.Equals(token.Value, ApiTokens.Property, StringComparison.Ordinal)) { var propertyInfo = ParseProperty(ref context); } else if (string.Equals(token.Value, ApiTokens.Function, StringComparison.Ordinal)) { var functionInfo = ParseFunction(ref context); if (context.ScopeInfo is ClassInfo classInfo) { classInfo.Functions.Add(functionInfo); } else if (context.ScopeInfo is StructureInfo structureInfo) { structureInfo.Functions.Add(functionInfo); } else if (context.ScopeInfo is InterfaceInfo interfaceInfo) { interfaceInfo.Functions.Add(functionInfo); } else { throw new Exception($"Not supported free-function {functionInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class to use API bindings for it."); } } else if (string.Equals(token.Value, ApiTokens.Enum, StringComparison.Ordinal)) { var enumInfo = ParseEnum(ref context); context.ScopeInfo.AddChild(enumInfo); } else if (string.Equals(token.Value, ApiTokens.Struct, StringComparison.Ordinal)) { var structureInfo = ParseStructure(ref context); scopeType = structureInfo; context.ScopeInfo.AddChild(scopeType); context.CurrentAccessLevel = AccessLevel.Public; } else if (string.Equals(token.Value, ApiTokens.Field, StringComparison.Ordinal)) { var fieldInfo = ParseField(ref context); var scopeInfo = context.ValidScopeInfoFromStack; if (scopeInfo is ClassInfo classInfo) { classInfo.Fields.Add(fieldInfo); } else if (scopeInfo is StructureInfo structureInfo) { structureInfo.Fields.Add(fieldInfo); } else { throw new Exception($"Not supported location for field {fieldInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class or structure to use API bindings for it."); } } else if (string.Equals(token.Value, ApiTokens.Event, StringComparison.Ordinal)) { var eventInfo = ParseEvent(ref context); var scopeInfo = context.ValidScopeInfoFromStack; if (scopeInfo is ClassInfo classInfo) { classInfo.Events.Add(eventInfo); } else { throw new Exception($"Not supported location for event {eventInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class to use API bindings for it."); } } else if (string.Equals(token.Value, ApiTokens.Typedef, StringComparison.Ordinal)) { var typeInfo = ParseTypedef(ref context); fileInfo.AddChild(typeInfo); } else if (string.Equals(token.Value, ApiTokens.InjectCode, StringComparison.Ordinal)) { var injectCodeInfo = ParseInjectCode(ref context); fileInfo.AddChild(injectCodeInfo); } else if (string.Equals(token.Value, ApiTokens.Interface, StringComparison.Ordinal)) { if (!(context.ScopeInfo is FileInfo)) { throw new NotImplementedException("TODO: add support for nested interfaces in scripting API"); } var interfaceInfo = ParseInterface(ref context); scopeType = interfaceInfo; context.ScopeInfo.AddChild(scopeType); context.CurrentAccessLevel = AccessLevel.Public; } else if (string.Equals(token.Value, ApiTokens.AutoSerialization, StringComparison.Ordinal)) { if (context.ScopeInfo is ClassInfo classInfo) { classInfo.IsAutoSerialization = true; } else if (context.ScopeInfo is StructureInfo structureInfo) { structureInfo.IsAutoSerialization = true; } else { throw new Exception($"Not supported location for {ApiTokens.AutoSerialization} at line {tokenizer.CurrentLine}. Place it in the class or structure that uses API bindings."); } } } // Track access level inside class if (context.ScopeInfo != null && token.Type == TokenType.Colon && prevToken != null && prevToken.Type == TokenType.Identifier) { if (string.Equals(prevToken.Value, "public", StringComparison.Ordinal)) { context.CurrentAccessLevel = AccessLevel.Public; } else if (string.Equals(prevToken.Value, "protected", StringComparison.Ordinal)) { context.CurrentAccessLevel = AccessLevel.Protected; } else if (string.Equals(prevToken.Value, "private", StringComparison.Ordinal)) { context.CurrentAccessLevel = AccessLevel.Private; } } // Handle preprocessor blocks if (token.Type == TokenType.Preprocessor) { token = tokenizer.NextToken(); switch (token.Value) { case "define": { token = tokenizer.NextToken(); var name = token.Value; var value = string.Empty; token = tokenizer.NextToken(true); while (token.Type != TokenType.Newline) { value += token.Value; token = tokenizer.NextToken(true); } value = value.Trim(); context.PreprocessorDefines[name] = value; break; } case "if": case "elif": { // Parse condition var condition = string.Empty; token = tokenizer.NextToken(true); while (token.Type != TokenType.Newline) { var tokenValue = token.Value.Trim(); if (tokenValue.Length == 0) { token = tokenizer.NextToken(true); continue; } // Very simple defines processing var negate = tokenValue[0] == '!'; if (negate) { tokenValue = tokenValue.Substring(1); } tokenValue = ReplacePreProcessorDefines(tokenValue, context.PreprocessorDefines); tokenValue = ReplacePreProcessorDefines(tokenValue, moduleOptions.PublicDefinitions); tokenValue = ReplacePreProcessorDefines(tokenValue, moduleOptions.PrivateDefinitions); tokenValue = ReplacePreProcessorDefines(tokenValue, moduleOptions.CompileEnv.PreprocessorDefinitions); tokenValue = tokenValue.Replace("false", "0"); tokenValue = tokenValue.Replace("true", "1"); tokenValue = tokenValue.Replace("||", "|"); if (tokenValue.Length != 0 && tokenValue != "1" && tokenValue != "0" && tokenValue != "|") { tokenValue = "0"; } if (negate) { tokenValue = tokenValue == "1" ? "0" : "1"; } condition += tokenValue; token = tokenizer.NextToken(true); } // Filter condition bool modified; do { modified = false; if (condition.Contains("1|1")) { condition = condition.Replace("1|1", "1"); modified = true; } if (condition.Contains("1|0")) { condition = condition.Replace("1|0", "1"); modified = true; } if (condition.Contains("0|1")) { condition = condition.Replace("0|1", "1"); modified = true; } } while (modified); // Skip chunk of code of condition fails if (condition != "1") { ParsePreprocessorIf(fileInfo, tokenizer, ref token); } break; } case "ifdef": { // Parse condition var define = string.Empty; token = tokenizer.NextToken(true); while (token.Type != TokenType.Newline) { define += token.Value; token = tokenizer.NextToken(true); } // Check condition define = define.Trim(); if (!context.PreprocessorDefines.ContainsKey(define) && !moduleOptions.CompileEnv.PreprocessorDefinitions.Contains(define)) { ParsePreprocessorIf(fileInfo, tokenizer, ref token); } break; } } } // Scope tracking if (token.Type == TokenType.LeftCurlyBrace) { context.ScopeTypeStack.Push(scopeType); context.ScopeInfo = context.ScopeTypeStack.Peek(); scopeType = null; } else if (token.Type == TokenType.RightCurlyBrace) { context.ScopeTypeStack.Pop(); if (context.ScopeTypeStack.Count == 0) { throw new Exception($"Mismatch of the {{}} braces pair in file '{fileInfo.Name}' at line {tokenizer.CurrentLine}."); } context.ScopeInfo = context.ScopeTypeStack.Peek(); if (context.ScopeInfo is FileInfo) { context.CurrentAccessLevel = AccessLevel.Public; } } prevToken = token; } } catch (Exception ex) { Log.Error($"Failed to parse '{fileInfo.Name}' file to generate bindings."); Log.Exception(ex); throw; } }
public override void AddChild(ApiTypeInfo apiTypeInfo) { throw new NotSupportedException("API enums cannot have sub-types."); }
public override void AddChild(ApiTypeInfo apiTypeInfo) { apiTypeInfo.Namespace = null; base.AddChild(apiTypeInfo); }
private static ApiTypeInfo FindApiTypeInfoInner(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo parent) { foreach (var child in parent.Children) { if (child.Name == typeInfo.Type) { child.EnsureInited(buildData); return(child); } var result = FindApiTypeInfoInner(buildData, typeInfo, child); if (result != null) { return(result); } } return(null); }