Created
June 6, 2019 12:04
-
-
Save TinkerWorX/16acf7355cb08dca6c3a4d16b67b3402 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text.RegularExpressions; | |
using System.IO; | |
namespace TinkerWorX.JassToTypeScript | |
{ | |
internal class TypeDefinition | |
{ | |
public string Name { get; set; } | |
public string Parent { get; set; } | |
} | |
internal class ArgumentDefinition | |
{ | |
public string Type { get; set; } | |
public string Name { get; set; } | |
} | |
internal class NativeDefinition | |
{ | |
public string Name { get; set; } | |
public List<ArgumentDefinition> Arguments { get; set; } | |
public string ReturnType { get; set; } | |
} | |
internal class GlobalDefinition | |
{ | |
public bool IsConstant { get; set; } | |
public string Type { get; set; } | |
public bool IsArray { get; set; } | |
public string Name { get; set; } | |
public string Value { get; set; } | |
} | |
internal class FunctionDefinition | |
{ | |
public string Name { get; set; } | |
public List<ArgumentDefinition> Arguments { get; set; } | |
public string ReturnType { get; set; } | |
} | |
internal class LibraryDefinition | |
{ | |
public List<TypeDefinition> Types { get; } = new List<TypeDefinition>(); | |
public List<NativeDefinition> Natives { get; } = new List<NativeDefinition>(); | |
public List<GlobalDefinition> Globals { get; } = new List<GlobalDefinition>(); | |
public List<FunctionDefinition> Functions { get; } = new List<FunctionDefinition>(); | |
} | |
internal class Program | |
{ | |
private const string TYPE_DEFINITION = @"type\s+(?<name>\w+)\s+extends\s+(?<parent>\w+)"; | |
private const string NATIVE_DEFINITION = @"native\s+(?<name>\w+)\s+takes\s+(?<prototype>.+)"; | |
private const string GLOBAL_DEFINITION = @"(?<constant>constant)?\s*(?<type>\w+)(\s+(?<array>array))?\s+(?<name>\w+)(\s+=\s(?<value>.+))?"; | |
private const string FUNCTION_DEFINITION = @"function\s+(?<name>\w+)\s+takes\s+(?<prototype>.+)"; | |
private static string Clean(string input) | |
{ | |
input = input.Trim(); | |
while (input.IndexOf(" ") >= 0) | |
input = input.Replace(" ", " "); | |
if (input.IndexOf("//") >= 0) | |
input = input.Substring(0, input.IndexOf("//")); | |
while (input.IndexOf(" ") >= 0) | |
input = input.Replace(" ", " "); | |
return input; | |
} | |
private static void ParseLines(string[] lines, LibraryDefinition library) | |
{ | |
bool inGlobals = false; | |
foreach (var rawLine in lines) | |
{ | |
var line = Clean(rawLine); | |
if (line.StartsWith("//")) | |
continue; | |
if (inGlobals) | |
{ | |
inGlobals = !line.Contains("endglobals"); | |
if (!inGlobals) | |
continue; | |
var globalDefinition = Regex.Match(line, GLOBAL_DEFINITION); | |
if (globalDefinition.Success) | |
{ | |
var type = globalDefinition.Groups["type"].Value; | |
var value = globalDefinition.Groups["value"].Value; | |
//if (value.Length == 6 && value[0] == '\'' && value[5] == '\'') | |
// type = "string"; | |
library.Globals.Add(new GlobalDefinition | |
{ | |
IsConstant = !String.IsNullOrWhiteSpace(globalDefinition.Groups["constant"].Value), | |
Type = type, | |
IsArray = !String.IsNullOrWhiteSpace(globalDefinition.Groups["array"].Value), | |
Name = globalDefinition.Groups["name"].Value, | |
Value = value, | |
}); | |
continue; | |
} | |
} | |
else | |
{ | |
inGlobals = line.Contains("globals"); | |
if (inGlobals) | |
continue; | |
var typeDefinition = Regex.Match(line, TYPE_DEFINITION); | |
if (typeDefinition.Success) | |
{ | |
library.Types.Add(new TypeDefinition | |
{ | |
Name = typeDefinition.Groups["name"].Value, | |
Parent = typeDefinition.Groups["parent"].Value | |
}); | |
continue; | |
} | |
var nativeDefinition = Regex.Match(line, NATIVE_DEFINITION); | |
if (nativeDefinition.Success) | |
{ | |
var name = nativeDefinition.Groups["name"].Value; | |
var prototype = nativeDefinition.Groups["prototype"].Value; | |
var takes = Clean(prototype.Split(new string[] { "returns" }, StringSplitOptions.None)[0]); | |
var returns = Clean(prototype.Split(new string[] { "returns" }, StringSplitOptions.None)[1]); | |
library.Natives.Add(new NativeDefinition | |
{ | |
Name = name, | |
Arguments = takes.Equals("nothing") ? new List<ArgumentDefinition>() : takes.Split(',').Select(s => s.Trim()).Select(s => new ArgumentDefinition | |
{ | |
Type = s.Split(' ')[0], | |
Name = s.Split(' ')[1], | |
}).ToList(), | |
ReturnType = returns, | |
}); | |
continue; | |
} | |
var functionDefinition = Regex.Match(line, FUNCTION_DEFINITION); | |
if (functionDefinition.Success) | |
{ | |
var name = functionDefinition.Groups["name"].Value; | |
var prototype = functionDefinition.Groups["prototype"].Value; | |
var takes = Clean(prototype.Split(new string[] { "returns" }, StringSplitOptions.None)[0]); | |
var returns = Clean(prototype.Split(new string[] { "returns" }, StringSplitOptions.None)[1]); | |
library.Functions.Add(new FunctionDefinition | |
{ | |
Name = name, | |
Arguments = takes.Equals("nothing") ? new List<ArgumentDefinition>() : takes.Split(',').Select(s => s.Trim()).Select(s => new ArgumentDefinition | |
{ | |
Type = s.Split(' ')[0], | |
Name = s.Split(' ')[1], | |
}).ToList(), | |
ReturnType = returns, | |
}); | |
continue; | |
} | |
} | |
} | |
} | |
private static void ParseFile(string path, LibraryDefinition library) | |
{ | |
ParseLines(File.ReadAllLines(path), library); | |
} | |
private static string FixType(string type) | |
{ | |
switch (type) | |
{ | |
case "real": | |
case "integer": | |
type = "number"; | |
break; | |
case "nothing": | |
type = "void"; | |
break; | |
case "code": | |
type = "() => void"; | |
break; | |
//case "boolexpr": | |
//case "conditionfunc": | |
//case "filterfunc": | |
//type = "() => boolean"; | |
//break; | |
} | |
return type; | |
} | |
private static void Magic(NativeDefinition native) | |
{ | |
switch (native.Name) | |
{ | |
case "Condition": | |
case "Filter": | |
native.Arguments[0].Type = "() => boolean"; | |
break; | |
} | |
} | |
private static int Main(string[] args) | |
{ | |
if (args.Length < 2) | |
{ | |
Console.WriteLine("Usage: TinkerWorX.JassToTypeScript.exe input1.j [input2.j...] output.d.ts"); | |
return 1; | |
} | |
var inputFiles = args.Reverse().Skip(1).Reverse(); | |
var outputFile = args.Reverse().First(); | |
var library = new LibraryDefinition(); | |
foreach (var inputFile in inputFiles) | |
{ | |
Console.WriteLine($"Parsing: {inputFile}"); | |
ParseFile(inputFile, library); | |
} | |
Console.WriteLine($"Writing: {outputFile}"); | |
using (var stream = File.Open(outputFile, FileMode.Create, FileAccess.Write, FileShare.Write)) | |
using (var writer = new StreamWriter(stream)) | |
{ | |
writer.WriteLine("/** @noSelfInFile **/"); | |
writer.WriteLine(); | |
foreach (var type in library.Types) | |
{ | |
writer.WriteLine($"declare abstract class {type.Name} extends {type.Parent} {{ __{type.Name}: never; }}"); | |
} | |
writer.WriteLine(); | |
foreach (var native in library.Natives) | |
{ | |
Magic(native); | |
writer.Write($"declare function {native.Name}("); | |
if (native.Arguments.Any()) | |
writer.Write($"{native.Arguments.Select(arg => $"{arg.Name}: {FixType(arg.Type)}").Aggregate((a, b) => a + ", " + b)}"); | |
writer.WriteLine($"): {FixType(native.ReturnType)}"); | |
} | |
writer.WriteLine(); | |
foreach (var global in library.Globals) | |
{ | |
writer.Write("declare"); | |
if (global.IsConstant) | |
writer.Write(" const"); | |
else | |
writer.Write(" var"); | |
writer.Write($" {global.Name}"); | |
writer.WriteLine($": {FixType(global.Type)}"); | |
} | |
writer.WriteLine(); | |
foreach (var function in library.Functions) | |
{ | |
writer.Write($"declare function {function.Name}("); | |
if (function.Arguments.Any()) | |
writer.Write($"{function.Arguments.Select(arg => $"{arg.Name}: {FixType(arg.Type)}").Aggregate((a, b) => a + ", " + b)}"); | |
writer.WriteLine($"): {FixType(function.ReturnType)}"); | |
} | |
} | |
return 0; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment