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 + ");");
}
}