diff --git a/zenscript-code-model/src/main/antlr/ZenScriptParser.g4 b/zenscript-code-model/src/main/antlr/ZenScriptParser.g4 index d714e69b..a4c7cfca 100644 --- a/zenscript-code-model/src/main/antlr/ZenScriptParser.g4 +++ b/zenscript-code-model/src/main/antlr/ZenScriptParser.g4 @@ -1,4 +1,7 @@ parser grammar ZenScriptParser; +@parser::members { + public boolean dzs = false; +} options { tokenVocab = ZenScriptLexer; } @@ -11,11 +14,20 @@ topLevelElement | classDeclaration | functionDeclaration | expandFunctionDeclaration - | statement + | topLevelStatement + ; + +classDeclaration + : 'zenClass' simpleClassName extendsSpecifier? classBody // extended for dzs + ; + + +extendsSpecifier + : 'extends' qualifiedName (',' qualifiedName)* ; importDeclaration - : 'import' qualifiedName ('as' alias = simpleName)? ';' + : 'import' qualifiedName ('as' alias = simpleName)? tailingSemi=';'? ; qualifiedName @@ -32,21 +44,18 @@ simpleName ; functionDeclaration - : prefix='static'? 'function' simpleName '(' (formalParameter (',' formalParameter)*)? ')' ('as' returnType)? functionBody - | prefix=('static' | 'global')? 'function' simpleName? '(' (formalParameter (',' formalParameter)*)? ')' 'as' returnType ';' // dzs - ; + // global is only in dzs + : prefix=('static' | 'global')? 'function' simpleName? '(' (formalParameter (',' formalParameter)*)? ')' ('as' returnType)? ( + {!dzs}? functionBody | + {dzs}? tailingSemi=';' // dzs + ); expandFunctionDeclaration : '$expand' typeLiteral '$' simpleName '(' (formalParameter (',' formalParameter)*)? ')' ('as' returnType)? functionBody ; formalParameter - : simpleName ('as' typeLiteral)? ('=' defaultValue)? - | varargsPrefix simpleName ('as' typeLiteral)? ('=' defaultValue)? //dzs - ; - -varargsPrefix // dzs - : '...' + : ({dzs}? varargsPrefix='...')? simpleName ('as' typeLiteral)? ('=' defaultValue)? ; defaultValue @@ -61,9 +70,6 @@ functionBody : '{' statement* '}' ; -classDeclaration - : 'zenClass' simpleClassName ('extends' qualifiedName (',' qualifiedName)*)? classBody // extended for dzs - ; simpleClassName : simpleName @@ -80,14 +86,14 @@ simpleClassName ; classBody - : '{' classMemberDeclaration* '}' + : '{' additionalBracket='{'? classMemberDeclaration* '}' ; classMemberDeclaration - : variableDeclaration - | constructorDeclaration + : constructorDeclaration | functionDeclaration - | operatorFunctionDeclaration // dzs + | {dzs}? operatorFunctionDeclaration // dzs + | variableDeclaration | invaildStatementInClassBody ; @@ -96,8 +102,15 @@ invaildStatementInClassBody ; constructorDeclaration - : 'zenConstructor' '(' (formalParameter (',' formalParameter)*)? ')' constructorBody - | 'zenConstructor' '(' (formalParameter (',' formalParameter)*)? ')' ';' // dzs + : 'zenConstructor' '(' (formalParameter (',' formalParameter)*)? ')' ( + {!dzs}? constructorBody | + {dzs}? tailingSemi=';' + ) + ; + + +topLevelStatement + : statement ; constructorBody @@ -105,8 +118,7 @@ constructorBody ; variableDeclaration - : prefix=('var' | 'val' | 'static' | 'global') simpleName ('as' typeLiteral)? ('=' initializer = expression)? ';' - | prefix=('var' | 'val' | 'static' | 'global') simpleName 'as' typeLiteral ';' //dzs + : prefix=('var' | 'val' | 'static' | 'global') simpleName? ('as' typeLiteral)? ({!dzs}? ('=' initializer = expression)?) tailingSemi=';'? ; operatorFunctionDeclaration // dzs @@ -157,7 +169,7 @@ blockStatement ; returnStatement - : 'return' expression? ';' + : 'return' expression? tailingSemi=';'? ; breakStatement @@ -189,9 +201,12 @@ whileStatement ; expressionStatement - : expression ';'? + : expression tailingSemi=';'? ; + + + // Paraphrased from https://github.com/CraftTweaker/ZenScript/blob/master/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpression.java expression // Not really sure about this diff --git a/zenscript-code-model/src/main/kotlin/raylras/zen/model/CompilationUnit.kt b/zenscript-code-model/src/main/kotlin/raylras/zen/model/CompilationUnit.kt index bc9b332e..327d5b96 100644 --- a/zenscript-code-model/src/main/kotlin/raylras/zen/model/CompilationUnit.kt +++ b/zenscript-code-model/src/main/kotlin/raylras/zen/model/CompilationUnit.kt @@ -3,6 +3,7 @@ package raylras.zen.model import org.antlr.v4.runtime.CommonTokenStream import org.antlr.v4.runtime.tree.ParseTree import org.antlr.v4.runtime.tree.ParseTreeWalker +import raylras.zen.model.diagnose.DiagnoseHandler import raylras.zen.model.parser.ZenScriptLexer import raylras.zen.model.scope.Scope import raylras.zen.model.symbol.* @@ -21,6 +22,7 @@ class CompilationUnit(val path: Path, val env: CompilationEnvironment) { val scopeMap = IdentityHashMap() val symbolMap = IdentityHashMap() + var diagnoseHandler: DiagnoseHandler = DiagnoseHandler(this) lateinit var tokenStream: CommonTokenStream lateinit var parseTree: ParseTree @@ -156,6 +158,7 @@ class CompilationUnit(val path: Path, val env: CompilationEnvironment) { classMap = emptyMap() expandFunctionMap = emptyMap() staticSymbolMap = emptyMap() + diagnoseHandler.clear() } override fun toString(): String = path.toString() diff --git a/zenscript-code-model/src/main/kotlin/raylras/zen/model/Compilations.kt b/zenscript-code-model/src/main/kotlin/raylras/zen/model/Compilations.kt index 6891a9a8..fc979cfe 100644 --- a/zenscript-code-model/src/main/kotlin/raylras/zen/model/Compilations.kt +++ b/zenscript-code-model/src/main/kotlin/raylras/zen/model/Compilations.kt @@ -4,6 +4,9 @@ import org.antlr.v4.runtime.* import org.antlr.v4.runtime.atn.PredictionMode import org.antlr.v4.runtime.misc.ParseCancellationException import org.antlr.v4.runtime.tree.ParseTree +import raylras.zen.model.diagnose.ParserErrorListener +import raylras.zen.model.diagnose.PrettyErrorStrategy +import raylras.zen.model.diagnose.resolveSyntaxErrors import raylras.zen.model.parser.ZenScriptLexer import raylras.zen.model.parser.ZenScriptParser import raylras.zen.model.resolve.resolveDeclarations @@ -51,10 +54,12 @@ fun createUnit(unitPath: Path, env: CompilationEnvironment): CompilationUnit { return unit } -fun CompilationEnvironment.load() { +fun CompilationEnvironment.load(unitCallback: (CompilationUnit) -> Unit = {}) { this.clear() this.getUnitPaths().forEach { path -> - createUnit(path, this).load() + val unit = createUnit(path, this) + unit.load() + unitCallback(unit) } } @@ -69,10 +74,11 @@ fun CompilationUnit.load(source: String) { fun CompilationUnit.load(charStream: CharStream) { this.clear() val tokenStream = lex(charStream) - val parseTree = parse(tokenStream) + val parseTree = parse(tokenStream, ParserErrorListener(this.diagnoseHandler), this.isDzsUnit) this.tokenStream = tokenStream this.parseTree = parseTree this.resolveDeclarations() + this.resolveSyntaxErrors() } fun lex(charStream: CharStream): CommonTokenStream { @@ -81,19 +87,21 @@ fun lex(charStream: CharStream): CommonTokenStream { return CommonTokenStream(lexer) } -fun parse(tokenStream: TokenStream): ParseTree { +fun parse(tokenStream: TokenStream, errorListener: ANTLRErrorListener, isDzs: Boolean): ParseTree { val parser = ZenScriptParser(tokenStream) parser.removeErrorListeners() // faster but less robust strategy, effective when no syntax errors parser.interpreter.predictionMode = PredictionMode.SLL parser.errorHandler = BailErrorStrategy() + parser.dzs = isDzs try { return parser.compilationUnit() } catch (_: ParseCancellationException) { parser.reset() // fall back to default strategy, slower but more robust parser.interpreter.predictionMode = PredictionMode.LL - parser.errorHandler = DefaultErrorStrategy() + parser.errorHandler = PrettyErrorStrategy() + parser.addErrorListener(errorListener) return parser.compilationUnit() } } diff --git a/zenscript-code-model/src/main/kotlin/raylras/zen/model/diagnose/DiagnoseEntity.kt b/zenscript-code-model/src/main/kotlin/raylras/zen/model/diagnose/DiagnoseEntity.kt new file mode 100644 index 00000000..56285522 --- /dev/null +++ b/zenscript-code-model/src/main/kotlin/raylras/zen/model/diagnose/DiagnoseEntity.kt @@ -0,0 +1,20 @@ +package raylras.zen.model.diagnose + +import raylras.zen.util.TextRange + + +enum class DiagnoseLevel { + Error, + Warning, + Hint +} +data class DiagnoseEntity( + val level: DiagnoseLevel, + val msg: String, + val range: TextRange, + +) { + + +} + diff --git a/zenscript-code-model/src/main/kotlin/raylras/zen/model/diagnose/DiagnoseHandler.kt b/zenscript-code-model/src/main/kotlin/raylras/zen/model/diagnose/DiagnoseHandler.kt new file mode 100644 index 00000000..7a41a434 --- /dev/null +++ b/zenscript-code-model/src/main/kotlin/raylras/zen/model/diagnose/DiagnoseHandler.kt @@ -0,0 +1,25 @@ +package raylras.zen.model.diagnose + +import raylras.zen.model.CompilationUnit +import raylras.zen.util.TextRange + +class DiagnoseHandler( + val unit: CompilationUnit +) { + val entities = mutableListOf() + fun addEntity(entity: DiagnoseEntity) { + entities.add(entity) + } + + fun addError(msg: String, range: TextRange) { + addEntity(DiagnoseEntity(DiagnoseLevel.Error, msg, range)) + } + + fun addSystemError(msg: String) { + + } + + fun clear() { + entities.clear() + } +} \ No newline at end of file diff --git a/zenscript-code-model/src/main/kotlin/raylras/zen/model/diagnose/ExpectedTokenVisitor.kt b/zenscript-code-model/src/main/kotlin/raylras/zen/model/diagnose/ExpectedTokenVisitor.kt new file mode 100644 index 00000000..f89bbe5e --- /dev/null +++ b/zenscript-code-model/src/main/kotlin/raylras/zen/model/diagnose/ExpectedTokenVisitor.kt @@ -0,0 +1,14 @@ +package raylras.zen.model.diagnose + +import org.antlr.v4.runtime.Token +import org.antlr.v4.runtime.misc.IntSet +import org.antlr.v4.runtime.misc.IntervalSet +import raylras.zen.model.Visitor + +class ExpectedTokenVisitor( + +): Visitor() { + val result: IntervalSet = IntervalSet() + + +} \ No newline at end of file diff --git a/zenscript-code-model/src/main/kotlin/raylras/zen/model/diagnose/MissingTokenException.kt b/zenscript-code-model/src/main/kotlin/raylras/zen/model/diagnose/MissingTokenException.kt new file mode 100644 index 00000000..84051ef3 --- /dev/null +++ b/zenscript-code-model/src/main/kotlin/raylras/zen/model/diagnose/MissingTokenException.kt @@ -0,0 +1,14 @@ +package raylras.zen.model.diagnose + +import org.antlr.v4.runtime.Parser +import org.antlr.v4.runtime.ParserRuleContext +import org.antlr.v4.runtime.RecognitionException + +class MissingTokenException(recognizer: Parser) : RecognitionException(recognizer, recognizer.inputStream, recognizer.context) { + + init { + offendingToken = recognizer.currentToken + } + +} + diff --git a/zenscript-code-model/src/main/kotlin/raylras/zen/model/diagnose/ParserErrorListener.kt b/zenscript-code-model/src/main/kotlin/raylras/zen/model/diagnose/ParserErrorListener.kt new file mode 100644 index 00000000..dcfee108 --- /dev/null +++ b/zenscript-code-model/src/main/kotlin/raylras/zen/model/diagnose/ParserErrorListener.kt @@ -0,0 +1,56 @@ +package raylras.zen.model.diagnose + +import org.antlr.v4.runtime.BaseErrorListener +import org.antlr.v4.runtime.CommonTokenStream +import org.antlr.v4.runtime.FailedPredicateException +import org.antlr.v4.runtime.NoViableAltException +import org.antlr.v4.runtime.RecognitionException +import org.antlr.v4.runtime.Recognizer +import org.antlr.v4.runtime.Token +import raylras.zen.util.ANTLR_BASE_LINE +import raylras.zen.util.TextRange +import raylras.zen.util.textRange + +class ParserErrorListener( + private val diagnoseHandler: DiagnoseHandler +) : BaseErrorListener() { + override fun syntaxError( + recognizer: Recognizer<*, *>, + offendingSymbol: Any?, + line: Int, + charPositionInLine: Int, + msg: String, + e: RecognitionException? + ) { + val token = offendingSymbol as? Token ?: return + val range = when (e) { + is FailedPredicateException -> { + // grammar problem + diagnoseHandler.addSystemError(msg + ", at: " + token.textRange.toString()) + return + } + + is NoViableAltException -> { + val startToken = e.startToken + val startLine = startToken.line - ANTLR_BASE_LINE + val startColumn = startToken.charPositionInLine + val endLine = token.line - ANTLR_BASE_LINE + val endColumn = token.charPositionInLine + token.text.length + TextRange(startLine, startColumn, endLine, endColumn) + } + + is MissingTokenException -> { + val tokenStream = recognizer.inputStream as? CommonTokenStream ?: return + + val prev = tokenStream.LT(-1) + val prevLine = prev.line - ANTLR_BASE_LINE + val prevColumn = prev.charPositionInLine + prev.text.length + TextRange(prevLine, prevColumn, prevLine, prevColumn + 1) + + } + + else -> token.textRange + } + diagnoseHandler.addError(msg, range) + } +} \ No newline at end of file diff --git a/zenscript-code-model/src/main/kotlin/raylras/zen/model/diagnose/PrettyErrorStrategy.kt b/zenscript-code-model/src/main/kotlin/raylras/zen/model/diagnose/PrettyErrorStrategy.kt new file mode 100644 index 00000000..36bc214d --- /dev/null +++ b/zenscript-code-model/src/main/kotlin/raylras/zen/model/diagnose/PrettyErrorStrategy.kt @@ -0,0 +1,87 @@ +package raylras.zen.model.diagnose + +import org.antlr.v4.runtime.* +import org.antlr.v4.runtime.misc.IntervalSet +import raylras.zen.util.l10n.L10N + +class PrettyErrorStrategy: DefaultErrorStrategy() { + + override fun reportNoViableAlternative(recognizer: Parser, e: NoViableAltException) { +// val tokens = recognizer.inputStream +// val input: String +// input = if (tokens != null) { +// if (e.startToken.type == Token.EOF) "" else tokens.getText(e.startToken, e.offendingToken) +// } else { +// "" +// } + val msg = L10N.localize("error_illegal_input") + recognizer.notifyErrorListeners(e.offendingToken, msg, e) + } + + override fun reportInputMismatch(recognizer: Parser, e: InputMismatchException) { + + val tokenName = getTokenErrorDisplay(e.offendingToken) + val expecting = getUserExpectedTokens(recognizer) + + val msg = if(expecting.size() == 0) { + L10N.localize("error_illegal_input_expecting",tokenName, expecting.toString(recognizer.vocabulary)) + } else { + L10N.localize("error_illegal_input",tokenName) + } + recognizer.notifyErrorListeners(e.offendingToken, msg, e) + } + + override fun reportFailedPredicate(recognizer: Parser, e: FailedPredicateException) { + val ruleName = recognizer.ruleNames[recognizer.context.ruleIndex] + val msg = "failed to predicate rule for " + ruleName + " " + e.message + recognizer.notifyErrorListeners(e.offendingToken, msg, e) + + } + + + override fun reportUnwantedToken(recognizer: Parser) { + if (inErrorRecoveryMode(recognizer)) { + return + } + + beginErrorCondition(recognizer) + + val t = recognizer.currentToken + val tokenName = getTokenErrorDisplay(t) + val expecting = getUserExpectedTokens(recognizer) + val msg = if(expecting.size() == 0) { + L10N.localize("error_unexpected_token_expecting", tokenName, expecting.toString(recognizer.vocabulary)) + } else { + L10N.localize("error_unexpected_token", tokenName) + } + recognizer.notifyErrorListeners(t, msg, null) + } + + override fun reportMissingToken(recognizer: Parser) { + + if (inErrorRecoveryMode(recognizer)) { + return + } + beginErrorCondition(recognizer) + + val t = recognizer.currentToken + + val expecting = getUserExpectedTokens(recognizer) + val msg = L10N.localize("error_missing_token", expecting.toString(recognizer.vocabulary)) + + recognizer.notifyErrorListeners(t, msg, MissingTokenException(recognizer)) + } + + private fun getUserExpectedTokens(recognizer: Parser): IntervalSet { + // TODO: better display expected tokens+ + return recognizer.expectedTokens +// if(autoToken.size() < 2) { +// return autoToken +// } +// +// +// val visitor = ExpectedTokenVisitor() +// recognizer.context.accept(visitor) +// return visitor.result + } +} \ No newline at end of file diff --git a/zenscript-code-model/src/main/kotlin/raylras/zen/model/diagnose/SyntaxErrorVisitor.kt b/zenscript-code-model/src/main/kotlin/raylras/zen/model/diagnose/SyntaxErrorVisitor.kt new file mode 100644 index 00000000..15288d2d --- /dev/null +++ b/zenscript-code-model/src/main/kotlin/raylras/zen/model/diagnose/SyntaxErrorVisitor.kt @@ -0,0 +1,196 @@ +package raylras.zen.model.diagnose + +import org.antlr.v4.runtime.ParserRuleContext +import org.antlr.v4.runtime.Token +import org.antlr.v4.runtime.tree.ErrorNode +import org.antlr.v4.runtime.tree.ParseTree +import org.antlr.v4.runtime.tree.TerminalNode +import raylras.zen.model.CompilationUnit +import raylras.zen.model.Visitor +import raylras.zen.model.isDzsUnit +import raylras.zen.model.parser.ZenScriptParser +import raylras.zen.util.TextRange +import raylras.zen.util.l10n.L10N +import raylras.zen.util.textRange + + +fun CompilationUnit.resolveSyntaxErrors() { + this.accept(SyntaxErrorVisitor(this.diagnoseHandler, this.isDzsUnit)) +} + +class SyntaxErrorVisitor(private val diagnoseHandler: DiagnoseHandler, private val isDzs: Boolean) : + Visitor() { + + private fun addMissingToken(range: TextRange, token: String) { + + val msg = L10N.localize("error_missing_token", token) + diagnoseHandler.addError(msg, range) + } + + private fun addMissingTokenAtLast(ctx: ParseTree, token: String) { + val ctxEnd = ctx.textRange.end + val missingRange = TextRange(ctxEnd.line, ctxEnd.column, ctxEnd.line, ctxEnd.column + 1) + addMissingToken(missingRange, "'$token'") + } + + + private fun addExpectingRuleAtLast(ctx: ParseTree, expecting: String) { + val ctxEnd = ctx.textRange.end + val missingRange = TextRange(ctxEnd.line, ctxEnd.column, ctxEnd.line, ctxEnd.column + 1) + val msg = L10N.localize("error_expecting_rule", expecting) + diagnoseHandler.addError(msg, missingRange) + } + + private fun addNotSupported(token: Token) { + val msg = L10N.localize("error_not_supported_token", token.text) + val range = token.textRange + diagnoseHandler.addError(msg, range) + } + + private fun addNotSupported(node: TerminalNode) { + val msg = L10N.localize("error_not_supported_token", node.text) + val range = node.textRange + diagnoseHandler.addError(msg, range) + } + + private fun addAdditionalToken(token: Token) { + val msg = L10N.localize("error_unexpected_token", "\"${token.text}\"") + val range = token.textRange + diagnoseHandler.addError(msg, range) + } + + + private fun addInvalidInput(ctx: ParserRuleContext, expected: String) { + val msg = L10N.localize("error_illegal_input_expecting", expected) + val range = ctx.textRange + diagnoseHandler.addError(msg, range) + } + + override fun visitClassBody(ctx: ZenScriptParser.ClassBodyContext): Boolean { + if (super.visitClassBody(ctx)) { + return true + } + if(ctx.additionalBracket != null) { + addAdditionalToken(ctx.additionalBracket) + } + return false + } + + override fun visitInvaildStatementInClassBody(ctx: ZenScriptParser.InvaildStatementInClassBodyContext): Boolean { + addInvalidInput(ctx, L10N.localize("error_detail_member_declaration")) + return false + } + override fun visitExpressionStatement(ctx: ZenScriptParser.ExpressionStatementContext): Boolean { + if (super.visitExpressionStatement(ctx)) { + return true + } + if (ctx.tailingSemi == null) { + addMissingTokenAtLast(ctx, ";") + } + return false + } + + override fun visitImportDeclaration(ctx: ZenScriptParser.ImportDeclarationContext): Boolean { + if (super.visitImportDeclaration(ctx)) { + return true + } + if (ctx.tailingSemi == null) { + addMissingTokenAtLast(ctx, ";") + } + return false + } + + override fun visitReturnStatement(ctx: ZenScriptParser.ReturnStatementContext): Boolean { + if (super.visitReturnStatement(ctx)) { + return true + } + if (ctx.tailingSemi == null) { + addMissingTokenAtLast(ctx, ";") + } + return false + } + + override fun visitIntersectionType(ctx: ZenScriptParser.IntersectionTypeContext): Boolean { + if (super.visitIntersectionType(ctx)) { + return true + } + if (isDzs) { + return false + } + for (andNode in ctx.AND()) { + addNotSupported(andNode) + } + return false + } + + override fun visitUnionType(ctx: ZenScriptParser.UnionTypeContext): Boolean { + if (super.visitUnionType(ctx)) { + return true + } + if (isDzs) { + return false + } + for (orNode in ctx.OR()) { + addNotSupported(orNode) + } + return false + } + + override fun visitFunctionDeclaration(ctx: ZenScriptParser.FunctionDeclarationContext): Boolean { + if (super.visitFunctionDeclaration(ctx)) { + return true + } + if (isDzs) { + return false + } + + if(ctx.simpleName() == null) { + addExpectingRuleAtLast(ctx.FUNCTION(), L10N.localize("error_detail_function_name")) + } + + if (ctx.prefix?.type == ZenScriptParser.GLOBAL) { + addNotSupported(ctx.prefix) + } + return false + + } + + override fun visitClassDeclaration(ctx: ZenScriptParser.ClassDeclarationContext): Boolean { + if (super.visitClassDeclaration(ctx)) { + return true + } + if (isDzs) { + return false + } + val extendsSpecifier = ctx.extendsSpecifier() + ?: return false + + addNotSupported(extendsSpecifier.EXTENDS()) + return false + } + + override fun visitVariableDeclaration(ctx: ZenScriptParser.VariableDeclarationContext): Boolean { + if(super.visitVariableDeclaration(ctx)) { + return true + } + if(ctx.simpleName() == null) { + addExpectingRuleAtLast(ctx, L10N.localize("error_detail_variable_name")) + } + if(ctx.tailingSemi == null) { + addMissingTokenAtLast(ctx, ";") + } + + return false + } + + // if there are any error node here, do not report more errors in this node + override fun visitErrorNode(node: ErrorNode?): Boolean { + return true + } + + override fun defaultResult(): Boolean { + return false + } + + +} \ No newline at end of file diff --git a/zenscript-code-model/src/main/kotlin/raylras/zen/model/symbol/impl/ClassSymbolImpl.kt b/zenscript-code-model/src/main/kotlin/raylras/zen/model/symbol/impl/ClassSymbolImpl.kt index 5e17d03b..4f0bb769 100644 --- a/zenscript-code-model/src/main/kotlin/raylras/zen/model/symbol/impl/ClassSymbolImpl.kt +++ b/zenscript-code-model/src/main/kotlin/raylras/zen/model/symbol/impl/ClassSymbolImpl.kt @@ -47,7 +47,7 @@ fun createClassSymbol( } override val interfaces: Sequence by lazy { - ctx.qualifiedName() + ctx.extendsSpecifier()?.qualifiedName() ?.mapNotNull { resolveType(it, unit) as? ClassType } ?.asSequence() .orEmpty() diff --git a/zenscript-code-model/src/main/kotlin/raylras/zen/model/symbol/impl/ParameterSymbolImpl.kt b/zenscript-code-model/src/main/kotlin/raylras/zen/model/symbol/impl/ParameterSymbolImpl.kt index 8716a7e8..b583b361 100644 --- a/zenscript-code-model/src/main/kotlin/raylras/zen/model/symbol/impl/ParameterSymbolImpl.kt +++ b/zenscript-code-model/src/main/kotlin/raylras/zen/model/symbol/impl/ParameterSymbolImpl.kt @@ -23,7 +23,7 @@ fun createParameterSymbol( callback(object : ParameterSymbol, TypeAnnotatable, ParseTreeLocatable { override val isOptional: Boolean by lazy { ctx.defaultValue() != null } - override val isVararg: Boolean by lazy { ctx.varargsPrefix() != null } + override val isVararg: Boolean by lazy { ctx.varargsPrefix != null } override val simpleName: String by lazy { simpleNameCtx.text } diff --git a/zenscript-language-server/src/main/kotlin/raylras/zen/lsp/ZenLanguageService.kt b/zenscript-language-server/src/main/kotlin/raylras/zen/lsp/ZenLanguageService.kt index 49b39d62..81582124 100644 --- a/zenscript-language-server/src/main/kotlin/raylras/zen/lsp/ZenLanguageService.kt +++ b/zenscript-language-server/src/main/kotlin/raylras/zen/lsp/ZenLanguageService.kt @@ -7,6 +7,7 @@ import org.eclipse.lsp4j.services.WorkspaceService import org.slf4j.LoggerFactory import raylras.zen.lsp.StandardIOLauncher.CLIENT import raylras.zen.lsp.provider.* +import raylras.zen.lsp.provider.data.publish import raylras.zen.model.* import raylras.zen.util.* import raylras.zen.util.l10n.L10N @@ -50,6 +51,7 @@ class ZenLanguageService : TextDocumentService, WorkspaceService { val unit = uri.toCompilationUnit()!! val source = params.contentChanges[0].text unit.load(source) + unit.diagnoseHandler.publish() }.let { logger.finish(::didChange, uri, duration = it) } @@ -256,11 +258,14 @@ class ZenLanguageService : TextDocumentService, WorkspaceService { getEnv(path)?.let { env -> when (event.type) { FileChangeType.Created -> { - createUnit(path, env).load() + val unit = createUnit(path, env) + unit.load() + unit.diagnoseHandler.publish() } FileChangeType.Changed -> { env.unitMap[path]!!.load() + env.unitMap[path]!!.diagnoseHandler.publish() } FileChangeType.Deleted -> env.unitMap.remove(path) @@ -287,7 +292,9 @@ class ZenLanguageService : TextDocumentService, WorkspaceService { private fun createEnv(documentPath: Path) { val compilationRoot = findUpwardsOrSelf(documentPath, DEFAULT_ROOT_DIRECTORY) val env = CompilationEnvironment(compilationRoot) - env.load() + env.load { + it.diagnoseHandler.publish() + } environments.add(env) checkDzs(env) } diff --git a/zenscript-language-server/src/main/kotlin/raylras/zen/lsp/provider/data/Diagonses.kt b/zenscript-language-server/src/main/kotlin/raylras/zen/lsp/provider/data/Diagonses.kt new file mode 100644 index 00000000..2c144155 --- /dev/null +++ b/zenscript-language-server/src/main/kotlin/raylras/zen/lsp/provider/data/Diagonses.kt @@ -0,0 +1,31 @@ +package raylras.zen.lsp.provider.data; + +import org.eclipse.lsp4j.Diagnostic +import org.eclipse.lsp4j.DiagnosticSeverity +import org.eclipse.lsp4j.PublishDiagnosticsParams +import raylras.zen.lsp.StandardIOLauncher +import raylras.zen.model.diagnose.DiagnoseEntity +import raylras.zen.model.diagnose.DiagnoseHandler +import raylras.zen.model.diagnose.DiagnoseLevel +import raylras.zen.util.toLspRange + + +fun DiagnoseEntity.toLSPDiagnostic(): Diagnostic { + + val level = when(this.level) { + DiagnoseLevel.Error -> DiagnosticSeverity.Error + DiagnoseLevel.Warning -> DiagnosticSeverity.Warning + DiagnoseLevel.Hint -> DiagnosticSeverity.Hint + } + + return Diagnostic(this.range.toLspRange(), this.msg, level, "zenscript") +} + +fun DiagnoseHandler.publish() { + val params = PublishDiagnosticsParams() + params.uri = this.unit.path.toUri().toString() + for (entity in this.entities) { + params.diagnostics.add(entity.toLSPDiagnostic()) + } + StandardIOLauncher.CLIENT?.publishDiagnostics(params) +} \ No newline at end of file diff --git a/zenscript-language-server/src/main/resources/l10n.properties b/zenscript-language-server/src/main/resources/l10n.properties index 64bbf953..69fea30a 100644 --- a/zenscript-language-server/src/main/resources/l10n.properties +++ b/zenscript-language-server/src/main/resources/l10n.properties @@ -4,3 +4,15 @@ completion_keyword=Keyword dzs_not_found=Cannot find .dzs file directory, some features are not available. Please install the Minecraft mod [ProbeZS](https://github.com/friendlyhj/ProbeZS) to generate. minecraft_instance_not_running=Failed to query {0}, make sure your Minecraft instance is running query_bracket_handler_failed=Failed to query {0}: {1} + +error_unexpected_token_expecting=Unexpected token: {0}, expecting: {1} +error_unexpected_token=Unexpected token {0} +error_illegal_input=Illegal input here +error_illegal_input_expecting=Illegal input, expecting {0} here +error_missing_token=Missing {0} +error_expecting_rule=Expecting {0} +error_not_supported_token={0} is not supported in zs + +error_detail_member_declaration=member declaration +error_detail_variable_name=variable name +error_detail_function_name=function name