示例#1
0
        public async Task Start(IEnumerable <ModProject> projects)
        {
            // TODO: do error checking here. Make sure the last thing references all previous ones.
            var last = projects.Last();

            // Make sure the last one generates code, otherwise it's not needed in the list
            Debug.Assert(last.ToGenerate, "The last project in list must generate code, otherwise it's pointless");

            // Make sure at least one of the projects generates code
            // Debug.Assert(projects.Any(t => t.ToGenerate), "Nothing generates code");

            if (!InitWorkspace())
            {
                return;
            }

            _env = new GenerationEnvironment(projects);
            var lastProject = await msWorkspace.OpenProjectAsync(last.ProjectPath);

            if (projectLoadFailure)
            {
                return;
            }

            _env.Init(lastProject.Solution, await lastProject.GetCompilationAsync());

            // Now, go through the referenced projects, specified as projects manually,
            // and analyze their code, perhaps generate is we need to generate code
            foreach (var project in projects)
            {
                Console.WriteLine($"Appending {project.AssemblyName}");
                await Generate(project);
            }
        }
示例#2
0
        public IEnumerable <ExportedMethodSymbolWrapper> GetNonNativeExportedMethods(GenerationEnvironment env)
        {
            foreach (var method in GetMethods())
            {
                if (method.TryGetExportAttribute(out var attribute))
                {
                    env.errorContext.PushThing(method);

                    if (attribute.Chain != null)
                    {
                        var m = new ExportedMethodSymbolWrapper(method, attribute);
                        if (m.TryInit(env))
                        {
                            yield return(m);
                        }
                    }
                    else
                    {
                        // For now, add this check here but it should probably be elsewhere
                        // Report an error if the class is not a behavior.
                        if (!(this is BehaviorSymbolWrapper))
                        {
                            env.ReportError($"The class {ClassName} marked a method for export but did not specify the chain path. Note: one may omit the chain path only if the method being exported is inside a behavior class.");
                        }
                        env.errorContext.PopThing();
                        yield break;
                    }

                    env.errorContext.PopThing();
                }
            }
        }
 private IEnumerable <ExportedMethodSymbolWrapper> GetAllExportedMethods(GenerationEnvironment env)
 {
     foreach (var method in GetMethods())
     {
         if (method.TryGetExportAttribute(out var attribute))
         {
             // If the chain string is null, it means that the methods reference the behavior
             // class they are defined in.
             // TODO: This actually does have to specify the chain, just without the behavior class part.
             // Either specify these two separately, as in Chain = "Do", Behavior = "Attackable"
             // Or split by dot at this point.
             if (attribute.Chain is null)
             {
                 var m = new ExportedMethodSymbolWrapper(method, attribute);
                 if (m.TryInit(env, Chains.First()))
                 {
                     yield return(m);
                 }
             }
             else
             {
                 var m = new ExportedMethodSymbolWrapper(method, attribute);
                 if (m.TryInit(env))
                 {
                     yield return(m);
                 }
             }
         }
     }
 }
        /// <summary>
        /// This function is independent of the type the Context is defined in.
        /// This function hashes fields for faster lookups.
        /// It also caches the necessary fields for convenient iteration.
        /// </summary>
        private bool TryHashFields(GenerationEnvironment env)
        {
            if (symbol.GetMembers().OfType <IPropertySymbol>().Any(p => p.Name == "actor"))
            {
                ActorName = "actor";
            }

            foreach (var s in symbol.GetTypeHierarchy())
            {
                foreach (var field in s.GetInstanceFields())
                {
                    fieldsHashed.Add(field.Name, field);

                    if (field.Name == "actor" && field.Type == RelevantSymbols.entity)
                    {
                        ActorName = "actor";
                    }
                    else if (!field.HasAttribute(RelevantSymbols.OmitAttribute.symbol) &&
                             !field.IsImplicitlyDeclared)
                    {
                        if (ActorName is null && field.Type == RelevantSymbols.entity)
                        {
                            ActorName = field.Name;
                        }
                        else
                        {
                            notOmitted.Add(field);
                        }
                    }
                }
            }
示例#5
0
        private bool Init(GenerationEnvironment env)
        {
            var uid = exportAttribute.Chain;

            return(uid.ValidateChainUidAgainst(env) &&
                   env.chains.TryGetValue(uid, out var chain) &&
                   InitWithChain(env, chain));
        }
示例#6
0
 public static IEnumerable <T> InitAndAfterInit <T>(
     this IEnumerable <T> guys, GenerationEnvironment context) where T : TypeSymbolWrapperBase
 {
     return(guys
            .OrderBy(g => g.ClassName)
            .Where(g => g.TryInit(context))
            .ToArray() // all the inits must run first
                       // this is the reason we have two functions in the first place
            .Where(g => g.TryAfterInit(context)));
 }
        protected override bool Init(GenerationEnvironment env)
        {
            env.errorContext.ClearFlag();

            if (!(base.Init(env)))
            {
                return(false);
            }

            symbol.TryGetAttribute(RelevantSymbols.AutoActivationAttribute, out var autoActivation);
            var exportedChains = GetExportedChains(env);

            if (autoActivation is null)
            {
                Chains = exportedChains.ToArray();

                if (Chains.Length == 0)
                {
                    env.ReportError("Behaviors must define at least a chain. Otherwise, they are just components");
                }
            }
            else
            {
                if (exportedChains.Any())
                {
                    env.ReportError($"Behaviors decorated with AutoActivation cannot define additional chains.");
                }
                else
                {
                    if (!TryInitActivationContext(env, out var context))
                    {
                        return(false);
                    }

                    Chains = new IChainWrapper[] {
                        new BehaviorActivationChainWrapper("Check", symbol, context),
                        new BehaviorActivationChainWrapper("Do", symbol, context)
                    };

                    foreach (var chain in Chains)
                    {
                        env.TryAddChain(chain);
                    }

                    ActivationAlias = autoActivation.Alias;

                    if (env.aliases.Contains(ActivationAlias))
                    {
                        env.ReportError($"Duplicate alias name {ActivationAlias} in behavior {symbol.Name}.");
                    }
                }
            }

            return(!env.errorContext.Flag);
        }
示例#8
0
 protected virtual bool AfterInit(GenerationEnvironment env)
 {
     if (exportedMethods == null)
     {
         exportedMethods = GetNonNativeExportedMethods(env).ToArray();
     }
     if (contributedChains == null)
     {
         contributedChains = GetMoreChainsChains(env).ToArray();
     }
     return(true);
 }
 private IEnumerable <ChainSymbolWrapper> GetExportedChains(GenerationEnvironment env)
 {
     foreach (var field in symbol.GetInstanceFields())
     {
         if (field.TryGetAttribute(RelevantSymbols.ChainAttribute, out var chainAttribute))
         {
             var wrapped = new ChainSymbolWrapper(field, chainAttribute);
             if (wrapped.Init(env) && env.TryAddChain(wrapped))
             {
                 yield return(wrapped);
             }
         }
     }
 }
        protected override bool Init(GenerationEnvironment env)
        {
            // Workaround: If it is marked as ExportingClass, do after init after all inits.
            if (symbol.HasAttribute(RelevantSymbols.ExportingClassAttribute.symbol))
            {
                return(base.Init(env) && env.TryAddExportingClass(this));
            }

            // If there are no exported methods, this class is unusable anyway
            // and should be either given another symbol, or thrown away.
            return(base.Init(env) && base.AfterInit(env) &&
                   (exportedMethods.Length > 0 || contributedChains.Length > 0) &&
                   env.TryAddExportingClass(this));
        }
示例#11
0
        public bool Init(GenerationEnvironment env)
        {
            // 1. Lookup the type of the context. It is the most nested generic type of the index type.
            var contextType = (INamedTypeSymbol)symbol.Type.GetLeafTypeArguments().First();

            // 2. See if that context has already been cached in the environment.
            // 3. Initialize the context if it is not found.
            if (!env.TryGetContextLazy(contextType, out var context))
            {
                return(false);
            }

            Context = context;

            return(true);
        }
示例#12
0
        public AliasMethodSymbolWrapper[] GetAliasMethods(GenerationEnvironment env)
        {
            // Find aliases
            var aliasMethods = GetAliasMethodsHelper().ToArray();

            // Add alias strings to global aliases
            foreach (var aliasMethod in aliasMethods)
            {
                if (!env.aliases.Add(aliasMethod.Alias))
                {
                    env.ReportError($"Aliases must be unique across all types. When processing the {symbol.Name} behavior, found a duplicate alias name: {aliasMethod.Alias}");
                }
            }

            return(aliasMethods);
        }
        public bool TryInitActivationContext(GenerationEnvironment env, out ContextSymbolWrapper context)
        {
            // Initialize the context class symbol wrapper
            var ctx_symbol = symbol.GetMembers().FirstOrDefault(s => s.Name == "Context");

            if (ctx_symbol == null)
            {
                env.ReportError($"The {symbol.Name} behavior did not define a nested Context class.\nNote: Any behavior must define a Context class. If you do not have any chains in the behavior, make it a simple component. Behaviors by design differ from components in that they exploit chains.");
                context = default;
                return(false);
            }
            if (!(ctx_symbol is INamedTypeSymbol named_ctx_symbol))
            {
                env.ReportError($"The Context defined inside {symbol.Name} must be a class");
                context = default;
                return(false);
            }
            return(env.TryCacheContext(named_ctx_symbol, out context));
        }
示例#14
0
        public static bool ValidateChainUidAgainst(this string uid, GenerationEnvironment env)
        {
            if (!ChainIdentifier.TryParse(uid, env.errorContext, out var parsed))
            {
                return(false);
            }

            if (env.exportingClasses.TryGetValue(parsed.Class, out var exportingType))
            {
                switch (parsed.Type)
                {
                case ChainContributionType.More:
                case ChainContributionType.Global:
                    return(TrueOr(
                               exportingType.contributedChains.Any(chain => chain.Name == parsed.Chain &&
                                                                   chain.ContributionType == parsed.Type),
                               () => env.ReportError($"{uid} references a non-existent chain: {parsed.Chain}.")));

                case ChainContributionType.Instance:
                    if (exportingType is BehaviorSymbolWrapper behavior)
                    {
                        if (behavior.Chains.Any(chain => chain.Name == parsed.Chain))
                        {
                            return(true);
                        }
                        env.ReportError($"{uid} references a non-existent behavior chain: {parsed.Chain}.");
                    }
                    else
                    {
                        env.ReportError($"{uid} referenced an exporting class that was not a behavior: {parsed.Class}. If you meant a static class, use '+{uid}' instead.");
                    }
                    return(false);

                default:
                    return(false);
                }
            }
            else
            {
                env.ReportError($"{uid} references a non-existent exporting class: {parsed.Class}");
            }
            return(false);
        }
        protected override bool Init(GenerationEnvironment env)
        {
            if (symbol.HasAttribute(RelevantSymbols.InstanceExportAttribute.symbol))
            {
                env.ReportError($"Components must not have the InstanceExportAttribute since its usage is ambiguous for components.");
                return(false);
            }

            if (base.Init(env))
            {
                flaggedFields  = GetFlaggedFields();
                aliasMethods   = GetAliasMethods(env);
                injectedFields = GetInjectedFields().ToArray();

                HasInitInWorldMethod = symbol.GetMethods().Any(m => !m.IsStatic && m.Name == "InitInWorld");
                return(env.TryAddExportingClass(this));
            }

            return(false);
        }
示例#16
0
 protected virtual bool Init(GenerationEnvironment env)
 {
     usings = GetUsingSyntax(env.Solution);
     return(true);
 }
 protected override bool AfterInit(GenerationEnvironment env)
 {
     return(base.AfterInit(env));
 }
示例#18
0
        public string GetAdapterBody(GenerationEnvironment env)
        {
            StringBuilder sb_params = new StringBuilder();
            StringBuilder sb_call   = new StringBuilder();

            // If the method returns bool, it is treated toward propagation.
            if (SymbolEqualityComparer.Default.Equals(symbol.ReturnType, RelevantSymbols.boolType))
            {
                sb_call.Append("ctx.Propagate = ");
            }

            sb_call.Append(GetNamePrefixAtCall());
            sb_call.Append($"{Name}(");

            foreach (var s in symbol.Parameters)
            {
                // TODO: allow get properties
                if (s.Name == Context.ActorName)
                {
                    sb_call.Append($"ctx.{Context.ActorName}, ");
                }
                // If the parameter is of Context type
                else if (SymbolEqualityComparer.Default.Equals(s.Type, Context.symbol))
                {
                    // The parameters need not be appended, since the handlers take ctx by default.
                    sb_call.Append("ctx, ");
                }
                // if ctx class has a field of that name and type, reference it directly
                else if (Context.ContainsFieldWithNameAndType(s.Name, s.Type))
                {
                    if (s.RefKind == RefKind.None)
                    {
                        sb_params.AppendLine($"var _{s.Name} = ctx.{s.Name};");
                        sb_call.Append($"_{s.Name}, ");
                    }
                    else
                    {
                        sb_call.Append($"{s.RefKind.AsKeyword()} ctx.{s.Name}, ");
                    }
                }
                // if it is of a component type, retrieve it from the entity
                else if (s.Type.HasInterface(RelevantSymbols.IComponent))
                {
                    // if the name contains the name of an entity type field
                    // of the context followed by an underscore, get the component
                    // from that entity and save it.
                    int  indexOf_ = s.Name.IndexOf('_');
                    bool success  = false;
                    if (indexOf_ != -1)
                    {
                        string entity_name = s.Name.Substring(0, indexOf_);
                        if (Context.ContainsEntity(entity_name))
                        {
                            success = true;
                            sb_params.AppendLine($"var _{s.Name} = ctx.{entity_name}.Get{s.Type.Name}();");
                            sb_call.Append($"_{s.Name}, ");
                        }
                        else
                        {
                            // TODO: Report warning?
                        }
                    }
                    if (!success)
                    {
                        // get the component from entity. For now, assume that
                        // the entity is assumed to always contain the given component.
                        sb_params.AppendLine($"var _{s.Name} = ctx.actor.Get{s.Type.Name}();");
                        sb_call.Append($"_{s.Name}, ");
                    }
                }
                else
                {
                    if (SymbolEqualityComparer.Default.Equals(s.Type, RelevantSymbols.entity))
                    {
                        env.ReportError($"The entity must be named \"actor\", like on the context class");
                        return(null);
                    }

                    env.ReportError($"The name \"{s.Name}\" is invalid. It does not correspond directly to any of the Context fields and the type of the parameter was not a component type");
                    return(null);
                }
            }

            if (!symbol.Parameters.IsEmpty)
            {
                sb_call.Remove(sb_call.Length - 2, 2);
            }
            sb_call.Append(");");

            sb_params.Append(sb_call.ToString());
            return(sb_params.ToString());
        }
示例#19
0
 public bool TryInit(GenerationEnvironment env, IChainWrapper chain)
 => env.DoScoped(this, () => InitWithChain(env, chain));
示例#20
0
 public bool TryAfterInit(GenerationEnvironment env)
 => env.DoScoped(this, () => AfterInit(env));
 // This must be called after all the behaviors have been added to the dictionary
 // Since this could query them for context and chains.
 protected override bool AfterInit(GenerationEnvironment env)
 {
     exportedMethods = GetAllExportedMethods(env).ToArray();
     return(base.AfterInit(env));
 }
示例#22
0
 private bool InitWithChain(GenerationEnvironment env, IChainWrapper chain)
 {
     this.ReferencedChain = chain;
     AdapterBody          = GetAdapterBody(env);
     return(!(AdapterBody is null));
 }
 public bool TryInit(GenerationEnvironment env) => TryHashFields(env);