diff --git a/Suslang/Suslang.cs b/Suslang/Suslang.cs index 7ccd1f8..4b448ff 100644 --- a/Suslang/Suslang.cs +++ b/Suslang/Suslang.cs @@ -20,6 +20,7 @@ public enum TokenType { Asterisk, Slash, Percent, + Dot, } public struct Position { @@ -46,6 +47,10 @@ public class Lexer { 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())) { @@ -57,31 +62,31 @@ public class Lexer { private Token intToken() { var start = position(); - var value = ""; - value += current(); step(); - while (!done() && "1234567890".Contains(current())) { - value += current(); + while (!done() && "1234567890".Contains(current())) step(); - } return token(TokenType.Int, start); } private Token idToken() { var start = position(); - var value = ""; - value += current(); step(); - while (!done() && "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_".Contains(current())) { - value += current(); + while (!done() && "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_".Contains(current())) step(); - } - return token(TokenType.Id, start); + 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': + break; + case '.': + break; case '(': step(); 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() { type = type, position = start, @@ -130,16 +161,240 @@ public class Lexer { } 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 { + private int i = 0; + private string text; + private List tokens; + + public Parser(string text, List tokens) { + this.text = text; + this.tokens = tokens; + } + 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 {