private static string ParseConditionalNodes(string value, JavascriptContainer container) { if (KirinConditional.IsConditional(value, out var tree)) { return(ParseConditionalNodes(tree, container)); } return(SanitizeValue(value, container)); }
private static string ParseArithmeticNodes(string value, JavascriptContainer container) { if (KirinArithmetic.IsArithmetic(value, out var tree)) { return(ParseArithmeticNodes(tree, container)); } return(SanitizeValue(value, container)); }
private static string ParseArithmeticNodes(KirinArithmetic.ArithmeticCheckResult tree, JavascriptContainer container) { string left = ParseArithmeticNodes(tree.Left, container); string right = ParseArithmeticNodes(tree.Right, container); string exp = tree.Expression; return($"{ left } { exp } { right }"); }
private static string ParseConditionalNodes(KirinConditional.ConditionalCheckResult tree, JavascriptContainer container) { string left = ParseConditionalNodes(tree.Left, container); string right = ParseConditionalNodes(tree.Right, container); string exp = tree.Expression; return($"{ left } { exp } { right }"); }
private static string SanitizeValue(string value, JavascriptContainer container, KirinVariableType?expectedType = null) { var eType = FiMHelper.DeclarationType.Determine(" " + value, out string eKeyword, strict: false); if (eType != KirinVariableType.UNKNOWN) { value = value.Substring(eKeyword.Length); expectedType = eType; } // Nothing if ((value == null || value == "nothing") && expectedType != null) { return(DefaultValue((KirinVariableType)expectedType)); } if (KirinLiteral.TryParse(value, out object val)) { if (val.GetType() == typeof(bool)) { return((bool)val == true ? "true" : "false"); } return(value); } // Calling an existing method if (container.report.GetParagraphLazy(value) != null) { string args = ""; string pName = value; if (pName.Contains(KirinFunctionCall.FunctionParam)) { int pIndex = pName.IndexOf(KirinFunctionCall.FunctionParam); pName = pName.Substring(0, pIndex); args = string.Join(", ", value .Substring(pName.Length + KirinFunctionCall.FunctionParam.Length) .Split(new string[] { " and " }, StringSplitOptions.None) .Select(v => SanitizeValue(v, container)) ); } if (container.internalFunctions.ContainsKey(pName)) { return($"{ container.internalFunctions[pName] }({ args })"); } return($"this.{ SanitizeName(pName, container) }({ args })"); } // Array if (expectedType != null && FiMHelper.IsTypeArray((KirinVariableType)expectedType)) { string args = string.Join(", ", value .Split(new string[] { " and " }, StringSplitOptions.None) .Select(v => SanitizeValue(v, container)) ); return($"[, { args }]"); } // Array index if (IsArrayIndex(value)) { var match = GetArrayIndex(value); var index = SanitizeValue(match.RawIndex, container); var variable = SanitizeValue(match.RawVariable, container); return($"{ variable }[ fim_index({ variable }, {index}) ]"); } // Arithmetic if (KirinArithmetic.IsArithmetic(value, out var arithmeticResult)) { return(ParseArithmeticNodes(arithmeticResult, container)); } // Conditional if (KirinConditional.IsConditional(value, out var conditionalResult)) { return(ParseConditionalNodes(conditionalResult, container)); } // Calling an existing variable (hopefully) return(SanitizeName(value, container)); }
public static string Transpile( FiMReport report, string indent = " ", Func <string, JavascriptInternalFunction> onInternalFunction = null, string output = "console.log", string outputNewline = "console.log", string input = "prompt" ) { StringBuilder sb = new StringBuilder(); var container = new JavascriptContainer() { report = report, globalVariables = new List <string>(), internalFunctions = new Dictionary <string, string>(), output = output, outputNewline = outputNewline, input = input, }; sb.AppendLine("'use strict';"); sb.AppendLine(""); sb.AppendLine("/**"); sb.AppendLine(" * "); sb.AppendLine($" * This report, entitled \"{ report.Info.Name }\", was written by: { report.Author.Name } "); sb.AppendLine(" * "); sb.AppendLine(" */"); sb.AppendLine(""); // Helpers // fim_index(x,y) = Index arrays normally, minus one for strings. sb.AppendLine("function fim_index(x,y){return y-(Array.isArray(x)?0:1)}"); // fim_forin(x) = Removes the first (null) element if passed an array sb.AppendLine("function fim_forin(x){return Array.isArray(x)?x.slice(1):x}"); Dictionary <string, string> internalHelpers = new Dictionary <string, string>(); foreach (var paragraph in report.Paragraphs.Reverse().Where(p => p.FunctionType == "KirinInternalFunction")) { if (onInternalFunction == null) { throw new Exception("Report requests an internal function conversion: " + paragraph.Name); } var iFunc = onInternalFunction(paragraph.Name); sb.AppendLine(iFunc.Function); container.internalFunctions.Add(paragraph.Name, iFunc.Name); } sb.AppendLine(""); sb.AppendLine($"const { SanitizeName(report.Info.Recipient) } = function() {{}}"); string name = SanitizeName(report.Info.Name); if (int.TryParse(name[0].ToString(), out int _)) { name = $"_{name}"; } sb.AppendLine($"function { name }() {{"); foreach (var variable in report.Variables.GlobalVariables.Reverse()) { sb.AppendLine($"{ indent }this.{ SanitizeName(variable.Name) } = { SanitizeObject(variable.Value, variable.Type) }"); container.globalVariables.Add(variable.Name); } foreach (var paragraph in report.Paragraphs.Reverse().Where(p => p.FunctionType != "KirinInternalFunction")) { var func = (KirinFunction)paragraph.Function; string args = ""; if (func.Arguments != null) { args = string.Join(", ", func.Arguments.Select(a => $"/** @type {{{ KirinTypeAsJavascriptType(a.Type) }}} */ { SanitizeName(a.Name) } = { DefaultValue(a.Type) }" ) ); } sb.AppendLine($"{ indent }this.{ SanitizeName(paragraph.Name) } = function({ args }) {{"); TranspileStatements(sb, container, func.Statement, indent, 2); sb.AppendLine($"{ indent }}}"); } if (report.MainParagraph != null) { sb.AppendLine(""); sb.AppendLine($"{ indent }this.today = function() {{"); sb.AppendLine($"{ indent }{ indent }this.{ SanitizeName(report.MainParagraph.Name) }();"); sb.AppendLine($"{ indent }}}"); } sb.AppendLine($"}}"); sb.AppendLine($"{name}.prototype = new {SanitizeName(report.Info.Recipient)}();"); sb.AppendLine(""); sb.AppendLine($"new {name}().today();"); return(sb.ToString()); }
private static void TranspileStatements( StringBuilder sb, JavascriptContainer container, KirinStatement statement, string indent, int depth = 1 ) { string _i = string.Concat(Enumerable.Repeat(indent, depth)); string __i = string.Concat(Enumerable.Repeat(indent, depth + 1)); string ___i = string.Concat(Enumerable.Repeat(indent, depth + 2)); foreach (var node in statement.Body) { switch (node.NodeType) { case "KirinPrint": { var n = (KirinPrint)node; sb.AppendLine($"{_i}{ (n.NewLine ? container.outputNewline : container.output) }({ SanitizeValue(n.RawParameters, container) });"); } break; case "KirinFunctionCall": { var n = (KirinFunctionCall)node; string args = ""; if (n.RawParameters != string.Empty) { args = string.Join(", ", n.RawParameters .Split(new string[] { " and " }, StringSplitOptions.None) .Select(v => SanitizeValue(v, container)) ); } sb.AppendLine($"{_i}this.{ SanitizeName(n.FunctionName, container) }({ args });"); } break; case "KirinVariableDeclaration": { var n = (KirinVariableDeclaration)node; string val = SanitizeValue(n.RawValue, container, n.ExpectedType); string modif = n.Constant ? "const" : "let"; sb.AppendLine($"{_i}{modif} { SanitizeName(n.Name, container) } = { val };"); } break; case "KirinListModify": { var n = (KirinListModify)node; string index = SanitizeValue(n.RawIndex, container); string value = SanitizeValue(n.RightOp, container); sb.AppendLine($"{_i}{ SanitizeName(n.LeftOp, container) }[{ index }] = { value };"); } break; case "KirinUnary": { var n = (KirinUnary)node; string value = SanitizeValue(n.RawVariable, container); if (n.Increment) { sb.AppendLine($"{_i}{ value } = ({ value } || 0) + 1.0"); } else { sb.AppendLine($"{_i}{ value } = ({ value } || 0) - 1.0"); } } break; case "KirinInput": { var n = (KirinInput)node; string name = SanitizeName(n.RawVariable, container); string prompt = $"{ n.RawVariable } is asking for an input: "; if (n.PromptString != string.Empty) { prompt = n.PromptString; } sb.AppendLine($"{_i}{name} = { container.input }(\"{ prompt }\");"); } break; case "KirinIfStatement": { var n = (KirinIfStatement)node; for (int i = 0; i < n.Conditions.Count; i++) { var c = n.Conditions[i]; string ifType = i == 0 ? "if" : "else if"; if (i + 1 == n.Conditions.Count && c.Condition.Left == "correct" && c.Condition.Expression == "==" && c.Condition.Right == "correct") { ifType = "else"; } string check = ParseConditionalNodes(c.Condition, container); if (ifType != "else") { ifType += $" ({ check })"; } sb.AppendLine($"{_i}{ifType} {{"); TranspileStatements(sb, container, c.Statement, indent, depth + 1); sb.AppendLine($"{_i}}}"); } } break; case "KirinForToLoop": { var n = (KirinForToLoop)node; string name = SanitizeName(n.VariableName, container); string min = SanitizeValue(n.RawFrom, container); string max = SanitizeValue(n.RawTo, container); string interval = SanitizeValue(n.RawInterval, container); // HACK: uh. if (interval == string.Empty) { interval = $"({min} <= {max} ? 1 : -1)"; } string check = $"({interval} > 0 ? {name} <= {max} : {name} >= {max})"; sb.AppendLine($"{_i}for (let { name } = {min}; {check}; { name } += { interval }) {{"); TranspileStatements(sb, container, n.Statement, indent, depth + 1); sb.AppendLine($"{_i}}}"); } break; case "KirinForInLoop": { var n = (KirinForInLoop)node; string name = SanitizeName(n.VariableName, container); string value = SanitizeValue(n.RawValue, container); sb.AppendLine($"{_i}for (let {name} of fim_forin({value})) {{"); TranspileStatements(sb, container, n.Statement, indent, depth + 1); sb.AppendLine($"{_i}}}"); } break; case "KirinWhileLoop": { var n = (KirinWhileLoop)node; sb.AppendLine($"{_i}while ({ ParseConditionalNodes(n.Condition, container) }) {{"); TranspileStatements(sb, container, n.Statement, indent, depth + 1); sb.AppendLine($"{_i}}}"); } break; case "KirinSwitch": { var n = (KirinSwitch)node; sb.AppendLine($"{_i}switch ({ SanitizeName(n.RawVariable, container) }) {{"); if (n.Cases.Count > 0) { foreach (var c in n.Cases) { string condition = SanitizeName(c.Key, container); if (KirinSwitchCase.IsValidNumberPlace(condition, out var index)) { condition = index.ToString(); } sb.AppendLine($"{__i}case ({ condition }): {{"); TranspileStatements(sb, container, c.Value, indent, depth + 2); sb.AppendLine($"{__i}}}"); sb.AppendLine($"{__i}break;"); } } if (n.DefaultCase != null) { sb.AppendLine($"{__i}default: {{"); TranspileStatements(sb, container, n.DefaultCase, indent, depth + 2); sb.AppendLine($"{__i}}}"); sb.AppendLine($"{__i}break;"); } sb.AppendLine($"{_i}}}"); } break; case "KirinVariableModify": { var n = (KirinVariableModify)node; sb.AppendLine($"{_i}{SanitizeName(n.LeftOp, container)} = {SanitizeValue(n.RightOp, container)};"); } break; case "KirinReturn": { var n = (KirinReturn)node; sb.AppendLine($"{_i}return { SanitizeValue(n.RawParameters, container) };"); } break; default: { sb.AppendLine($"{_i}{node.NodeType}"); } break; } } }