using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Text.RegularExpressions; namespace SuperBASIC { class Parser { [Serializable] public class ParseException : Exception { public ParseException() { } public ParseException(string message) : base(message) { } public ParseException(string message, Exception inner) : base(message, inner) { } protected ParseException( System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { } } readonly Library library; readonly Runtime runtime; public Parser(Runtime rt) { runtime = rt; library = rt.lib; } public Bytecode ParseFile(string filepath) { Bytecode c = new Bytecode(); string[] sourceLines = File.ReadAllLines(filepath); Regex lws = new Regex(@"\s+"); Regex leadings = new Regex(@"^\s+"); Regex trailings = new Regex(@"\s+$"); List codeLines = new List(); List lineSpans = new List(); int a = 0; foreach (string line in sourceLines) { a++; string l = lws.Replace(line, " "); l = leadings.Replace(l, ""); l = trailings.Replace(l, ""); if(l != String.Empty) { lineSpans.Add(a); a = 0; codeLines.Add(l); } } for(int idx = 0; idx < codeLines.Count; idx++) { string line = codeLines[idx]; var components = line.Split(' '); if(!library.nameResolution.ContainsKey(components[0])) { int lineIndex = 0; foreach (int cnt in lineSpans.GetRange(0, idx)) lineIndex += cnt; throw new ParseException($"Unknown operation \"{components[0]}\"\n\tat line {lineIndex}"); } int opcode = library.nameResolution[components[0]]; int arity = library.arities[opcode]; if(arity != components.Length-1) { int lineIndex = 0; foreach (int cnt in lineSpans.GetRange(0, idx)) lineIndex += cnt; throw new ParseException($"Operation {components[0]} was provided with the wrong number of arguments\n\tExpected {arity} found {components.Length-1}\n\tat line {lineIndex}"); } c.bytecode.Add(new BasicNumber(runtime, opcode)); foreach (string elem in components.AsSpan(1)) { if (elem != "$") { c.bytecode.Add(new BasicNumber(runtime, float.Parse(elem))); } else { c.bytecode.Add(new BasicNumber(runtime)); } } } return c; } } }