diff --git a/source/tlang/compiler/lexer/core/tokens.d b/source/tlang/compiler/lexer/core/tokens.d index 91b803ed..384d4156 100644 --- a/source/tlang/compiler/lexer/core/tokens.d +++ b/source/tlang/compiler/lexer/core/tokens.d @@ -1,14 +1,18 @@ /** - * Token definition + * Token and related types definitions */ module tlang.compiler.lexer.core.tokens; -import std.string : cmp; +import std.string : cmp, format; import std.conv : to; +import tlang.compiler.reporting : Coords; +import tlang.compiler.reporting : LineInfo; /** * Defines a `Token` that a lexer * would be able to produce + * + * Authors: Tristan Brice Velloza Kildaire (deavmi) */ public final class Token { @@ -22,6 +26,12 @@ public final class Token */ private ulong line, column; + /** + * The line this token was + * lex'd from + */ + private string origin; + /** * Constructs a new `Token` with the given * contents and line information @@ -38,6 +48,30 @@ public final class Token this.column = column; } + /** + * Sets the origin string. + * This is the line in which + * this token was derived from. + * + * Params: + * line = the line + */ + public void setOrigin(string line) + { + this.origin = line; + } + + /** + * Returns the origin string (if any) + * from which this token was derived + * + * Returns: the line + */ + private string getOrigin() + { + return this.origin; + } + /** * Overrides the `==` operator to do equality * based on the stored token's contents @@ -73,4 +107,26 @@ public final class Token { return token; } + + /** + * Returns the coordinates of + * this token + * + * Returns: the `Coords` + */ + public Coords getCoords() + { + return Coords(this.line, this.column); + } + + /** + * Returns the line information + * of this particular token + * + * Returns: the `LineInfo` + */ + public LineInfo getLineInfo() + { + return LineInfo(getOrigin(), getCoords()); + } } \ No newline at end of file diff --git a/source/tlang/compiler/parsing/core.d b/source/tlang/compiler/parsing/core.d index 328f677d..bafea3ab 100644 --- a/source/tlang/compiler/parsing/core.d +++ b/source/tlang/compiler/parsing/core.d @@ -9,11 +9,18 @@ import tlang.compiler.lexer.core; import core.stdc.stdlib; import tlang.misc.exceptions : TError; import tlang.compiler.parsing.exceptions; +import tlang.compiler.reporting : LineInfo; import tlang.compiler.core : Compiler; import std.string : format; import tlang.compiler.modman; -// TODO: Technically we could make a core parser etc +/** + * Provided with a lexer this + * can parse the tokens into + * an in-memory AST structure + * + * Authors: Tristan Brice Velloza Kildaire (deavmi) + */ public final class Parser { /** @@ -21,11 +28,27 @@ public final class Parser */ private LexerInterface lexer; - /** + /** * The associated compiler */ private Compiler compiler; + /** + * Returns the `LineInfo` of + * the lexer's current token. + * + * This is useful when you want + * to associate a line with + * an AST node right before + * returning. + * + * Returns: the `LineInfo` + */ + private LineInfo getCurrentLineInfo() + { + return this.lexer.getCurrentToken().getLineInfo(); + } + /** * Crashes the program if the given token is not a symbol * the same as the givne expected one @@ -40,8 +63,6 @@ public final class Parser if (!isFine) { throw new SyntaxError(this, symbol, token); - // expect("Expected symbol of type " ~ to!(string)(symbol) ~ " but got " ~ to!( - // string)(actualType) ~ " with " ~ token.toString()); } } @@ -290,6 +311,9 @@ public final class Parser /* Parent the branch to the WhileLoop */ parentToContainer(whileLoop, [branch]); + /* Store line information at this point into AST node */ + whileLoop.setLineInfo(getCurrentLineInfo()); + WARN("parseWhile(): Leave"); return whileLoop; diff --git a/source/tlang/compiler/parsing/exceptions.d b/source/tlang/compiler/parsing/exceptions.d index 7003e0d9..857e1f08 100644 --- a/source/tlang/compiler/parsing/exceptions.d +++ b/source/tlang/compiler/parsing/exceptions.d @@ -6,6 +6,7 @@ import tlang.compiler.symbols.check; import tlang.compiler.symbols.data; import tlang.compiler.lexer.core.tokens : Token; import std.conv : to; +import tlang.compiler.reporting : LineInfo, report; public class ParserException : TError { @@ -38,6 +39,10 @@ public final class SyntaxError : ParserException super(parser); - msg = "Syntax error: Expected "~to!(string)(expected)~" but got "~to!(string)(provided)~", see "~providedToken.toString(); + msg = report + ( + "Syntax error: Expected "~to!(string)(expected)~" but got "~to!(string)(provided)~", see "~providedToken.toString(), + providedToken + ); } } \ No newline at end of file diff --git a/source/tlang/compiler/reporting.d b/source/tlang/compiler/reporting.d new file mode 100644 index 00000000..e13d2c3d --- /dev/null +++ b/source/tlang/compiler/reporting.d @@ -0,0 +1,187 @@ +/** + * Reporting types and utilities + * for error reporting + * + * Authors: Tristan Brice Velloza Kildaire (deavmi) + */ +module tlang.compiler.reporting; + +import tlang.compiler.lexer.core.tokens; +import std.string : strip, format; + +/** + * Represents coordinates + * + * Authors: Tristan Brice Velloza Kildaire (deavmi) + */ +public struct Coords +{ + private ulong line; + private ulong column; + + /** + * Constructs a new set of coordinates + * + * Params: + * line = the line + * column = the column + */ + this(ulong line, ulong column) + { + this.line = line; + this.column = column; + } + + /** + * Returns the line + * + * Returns: line index + */ + public ulong getLine() + { + return this.line; + } + + /** + * Returns the column + * + * Returns: column index + */ + public ulong getColumn() + { + return this.column; + } + + /** + * Returns a string representation + * + * Returns: the coordinates + */ + public string toString() + { + return format("line %d, column %d", this.line, this.column); + } +} + +/** + * Represents line information + * which couples the line itself + * with the coordinates as well + * + * Authors: Tristan Brice Velloza Kildaire (deavmi) + */ +public struct LineInfo +{ + private string line; + private Coords location; + + /** + * Constructs a new `LineInfo` + * combining the line and its + * location + * + * Params: + * line = the line itself + * location = the location + */ + this(string line, Coords location) + { + this.line = line; + this.location = location; + } + + /** + * Returns the line itself + * + * Returns: the line + */ + public string getLine() + { + return this.line; + } + + /** + * Returns the location + * of this line + * + * Returns: the `Coords` + */ + public Coords getLocation() + { + return this.location; + } + + /** + * Returns the string represenation + * of this line info + * + * Returns: a string + */ + public string toString() + { + return format("%s at %s", getLine(), getLocation()); + } +} + +public string report(string message, LineInfo linfo, ulong cursor = 0) +{ + // Obtain the offending line + string offendingLine = linfo.getLine(); + + // Obtain where the offending line occurs + Coords offendingLocation = linfo.getLocation(); + + import std.stdio; + import niknaks.debugging : genX; + string pointer = format("%s^", genX(cursor, " ")); + + /** + * + * + * + * ^ (at pos) + * + * At + */ + string fullMessage = format + ( + "%s\n\n\t%s\n\t%s\n\nAt %s", + message, + offendingLine, + pointer, + offendingLocation + ); + + return fullMessage; +} + +public string report(string message, Token offendingToken) +{ + // FIXME: Double check the boundries here + ulong pointerPos = offendingToken.getCoords().getColumn() < message.length ? offendingToken.getCoords().getColumn() : 0; + assert(pointerPos < message.length); + + return report(message, offendingToken.getLineInfo(), pointerPos); +} + +version(unittest) +{ + import tlang.misc.logging; +} + +unittest +{ + string line = "int i = 20"; + import tlang.compiler.lexer.kinds.basic : BasicLexer; + BasicLexer lex = new BasicLexer(line); + lex.performLex(); + + // TODO: In future when BasicLexer is updated + // we should remove this + lex.nextToken(); + Token offending = lex.getCurrentToken(); + offending.setOrigin(line); + + string s = report("Cannot name a variable i", offending); + INFO(s); +} \ No newline at end of file diff --git a/source/tlang/compiler/symbols/data.d b/source/tlang/compiler/symbols/data.d index 7388fba0..379c536a 100644 --- a/source/tlang/compiler/symbols/data.d +++ b/source/tlang/compiler/symbols/data.d @@ -187,9 +187,38 @@ public final class Program : Container } } -public class Statement -{ +import tlang.compiler.reporting : LineInfo; +public abstract class Statement +{ + /** + * Line information + */ + private LineInfo linfo; + + /** + * Sets the line information + * for this AST node + * + * Params: + * linfo = the `LineInfo` + */ + public final void setLineInfo(LineInfo linfo) + { + this.linfo = linfo; + } + + /** + * Gets the line information + * from where this was retrieved + * + * Returns: the line info + */ + public final LineInfo getLineInfo() + { + return this.linfo; + } + public byte weight = 0; /* !!!! BEGIN TYPE CHECK ROUTINES AND DATA !!!! */