Ludovic 'Archivist' Lagouardette 2 роки тому
джерело
коміт
d502c497f1
6 змінених файлів з 172 додано та 41 видалено
  1. +2
    -2
      BasicNumber.cs
  2. +26
    -0
      Functions/Goto.cs
  3. +130
    -33
      Parser.cs
  4. +2
    -0
      Program.cs
  5. +5
    -4
      Runtime.cs
  6. +7
    -2
      Test.basic

+ 2
- 2
BasicNumber.cs Переглянути файл

@ -15,7 +15,7 @@ namespace SuperBASIC
{
internal NumberType type;
readonly Runtime runtime;
internal Runtime runtime;
readonly private float number;
readonly private int operand;
@ -90,6 +90,6 @@ namespace SuperBASIC
}
public static implicit operator float(BasicNumber v) => v.GetValue();
public override string ToString() => $"{number}";
public override string ToString() => $"{GetValue()}";
}
}

+ 26
- 0
Functions/Goto.cs Переглянути файл

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace SuperBASIC.Functions
{
class Goto : IFunction
{
public float Apply(List<BasicNumber> arguments)
{
// Substract its own arity +1
arguments[0].runtime.pc = (int)arguments[0].GetOperand() - 2;
return 0f;
}
}
class JumpZero : IFunction
{
public float Apply(List<BasicNumber> arguments)
{
if(arguments[0] == 0)
// Substract its own arity +1
arguments[1].runtime.pc = (int)arguments[1].GetOperand() - 3;
return 0f;
}
}
}

+ 130
- 33
Parser.cs Переглянути файл

@ -28,43 +28,25 @@ namespace SuperBASIC
library = rt.lib;
}
public Bytecode ParseFile(string filepath)
internal enum Controls
{
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, Then, Else, EndIf,
For, To, Step, Next,
While, Wend,
Repeat, Until,
Do, LoopCond
}
if (l != String.Empty)
{
lineSpans.Add(a);
a = 0;
codeLines.Add(l);
}
}
internal struct FlowControlTag
{
public Controls ctrl;
public int index;
}
for (int idx = 0; idx < codeLines.Count; idx++)
private void ParseExpression(Bytecode c, string[] components, int idx, List<int> lineSpans)
{
if (library.nameResolution.ContainsKey(components[0]))
{
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 + 1)) lineIndex += cnt;
throw new ParseException($"Unknown operation \"{components[0]}\"\n\tat line {lineIndex}");
}
int opcode = library.nameResolution[components[0]];
int arity = library.arities[opcode];
@ -117,7 +99,122 @@ namespace SuperBASIC
}
}
}
else
{
int lineIndex = 0;
foreach (int cnt in lineSpans.GetRange(0, idx + 1)) lineIndex += cnt;
throw new ParseException($"Unknown operation \"{components[0]}\"\n\tat line {lineIndex}");
}
}
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>();
Stack<FlowControlTag> labelStack = new Stack<FlowControlTag>();
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(' ');
var label = new FlowControlTag();
switch (components[0])
{
case "IF":
// Role: manages the expression
label.ctrl = Controls.If;
label.index = c.bytecode.Count;
labelStack.Push(label);
ParseExpression(c, components[1..], idx, lineSpans);
break;
case "THEN":
// Role: Jumps to the far block if false
// 1. Adds the label to update the destination
label.ctrl = Controls.Then;
label.index = c.bytecode.Count;
labelStack.Push(label);
// 2. Adds the jumper
c.bytecode.Add(new BasicNumber(runtime, library.nameResolution["JZ"]));
c.bytecode.Add(new BasicNumber(runtime));
c.bytecode.Add(new BasicNumber(runtime, 0f));
break;
case "ELSE":
//Role: skips its section if encountered, marks the THEN for JZ
//1. Gets the THEN label
label = labelStack.Pop();
if(label.ctrl != Controls.Then)
{
int lineIndex = 0;
foreach (int cnt in lineSpans.GetRange(0, idx + 1)) lineIndex += cnt;
throw new ParseException($"Orphaned ELSE\n\tat line {lineIndex}");
}
//2. Self tags for the ENDIF to complete the GOTO
var label_self = new FlowControlTag
{
ctrl = Controls.Else,
index = c.bytecode.Count
};
labelStack.Push(label_self);
//3. Pushes the GOTO
c.bytecode.Add(new BasicNumber(runtime, library.nameResolution["GOTO"]));
c.bytecode.Add(new BasicNumber(runtime, 0f));
//4. Place the THEN destination after the GOTO
c.bytecode[label.index+2] = new BasicNumber(runtime, c.bytecode.Count);
break;
case "ENDIF":
//Role: Destination if depending on what is skipped
label = labelStack.Pop();
if (label.ctrl == Controls.Else)
{
//Case 1: update the GOTO
c.bytecode[label.index + 1] = new BasicNumber(runtime, c.bytecode.Count);
}
else if (label.ctrl == Controls.Then)
{
//Case 2: update the JZ
c.bytecode[label.index + 2] = new BasicNumber(runtime, c.bytecode.Count);
}
else
{
int lineIndex = 0;
foreach (int cnt in lineSpans.GetRange(0, idx + 1)) lineIndex += cnt;
throw new ParseException($"Orphaned ENDIF\n\tat line {lineIndex}");
}
// Controls the syntax
if (labelStack.Pop().ctrl != Controls.If)
{
int lineIndex = 0;
foreach (int cnt in lineSpans.GetRange(0, idx + 1)) lineIndex += cnt;
throw new ParseException($"Orphaned ENDIF despite THEN\n\tat line {lineIndex}");
}
break;
default:
ParseExpression(c, components, idx, lineSpans);
break;
}
}
return c;
}
}

+ 2
- 0
Program.cs Переглянути файл

@ -17,6 +17,8 @@ namespace SuperBASIC
lib.AddFunction(new Functions.Print(), 1, "PRINT");
lib.AddFunction(new Functions.Multiply(), 2, "MULTIPLY");
lib.AddFunction(new Functions.Compare(), 2, "COMPARE");
lib.AddFunction(new Functions.JumpZero(), 2, "JZ");
lib.AddFunction(new Functions.Goto(), 1, "GOTO");
lib.AddFunction(new Functions.Pi(), 0, "PI");
lib.AddFunction(new Functions.Euler(), 0, "EULER");
Runtime r = new Runtime(lib);

+ 5
- 4
Runtime.cs Переглянути файл

@ -9,6 +9,7 @@ namespace SuperBASIC
float register;
internal Library lib;
Bytecode code;
internal int pc = 0;
public Runtime(Library library)
{
@ -22,14 +23,14 @@ namespace SuperBASIC
public void Run()
{
for(kt">int idx = 0; idx < code.bytecode.Count;)
for(n">pc = 0; pc < code.bytecode.Count;)
{
int opcode = code.bytecode[idx].GetOperand();
int opcode = code.bytecode[pc].GetOperand();
int arity = lib.arities[opcode];
IFunction op = lib.functions[opcode];
var args = code.bytecode.GetRange(idx + 1, arity);
var args = code.bytecode.GetRange(pc + 1, arity);
SetRegister(op.Apply(args));
idx += arity + 1;
pc += arity + 1;
}
}

+ 7
- 2
Test.basic Переглянути файл

@ -1,4 +1,9 @@
EULER
MULTIPLY $ $
COMPARE $ 7.3890557
PRINT $
IF COMPARE $ 7.3890557
THEN
PRINT 25
ELSE
PRINT 10
ENDIF

Завантаження…
Відмінити
Зберегти