170 lines
4.9 KiB
C#
170 lines
4.9 KiB
C#
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.IO;
|
||
|
using System.Text;
|
||
|
|
||
|
namespace tlang {
|
||
|
public class TLangGenerator
|
||
|
{
|
||
|
Lazy<(List<LexToken> Tokens,string Source)> srcCode;
|
||
|
public TLangGenerator(string src)
|
||
|
{
|
||
|
srcCode=new Lazy<(List<LexToken> Tokens,string Source)>(()=>{
|
||
|
string text = Lex(src);
|
||
|
Lexer lexer=new Lexer(new StringReader(text));
|
||
|
|
||
|
return (lexer.Tokens,text);
|
||
|
});
|
||
|
}
|
||
|
public string SourceCode
|
||
|
{
|
||
|
get => srcCode.Value.Source;
|
||
|
}
|
||
|
public string Execute(Action<RootEnvironment> parser)
|
||
|
{
|
||
|
|
||
|
Parser p = new Parser(srcCode.Value.Tokens);
|
||
|
RootEnvironment env=new RootEnvironment();
|
||
|
p.LoadEnvironment(env);
|
||
|
StringBuilder builder=new StringBuilder();
|
||
|
env.SetVariable("__writer",new TExternalMethod((args)=>{
|
||
|
if(args.Length >= 1)
|
||
|
{
|
||
|
foreach(var arg in args)
|
||
|
{
|
||
|
builder.Append(arg.ToString());
|
||
|
}
|
||
|
}
|
||
|
return TObject.Null;
|
||
|
}));
|
||
|
parser?.Invoke(env);
|
||
|
|
||
|
p.Execute(env);
|
||
|
|
||
|
|
||
|
//
|
||
|
return builder.ToString();
|
||
|
}
|
||
|
|
||
|
private string Lex(string text)
|
||
|
{
|
||
|
StringBuilder tlangFile = new StringBuilder();
|
||
|
StringBuilder b=new StringBuilder();
|
||
|
|
||
|
bool lastInCode=true;
|
||
|
bool inCode=false;
|
||
|
int i = 0;
|
||
|
bool isDone = false;
|
||
|
Action<int> appendChar = (c)=>{
|
||
|
if((lastInCode != inCode || isDone) && b.Length > 0)
|
||
|
{
|
||
|
if(inCode ^ (isDone && !inCode))
|
||
|
{
|
||
|
tlangFile.Append("__writer(\"");
|
||
|
b.Replace("\n","\\n").Replace("\t","\\t").Replace("\r","\\r").Replace("\"","\\\"").Replace("\'","\\\'");
|
||
|
foreach(var _c in b.ToString())
|
||
|
{
|
||
|
if(_c < 32 || _c > 126)
|
||
|
{
|
||
|
tlangFile.Append($"\\x{((int)_c).ToString("X2")}");
|
||
|
}else{
|
||
|
tlangFile.Append(_c);
|
||
|
}
|
||
|
}
|
||
|
b.Clear();
|
||
|
|
||
|
tlangFile.Append("\");");
|
||
|
}
|
||
|
if(!inCode ^ (isDone && inCode))
|
||
|
{
|
||
|
if(b.Length > 0)
|
||
|
{
|
||
|
tlangFile.Append(b);
|
||
|
}
|
||
|
b.Clear();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(c != -1)
|
||
|
{
|
||
|
b.Append((char)c);
|
||
|
}
|
||
|
|
||
|
lastInCode = inCode;
|
||
|
};
|
||
|
bool escapedByAt = false;
|
||
|
while(i < text.Length)
|
||
|
{
|
||
|
char curChar = text[i++];
|
||
|
int nextChar = i<text.Length ? text[i] : -1;
|
||
|
switch(curChar)
|
||
|
{
|
||
|
case '@':
|
||
|
if(nextChar == '{')
|
||
|
{
|
||
|
appendChar('{');
|
||
|
i++;
|
||
|
}else if(nextChar == '}') {
|
||
|
appendChar('}');
|
||
|
i++;
|
||
|
}else if(nextChar == '@')
|
||
|
{
|
||
|
appendChar('@');
|
||
|
i++;
|
||
|
}else{
|
||
|
if(!escapedByAt)
|
||
|
{
|
||
|
|
||
|
inCode=true;
|
||
|
escapedByAt=true;
|
||
|
//we are a variable
|
||
|
|
||
|
foreach(var c in "__writer(")
|
||
|
appendChar(c);
|
||
|
|
||
|
}else
|
||
|
if(escapedByAt)
|
||
|
{
|
||
|
appendChar(')');
|
||
|
appendChar(';');
|
||
|
inCode = false;
|
||
|
escapedByAt = false;
|
||
|
|
||
|
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case '{':
|
||
|
if(nextChar == '{')
|
||
|
{
|
||
|
|
||
|
i++;
|
||
|
inCode = !inCode;
|
||
|
|
||
|
}else{
|
||
|
appendChar(curChar);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case '}':
|
||
|
if(nextChar == '}')
|
||
|
{
|
||
|
i++;
|
||
|
inCode = !inCode;
|
||
|
|
||
|
}else{
|
||
|
appendChar(curChar);
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
appendChar(curChar);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
isDone=true;
|
||
|
appendChar(-1);
|
||
|
return tlangFile.ToString();
|
||
|
}
|
||
|
}
|
||
|
}
|