diff --git a/AST.fu b/AST.fu index a609ae4b..f4a1e5eb 100644 --- a/AST.fu +++ b/AST.fu @@ -1139,6 +1139,10 @@ public abstract class FuNamedValue : FuSymbol public abstract class FuMember : FuNamedValue { internal FuVisibility Visibility; + internal int StartLine; + internal int StartColumn; + internal int EndLine; + internal int EndColumn; protected FuMember() { } diff --git a/Parser.fu b/Parser.fu index 364d33c3..e1a35aca 100644 --- a/Parser.fu +++ b/Parser.fu @@ -552,7 +552,7 @@ public class FuParser : FuLexer NextToken(); Expect(FuToken.Assign); konst.Value = ParseConstInitializer(); - Expect(FuToken.Semicolon); + CloseMember(FuToken.Semicolon, konst); return konst; } @@ -581,13 +581,13 @@ public class FuParser : FuLexer } } - FuBlock# ParseBlock!() + FuBlock# ParseBlock!(FuMethodBase!? method) { FuBlock# result = new FuBlock { Loc = this.TokenLoc }; Expect(FuToken.LeftBrace); while (!See(FuToken.RightBrace) && !See(FuToken.EndOfFile)) result.Statements.Add(ParseStatement()); - Expect(FuToken.RightBrace); + CloseMember(FuToken.RightBrace, method); return result; } @@ -739,13 +739,37 @@ public class FuParser : FuLexer return result; } - FuReturn# ParseReturn!() + int GetCurrentLine() => this.Host.Program.LineLocs.Count - this.Host.Program.SourceFiles.Last().Line - 1; + + int GetTokenColumn() => this.TokenLoc - this.Host.Program.LineLocs.Last(); + + void SetMemberEnd(FuMember! member) + { + member.EndLine = GetCurrentLine(); + member.EndColumn = this.Loc - this.Host.Program.LineLocs.Last(); + } + + void CloseMember!(FuToken expected, FuMember!? member) + { + if (member != null) + SetMemberEnd(member); + Expect(expected); + } + + void CloseContainer!(FuContainerType! type) + { + type.EndLine = GetCurrentLine(); + type.EndColumn = this.Loc - this.Host.Program.LineLocs.Last(); + Expect(FuToken.RightBrace); + } + + FuReturn# ParseReturn!(FuMethod!? method) { FuReturn# result = new FuReturn { Loc = this.TokenLoc }; NextToken(); if (!See(FuToken.Semicolon)) result.Value = ParseExpr(); - Expect(FuToken.Semicolon); + CloseMember(FuToken.Semicolon, method); return result; } @@ -831,7 +855,7 @@ public class FuParser : FuLexer { switch (this.CurrentToken) { case FuToken.LeftBrace: - return ParseBlock(); + return ParseBlock(null); case FuToken.Assert: return ParseAssert(); case FuToken.Break: @@ -853,7 +877,7 @@ public class FuParser : FuLexer case FuToken.Native: return ParseNative(); case FuToken.Return: - return ParseReturn(); + return ParseReturn(null); case FuToken.Switch: return ParseSwitch(); case FuToken.Throw: @@ -923,22 +947,11 @@ public class FuParser : FuLexer } while (Eat(FuToken.Comma)); } if (method.CallType == FuCallType.Abstract) - Expect(FuToken.Semicolon); + CloseMember(FuToken.Semicolon, method); else if (See(FuToken.FatArrow)) - method.Body = ParseReturn(); + method.Body = ParseReturn(method); else if (Check(FuToken.LeftBrace)) - method.Body = ParseBlock(); - } - - int GetCurrentLine() => this.Host.Program.LineLocs.Count - this.Host.Program.SourceFiles.Last().Line - 1; - - int GetTokenColumn() => this.TokenLoc - this.Host.Program.LineLocs.Last(); - - void CloseContainer!(FuContainerType! type) - { - type.EndLine = GetCurrentLine(); - type.EndColumn = this.Loc - this.Host.Program.LineLocs.Last(); - Expect(FuToken.RightBrace); + method.Body = ParseBlock(method); } static string CallTypeToString(FuCallType callType) @@ -969,6 +982,9 @@ public class FuParser : FuLexer while (!See(FuToken.RightBrace) && !See(FuToken.EndOfFile)) { doc = ParseDoc(); + int line = GetCurrentLine(); + int column = GetTokenColumn(); + FuVisibility visibility; switch (this.CurrentToken) { case FuToken.Internal: @@ -991,6 +1007,8 @@ public class FuParser : FuLexer if (See(FuToken.Const)) { // const FuConst# konst = ParseConst(visibility); + konst.StartLine = line; + konst.StartColumn = column; konst.Documentation = doc; AddSymbol(klass, konst); continue; @@ -1014,10 +1032,11 @@ public class FuParser : FuLexer } if (visibility == FuVisibility.Private) visibility = FuVisibility.Internal; // TODO - klass.Constructor = new FuMethodBase { Loc = call.Loc, Documentation = doc, Visibility = visibility, Parent = klass, - Type = this.Host.Program.System.VoidType, Name = klass.Name, Body = ParseBlock() }; + klass.Constructor = new FuMethodBase { StartLine = line, StartColumn = column, Loc = call.Loc, Documentation = doc, + Visibility = visibility, Parent = klass, Type = this.Host.Program.System.VoidType, Name = klass.Name }; klass.Constructor.Parameters.Parent = klass; klass.Constructor.AddThis(klass, true); + klass.Constructor.Body = ParseBlock(klass.Constructor); continue; } @@ -1049,7 +1068,8 @@ public class FuParser : FuLexer if (visibility == FuVisibility.Private && callType != FuCallType.Static && callType != FuCallType.Normal) ReportError($"{CallTypeToString(callType)} method cannot be private"); - FuMethod# method = new FuMethod { Loc = loc, Documentation = doc, Visibility = visibility, CallType = callType, TypeExpr = type, Name = name }; + FuMethod# method = new FuMethod { StartLine = line, StartColumn = column, Loc = loc, Documentation = doc, + Visibility = visibility, CallType = callType, TypeExpr = type, Name = name }; ParseMethod(klass, method); continue; } @@ -1061,9 +1081,10 @@ public class FuParser : FuLexer ReportError($"Field cannot be {CallTypeToString(callType)}"); if (type == this.Host.Program.System.VoidType) ReportError("Field cannot be void"); - FuField# field = new FuField { Loc = loc, Documentation = doc, Visibility = visibility, TypeExpr = type, Name = name, Value = ParseInitializer() }; + FuField# field = new FuField { StartLine = line, StartColumn = column, Loc = loc, Documentation = doc, + Visibility = visibility, TypeExpr = type, Name = name, Value = ParseInitializer() }; AddSymbol(klass, field); - Expect(FuToken.Semicolon); + CloseMember(FuToken.Semicolon, field); } CloseContainer(klass); } @@ -1084,12 +1105,15 @@ public class FuParser : FuLexer Expect(FuToken.LeftBrace); do { FuConst# konst = new FuConst { Visibility = FuVisibility.Public, Documentation = ParseDoc(), Loc = this.TokenLoc, Name = this.StringValue, Type = enu, VisitStatus = FuVisitStatus.NotYet }; + konst.StartLine = GetCurrentLine(); + konst.StartColumn = GetTokenColumn(); Expect(FuToken.Id); if (Eat(FuToken.Assign)) konst.Value = ParseExpr(); else if (flags) ReportError("enum* symbol must be assigned a value"); AddSymbol(enu, konst); + SetMemberEnd(konst); } while (Eat(FuToken.Comma)); CloseContainer(enu); } diff --git a/editors/vscode/README.md b/editors/vscode/README.md index 9ae0bf4f..4ef7ebeb 100644 --- a/editors/vscode/README.md +++ b/editors/vscode/README.md @@ -2,6 +2,7 @@ A Visual Studio Code extension for the [Fusion programming language](https://fus * Syntax highlighting * Error highlighting +* Outline * Go to definition * Snippets diff --git a/editors/vscode/src/extension.ts b/editors/vscode/src/extension.ts index 0801f4a0..a912c2a3 100644 --- a/editors/vscode/src/extension.ts +++ b/editors/vscode/src/extension.ts @@ -19,7 +19,7 @@ // along with Fusion Transpiler. If not, see http://www.gnu.org/licenses/ import * as vscode from "vscode"; -import { FuParser, FuProgram, FuSystem, FuSema, FuSemaHost, FuEnum } from "./fucheck.js"; +import { FuParser, FuProgram, FuSystem, FuSema, FuSemaHost, FuContainerType, FuEnum, FuMember, FuField, FuMethod } from "./fucheck.js"; class VsCodeHost extends FuSemaHost { @@ -156,18 +156,36 @@ class VsCodeDefinitionProvider extends VsCodeHost class VsCodeSymbolProvider extends VsCodeHost { + #createSymbol(source: FuContainerType | FuMember, kind: vscode.SymbolKind): vscode.DocumentSymbol + { + const line = this.program.getLine(source.loc); + const column = source.loc - this.program.lineLocs[line]; + return new vscode.DocumentSymbol(source.name, "", kind, + new vscode.Range(source.startLine, source.startColumn, source.endLine, source.endColumn), + new vscode.Range(line, column, line, column + source.getLocLength())); + } + provideSymbols(document: vscode.TextDocument): vscode.DocumentSymbol[] { this.parseDocument(document, this.createParser()); const symbols : vscode.DocumentSymbol[] = []; for (let container = this.program.first; container != null; container = container.next) { - const loc = container.loc; - const line = this.program.getLine(loc); - const startColumn = loc - this.program.lineLocs[line]; - const endColumn = startColumn + container.getLocLength(); - symbols.push(new vscode.DocumentSymbol(container.name, "", container instanceof FuEnum ? vscode.SymbolKind.Enum : vscode.SymbolKind.Class, - new vscode.Range(container.startLine, container.startColumn, container.endLine, container.endColumn), - new vscode.Range(line, startColumn, line, endColumn))); + if (container instanceof FuEnum) { + const containerSymbol = this.#createSymbol(container, vscode.SymbolKind.Enum); + for (let member: FuMember = container.getFirstValue(); member != null; member = member.next) + containerSymbol.children.push(this.#createSymbol(member, vscode.SymbolKind.EnumMember)); + symbols.push(containerSymbol); + } + else { + const containerSymbol = this.#createSymbol(container, vscode.SymbolKind.Class); + if (container.constructor_ != null) + containerSymbol.children.push(this.#createSymbol(container.constructor_, vscode.SymbolKind.Constructor)); + for (let member = container.first; member != null; member = member.next) { + containerSymbol.children.push(this.#createSymbol(member, member instanceof FuMethod ? vscode.SymbolKind.Method : + member instanceof FuField ? vscode.SymbolKind.Field : vscode.SymbolKind.Constant)); + } + symbols.push(containerSymbol); + } } return symbols; } diff --git a/libfut.cpp b/libfut.cpp index 06ffe6b5..bbc86ef1 100644 --- a/libfut.cpp +++ b/libfut.cpp @@ -3697,7 +3697,7 @@ std::shared_ptr FuParser::parseConst(FuVisibility visibility) nextToken(); expect(FuToken::assign); konst->value = parseConstInitializer(); - expect(FuToken::semicolon); + closeMember(FuToken::semicolon, konst.get()); return konst; } @@ -3733,14 +3733,14 @@ std::shared_ptr FuParser::parseAssign(bool allowVar) } } -std::shared_ptr FuParser::parseBlock() +std::shared_ptr FuParser::parseBlock(FuMethodBase * method) { std::shared_ptr result = std::make_shared(); result->loc = this->tokenLoc; expect(FuToken::leftBrace); while (!see(FuToken::rightBrace) && !see(FuToken::endOfFile)) result->statements.push_back(parseStatement()); - expect(FuToken::rightBrace); + closeMember(FuToken::rightBrace, method); return result; } @@ -3903,14 +3903,44 @@ std::shared_ptr FuParser::parseNative() return result; } -std::shared_ptr FuParser::parseReturn() +int FuParser::getCurrentLine() const +{ + return std::ssize(this->host->program->lineLocs) - this->host->program->sourceFiles.back().line - 1; +} + +int FuParser::getTokenColumn() const +{ + return this->tokenLoc - this->host->program->lineLocs.back(); +} + +void FuParser::setMemberEnd(FuMember * member) const +{ + member->endLine = getCurrentLine(); + member->endColumn = this->loc - this->host->program->lineLocs.back(); +} + +void FuParser::closeMember(FuToken expected, FuMember * member) +{ + if (member != nullptr) + setMemberEnd(member); + expect(expected); +} + +void FuParser::closeContainer(FuContainerType * type) +{ + type->endLine = getCurrentLine(); + type->endColumn = this->loc - this->host->program->lineLocs.back(); + expect(FuToken::rightBrace); +} + +std::shared_ptr FuParser::parseReturn(FuMethod * method) { std::shared_ptr result = std::make_shared(); result->loc = this->tokenLoc; nextToken(); if (!see(FuToken::semicolon)) result->value = parseExpr(); - expect(FuToken::semicolon); + closeMember(FuToken::semicolon, method); return result; } @@ -4003,7 +4033,7 @@ std::shared_ptr FuParser::parseStatement() { switch (this->currentToken) { case FuToken::leftBrace: - return parseBlock(); + return parseBlock(nullptr); case FuToken::assert: return parseAssert(); case FuToken::break_: @@ -4025,7 +4055,7 @@ std::shared_ptr FuParser::parseStatement() case FuToken::native: return parseNative(); case FuToken::return_: - return parseReturn(); + return parseReturn(nullptr); case FuToken::switch_: return parseSwitch(); case FuToken::throw_: @@ -4099,28 +4129,11 @@ void FuParser::parseMethod(FuClass * klass, std::shared_ptr method) while (eat(FuToken::comma)); } if (method->callType == FuCallType::abstract) - expect(FuToken::semicolon); + closeMember(FuToken::semicolon, method.get()); else if (see(FuToken::fatArrow)) - method->body = parseReturn(); + method->body = parseReturn(method.get()); else if (check(FuToken::leftBrace)) - method->body = parseBlock(); -} - -int FuParser::getCurrentLine() const -{ - return std::ssize(this->host->program->lineLocs) - this->host->program->sourceFiles.back().line - 1; -} - -int FuParser::getTokenColumn() const -{ - return this->tokenLoc - this->host->program->lineLocs.back(); -} - -void FuParser::closeContainer(FuContainerType * type) -{ - type->endLine = getCurrentLine(); - type->endColumn = this->loc - this->host->program->lineLocs.back(); - expect(FuToken::rightBrace); + method->body = parseBlock(method.get()); } std::string_view FuParser::callTypeToString(FuCallType callType) @@ -4163,6 +4176,8 @@ void FuParser::parseClass(std::shared_ptr doc, int line, int column, expect(FuToken::leftBrace); while (!see(FuToken::rightBrace) && !see(FuToken::endOfFile)) { doc = parseDoc(); + int line = getCurrentLine(); + int column = getTokenColumn(); FuVisibility visibility; switch (this->currentToken) { case FuToken::internal: @@ -4183,6 +4198,8 @@ void FuParser::parseClass(std::shared_ptr doc, int line, int column, } if (see(FuToken::const_)) { std::shared_ptr konst = parseConst(visibility); + konst->startLine = line; + konst->startColumn = column; konst->documentation = doc; addSymbol(klass.get(), konst); continue; @@ -4206,16 +4223,18 @@ void FuParser::parseClass(std::shared_ptr doc, int line, int column, if (visibility == FuVisibility::private_) visibility = FuVisibility::internal; std::shared_ptr futemp0 = std::make_shared(); + futemp0->startLine = line; + futemp0->startColumn = column; futemp0->loc = call->loc; futemp0->documentation = doc; futemp0->visibility = visibility; futemp0->parent = klass.get(); futemp0->type = this->host->program->system->voidType; futemp0->name = klass->name; - futemp0->body = parseBlock(); klass->constructor = futemp0; klass->constructor->parameters.parent = klass.get(); klass->constructor->addThis(klass.get(), true); + klass->constructor->body = parseBlock(klass->constructor.get()); continue; } int loc = this->tokenLoc; @@ -4234,6 +4253,8 @@ void FuParser::parseClass(std::shared_ptr doc, int line, int column, if (visibility == FuVisibility::private_ && callType != FuCallType::static_ && callType != FuCallType::normal) reportError(std::format("{} method cannot be private", callTypeToString(callType))); std::shared_ptr method = std::make_shared(); + method->startLine = line; + method->startColumn = column; method->loc = loc; method->documentation = doc; method->visibility = visibility; @@ -4250,6 +4271,8 @@ void FuParser::parseClass(std::shared_ptr doc, int line, int column, if (type == this->host->program->system->voidType) reportError("Field cannot be void"); std::shared_ptr field = std::make_shared(); + field->startLine = line; + field->startColumn = column; field->loc = loc; field->documentation = doc; field->visibility = visibility; @@ -4257,7 +4280,7 @@ void FuParser::parseClass(std::shared_ptr doc, int line, int column, field->name = name; field->value = parseInitializer(); addSymbol(klass.get(), field); - expect(FuToken::semicolon); + closeMember(FuToken::semicolon, field.get()); } closeContainer(klass.get()); } @@ -4284,12 +4307,15 @@ void FuParser::parseEnum(std::shared_ptr doc, int line, int column, b konst->name = this->stringValue; konst->type = enu; konst->visitStatus = FuVisitStatus::notYet; + konst->startLine = getCurrentLine(); + konst->startColumn = getTokenColumn(); expect(FuToken::id); if (eat(FuToken::assign)) konst->value = parseExpr(); else if (flags) reportError("enum* symbol must be assigned a value"); addSymbol(enu.get(), konst); + setMemberEnd(konst.get()); } while (eat(FuToken::comma)); closeContainer(enu.get()); diff --git a/libfut.cs b/libfut.cs index cc618470..a27ea0df 100644 --- a/libfut.cs +++ b/libfut.cs @@ -2665,6 +2665,14 @@ protected FuMember() internal FuVisibility Visibility; + internal int StartLine; + + internal int StartColumn; + + internal int EndLine; + + internal int EndColumn; + public abstract bool IsStatic(); } @@ -4012,7 +4020,7 @@ FuConst ParseConst(FuVisibility visibility) NextToken(); Expect(FuToken.Assign); konst.Value = ParseConstInitializer(); - Expect(FuToken.Semicolon); + CloseMember(FuToken.Semicolon, konst); return konst; } @@ -4041,13 +4049,13 @@ FuExpr ParseAssign(bool allowVar) } } - FuBlock ParseBlock() + FuBlock ParseBlock(FuMethodBase method) { FuBlock result = new FuBlock { Loc = this.TokenLoc }; Expect(FuToken.LeftBrace); while (!See(FuToken.RightBrace) && !See(FuToken.EndOfFile)) result.Statements.Add(ParseStatement()); - Expect(FuToken.RightBrace); + CloseMember(FuToken.RightBrace, method); return result; } @@ -4199,13 +4207,37 @@ FuNative ParseNative() return result; } - FuReturn ParseReturn() + int GetCurrentLine() => this.Host.Program.LineLocs.Count - this.Host.Program.SourceFiles[^1].Line - 1; + + int GetTokenColumn() => this.TokenLoc - this.Host.Program.LineLocs[^1]; + + void SetMemberEnd(FuMember member) + { + member.EndLine = GetCurrentLine(); + member.EndColumn = this.Loc - this.Host.Program.LineLocs[^1]; + } + + void CloseMember(FuToken expected, FuMember member) + { + if (member != null) + SetMemberEnd(member); + Expect(expected); + } + + void CloseContainer(FuContainerType type) + { + type.EndLine = GetCurrentLine(); + type.EndColumn = this.Loc - this.Host.Program.LineLocs[^1]; + Expect(FuToken.RightBrace); + } + + FuReturn ParseReturn(FuMethod method) { FuReturn result = new FuReturn { Loc = this.TokenLoc }; NextToken(); if (!See(FuToken.Semicolon)) result.Value = ParseExpr(); - Expect(FuToken.Semicolon); + CloseMember(FuToken.Semicolon, method); return result; } @@ -4289,7 +4321,7 @@ FuStatement ParseStatement() { switch (this.CurrentToken) { case FuToken.LeftBrace: - return ParseBlock(); + return ParseBlock(null); case FuToken.Assert: return ParseAssert(); case FuToken.Break: @@ -4311,7 +4343,7 @@ FuStatement ParseStatement() case FuToken.Native: return ParseNative(); case FuToken.Return: - return ParseReturn(); + return ParseReturn(null); case FuToken.Switch: return ParseSwitch(); case FuToken.Throw: @@ -4383,22 +4415,11 @@ void ParseMethod(FuClass klass, FuMethod method) while (Eat(FuToken.Comma)); } if (method.CallType == FuCallType.Abstract) - Expect(FuToken.Semicolon); + CloseMember(FuToken.Semicolon, method); else if (See(FuToken.FatArrow)) - method.Body = ParseReturn(); + method.Body = ParseReturn(method); else if (Check(FuToken.LeftBrace)) - method.Body = ParseBlock(); - } - - int GetCurrentLine() => this.Host.Program.LineLocs.Count - this.Host.Program.SourceFiles[^1].Line - 1; - - int GetTokenColumn() => this.TokenLoc - this.Host.Program.LineLocs[^1]; - - void CloseContainer(FuContainerType type) - { - type.EndLine = GetCurrentLine(); - type.EndColumn = this.Loc - this.Host.Program.LineLocs[^1]; - Expect(FuToken.RightBrace); + method.Body = ParseBlock(method); } static string CallTypeToString(FuCallType callType) @@ -4434,6 +4455,8 @@ void ParseClass(FuCodeDoc doc, int line, int column, bool isPublic, FuCallType c Expect(FuToken.LeftBrace); while (!See(FuToken.RightBrace) && !See(FuToken.EndOfFile)) { doc = ParseDoc(); + int line = GetCurrentLine(); + int column = GetTokenColumn(); FuVisibility visibility; switch (this.CurrentToken) { case FuToken.Internal: @@ -4454,6 +4477,8 @@ void ParseClass(FuCodeDoc doc, int line, int column, bool isPublic, FuCallType c } if (See(FuToken.Const)) { FuConst konst = ParseConst(visibility); + konst.StartLine = line; + konst.StartColumn = column; konst.Documentation = doc; AddSymbol(klass, konst); continue; @@ -4475,9 +4500,10 @@ void ParseClass(FuCodeDoc doc, int line, int column, bool isPublic, FuCallType c } if (visibility == FuVisibility.Private) visibility = FuVisibility.Internal; - klass.Constructor = new FuMethodBase { Loc = call.Loc, Documentation = doc, Visibility = visibility, Parent = klass, Type = this.Host.Program.System.VoidType, Name = klass.Name, Body = ParseBlock() }; + klass.Constructor = new FuMethodBase { StartLine = line, StartColumn = column, Loc = call.Loc, Documentation = doc, Visibility = visibility, Parent = klass, Type = this.Host.Program.System.VoidType, Name = klass.Name }; klass.Constructor.Parameters.Parent = klass; klass.Constructor.AddThis(klass, true); + klass.Constructor.Body = ParseBlock(klass.Constructor); continue; } int loc = this.TokenLoc; @@ -4495,7 +4521,7 @@ void ParseClass(FuCodeDoc doc, int line, int column, bool isPublic, FuCallType c ReportError("Virtual methods disallowed in a sealed class"); if (visibility == FuVisibility.Private && callType != FuCallType.Static && callType != FuCallType.Normal) ReportError($"{CallTypeToString(callType)} method cannot be private"); - FuMethod method = new FuMethod { Loc = loc, Documentation = doc, Visibility = visibility, CallType = callType, TypeExpr = type, Name = name }; + FuMethod method = new FuMethod { StartLine = line, StartColumn = column, Loc = loc, Documentation = doc, Visibility = visibility, CallType = callType, TypeExpr = type, Name = name }; ParseMethod(klass, method); continue; } @@ -4505,9 +4531,9 @@ void ParseClass(FuCodeDoc doc, int line, int column, bool isPublic, FuCallType c ReportError($"Field cannot be {CallTypeToString(callType)}"); if (type == this.Host.Program.System.VoidType) ReportError("Field cannot be void"); - FuField field = new FuField { Loc = loc, Documentation = doc, Visibility = visibility, TypeExpr = type, Name = name, Value = ParseInitializer() }; + FuField field = new FuField { StartLine = line, StartColumn = column, Loc = loc, Documentation = doc, Visibility = visibility, TypeExpr = type, Name = name, Value = ParseInitializer() }; AddSymbol(klass, field); - Expect(FuToken.Semicolon); + CloseMember(FuToken.Semicolon, field); } CloseContainer(klass); } @@ -4528,12 +4554,15 @@ void ParseEnum(FuCodeDoc doc, int line, int column, bool isPublic) Expect(FuToken.LeftBrace); do { FuConst konst = new FuConst { Visibility = FuVisibility.Public, Documentation = ParseDoc(), Loc = this.TokenLoc, Name = this.StringValue, Type = enu, VisitStatus = FuVisitStatus.NotYet }; + konst.StartLine = GetCurrentLine(); + konst.StartColumn = GetTokenColumn(); Expect(FuToken.Id); if (Eat(FuToken.Assign)) konst.Value = ParseExpr(); else if (flags) ReportError("enum* symbol must be assigned a value"); AddSymbol(enu, konst); + SetMemberEnd(konst); } while (Eat(FuToken.Comma)); CloseContainer(enu); diff --git a/libfut.hpp b/libfut.hpp index e1d115a4..129e351e 100644 --- a/libfut.hpp +++ b/libfut.hpp @@ -1238,6 +1238,10 @@ class FuMember : public FuNamedValue FuMember(); public: FuVisibility visibility; + int startLine; + int startColumn; + int endLine; + int endColumn; }; class FuVar : public FuNamedValue @@ -1604,7 +1608,7 @@ class FuParser : public FuLexer std::shared_ptr parseVar(std::shared_ptr type, bool initializer); std::shared_ptr parseConst(FuVisibility visibility); std::shared_ptr parseAssign(bool allowVar); - std::shared_ptr parseBlock(); + std::shared_ptr parseBlock(FuMethodBase * method); std::shared_ptr parseAssert(); std::shared_ptr parseBreak(); std::shared_ptr parseContinue(); @@ -1616,16 +1620,18 @@ class FuParser : public FuLexer std::shared_ptr parseIf(); std::shared_ptr parseLock(); std::shared_ptr parseNative(); - std::shared_ptr parseReturn(); + int getCurrentLine() const; + int getTokenColumn() const; + void setMemberEnd(FuMember * member) const; + void closeMember(FuToken expected, FuMember * member); + void closeContainer(FuContainerType * type); + std::shared_ptr parseReturn(FuMethod * method); std::shared_ptr parseSwitch(); std::shared_ptr parseThrow(); std::shared_ptr parseWhile(); std::shared_ptr parseStatement(); FuCallType parseCallType(); void parseMethod(FuClass * klass, std::shared_ptr method); - int getCurrentLine() const; - int getTokenColumn() const; - void closeContainer(FuContainerType * type); static std::string_view callTypeToString(FuCallType callType); void parseClass(std::shared_ptr doc, int line, int column, bool isPublic, FuCallType callType); void parseEnum(std::shared_ptr doc, int line, int column, bool isPublic); diff --git a/libfut.js b/libfut.js index c120a6eb..10074a29 100644 --- a/libfut.js +++ b/libfut.js @@ -2777,6 +2777,10 @@ export class FuMember extends FuNamedValue super(); } visibility; + startLine; + startColumn; + endLine; + endColumn; } export class FuVar extends FuNamedValue @@ -4216,7 +4220,7 @@ export class FuParser extends FuLexer this.nextToken(); this.expect(FuToken.ASSIGN); konst.value = this.#parseConstInitializer(); - this.expect(FuToken.SEMICOLON); + this.#closeMember(FuToken.SEMICOLON, konst); return konst; } @@ -4245,13 +4249,13 @@ export class FuParser extends FuLexer } } - #parseBlock() + #parseBlock(method) { let result = Object.assign(new FuBlock(), { loc: this.tokenLoc }); this.expect(FuToken.LEFT_BRACE); while (!this.see(FuToken.RIGHT_BRACE) && !this.see(FuToken.END_OF_FILE)) result.statements.push(this.#parseStatement()); - this.expect(FuToken.RIGHT_BRACE); + this.#closeMember(FuToken.RIGHT_BRACE, method); return result; } @@ -4404,13 +4408,43 @@ export class FuParser extends FuLexer return result; } - #parseReturn() + #getCurrentLine() + { + return this.host.program.lineLocs.length - this.host.program.sourceFiles.at(-1).line - 1; + } + + #getTokenColumn() + { + return this.tokenLoc - this.host.program.lineLocs.at(-1); + } + + #setMemberEnd(member) + { + member.endLine = this.#getCurrentLine(); + member.endColumn = this.loc - this.host.program.lineLocs.at(-1); + } + + #closeMember(expected, member) + { + if (member != null) + this.#setMemberEnd(member); + this.expect(expected); + } + + #closeContainer(type) + { + type.endLine = this.#getCurrentLine(); + type.endColumn = this.loc - this.host.program.lineLocs.at(-1); + this.expect(FuToken.RIGHT_BRACE); + } + + #parseReturn(method) { let result = Object.assign(new FuReturn(), { loc: this.tokenLoc }); this.nextToken(); if (!this.see(FuToken.SEMICOLON)) result.value = this.#parseExpr(); - this.expect(FuToken.SEMICOLON); + this.#closeMember(FuToken.SEMICOLON, method); return result; } @@ -4494,7 +4528,7 @@ export class FuParser extends FuLexer { switch (this.currentToken) { case FuToken.LEFT_BRACE: - return this.#parseBlock(); + return this.#parseBlock(null); case FuToken.ASSERT: return this.#parseAssert(); case FuToken.BREAK: @@ -4516,7 +4550,7 @@ export class FuParser extends FuLexer case FuToken.NATIVE: return this.#parseNative(); case FuToken.RETURN: - return this.#parseReturn(); + return this.#parseReturn(null); case FuToken.SWITCH: return this.#parseSwitch(); case FuToken.THROW: @@ -4588,28 +4622,11 @@ export class FuParser extends FuLexer while (this.eat(FuToken.COMMA)); } if (method.callType == FuCallType.ABSTRACT) - this.expect(FuToken.SEMICOLON); + this.#closeMember(FuToken.SEMICOLON, method); else if (this.see(FuToken.FAT_ARROW)) - method.body = this.#parseReturn(); + method.body = this.#parseReturn(method); else if (this.check(FuToken.LEFT_BRACE)) - method.body = this.#parseBlock(); - } - - #getCurrentLine() - { - return this.host.program.lineLocs.length - this.host.program.sourceFiles.at(-1).line - 1; - } - - #getTokenColumn() - { - return this.tokenLoc - this.host.program.lineLocs.at(-1); - } - - #closeContainer(type) - { - type.endLine = this.#getCurrentLine(); - type.endColumn = this.loc - this.host.program.lineLocs.at(-1); - this.expect(FuToken.RIGHT_BRACE); + method.body = this.#parseBlock(method); } static #callTypeToString(callType) @@ -4645,6 +4662,8 @@ export class FuParser extends FuLexer this.expect(FuToken.LEFT_BRACE); while (!this.see(FuToken.RIGHT_BRACE) && !this.see(FuToken.END_OF_FILE)) { doc = this.#parseDoc(); + let line = this.#getCurrentLine(); + let column = this.#getTokenColumn(); let visibility; switch (this.currentToken) { case FuToken.INTERNAL: @@ -4665,6 +4684,8 @@ export class FuParser extends FuLexer } if (this.see(FuToken.CONST)) { let konst = this.#parseConst(visibility); + konst.startLine = line; + konst.startColumn = column; konst.documentation = doc; this.#addSymbol(klass, konst); continue; @@ -4687,9 +4708,10 @@ export class FuParser extends FuLexer } if (visibility == FuVisibility.PRIVATE) visibility = FuVisibility.INTERNAL; - klass.constructor_ = Object.assign(new FuMethodBase(), { loc: call.loc, documentation: doc, visibility: visibility, parent: klass, type: this.host.program.system.voidType, name: klass.name, body: this.#parseBlock() }); + klass.constructor_ = Object.assign(new FuMethodBase(), { startLine: line, startColumn: column, loc: call.loc, documentation: doc, visibility: visibility, parent: klass, type: this.host.program.system.voidType, name: klass.name }); klass.constructor_.parameters.parent = klass; klass.constructor_.addThis(klass, true); + klass.constructor_.body = this.#parseBlock(klass.constructor_); continue; } let loc = this.tokenLoc; @@ -4707,7 +4729,7 @@ export class FuParser extends FuLexer this.reportError("Virtual methods disallowed in a sealed class"); if (visibility == FuVisibility.PRIVATE && callType != FuCallType.STATIC && callType != FuCallType.NORMAL) this.reportError(`${FuParser.#callTypeToString(callType)} method cannot be private`); - let method = Object.assign(new FuMethod(), { loc: loc, documentation: doc, visibility: visibility, callType: callType, typeExpr: type, name: name }); + let method = Object.assign(new FuMethod(), { startLine: line, startColumn: column, loc: loc, documentation: doc, visibility: visibility, callType: callType, typeExpr: type, name: name }); this.#parseMethod(klass, method); continue; } @@ -4717,9 +4739,9 @@ export class FuParser extends FuLexer this.reportError(`Field cannot be ${FuParser.#callTypeToString(callType)}`); if (type == this.host.program.system.voidType) this.reportError("Field cannot be void"); - let field = Object.assign(new FuField(), { loc: loc, documentation: doc, visibility: visibility, typeExpr: type, name: name, value: this.#parseInitializer() }); + let field = Object.assign(new FuField(), { startLine: line, startColumn: column, loc: loc, documentation: doc, visibility: visibility, typeExpr: type, name: name, value: this.#parseInitializer() }); this.#addSymbol(klass, field); - this.expect(FuToken.SEMICOLON); + this.#closeMember(FuToken.SEMICOLON, field); } this.#closeContainer(klass); } @@ -4740,12 +4762,15 @@ export class FuParser extends FuLexer this.expect(FuToken.LEFT_BRACE); do { let konst = Object.assign(new FuConst(), { visibility: FuVisibility.PUBLIC, documentation: this.#parseDoc(), loc: this.tokenLoc, name: this.stringValue, type: enu, visitStatus: FuVisitStatus.NOT_YET }); + konst.startLine = this.#getCurrentLine(); + konst.startColumn = this.#getTokenColumn(); this.expect(FuToken.ID); if (this.eat(FuToken.ASSIGN)) konst.value = this.#parseExpr(); else if (flags) this.reportError("enum* symbol must be assigned a value"); this.#addSymbol(enu, konst); + this.#setMemberEnd(konst); } while (this.eat(FuToken.COMMA)); this.#closeContainer(enu);