public static bool TryParse(string content, FiMReport report, int start, int length, out KirinNode result) { result = null; if (!content.StartsWith("I ")) { return(false); } var match = Keywords.FirstOrDefault(k => content.StartsWith(k)); if (match == null) { return(false); } string value = content.Substring(match.Length); var node = new KirinFunctionCall(start, length) { FunctionName = value }; int keywordIndex = value.IndexOf(FunctionParam); if (keywordIndex > -1) { node.FunctionName = value.Substring(0, keywordIndex); node.RawParameters = value.Substring(keywordIndex + FunctionParam.Length); } else { node.RawParameters = string.Empty; } result = node; return(true); }
public static void AddExperimentalFunctions(FiMReport report) { report.AddParagraph("count of an array", new Func <IDictionary, double>((value) => { return(value.Keys.Count); })); report.AddParagraph("length of a string", new Func <string, double>((value) => { return(value.Length); })); report.AddParagraph("convert a number to char", new Func <double, char>((value) => { return((char)value); })); report.AddParagraph("convert a char to num", new Func <char, double>((value) => { return((double)char.Parse(value.ToString())); })); report.AddParagraph("convert a number to literal string", new Func <double, string>((value) => { return(value.ToString()); })); report.AddParagraph("convert a char to literal num", new Func <char, double>((value) => { return(int.Parse(value.ToString())); })); report.AddParagraph("square root of a num", new Func <double, double>((value) => { return(Math.Sqrt(value)); })); }
public static FiMReport GetReport(string path) { FiMReport report = FiMReport.FromFile(path); FiMSharp.CLI.Program.AddExperimentalFunctions(report); report.Output = (l) => Console.Write(l); report.Input = (p, _) => { if (string.IsNullOrWhiteSpace(p)) { Console.Write(p); } return(Console.ReadLine()); }; return(report); }
// READ FILE CONTENT public void ReadReport(string path) { Report = new FiMReport(); // CHECKS if (!Directory.Exists(path)) { throw new FiMException("Directory doesn't exist!"); } if (!File.Exists(path + "/main.fpp")) { throw new FiMException("main.fpp doesn't exist!"); } // List <string> lines = File.ReadAllLines(path + "/main.fpp").ToList(); #region Read main.fpp File try { lines = NetFIMMethods.SanitizeReport(lines); } catch (Exception e) { throw e; } // lets be strict on the report if (!lines.First().StartsWith(NetFIMGlobal.ReportStart)) { throw new Exception("Invalid Report beginning"); } else { Report.ReportName = lines.First().Remove(0, NetFIMGlobal.ReportStart.Length); } lines = NetFIMMethods.RemoveLinesAfterReportEnd(lines); if (!lines.Last().StartsWith(NetFIMGlobal.ReportEnd)) { throw new Exception("Invalid Report ending"); } else { Report.Writer = lines.Last().Remove(0, NetFIMGlobal.ReportEnd.Length); } // Grab global variables bool gv_inside = false; for (int i = 0; i < lines.Count; i++) { string line = lines[i]; if (line.Replace("Today ", "").StartsWith("I learned ")) { gv_inside = true; } if (line.StartsWith("That's all about ")) { gv_inside = false; } if (!gv_inside) { if (line.StartsWith(NetFIMGlobal.Keywords.Method_Declare)) { string variableName; FiMObject obj; try { obj = NetFIMMethods.InterpretVariableDecleration(line, out variableName, Report.GlobalVariables); } catch (Exception e) { throw new Exception("[" + i + "] " + e.Message); } if (Report.GlobalVariables.ContainsKey(variableName)) { throw new Exception("A global variable with the name '" + variableName + "' already exists."); } Report.GlobalVariables.Add(variableName, obj); } } } // Grab functions for (int i = 0; i < lines.Count(); i++) { string line = lines[i]; if (line.Replace("Today ", "").StartsWith("I learned ")) { string funcName = line.Replace("Today ", "").Replace("I learned ", ""); FiMObjectTypes funcType = FiMObjectTypes.UNKNOWN; FiMObjectTypes retrType = FiMObjectTypes.UNKNOWN; string retrName = null; if (NetFIMGlobal.Keywords.Method_Return.Any(x => funcName.Contains(x)) || funcName.Contains(NetFIMGlobal.Keywords.Method_Parameter)) { if (NetFIMGlobal.Keywords.Method_Return.Any(x => funcName.Contains(x))) { string r_ph = NetFIMGlobal.Keywords.Method_Return.Where(x => funcName.Contains(x)).FirstOrDefault(); string r_ap; try { funcType = NetFIMMethods.GetVariableInitializerType(funcName.Split(new string[] { r_ph }, StringSplitOptions.None)[1].TrimStart(' ').TrimEnd(' '), out r_ap); } catch (Exception ex) { throw new Exception("Invalid Paragraph return - " + ex); } // funcName = funcName.Replace(" " + r_ph + " " + r_ap, ""); } if (funcName.Contains(NetFIMGlobal.Keywords.Method_Parameter)) { string p_ap; try { retrType = NetFIMMethods.GetVariableInitializerType(funcName.Split(new string[] { NetFIMGlobal.Keywords.Method_Parameter }, StringSplitOptions.None)[1].TrimStart(' ').TrimEnd(' '), out p_ap); retrName = funcName.Split(new string[] { NetFIMGlobal.Keywords.Method_Parameter + " " + p_ap }, StringSplitOptions.None)[1].TrimStart(' '); } catch { throw new Exception("Invalid Paragraph parameter"); } // funcName = funcName.Split(new string[] { NetFIMGlobal.Keywords.Method_Parameter + " " + p_ap }, StringSplitOptions.None)[0].TrimEnd(' '); } } if (NetFIMGlobal.KeywordsList.Any(x => funcName.Contains(x))) { throw new Exception("Paragraph name '" + funcName + "' has broken one or more restrictions"); } List <string> iLines = new List <string>(); int il = i + 1; bool ibrk = false; while (!ibrk) { string inn = lines[il]; if (inn.StartsWith("That's all about ")) { // might also want to do the check here if (inn.Substring("That's all about ".Length) != funcName) { throw new Exception("Paragraph '" + funcName + "' doesn't end with the same name"); } ibrk = true; } else { iLines.Add(inn); } il++; } if (!ibrk) { throw new Exception("Paragraph '" + funcName + "' doesn't end properly"); } FiMParagraph paragraph = new FiMParagraph(iLines, i, line.StartsWith("Today "), returnType: funcType, paramType: retrType, paramName: retrName); if (Report.Paragraphs.Any(x => x.Value.isMainFunction == true && line.StartsWith("Today "))) { throw new Exception("Multiple main paragraphs found"); } Report.Paragraphs.Add(funcName, paragraph); i = il; } } #endregion }
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()); }
static void Main(string[] args) { if (args.Length > 0) { string report_path = ""; bool pretty = false; bool show_help = false; bool experimental = false; bool js = false; OptionSet p = new OptionSet() .Add("p|pretty", "Prettify console output.", v => pretty = true) .Add("h|help", "Show this message and exit.", v => show_help = true) .Add("e|experimental", "Add experimental functions", v => experimental = true) .Add("j|javascript", "Convert file to Javascript", v => js = true); List <string> extra = p.Parse(args); if (show_help) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { Console.WriteLine("Usage: fim [options] <report_path>"); } else { Console.WriteLine("Usage: ./fim [options] <report_path>"); } Console.WriteLine("Interprets the specified FiM++ report."); Console.WriteLine(); Console.WriteLine("Options:"); p.WriteOptionDescriptions(Console.Out); return; } if (extra.Count > 0) { report_path = extra[0]; } if (string.IsNullOrWhiteSpace(report_path) || !FileExists(report_path)) { throw new FileNotFoundException($"Cannot find report '{ report_path }'"); } #if DEBUG var debugTime = new System.Diagnostics.Stopwatch(); debugTime.Start(); #endif FiMReport report = FiMReport.FromFile(report_path); if (experimental) { AddExperimentalFunctions(report); } if (js) { string filename = Path.GetFileNameWithoutExtension(report_path); string old_path = Path.GetPathRoot(report_path); string new_path = Path.GetFullPath(Path.Combine(old_path, filename + ".js")); File.WriteAllText( new_path, Changeling.Javascript.Transpile( report, onInternalFunction: (name) => { var func = new Changeling.Javascript.JavascriptInternalFunction() { Name = "", Function = "" }; switch (name) { case "count of an array": { func.Name = "fim_count"; func.Function = "function fim_count(a){return a.filter(x=>x).length}"; } break; case "length of a string": { func.Name = "fim_length"; func.Function = "function fim_length(s){return s.length}"; } break; case "convert a number to char": { func.Name = "fim_ntc"; func.Function = "function fim_ntc(n){return String.fromCharCode(n)}"; } break; case "convert a char to num": { func.Name = "fim_ctn"; func.Function = "function fim_ctn(c){return c.charCodeAt(0)}"; } break; case "convert a number to literal string": { func.Name = "fim_ntls"; func.Function = "function fim_ntls(n){return n+''}"; } break; case "convert a char to literal num": { func.Name = "fim_ctln"; func.Function = "function fim_ctln(c){return +c}"; } break; case "square root of a num": { func.Name = "fim_sqrt"; func.Function = "function fim_sqrt(n){return Math.sqrt(n)}"; } break; } return(func); } ) ); } else { report.Output = (l) => Console.Write(l); report.Input = (p, n) => { if (string.IsNullOrEmpty(p)) { Console.Write($"{n} is asking for an input: "); } else { Console.Write(p); } return(Console.ReadLine()); }; if (pretty) { Console.WriteLine($"[ FiMSharp v{GetVersion()} ]"); Console.WriteLine($"Report Name: {report.Info.Name}"); Console.WriteLine($"Student Name: {report.Author.Name}"); Console.WriteLine("[@]===[@]"); } report.MainParagraph?.Execute(); if (pretty) { Console.WriteLine("[@]===[@]"); } } #if DEBUG Console.WriteLine($"[Debug] Code execution took {debugTime.Elapsed:d\\.hh\\:mm\\:ss\\:fff}."); #endif } }