public void MODULE_CALL()

in src/Trinity.TSL/Trinity.TSL.Metagen/VM.cs [452:884]


        public void MODULE_CALL()
        {
            string module_call_code;
            if (b_is_module)
            {
                module_call_code = @"
{
    ModuleContext module_ctx;
    module_ctx.m_stack_depth = context->m_stack_depth + 1;
";
            }
            else
            {
                module_call_code = @"
{
    ModuleContext module_ctx;
    module_ctx.m_stack_depth = 0;
";
            }

            for (int i = 2; i < argc(); ++i)
            {
                module_call_code += @"module_ctx.m_arguments.push_back(" + META_GetString(META_Translate(arg(i))) + @");
";
            }

            module_call_code += "std::string* module_content = Modules::" + arg(0) + "(" + META_Translate(arg(1)) + @", &module_ctx);
    source->append(*module_content);
    delete module_content;
}";

            OutputToMeta(module_call_code);
        }

        public void MODULE_BEGIN()
        {
            // MODULE_BEGIN is ignored.
        }

        public void MODULE_END()
        {
            // MODULE_END is ignored.
        }

        public void MUTE()
        {
            state.muted = true;
        }

        public void MUTE_END()
        {
            state.muted = false;
        }

        private void GenerateTemplatePrologue()
        {
            OutputToMeta(
@"#include ""common.h""
#include <string>
#include ""SyntaxNode.h""

using std::string;

namespace Trinity
{
    namespace Codegen
    {
        string* "); OutputToMeta(name + "("); OutputToMeta(FindTarget() + @"* node)
        {
            string* source = new string();
            ");
        }


        private void GenerateTemplateEpilogue()
        {
            OutputToMeta(
@"
            return source;
        }
    }
}");
        }

        private void GenerateModulePrologue()
        {
            OutputToMeta(
@"#include ""common.h""
#include <string>
#include ""SyntaxNode.h""

using std::string;

namespace Trinity
{
    namespace Codegen
    {
        namespace Modules
        {
            string* "); OutputToMeta(name + "("); OutputToMeta(FindTarget() + @"* node, ModuleContext* context)
            {
                string* source = new string();
                ");
        }

        private void GenerateModuleEpilogue()
        {
            OutputToMeta(
@"
                return source;
            }
        }
    }
}");
        }


        private bool IsModule()
        {
            return instructions.Any(inst => inst.type == MetaType.MODULE_BEGIN);
        }

        private string FindTarget()
        {
            foreach (var token in instructions)
                if (token.type == MetaType.TARGET)
                    return token.text[0];
            throw new Exception("No target found.");
        }

        private void InitializeMethodTable()
        {
            d_instruction_method_table = new Dictionary<string, InstructionDelegate>();
            Type vm_type = typeof(VM);

            foreach (var method_desc in vm_type.GetMethods())
            {
                if (method_desc.Name == method_desc.Name.ToUpperInvariant())
                {
                    d_instruction_method_table[method_desc.Name] = (InstructionDelegate)Delegate.CreateDelegate(typeof(InstructionDelegate), this, method_desc);
                }
            }
        }

        public void Execute()
        {
            b_is_module = IsModule();
            var target  = FindTarget();

            if (b_is_module)
            {
                GenerateModulePrologue();
                instructions.FocusOnModule();
            }
            else
            {
                GenerateTemplatePrologue();
            }

            Reset();

            Execute_Core();

            if (b_is_module)
            {
                GenerateModuleEpilogue();
            }
            else
            {
                GenerateTemplateEpilogue();
            }
        }

        private void Execute_Core()
        {
            InitializeMethodTable();

            for (; state.ip < instructions.Count; ++state.ip)
            {
                if (state.muted)
                {
                    if (current_instruction.type == MetaType.MUTE_END)
                        state.muted = false;
                }
                else
                {
                    //var func = typeof(VM).GetMethod(current_instruction.type.ToString());
                    try
                    {
                        //func.Invoke(this, null);
                        d_instruction_method_table[current_instruction.type.ToString()]();
                    }
                    catch 
                    {
                        Console.WriteLine("Current instruction:");
                        Console.WriteLine(current_instruction);
                        throw;
                    }
                }
            }
            FlushLiteralBuffer();

            if (stack.Count != 0)
            {
                Console.WriteLine("\nStack not empty after executing all instructions.");
                throw new Exception();
            }
        }


        private string arg(int idx)
        {
            return current_instruction.text[idx];
        }

        private int argc()
        {
            return current_instruction.text.Count;
        }

        private void Push()
        {
            stack.Push(current_instruction);
            switch (current_instruction.type)
            {
                case MetaType.FOREACH:
                    ++state.foreach_depth;
                    break;
                case MetaType.IF:
                    ++state.if_depth;
                    break;
            }
        }

        private void Pop()
        {
            var inst = stack.Pop();
            switch (inst.type)
            {
                case MetaType.FOREACH:
                    string sep = separator_stack.Pop();
                    if (sep != String.Empty)
                    {
                        OutputToMeta("if (" + 
                        META_ForeachIteratorName() + " < " + 
                        META_GetListSize(d_iteratable_target[inst]) + " - 1)");
                        OutputMetaStringToTemplate('"'+sep+'"');
                    }
                    --state.foreach_depth;
                    break;
                case MetaType.IF:
                    --state.if_depth;
                    break;
            }
        }

        private MetaToken Find_LISTVAR()
        {
            //XXX only scans for the first LIST, when nested and inner loop has list in the front
            //it will be picked up by outer loop
            //Workaround: introduce USE_LIST
            return instructions[instructions.FindIndex(state.ip, i => Is_LISTVAR(i))];
        }

        private bool Is_LISTVAR(MetaToken i)
        {
            if (i.type == MetaType.USE_LIST)
                return true;
            if (i.type != MetaType.TEMPLATE)
                return false;
            var template_name = i.text[0];
            if (null != LISTVAR_GetListName(template_name))
                return true;
            return false;
        }

        private string LISTVAR_GetListName(string str)
        {
            if (d_var_explicit_host.ContainsKey(str))
                return d_var_explicit_host[str];
            string list_name = null;
            foreach (var kvp in d_list)
                if (str.StartsWith(kvp.Key, StringComparison.Ordinal))
                    if (list_name == null || list_name.Length < kvp.Key.Length)
                        list_name = kvp.Key;
            if (list_name != null)
                return list_name;
            return null;
        }

        /**
         * The rule of matching VAR prefix:
         *   1. There is a MAP_VAR which is a proper prefix of the string
         *     *AND*
         *   2. If multiple prefixes exist, choose the longest one.
         *   3. If str is a defined variable, drop any var prefix.
         *   
         *   (A proper prefix is a strict prefix, that is, the length is smaller)
         */
        private string VAR_prefix(string str)
        {
            if (d_var.ContainsKey(str))
                return null;
            string var_prefix = null;
            foreach (var kvp in d_var)
                if (str.StartsWith(kvp.Key, StringComparison.Ordinal) && str.Length != kvp.Key.Length)
                {
                    if (var_prefix == null || var_prefix.Length < kvp.Key.Length)
                        var_prefix = kvp.Key;
                }
            return var_prefix;
        }

        private int Find_END()
        {
            int required_end = 1;
            for (int idx = state.ip + 1; idx < instructions.Count; ++idx)
            {
                switch (instructions[idx].type)
                {
                    case MetaType.FOREACH:
                    case MetaType.IF:
                        required_end++;
                        break;
                    case MetaType.END:
                        required_end--;
                        if (required_end == 0)
                            return idx;
                        break;
                }
            }
            throw new Exception("Unmatched END");
        }

        private void Reset()
        {
            stack                     = new Stack<MetaToken>();
            separator_stack           = new Stack<string>();
            state                     = new VMState();
            d_var                     = new Dictionary<string, string>();
            d_var_explicit_host       = new Dictionary<string, string>();
            d_list                    = new Dictionary<string, string>();
            d_list_iterator_depth     = new Dictionary<string, int>();
            d_list_explicit_host      = new Dictionary<string, string>();
            d_metavar_depth           = new Dictionary<string, int>();//TODO should be able to pop meta var?
            d_iteratable_target       = new Dictionary<MetaToken, MetaToken>();
            l_literal_buffer          = new List<string>();
            b_is_sub_vm               = false;
        }

        private void FlushLiteralBuffer()
        {
            if (b_is_sub_vm)
            {
                source.Append(string.Join("", l_literal_buffer));
                l_literal_buffer.Clear();
                return;
            }

            if (l_literal_buffer == null || l_literal_buffer.Count == 0)
                return;

            /**
             * Format logic:
             *  1. Replace continuous <CR><CR> with a single <CR>
             *  2. Then, replace <CR><spaces><CR> with a single<CR>,
             *     unless it follows a '}', or #region, #endregion.
             */
            var text = string.Join("", l_literal_buffer);
            var rgx  = new Regex(@"(?:\r?\n){2,}");
            text     = rgx.Replace(text, "\r\n");
            rgx      = new Regex(@"(?<!\}|\#.*)\r\n\s*\r\n");
            text     = rgx.Replace(text, "\r\n");

            /**
             * We chop literals into chunks of max size s_MAX_LITERAL_LENGTH.
             * This is due to that MSVC has a maximum string length restriction.
             */
            int len = text.Length;
            int pos = 0;

            while (len > 0)
            {
                source.Append("source->append(R\"::(");

                if (len <= s_MAX_LITERAL_LENGTH)
                {
                    source.Append(text.Substring(pos, len));
                    len = 0;
                }
                else
                {
                    source.Append(text.Substring(pos, s_MAX_LITERAL_LENGTH));
                    len -= s_MAX_LITERAL_LENGTH;
                    pos += s_MAX_LITERAL_LENGTH;
                }

                source.Append(")::\");\r\n");
            }

            l_literal_buffer.Clear();
        }

        private void OutputLiteralToTemplate(string text)
        {
            if (0 == text.Count(x => !char.IsWhiteSpace(x)))
            {
                if(text.Contains('\n'))
                    return;
            }
            l_literal_buffer.Add(text);
        }

        /**
         * Output directly into codegen, rather than template.
         */
        private void OutputToMeta(string meta_code)
        {
            FlushLiteralBuffer();
            source.Append(meta_code);
            if (!b_is_sub_vm)
                source.Append("\r\n");
        }

        private void OutputMetaStringToTemplate(string meta_expr)
        {
            FlushLiteralBuffer();
            if (b_is_sub_vm)
                OutputToMeta(meta_expr);
            else
                OutputToMeta("source->append(" + meta_expr + ");");
        }
    }