This commit is contained in:
Simon 2023-03-14 12:53:56 +01:00
parent 181250cd01
commit 3032623f70
12 changed files with 611 additions and 604 deletions

View File

@ -7,10 +7,6 @@
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<Folder Include="Suslang/"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.1.0" />
</ItemGroup>

View File

@ -1,6 +1,8 @@
using Amogulator.Suslang;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Linq;
namespace Amogulator;
public partial class AmogulatorVM : ObservableObject {
@ -10,13 +12,17 @@ public partial class AmogulatorVM : ObservableObject {
[RelayCommand]
private void run() {
var ast = new Parser(this.Input, new Lexer(this.Input)).parseExpr();
var compiler = new Compiler();
compiler.compileExpr(ast);
var bytecode = compiler.result();
var vm = new VM(bytecode);
vm.evaluate();
this.Output = ast.astString(0) + "\n\nresult = " + ((IntValue) vm.stack.Peek()).value;
try {
var ast = new Parser(this.Input, new Lexer(this.Input)).parseExpr();
var compiler = new Compiler();
compiler.compileExpr(ast);
var bytecode = compiler.result();
var vm = new VM(bytecode);
vm.evaluate();
this.Output = $"ast = {ast.astString(0)}\n\nbytecode = [\n{string.Join("", bytecode.Select((op) => " " + op.operationString() + "\n"))}]\n\nresult = {vm.stack.Peek().valueString()}";
} catch (Exception e) {
this.Output = $"error = {e.Message}";
}
}
}

View File

@ -39,6 +39,8 @@
FontFamily="Consolas monospace"
Background="Black"
Foreground="White"
TextWrapping="Wrap"
AcceptsReturn="True"
/>
<Button
Grid.Column="0"

View File

@ -1,17 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows;
namespace Amogulator;
/// <summary>
@ -21,8 +8,4 @@ public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
}
private void Label_Scroll(object sender, System.Windows.Controls.Primitives.ScrollEventArgs e) {
}
}

85
Suslang/AST.cs Normal file
View File

@ -0,0 +1,85 @@
using System;
namespace Amogulator.Suslang;
public interface Expr {
string astString(int depth);
}
public class ErrorExpr : Expr {
public Position position;
public string message;
public string astString(int depth) =>
$"ErrorExpr {{ {this.position.line}:{this.position.col} \"{this.message}\" }}";
}
public class IdExpr : Expr {
public string value;
public string astString(int depth) =>
$"IdExpr {{ \"{this.value}\" }}";
}
public class IntExpr : Expr {
public int value;
public string astString(int depth) =>
$"IntExpr {{ {this.value} }}";
}
public class FloatExpr : Expr {
public double value;
public string astString(int depth) =>
$"FloatExpr {{ {this.value} }}";
}
public class StringExpr : Expr {
public string value;
public string astString(int depth) =>
$"FloatExpr {{ \"{this.value}\" }}";
}
public class BoolExpr : Expr {
public bool value;
public string astString(int depth) =>
$"BoolExpr {{ {this.value} }}";
}
public enum UnaryType {
Not,
Negate,
}
public class UnaryExpr : Expr {
public UnaryType type;
public Expr value;
public string astString(int depth) =>
$"UnaryExpr {{\n{new string(' ', (depth + 1) * 4)}type: "
+ $"{this.type},\n{new string(' ', (depth + 1) * 4)}value: "
+ $"{this.value.astString(depth + 1)}\n{new String(' ', depth * 4)}}}";
}
public enum BinaryType {
Add,
Subtract,
Multiply,
Divide,
Modulo,
}
public class BinaryExpr : Expr {
public BinaryType type;
public Expr left, right;
public string astString(int depth) =>
$"BinaryExpr {{\n{new String(' ', (depth + 1) * 4)}type: "
+ $"{this.type.ToString()},\n{new String(' ', (depth + 1) * 4)}left: "
+ $"{this.left.astString(depth + 1)},\n{new String(' ', (depth + 1) * 4)}right: "
+ $"{this.right.astString(depth + 1)},\n{new String(' ', depth * 4)}}}";
}

41
Suslang/Bytecode.cs Normal file
View File

@ -0,0 +1,41 @@
namespace Amogulator.Suslang;
public interface Operation {
string operationString();
}
public class PushIntOperation : Operation {
public int value;
public string operationString() => $"PushIntOperation {{ {value} }}";
}
public class PushFloatOperation : Operation {
public double value;
public string operationString() => $"PushFloatOperation {{ {value} }}";
}
public class PushStringOperation : Operation {
public string value;
public string operationString() => $"PushStringOperation {{ \"{value}\" }}";
}
public class PushBoolOperation : Operation {
public bool value;
public string operationString() => $"PushBoolOperation {{ {value} }}";
}
public class AddIntOperation : Operation { public string operationString() => $"AddIntOperation"; }
public class SubtractIntOperation : Operation { public string operationString() => $"SubtractIntOperation"; }
public class MultiplyIntOperation : Operation { public string operationString() => $"MultiplyIntOperation"; }
public class DivideIntOperation : Operation { public string operationString() => $"DivideIntOperation"; }
public class ModuloIntOperation : Operation { public string operationString() => $"ModuloIntOperation"; }
public class AddFloatOperation : Operation { public string operationString() => $"AddFloatOperation"; }
public class SubtractFloatOperation : Operation { public string operationString() => $"SubtractFloatOperation"; }
public class MultiplyFloatOperation : Operation { public string operationString() => $"MultiplyFloatOperation"; }
public class DivideFloatOperation : Operation { public string operationString() => $"DivideFloatOperation"; }
public class ModuloFloatOperation : Operation { public string operationString() => $"ModuloFloatOperation"; }

72
Suslang/Compiler.cs Normal file
View File

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
namespace Amogulator.Suslang;
public class Compiler {
private List<Operation> operations = new();
public List<Operation> result() => this.operations;
public string compileExpr(Expr expr) =>
expr switch {
IntExpr => compileIntExpr((IntExpr) expr),
FloatExpr => compileFloatExpr((FloatExpr) expr),
StringExpr => compileStringExpr((StringExpr) expr),
BoolExpr => compileBoolExpr((BoolExpr) expr),
UnaryExpr => compileUnaryExpr((UnaryExpr) expr),
BinaryExpr => compileBinaryExpr((BinaryExpr) expr),
_ => throw new NotImplementedException(),
};
public string compileIntExpr(IntExpr expr) {
this.operations.Add(new PushIntOperation() { value = expr.value });
return "int";
}
public string compileFloatExpr(FloatExpr expr) {
this.operations.Add(new PushFloatOperation() { value = expr.value });
return "float";
}
public string compileStringExpr(StringExpr expr) {
this.operations.Add(new PushStringOperation() { value = expr.value });
return "string";
}
public string compileBoolExpr(BoolExpr expr) {
this.operations.Add(new PushBoolOperation() { value = expr.value });
return "bool";
}
public string compileUnaryExpr(UnaryExpr expr) {
throw new NotImplementedException();
}
public string compileBinaryExpr(BinaryExpr expr) {
var leftType = compileExpr(expr.left);
var rightType = compileExpr(expr.right);
if ((leftType, rightType) == ("int", "int")) {
this.operations.Add(expr.type switch {
BinaryType.Add => new AddIntOperation(),
BinaryType.Subtract => new SubtractIntOperation(),
BinaryType.Multiply => new MultiplyIntOperation(),
BinaryType.Divide => new DivideIntOperation(),
BinaryType.Modulo => new ModuloIntOperation(),
});
return "int";
} else if ((leftType, rightType) == ("float", "float")) {
this.operations.Add(expr.type switch {
BinaryType.Add => new AddFloatOperation(),
BinaryType.Subtract => new SubtractFloatOperation(),
BinaryType.Multiply => new MultiplyFloatOperation(),
BinaryType.Divide => new DivideFloatOperation(),
BinaryType.Modulo => new ModuloFloatOperation(),
});
return "float";
} else {
throw new Exception("type mismatch");
}
}
}

135
Suslang/Lexer.cs Normal file
View File

@ -0,0 +1,135 @@
namespace Amogulator.Suslang;
public class Lexer {
private int i = 0;
private string text;
private int line = 1;
private int col = 1;
public Lexer(string text) {
this.text = text;
}
public Token next() {
if (done()) {
return token(TokenType.Eof, position());
} else if (" \t\r\n".Contains(current())) {
while (!done() && " \t\r\n".Contains(current()))
step();
return next();
} else if ("123456789".Contains(current())) {
return intToken();
} else if ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_".Contains(current())) {
return idToken();
} else {
return staticToken();
}
}
private Token intToken() {
var start = position();
step();
while (!done() && "1234567890".Contains(current()))
step();
return token(TokenType.Int, start);
}
private Token idToken() {
var start = position();
step();
while (!done() && "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_".Contains(current()))
step();
return this.text.Substring(start.index, this.i - start.index) switch {
"false" => token(TokenType.False, start),
"true" => token(TokenType.True, start),
_ => token(TokenType.Id, start),
};
}
private Token staticToken() {
var start = position();
switch (current()) {
case '0':
return zeroToken();
case '.':
return dotToken();
case '(':
step();
return token(TokenType.LParen, start);
case ')':
step();
return token(TokenType.RParen, start);
case '+':
step();
return token(TokenType.Plus, start);
case '-':
step();
return token(TokenType.Minus, start);
case '*':
step();
return token(TokenType.Asterisk, start);
case '/':
step();
return token(TokenType.Slash, start);
case '%':
step();
return token(TokenType.Percent, start);
default:
step();
return token(TokenType.Invalid, start);
}
}
private Token zeroToken() {
var start = position();
step();
if (!done() && (current() == 'x' || current() == 'X')) {
while (!done() && "1234567890abcdefABCDEF".Contains(current()))
step();
return token(TokenType.Hex, start);
} else {
while (!done() && "1234567890".Contains(current()))
step();
return token(TokenType.Int, start);
}
}
private Token dotToken() {
var start = position();
step();
if (!done() && "1234567890".Contains(current())) {
while (!done() && "1234567890".Contains(current()))
step();
return token(TokenType.Decimal, start);
} else {
return token(TokenType.Dot, start);
}
}
private Token token(TokenType type, Position start) => new Token() {
type = type,
position = start,
length = this.i - start.index,
};
private Position position() => new Position() {
index = this.i,
line = this.line,
col = this.col,
};
private char current() => this.text[this.i];
private bool done() => this.i >= this.text.Length;
private void step() {
this.i += 1;
if (!done()) {
if (current() == '\n') {
this.line += 1;
this.col = 1;
} else {
this.col += 1;
}
}
}
}

155
Suslang/Parser.cs Normal file
View File

@ -0,0 +1,155 @@
namespace Amogulator.Suslang;
public class Parser {
private string text;
private Lexer lexer;
private Token currentToken;
public Parser(string text, Lexer lexer) {
this.text = text;
this.lexer = lexer;
this.currentToken = this.lexer.next();
}
public Expr parseExpr() {
return parsePrec11();
}
public Expr parsePrec11() {
var left = parsePrec12();
if (!done() && current().type == TokenType.Plus) {
step();
var right = parsePrec11();
return new BinaryExpr() {
type = BinaryType.Add,
left = left,
right = right,
};
} else if (!done() && current().type == TokenType.Minus) {
step();
var right = parsePrec11();
return new BinaryExpr() {
type = BinaryType.Subtract,
left = left,
right = right,
};
} else {
return left;
}
}
public Expr parsePrec12() {
var left = parsePrec13();
if (!done() && current().type == TokenType.Asterisk) {
step();
var right = parsePrec12();
return new BinaryExpr() {
type = BinaryType.Multiply,
left = left,
right = right,
};
} else if (!done() && current().type == TokenType.Slash) {
step();
var right = parsePrec12();
return new BinaryExpr() {
type = BinaryType.Divide,
left = left,
right = right,
};
} else if (!done() && current().type == TokenType.Percent) {
step();
var right = parsePrec12();
return new BinaryExpr() {
type = BinaryType.Modulo,
left = left,
right = right,
};
} else {
return left;
}
}
public Expr parsePrec13() {
return parsePrec18();
}
public Expr parsePrec18() {
if (!done() && current().type == TokenType.LParen) {
step();
var expr = parseExpr();
if (done() || current().type != TokenType.RParen) {
var pos = position();
step();
return new ErrorExpr() {
position = pos,
message = "expected ')'"
};
}
step();
return expr;
} else {
return parseOperand();
}
}
public Expr parseOperand() {
var start = position();
if (done()) {
step();
return new ErrorExpr() {
position = start,
message = "expected value, got Eof",
};
} else if (current().type == TokenType.Int) {
var intToken = current();
step();
if (!done() && current().type == TokenType.Decimal) {
var decimalToken = current();
step();
return new FloatExpr() {
value = double.Parse(this.text.Substring(start.index, intToken.length + decimalToken.length).Replace(".", ",")),
};
} else {
return new IntExpr() {
value = int.Parse(this.text.Substring(start.index, intToken.length)),
};
}
} else if (current().type == TokenType.Decimal) {
var decimalToken = current();
step();
return new FloatExpr() {
value = double.Parse("0" + this.text.Substring(start.index, decimalToken.length).Replace(".", ",")),
};
} else if (current().type == TokenType.String) {
var stringToken = current();
step();
return new StringExpr() {
value = this.text.Substring(start.index, stringToken.length),
};
} else if (current().type == TokenType.False) {
step();
return new BoolExpr() {
value = false,
};
} else if (current().type == TokenType.True) {
step();
return new BoolExpr() {
value = true,
};
} else {
step();
return new ErrorExpr() {
position = start,
message = "expected value",
};
}
}
private Position position() => current().position;
private Token current() => this.currentToken;
private bool done() => this.currentToken.type == TokenType.Eof;
private void step() {
this.currentToken = this.lexer.next();
}
}

View File

@ -1,575 +0,0 @@
using System;
using System.Collections.Generic;
namespace Amogulator.Suslang;
public enum TokenType {
Eof,
Invalid,
Id,
Int,
Hex,
Decimal,
String,
True,
False,
LParen,
RParen,
Plus,
Minus,
Asterisk,
Slash,
Percent,
Dot,
}
public struct Position {
public int index;
public int line, col;
}
public struct Token {
public TokenType type;
public Position position;
public int length;
}
public class Lexer {
private int i = 0;
private string text;
private int line = 1;
private int col = 1;
public Lexer(string text) {
this.text = text;
}
public Token next() {
if (done()) {
return token(TokenType.Eof, position());
} else if (" \t\r\n".Contains(current())) {
while (!done() && " \t\r\n".Contains(current()))
step();
return next();
} else if ("123456789".Contains(current())) {
return intToken();
} else if ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_".Contains(current())) {
return idToken();
} else {
return staticToken();
}
}
private Token intToken() {
var start = position();
step();
while (!done() && "1234567890".Contains(current()))
step();
return token(TokenType.Int, start);
}
private Token idToken() {
var start = position();
step();
while (!done() && "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_".Contains(current()))
step();
return this.text.Substring(start.index, this.i - start.index) switch {
"false" => token(TokenType.False, start),
"true" => token(TokenType.True, start),
_ => token(TokenType.Id, start),
};
}
private Token staticToken() {
var start = position();
switch (current()) {
case '0':
return zeroToken();
case '.':
return dotToken();
case '(':
step();
return token(TokenType.LParen, start);
case ')':
step();
return token(TokenType.RParen, start);
case '+':
step();
return token(TokenType.Plus, start);
case '-':
step();
return token(TokenType.Minus, start);
case '*':
step();
return token(TokenType.Asterisk, start);
case '/':
step();
return token(TokenType.Slash, start);
case '%':
step();
return token(TokenType.Percent, start);
default:
step();
return token(TokenType.Invalid, start);
}
}
private Token zeroToken() {
var start = position();
step();
if (!done() && (current() == 'x' || current() == 'X')) {
while (!done() && "1234567890abcdefABCDEF".Contains(current()))
step();
return token(TokenType.Hex, start);
} else {
while (!done() && "1234567890".Contains(current()))
step();
return token(TokenType.Int, start);
}
}
private Token dotToken() {
var start = position();
step();
if (!done() && "1234567890".Contains(current())) {
while (!done() && "1234567890".Contains(current()))
step();
return token(TokenType.Decimal, start);
} else {
return token(TokenType.Dot, start);
}
}
private Token token(TokenType type, Position start) => new Token() {
type = type,
position = start,
length = this.i - start.index,
};
private Position position() => new Position() {
index = this.i,
line = this.line,
col = this.col,
};
private char current() => this.text[this.i];
private bool done() => this.i >= this.text.Length;
private void step() {
this.i += 1;
if (!done()) {
if (current() == '\n') {
this.line += 1;
this.col = 1;
} else {
this.col += 1;
}
}
}
}
public interface Expr {
string astString(int depth);
}
public class ErrorExpr : Expr {
public Position position;
public string message;
public string astString(int depth) =>
$"ErrorExpr {{ {this.position.line}:{this.position.col} \"{this.message}\" }}";
}
public class IdExpr : Expr {
public string value;
public string astString(int depth) =>
$"IdExpr {{ \"{this.value}\" }}";
}
public class IntExpr : Expr {
public int value;
public string astString(int depth) =>
$"IntExpr {{ {this.value} }}";
}
public class FloatExpr : Expr {
public double value;
public string astString(int depth) =>
$"FloatExpr {{ {this.value} }}";
}
public class StringExpr : Expr {
public string value;
public string astString(int depth) =>
$"FloatExpr {{ \"{this.value}\" }}";
}
public class BoolExpr : Expr {
public bool value;
public string astString(int depth) =>
$"BoolExpr {{ {this.value} }}";
}
public enum UnaryType {
Not,
Negate,
}
public class UnaryExpr : Expr {
public UnaryType type;
public Expr value;
public string astString(int depth) =>
$"UnaryExpr {{\n{new string(' ', depth + 1)}type: "
+ $"{this.type},\n{new string(' ', depth + 1)}value: "
+ $"{this.value.astString(depth + 1)}\n{new String(' ', depth)}}}";
}
public enum BinaryType {
Add,
Subtract,
Multiply,
Divide,
Modulo,
}
public class BinaryExpr : Expr {
public BinaryType type;
public Expr left, right;
public string astString(int depth) =>
$"BinaryExpr {{\n{new String(' ', depth + 1)}type: "
+ $"{this.type.ToString()},\n{new String(' ', depth + 1)}left: "
+ $"{this.left.astString(depth + 1)},\n{new String(' ', depth + 1)}right: "
+ $"{this.right.astString(depth + 1)},\n{new String(' ', depth)}}}";
}
public class Parser {
private string text;
private Lexer lexer;
private Token currentToken;
public Parser(string text, Lexer lexer) {
this.text = text;
this.lexer = lexer;
this.currentToken = this.lexer.next();
}
public Expr parseExpr() {
return parsePrec11();
}
public Expr parsePrec11() {
var left = parsePrec12();
if (!done() && current().type == TokenType.Plus) {
step();
var right = parsePrec11();
return new BinaryExpr() {
type = BinaryType.Add,
left = left,
right = right,
};
} else if (!done() && current().type == TokenType.Minus) {
step();
var right = parsePrec11();
return new BinaryExpr() {
type = BinaryType.Subtract,
left = left,
right = right,
};
} else {
return left;
}
}
public Expr parsePrec12() {
var left = parsePrec13();
if (!done() && current().type == TokenType.Asterisk) {
step();
var right = parsePrec12();
return new BinaryExpr() {
type = BinaryType.Multiply,
left = left,
right = right,
};
} else if (!done() && current().type == TokenType.Slash) {
step();
var right = parsePrec12();
return new BinaryExpr() {
type = BinaryType.Divide,
left = left,
right = right,
};
} else if (!done() && current().type == TokenType.Percent) {
step();
var right = parsePrec12();
return new BinaryExpr() {
type = BinaryType.Modulo,
left = left,
right = right,
};
} else {
return left;
}
}
public Expr parsePrec13() {
return parsePrec18();
}
public Expr parsePrec18() {
if (!done() && current().type == TokenType.LParen) {
step();
var expr = parseExpr();
if (done() || current().type != TokenType.RParen) {
var pos = position();
step();
return new ErrorExpr() {
position = pos,
message = "expected ')'"
};
}
step();
return expr;
} else {
return parseOperand();
}
}
public Expr parseOperand() {
var start = position();
if (done()) {
step();
return new ErrorExpr() {
position = start,
message = "expected value, got Eof",
};
} else if (current().type == TokenType.Int) {
var intToken = current();
step();
if (!done() && current().type == TokenType.Decimal) {
var decimalToken = current();
step();
return new FloatExpr() {
value = double.Parse(this.text.Substring(start.index, intToken.length + decimalToken.length).Replace(".", ",")),
};
} else {
return new IntExpr() {
value = int.Parse(this.text.Substring(start.index, intToken.length)),
};
}
} else if (current().type == TokenType.Decimal) {
var decimalToken = current();
step();
return new FloatExpr() {
value = double.Parse("0" + this.text.Substring(start.index, decimalToken.length).Replace(".", ",")),
};
} else if (current().type == TokenType.String) {
var stringToken = current();
step();
return new StringExpr() {
value = this.text.Substring(start.index, stringToken.length),
};
} else if (current().type == TokenType.False) {
step();
return new BoolExpr() {
value = false,
};
} else if (current().type == TokenType.True) {
step();
return new BoolExpr() {
value = true,
};
} else {
step();
return new ErrorExpr() {
position = start,
message = "expected value",
};
}
}
private Position position() => current().position;
private Token current() => this.currentToken;
private bool done() => this.currentToken.type == TokenType.Eof;
private void step() {
this.currentToken = this.lexer.next();
}
}
public interface Operation { }
public class PushIntOperation : Operation {
public int value;
}
public class PushFloatOperation : Operation {
public double value;
}
public class PushStringOperation : Operation {
public string value;
}
public class PushBoolOperation : Operation {
public bool value;
}
public class AddIntOperation : Operation { }
public class SubtractIntOperation : Operation { }
public class MultiplyIntOperation : Operation { }
public class DivideIntOperation : Operation { }
public class ModuloIntOperation : Operation { }
public class AddFloatOperation : Operation { }
public class SubtractFloatOperation : Operation { }
public class MultiplyFloatOperation : Operation { }
public class DivideFloatOperation : Operation { }
public class ModuloFloatOperation : Operation { }
public class Compiler {
private List<Operation> operations = new();
public List<Operation> result() => this.operations;
public string compileExpr(Expr expr) {
switch (expr) {
case IntExpr:
return compileIntExpr((IntExpr) expr);
break;
case FloatExpr:
return compileFloatExpr((FloatExpr) expr);
break;
case StringExpr:
return compileStringExpr((StringExpr) expr);
break;
case BoolExpr:
return compileBoolExpr((BoolExpr) expr);
break;
case UnaryExpr:
return compileUnaryExpr((UnaryExpr) expr);
break;
case BinaryExpr:
return compileBinaryExpr((BinaryExpr) expr);
break;
default:
throw new NotImplementedException();
};
}
public string compileIntExpr(IntExpr expr) {
this.operations.Add(new PushIntOperation() { value = expr.value });
return "int";
}
public string compileFloatExpr(FloatExpr expr) {
this.operations.Add(new PushFloatOperation() { value = expr.value });
return "float";
}
public string compileStringExpr(StringExpr expr) {
this.operations.Add(new PushStringOperation() { value = expr.value });
return "string";
}
public string compileBoolExpr(BoolExpr expr) {
this.operations.Add(new PushBoolOperation() { value = expr.value });
return "bool";
}
public string compileUnaryExpr(UnaryExpr expr) {
throw new NotImplementedException();
}
public string compileBinaryExpr(BinaryExpr expr) {
var leftType = compileExpr(expr.left);
var rightType = compileExpr(expr.right);
if ((leftType, rightType) == ("int", "int")) {
this.operations.Add(expr.type switch {
BinaryType.Add => new AddIntOperation(),
BinaryType.Subtract => new SubtractIntOperation(),
BinaryType.Multiply => new MultiplyIntOperation(),
BinaryType.Divide => new DivideIntOperation(),
BinaryType.Modulo => new ModuloIntOperation(),
});
return "int";
} else if ((leftType, rightType) == ("int", "int")) {
this.operations.Add(expr.type switch {
BinaryType.Add => new AddFloatOperation(),
BinaryType.Subtract => new SubtractFloatOperation(),
BinaryType.Multiply => new MultiplyFloatOperation(),
BinaryType.Divide => new DivideFloatOperation(),
BinaryType.Modulo => new ModuloFloatOperation(),
});
return "float";
} else {
throw new Exception("type mismatch");
}
}
}
public interface Value { }
public class IntValue : Value { public int value; }
public class FloatValue : Value { public double value; }
public class VM {
private int pc = 0;
private List<Operation> program;
public Stack<Value> stack = new();
public VM(List<Operation> program) {
this.program = program;
}
public void evaluate() {
while (this.pc < this.program.Count) {
switch (this.program[this.pc]) {
case PushIntOperation op:
this.stack.Push(new IntValue() { value = op.value });
break;
case PushFloatOperation op:
this.stack.Push(new FloatValue() { value = op.value });
break;
case AddIntOperation:
this.stack.Push(new IntValue() { value = ((IntValue) this.stack.Pop()).value + ((IntValue) this.stack.Pop()).value });
break;
case SubtractIntOperation:
this.stack.Push(new IntValue() { value = ((IntValue) this.stack.Pop()).value - ((IntValue) this.stack.Pop()).value });
break;
case MultiplyIntOperation:
this.stack.Push(new IntValue() { value = ((IntValue) this.stack.Pop()).value * ((IntValue) this.stack.Pop()).value });
break;
case DivideIntOperation:
this.stack.Push(new IntValue() { value = ((IntValue) this.stack.Pop()).value / ((IntValue) this.stack.Pop()).value });
break;
case ModuloIntOperation:
this.stack.Push(new IntValue() { value = ((IntValue) this.stack.Pop()).value % ((IntValue) this.stack.Pop()).value });
break;
case AddFloatOperation:
this.stack.Push(new FloatValue() { value = ((FloatValue) this.stack.Pop()).value + ((FloatValue) this.stack.Pop()).value });
break;
case SubtractFloatOperation:
this.stack.Push(new FloatValue() { value = ((FloatValue) this.stack.Pop()).value - ((FloatValue) this.stack.Pop()).value });
break;
case MultiplyFloatOperation:
this.stack.Push(new FloatValue() { value = ((FloatValue) this.stack.Pop()).value * ((FloatValue) this.stack.Pop()).value });
break;
case DivideFloatOperation:
this.stack.Push(new FloatValue() { value = ((FloatValue) this.stack.Pop()).value / ((FloatValue) this.stack.Pop()).value });
break;
case ModuloFloatOperation:
this.stack.Push(new FloatValue() { value = ((FloatValue) this.stack.Pop()).value % ((FloatValue) this.stack.Pop()).value });
break;
default:
break;
}
this.pc += 1;
}
}
}

32
Suslang/Token.cs Normal file
View File

@ -0,0 +1,32 @@
namespace Amogulator.Suslang;
public enum TokenType {
Eof,
Invalid,
Id,
Int,
Hex,
Decimal,
String,
True,
False,
LParen,
RParen,
Plus,
Minus,
Asterisk,
Slash,
Percent,
Dot,
}
public struct Position {
public int index;
public int line, col;
}
public struct Token {
public TokenType type;
public Position position;
public int length;
}

75
Suslang/VM.cs Normal file
View File

@ -0,0 +1,75 @@
using System.Collections.Generic;
namespace Amogulator.Suslang;
public interface Value {
string valueString();
}
public class IntValue : Value {
public int value;
public string valueString() => $"{this.value}";
}
public class FloatValue : Value {
public double value;
public string valueString() => $"{this.value}";
}
public class VM {
private int pc = 0;
private List<Operation> program;
public Stack<Value> stack = new();
public VM(List<Operation> program) {
this.program = program;
}
public void evaluate() {
while (this.pc < this.program.Count) {
switch (this.program[this.pc]) {
case PushIntOperation op:
this.stack.Push(new IntValue() { value = op.value });
break;
case PushFloatOperation op:
this.stack.Push(new FloatValue() { value = op.value });
break;
case AddIntOperation:
this.stack.Push(new IntValue() { value = ((IntValue) this.stack.Pop()).value + ((IntValue) this.stack.Pop()).value });
break;
case SubtractIntOperation:
this.stack.Push(new IntValue() { value = ((IntValue) this.stack.Pop()).value - ((IntValue) this.stack.Pop()).value });
break;
case MultiplyIntOperation:
this.stack.Push(new IntValue() { value = ((IntValue) this.stack.Pop()).value * ((IntValue) this.stack.Pop()).value });
break;
case DivideIntOperation:
this.stack.Push(new IntValue() { value = ((IntValue) this.stack.Pop()).value / ((IntValue) this.stack.Pop()).value });
break;
case ModuloIntOperation:
this.stack.Push(new IntValue() { value = ((IntValue) this.stack.Pop()).value % ((IntValue) this.stack.Pop()).value });
break;
case AddFloatOperation:
this.stack.Push(new FloatValue() { value = ((FloatValue) this.stack.Pop()).value + ((FloatValue) this.stack.Pop()).value });
break;
case SubtractFloatOperation:
this.stack.Push(new FloatValue() { value = ((FloatValue) this.stack.Pop()).value - ((FloatValue) this.stack.Pop()).value });
break;
case MultiplyFloatOperation:
this.stack.Push(new FloatValue() { value = ((FloatValue) this.stack.Pop()).value * ((FloatValue) this.stack.Pop()).value });
break;
case DivideFloatOperation:
this.stack.Push(new FloatValue() { value = ((FloatValue) this.stack.Pop()).value / ((FloatValue) this.stack.Pop()).value });
break;
case ModuloFloatOperation:
this.stack.Push(new FloatValue() { value = ((FloatValue) this.stack.Pop()).value % ((FloatValue) this.stack.Pop()).value });
break;
default:
break;
}
this.pc += 1;
}
}
}