mirror of
https://github.com/irmen/prog8.git
synced 2024-10-17 10:24:55 +00:00
* fix (hack) .name, .source and .position of Modules from the parser (via temp. subclass ParsedModule)
The temporary subclass ParsedModule : Module is introduced to concentrate all the workaround stuff in one place *while still not changing any public signature* such as of the Module ctor. The convention used to indicate stuff from resources is still "<res:...>" not "@embedded@"- *note that this is caught by 3 tests in compiler*
This commit is contained in:
parent
44da7a302f
commit
137a89da15
@ -339,7 +339,7 @@ class Program(val name: String,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Module(override val name: String,
|
open class Module(override val name: String,
|
||||||
override var statements: MutableList<Statement>,
|
override var statements: MutableList<Statement>,
|
||||||
override val position: Position,
|
override val position: Position,
|
||||||
val source: Path) : Node, INameScope {
|
val source: Path) : Node, INameScope {
|
||||||
|
@ -41,7 +41,7 @@ private fun ParserRuleContext.toPosition() : Position {
|
|||||||
return Position(filename, start.line, start.charPositionInLine, stop.charPositionInLine + stop.text.length)
|
return Position(filename, start.line, start.charPositionInLine, stop.charPositionInLine + stop.text.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Prog8ANTLRParser.BlockContext.toAst(isInLibrary: Boolean, encoding: IStringEncoding) : Statement {
|
internal fun Prog8ANTLRParser.BlockContext.toAst(isInLibrary: Boolean, encoding: IStringEncoding) : Block {
|
||||||
val blockstatements = block_statement().map {
|
val blockstatements = block_statement().map {
|
||||||
when {
|
when {
|
||||||
it.variabledeclaration()!=null -> it.variabledeclaration().toAst(encoding)
|
it.variabledeclaration()!=null -> it.variabledeclaration().toAst(encoding)
|
||||||
@ -351,7 +351,7 @@ private fun Prog8ANTLRParser.DatatypeContext.toAst() = DataType.valueOf(text.upp
|
|||||||
private fun Prog8ANTLRParser.ArrayindexContext.toAst(encoding: IStringEncoding) : ArrayIndex =
|
private fun Prog8ANTLRParser.ArrayindexContext.toAst(encoding: IStringEncoding) : ArrayIndex =
|
||||||
ArrayIndex(expression().toAst(encoding), toPosition())
|
ArrayIndex(expression().toAst(encoding), toPosition())
|
||||||
|
|
||||||
private fun Prog8ANTLRParser.DirectiveContext.toAst() : Directive =
|
internal fun Prog8ANTLRParser.DirectiveContext.toAst() : Directive =
|
||||||
Directive(directivename.text, directivearg().map { it.toAst() }, toPosition())
|
Directive(directivename.text, directivearg().map { it.toAst() }, toPosition())
|
||||||
|
|
||||||
private fun Prog8ANTLRParser.DirectiveargContext.toAst() : DirectiveArg {
|
private fun Prog8ANTLRParser.DirectiveargContext.toAst() : DirectiveArg {
|
||||||
|
@ -4,6 +4,8 @@ import org.antlr.v4.runtime.*
|
|||||||
import prog8.ast.Module
|
import prog8.ast.Module
|
||||||
import prog8.ast.antlr.toAst
|
import prog8.ast.antlr.toAst
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
|
import prog8.ast.statements.Block
|
||||||
|
import prog8.ast.statements.Directive
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
|
|
||||||
|
|
||||||
@ -16,19 +18,10 @@ class ParseError(override var message: String, val position: Position, cause: Ru
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun RecognitionException.getPosition(provenance: String) : Position {
|
|
||||||
val offending = this.offendingToken
|
|
||||||
val line = offending.line
|
|
||||||
val beginCol = offending.charPositionInLine
|
|
||||||
val endCol = beginCol + offending.stopIndex - offending.startIndex // TODO: point to col *after* token?
|
|
||||||
val pos = Position(provenance, line, beginCol, endCol)
|
|
||||||
return pos
|
|
||||||
}
|
|
||||||
|
|
||||||
object Prog8Parser {
|
object Prog8Parser {
|
||||||
|
|
||||||
fun parseModule(src: SourceCode): Module {
|
fun parseModule(src: SourceCode): Module {
|
||||||
val antlrErrorListener = AntlrErrorListener(src.origin)
|
val antlrErrorListener = AntlrErrorListener(src)
|
||||||
val lexer = Prog8ANTLRLexer(src.getCharStream())
|
val lexer = Prog8ANTLRLexer(src.getCharStream())
|
||||||
lexer.removeErrorListeners()
|
lexer.removeErrorListeners()
|
||||||
lexer.addErrorListener(antlrErrorListener)
|
lexer.addErrorListener(antlrErrorListener)
|
||||||
@ -40,24 +33,55 @@ object Prog8Parser {
|
|||||||
|
|
||||||
val parseTree = parser.module()
|
val parseTree = parser.module()
|
||||||
|
|
||||||
// FIXME: hacking together a name for the module:
|
val module = ParsedModule(src)
|
||||||
var moduleName = src.origin
|
|
||||||
if (moduleName.startsWith("<res:")) {
|
|
||||||
moduleName = Path(moduleName.substring(5, moduleName.length - 1))
|
|
||||||
.fileName.toString()
|
|
||||||
} else if (!moduleName.startsWith("<")) {
|
|
||||||
moduleName = Path(moduleName).fileName.toString()
|
|
||||||
}
|
|
||||||
moduleName = moduleName.substringBeforeLast('.')
|
|
||||||
|
|
||||||
val module = parseTree.toAst(moduleName, source = Path(""), PetsciiEncoding)
|
// .linkParents called in ParsedModule.add
|
||||||
|
parseTree.directive().forEach { module.add(it.toAst()) }
|
||||||
|
// TODO: remove Encoding
|
||||||
|
parseTree.block().forEach { module.add(it.toAst(module.isLibrary(), PetsciiEncoding)) }
|
||||||
|
|
||||||
// TODO: use Module ctor directly
|
|
||||||
|
|
||||||
for (statement in module.statements) {
|
|
||||||
statement.linkParents(module)
|
|
||||||
}
|
|
||||||
return module
|
return module
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: hacking together a path string:
|
||||||
|
private fun SourceCode.pathString() =
|
||||||
|
origin
|
||||||
|
.substringAfter("<res:")
|
||||||
|
.substringAfter("<")
|
||||||
|
.substringBeforeLast(">")
|
||||||
|
|
||||||
|
private class ParsedModule(src: SourceCode) : Module(
|
||||||
|
// FIXME: hacking together a name for the module:
|
||||||
|
name = src.pathString()
|
||||||
|
.substringBeforeLast(".")
|
||||||
|
.substringAfterLast("/")
|
||||||
|
.substringAfterLast("\\")
|
||||||
|
.replace("String@", "anonymous_"),
|
||||||
|
// FIXME: hacking together a path
|
||||||
|
source = Path(src.pathString()),
|
||||||
|
statements = mutableListOf(),
|
||||||
|
position = Position(src.origin, 1, 0, 0)
|
||||||
|
) {
|
||||||
|
val provenance = Pair(src, Triple(1, 0, 0))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a [Directive] to [statements] and
|
||||||
|
* sets this Module as its [parent].
|
||||||
|
* Note: you can only add [Directive]s or [Block]s to a Module.
|
||||||
|
*/
|
||||||
|
fun add(child: Directive) {
|
||||||
|
child.linkParents(this)
|
||||||
|
statements.add(child)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Adds a [Block] to [statements] and
|
||||||
|
* sets this Module as its [parent].
|
||||||
|
* Note: you can only add [Directive]s or [Block]s to a Module.
|
||||||
|
*/
|
||||||
|
fun add(child: Block) {
|
||||||
|
child.linkParents(this)
|
||||||
|
statements.add(child)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private object Prog8ErrorStrategy: BailErrorStrategy() {
|
private object Prog8ErrorStrategy: BailErrorStrategy() {
|
||||||
@ -86,15 +110,24 @@ object Prog8Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AntlrErrorListener(val sourceCodeProvenance: String): BaseErrorListener() {
|
private class AntlrErrorListener(val src: SourceCode): BaseErrorListener() {
|
||||||
override fun syntaxError(recognizer: Recognizer<*, *>?, offendingSymbol: Any?, line: Int, charPositionInLine: Int, msg: String, e: RecognitionException?) {
|
override fun syntaxError(recognizer: Recognizer<*, *>?, offendingSymbol: Any?, line: Int, charPositionInLine: Int, msg: String, e: RecognitionException?) {
|
||||||
if (e == null) {
|
if (e == null) {
|
||||||
TODO("no RecognitionException - create your own ParseError")
|
TODO("no RecognitionException - create your own ParseError")
|
||||||
//throw ParseError()
|
//throw ParseError()
|
||||||
} else {
|
} else {
|
||||||
throw ParseError(msg, e.getPosition(sourceCodeProvenance), e)
|
throw ParseError(msg, e.getPosition(src.origin), e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun RecognitionException.getPosition(file: String) : Position {
|
||||||
|
val offending = this.offendingToken
|
||||||
|
val line = offending.line
|
||||||
|
val beginCol = offending.charPositionInLine
|
||||||
|
val endCol = beginCol + offending.stopIndex - offending.startIndex // TODO: point to col *after* token?
|
||||||
|
val pos = Position(file, line, beginCol, endCol)
|
||||||
|
return pos
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,27 @@
|
|||||||
package prog8tests
|
package prog8tests
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.base.Position
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
import java.nio.file.Path // TODO: use kotlin.io.path.Path instead
|
import java.nio.file.Path // TODO: use kotlin.io.path.Path instead
|
||||||
import kotlin.io.path.*
|
import kotlin.io.path.*
|
||||||
import prog8.ast.statements.Block
|
import prog8.ast.statements.Block
|
||||||
|
import prog8.ast.statements.Directive
|
||||||
import prog8.parser.ParseError
|
import prog8.parser.ParseError
|
||||||
import prog8.parser.Prog8Parser.parseModule
|
import prog8.parser.Prog8Parser.parseModule
|
||||||
import prog8.parser.SourceCode
|
import prog8.parser.SourceCode
|
||||||
|
|
||||||
|
|
||||||
class TestProg8Parser {
|
class TestProg8Parser {
|
||||||
|
val workingDir = Path("").absolute() // Note: Path(".") does NOT work..!
|
||||||
|
val fixturesDir = workingDir.resolve("test/fixtures")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testDirectoriesSanityCheck() {
|
||||||
|
assertEquals("compilerAst", workingDir.fileName.toString())
|
||||||
|
assertTrue(fixturesDir.isDirectory(), "sanity check; should be directory: $fixturesDir")
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testModuleSourceNeedNotEndWithNewline() {
|
fun testModuleSourceNeedNotEndWithNewline() {
|
||||||
@ -165,6 +176,51 @@ class TestProg8Parser {
|
|||||||
assertEquals(1, module.statements.size)
|
assertEquals(1, module.statements.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testModuleNameForSourceFromString() {
|
||||||
|
val srcText = """
|
||||||
|
main {
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
val module = parseModule(SourceCode.of(srcText))
|
||||||
|
|
||||||
|
// Note: assertContains has *actual* as first param
|
||||||
|
assertContains(module.name, Regex("^anonymous_[0-9a-f]+$"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testModuleNameForSourceFromPath() {
|
||||||
|
val path = fixturesDir.resolve("simple_main.p8")
|
||||||
|
|
||||||
|
val module = parseModule(SourceCode.fromPath(path))
|
||||||
|
|
||||||
|
assertEquals(path.nameWithoutExtension, module.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun assertPosition(actual: Position, expFile: String, expLine: Int, expStartCol: Int, expEndCol: Int) {
|
||||||
|
assertEquals(expLine, actual.line, ".position.line (1-based)")
|
||||||
|
assertEquals(expStartCol, actual.startCol, ".position.startCol (0-based)" )
|
||||||
|
assertEquals(expEndCol, actual.endCol, ".position.endCol (0-based)")
|
||||||
|
assertEquals(expFile, actual.file, ".position.file")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun assertPosition(actual: Position, expFile: Regex, expLine: Int, expStartCol: Int, expEndCol: Int) {
|
||||||
|
assertEquals(expLine, actual.line, ".position.line (1-based)")
|
||||||
|
assertEquals(expStartCol, actual.startCol, ".position.startCol (0-based)" )
|
||||||
|
assertEquals(expEndCol, actual.endCol, ".position.endCol (0-based)")
|
||||||
|
// Note: assertContains expects *actual* value first
|
||||||
|
assertContains(actual.file, expFile, ".position.file")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun assertPositionOf(actual: Node, expFile: String, expLine: Int, expStartCol: Int, expEndCol: Int) =
|
||||||
|
assertPosition(actual.position, expFile, expLine, expStartCol, expEndCol)
|
||||||
|
|
||||||
|
fun assertPositionOf(actual: Node, expFile: Regex, expLine: Int, expStartCol: Int, expEndCol: Int) =
|
||||||
|
assertPosition(actual.position, expFile, expLine, expStartCol, expEndCol)
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testErrorLocationForSourceFromString() {
|
fun testErrorLocationForSourceFromString() {
|
||||||
val srcText = "bad * { }\n"
|
val srcText = "bad * { }\n"
|
||||||
@ -173,30 +229,41 @@ class TestProg8Parser {
|
|||||||
try {
|
try {
|
||||||
parseModule(SourceCode.of(srcText))
|
parseModule(SourceCode.of(srcText))
|
||||||
} catch (e: ParseError) {
|
} catch (e: ParseError) {
|
||||||
// Note: assertContains expects *actual* value first
|
assertPosition(e.position, Regex("^<String@[0-9a-f]+>$"), 1, 4, 4)
|
||||||
assertContains(e.position.file, Regex("^<String@[0-9a-f]+>$"))
|
|
||||||
assertEquals(1, e.position.line, "line; should be 1-based")
|
|
||||||
assertEquals(4, e.position.startCol, "startCol; should be 0-based" )
|
|
||||||
assertEquals(4, e.position.endCol, "endCol; should be 0-based")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testErrorLocationForSourceFromPath() {
|
fun testErrorLocationForSourceFromPath() {
|
||||||
val filename = "file_with_syntax_error.p8"
|
val path = fixturesDir.resolve("file_with_syntax_error.p8")
|
||||||
val path = Path.of("test", "fixtures", filename)
|
|
||||||
|
|
||||||
assertFailsWith<ParseError> { parseModule(SourceCode.fromPath(path)) }
|
assertFailsWith<ParseError> { parseModule(SourceCode.fromPath(path)) }
|
||||||
try {
|
try {
|
||||||
parseModule(SourceCode.fromPath(path))
|
parseModule(SourceCode.fromPath(path))
|
||||||
} catch (e: ParseError) {
|
} catch (e: ParseError) {
|
||||||
assertEquals(path.absolutePathString(), e.position.file, "provenance; should be the path's filename, incl. extension '.p8'")
|
assertPosition(e.position, path.absolutePathString(), 2, 6, 6)
|
||||||
assertEquals(2, e.position.line, "line; should be 1-based")
|
|
||||||
assertEquals(6, e.position.startCol, "startCol; should be 0-based" )
|
|
||||||
assertEquals(6, e.position.endCol, "endCol; should be 0-based")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testModulePositionForSourceFromString() {
|
||||||
|
val srcText = """
|
||||||
|
main {
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
val module = parseModule(SourceCode.of(srcText))
|
||||||
|
assertPositionOf(module, Regex("^<String@[0-9a-f]+>$"), 1, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testModulePositionForSourceFromPath() {
|
||||||
|
val path = fixturesDir.resolve("simple_main.p8")
|
||||||
|
|
||||||
|
val module = parseModule(SourceCode.fromPath(path))
|
||||||
|
assertPositionOf(module, path.absolutePathString(), 1, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testProg8Ast() {
|
fun testProg8Ast() {
|
||||||
val module = parseModule(SourceCode.of("""
|
val module = parseModule(SourceCode.of("""
|
||||||
|
Loading…
Reference in New Issue
Block a user