選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

96 行
2.4 KiB

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<string> codeLines = new List<string>();
List<int> lineSpans = new List<int>();
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;
}
}
}