add suslang base
This commit is contained in:
parent
1989bdd0fb
commit
e56a2a0e74
@ -20,6 +20,7 @@ public enum TokenType {
|
|||||||
Asterisk,
|
Asterisk,
|
||||||
Slash,
|
Slash,
|
||||||
Percent,
|
Percent,
|
||||||
|
Dot,
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Position {
|
public struct Position {
|
||||||
@ -46,6 +47,10 @@ public class Lexer {
|
|||||||
public Token next() {
|
public Token next() {
|
||||||
if (done()) {
|
if (done()) {
|
||||||
return token(TokenType.Eof, position());
|
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())) {
|
} else if ("123456789".Contains(current())) {
|
||||||
return intToken();
|
return intToken();
|
||||||
} else if ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_".Contains(current())) {
|
} else if ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_".Contains(current())) {
|
||||||
@ -57,31 +62,31 @@ public class Lexer {
|
|||||||
|
|
||||||
private Token intToken() {
|
private Token intToken() {
|
||||||
var start = position();
|
var start = position();
|
||||||
var value = "";
|
|
||||||
value += current();
|
|
||||||
step();
|
step();
|
||||||
while (!done() && "1234567890".Contains(current())) {
|
while (!done() && "1234567890".Contains(current()))
|
||||||
value += current();
|
|
||||||
step();
|
step();
|
||||||
}
|
|
||||||
return token(TokenType.Int, start);
|
return token(TokenType.Int, start);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Token idToken() {
|
private Token idToken() {
|
||||||
var start = position();
|
var start = position();
|
||||||
var value = "";
|
|
||||||
value += current();
|
|
||||||
step();
|
step();
|
||||||
while (!done() && "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_".Contains(current())) {
|
while (!done() && "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_".Contains(current()))
|
||||||
value += current();
|
|
||||||
step();
|
step();
|
||||||
}
|
this.text.Substring(start.index, this.i - start.index).switch {
|
||||||
return token(TokenType.Id, start);
|
"false" => token(TokenType.False, start),
|
||||||
|
"true" => token(TokenType.True, start),
|
||||||
|
_ => token(TokenType.Id, start),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private Token staticToken() {
|
private Token staticToken() {
|
||||||
var start = position();
|
var start = position();
|
||||||
switch (current()) {
|
switch (current()) {
|
||||||
|
case '0':
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
break;
|
||||||
case '(':
|
case '(':
|
||||||
step();
|
step();
|
||||||
return token(TokenType.LParen, start);
|
return token(TokenType.LParen, start);
|
||||||
@ -109,6 +114,32 @@ public class Lexer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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() {
|
private Token token(TokenType type, Position start) => new Token() {
|
||||||
type = type,
|
type = type,
|
||||||
position = start,
|
position = start,
|
||||||
@ -130,16 +161,240 @@ public class Lexer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public interface Expr {
|
public interface Expr {
|
||||||
|
string astString(int depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ErrorExpr : Expr {
|
||||||
|
public Position position;
|
||||||
|
public string message;
|
||||||
|
|
||||||
|
public override astString(int depth) =>
|
||||||
|
$"ErrorExpr {{ {this.position.line}:{this.position.col} \"{this.message}\" }}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public IdExpr : Expr {
|
||||||
|
public string value;
|
||||||
|
|
||||||
|
public override astString(int depth) =>
|
||||||
|
$"IdExpr {{ \"{this.value}\" }}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntExpr : Expr {
|
||||||
|
public int value;
|
||||||
|
|
||||||
|
public override astString(int depth) =>
|
||||||
|
$"IntExpr {{ {this.value} }}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public FloatExpr : Expr {
|
||||||
|
public double value;
|
||||||
|
|
||||||
|
public override astString(int depth) =>
|
||||||
|
$"FloatExpr {{ {this.value} }}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringExpr : Expr {
|
||||||
|
public string value;
|
||||||
|
|
||||||
|
public override astString(int depth) =>
|
||||||
|
$"FloatExpr {{ \"{this.value}\" }}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoolExpr : Expr {
|
||||||
|
public bool value;
|
||||||
|
|
||||||
|
public override astString(int depth) =>
|
||||||
|
$"BoolExpr {{ {this.value} }}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum UnaryType {
|
||||||
|
Not,
|
||||||
|
Negate,
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnaryExpr : Expr {
|
||||||
|
public UnaryType type;
|
||||||
|
public Expr value;
|
||||||
|
|
||||||
|
public override astString(int depth) =>
|
||||||
|
$"UnaryExpr {{\n{new String(' ', depth) + 1}type: "
|
||||||
|
+ $"{this.type.ToString()},\n{new String(' ', depth + 1)}value: "
|
||||||
|
+ $"{this.value.astString(depth + 1)}\n{new String(' ', depth)}}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum BinaryType {
|
||||||
|
Add,
|
||||||
|
Subtract,
|
||||||
|
Multiply,
|
||||||
|
Divide,
|
||||||
|
Modulus,
|
||||||
|
}
|
||||||
|
|
||||||
|
public BinaryExpr : Expr {
|
||||||
|
public BinaryType type;
|
||||||
|
public Expr left, right;
|
||||||
|
|
||||||
|
public override astString(int depth) =>
|
||||||
|
$"UnaryExpr {{\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 {
|
public class Parser {
|
||||||
|
private int i = 0;
|
||||||
|
private string text;
|
||||||
|
private List<Token> tokens;
|
||||||
|
|
||||||
|
public Parser(string text, List<Token> tokens) {
|
||||||
|
this.text = text;
|
||||||
|
this.tokens = tokens;
|
||||||
|
}
|
||||||
|
|
||||||
public Expr parseExpr() {
|
public Expr parseExpr() {
|
||||||
throw new NotImplementedException();
|
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.Subtraction,
|
||||||
|
left = left,
|
||||||
|
right = right,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expr parsePrec12() {
|
||||||
|
var left = parsePrec13();
|
||||||
|
if (!done() && current().type == TokenType.Asterisk) {
|
||||||
|
step();
|
||||||
|
var right = parePrec12();
|
||||||
|
return new BinaryExpr() {
|
||||||
|
type = BinaryType.Multiply,
|
||||||
|
left = left,
|
||||||
|
right = right,
|
||||||
|
};
|
||||||
|
} else if (!done() && current().type == TokenType.Slash) {
|
||||||
|
step();
|
||||||
|
var right = parePrec12();
|
||||||
|
return new BinaryExpr() {
|
||||||
|
type = BinaryType.Divide,
|
||||||
|
left = left,
|
||||||
|
right = right,
|
||||||
|
};
|
||||||
|
} else if (!done() && current().type == TokenType.Percent) {
|
||||||
|
step();
|
||||||
|
var right = parePrec12();
|
||||||
|
return new BinaryExpr() {
|
||||||
|
type = BinaryType.Modulus,
|
||||||
|
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 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)),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return new IntExpr() {
|
||||||
|
value = int.Parse(this.text.Substring(start.index, intToken.length));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} 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 char current() => this.tokens[this.i];
|
||||||
|
private bool done() => this.i >= this.text.Length;
|
||||||
|
|
||||||
|
private void step() {
|
||||||
|
this.i += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Operation {
|
public interface Operation {}
|
||||||
|
|
||||||
|
public class PushInt {
|
||||||
|
public int value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PushFloat {
|
||||||
|
public double value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Compiler {
|
public class Compiler {
|
||||||
|
Loading…
Reference in New Issue
Block a user