mirror of
https://github.com/irmen/prog8.git
synced 2024-12-24 16:29:21 +00:00
+ intro SourceCode, tying together source code text with its *origin*; Prog8Parser now only accepts this
This commit is contained in:
parent
7b89228fa7
commit
af209ad50e
@ -8,12 +8,12 @@ import prog8.ast.base.Position
|
||||
import prog8.ast.base.SyntaxError
|
||||
import prog8.ast.statements.Directive
|
||||
import prog8.ast.statements.DirectiveArg
|
||||
import java.io.InputStream
|
||||
import kotlin.io.FileSystemException
|
||||
import java.net.URL
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
import java.nio.file.Path // TODO: use kotlin.io.paths.Path instead
|
||||
import java.nio.file.Paths // TODO: use kotlin.io.paths.Path instead
|
||||
|
||||
|
||||
fun moduleName(fileName: Path) = fileName.toString().substringBeforeLast('.')
|
||||
@ -38,7 +38,7 @@ class ModuleImporter(private val program: Program,
|
||||
else
|
||||
println("")
|
||||
|
||||
val module = Prog8Parser.parseModule(filePath)
|
||||
val module = Prog8Parser.parseModule(SourceCode.fromPath(filePath))
|
||||
|
||||
module.program = program
|
||||
module.linkParents(program.namespace)
|
||||
@ -65,7 +65,7 @@ class ModuleImporter(private val program: Program,
|
||||
private fun importModule(stream: CharStream, modulePath: Path): Module {
|
||||
val parser = Prog8Parser
|
||||
val sourceText = stream.toString()
|
||||
val moduleAst = parser.parseModule(sourceText)
|
||||
val moduleAst = parser.parseModule(SourceCode.of(sourceText))
|
||||
moduleAst.program = program
|
||||
moduleAst.linkParents(program.namespace)
|
||||
program.modules.add(moduleAst)
|
||||
@ -92,16 +92,13 @@ class ModuleImporter(private val program: Program,
|
||||
if(existing!=null)
|
||||
return null
|
||||
|
||||
val rsc = tryGetModuleFromResource("$moduleName.p8", compilationTargetName)
|
||||
val srcCode = tryGetModuleFromResource("$moduleName.p8", compilationTargetName)
|
||||
val importedModule =
|
||||
if(rsc!=null) {
|
||||
if (srcCode != null) { // found in resources
|
||||
// load the module from the embedded resource
|
||||
val (resource, resourcePath) = rsc
|
||||
resource.use {
|
||||
println("importing '$moduleName' (library)")
|
||||
val content = it.reader().readText().replace("\r\n", "\n")
|
||||
importModule(CharStreams.fromString(content), Module.pathForResource(resourcePath))
|
||||
}
|
||||
println("importing '$moduleName' (library): ${srcCode.origin}")
|
||||
val path = Path.of(URL(srcCode.origin).file)
|
||||
importModule(srcCode.getCharStream(), path)
|
||||
} else {
|
||||
val modulePath = tryGetModuleFromFile(moduleName, source, import.position)
|
||||
importModule(modulePath)
|
||||
@ -120,17 +117,16 @@ class ModuleImporter(private val program: Program,
|
||||
importedModule.statements.addAll(0, directives)
|
||||
}
|
||||
|
||||
private fun tryGetModuleFromResource(name: String, compilationTargetName: String): Pair<InputStream, String>? {
|
||||
val targetSpecificPath = "/prog8lib/$compilationTargetName/$name"
|
||||
val targetSpecificResource = object{}.javaClass.getResourceAsStream(targetSpecificPath)
|
||||
if(targetSpecificResource!=null)
|
||||
return Pair(targetSpecificResource, targetSpecificPath)
|
||||
|
||||
val generalPath = "/prog8lib/$name"
|
||||
val generalResource = object{}.javaClass.getResourceAsStream(generalPath)
|
||||
if(generalResource!=null)
|
||||
return Pair(generalResource, generalPath)
|
||||
|
||||
private fun tryGetModuleFromResource(name: String, compilationTargetName: String): SourceCode? {
|
||||
// try target speficic first
|
||||
try {
|
||||
return SourceCode.fromResources("/prog8lib/$compilationTargetName/$name")
|
||||
} catch (e: FileSystemException) {
|
||||
}
|
||||
try {
|
||||
return SourceCode.fromResources("/prog8lib/$name")
|
||||
} catch (e: FileSystemException) {
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
|
@ -27,17 +27,9 @@ private fun RecognitionException.getPosition(provenance: String) : Position {
|
||||
|
||||
object Prog8Parser {
|
||||
|
||||
fun parseModule(srcPath: Path): Module {
|
||||
return parseModule(CharStreams.fromPath(srcPath), srcPath.fileName.toString())
|
||||
}
|
||||
|
||||
fun parseModule(srcText: String): Module {
|
||||
return parseModule(CharStreams.fromString(srcText), "<String@${System.identityHashCode(srcText).toString(16)}>")
|
||||
}
|
||||
|
||||
private fun parseModule(chars: CharStream, provenance: String): Module {
|
||||
val antlrErrorListener = AntlrErrorListener(provenance)
|
||||
val lexer = Prog8ANTLRLexer(chars)
|
||||
fun parseModule(src: SourceCode): Module {
|
||||
val antlrErrorListener = AntlrErrorListener(src.origin)
|
||||
val lexer = Prog8ANTLRLexer(src.getCharStream())
|
||||
lexer.removeErrorListeners()
|
||||
lexer.addErrorListener(antlrErrorListener)
|
||||
val tokens = CommonTokenStream(lexer)
|
||||
|
118
compilerAst/src/prog8/parser/SourceCode.kt
Normal file
118
compilerAst/src/prog8/parser/SourceCode.kt
Normal file
@ -0,0 +1,118 @@
|
||||
package prog8.parser
|
||||
|
||||
import org.antlr.v4.runtime.CharStream
|
||||
import org.antlr.v4.runtime.CharStreams
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
|
||||
/**
|
||||
* Encapsulates - and ties together - actual source code (=text)
|
||||
* and its [origin].
|
||||
*/
|
||||
abstract class SourceCode() {
|
||||
/**
|
||||
* Where this [SourceCode] instance came from.
|
||||
* This can be one of the following:
|
||||
* * a normal string representation of a [java.nio.file.Path], if it originates from a file (see [fromPath])
|
||||
* * `<String@44c56085>` if was created via [of]
|
||||
* * `<res:/x/y/z.ext>` if it came from resources (see [fromResources])
|
||||
*/
|
||||
abstract val origin: String
|
||||
abstract fun getCharStream(): CharStream
|
||||
|
||||
/**
|
||||
* The source code as plain string.
|
||||
* *Note: this is meant for testing and debugging, do NOT use in application code!*
|
||||
*/
|
||||
fun asString() = this.getCharStream().toString()
|
||||
|
||||
/**
|
||||
* Deliberately does NOT return the actual text.
|
||||
* Use [getCharStream].
|
||||
*/
|
||||
final override fun toString() = super.toString()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// "static" factory methods
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Turn a plain String into a [SourceCode] object.
|
||||
* [origin] will be something like `<String@44c56085>`.
|
||||
*/
|
||||
fun of(text: String): SourceCode {
|
||||
return object : SourceCode() {
|
||||
override val origin = "<String@${System.identityHashCode(text).toString(16)}>"
|
||||
override fun getCharStream(): CharStream {
|
||||
return CharStreams.fromString(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get [SourceCode] from the file represented by the specified Path.
|
||||
* This does not actually *access* the file, but it does check
|
||||
* whether it
|
||||
* * exists
|
||||
* * is a regular file (ie: not a directory)
|
||||
* * and is actually readable
|
||||
*
|
||||
* [origin] will be the given path in absolute and normalized form.
|
||||
* @throws NoSuchFileException if the file does not exist
|
||||
* @throws AccessDeniedException if the given path points to a directory or the file is non-readable for some other reason
|
||||
*/
|
||||
fun fromPath(path: Path): SourceCode {
|
||||
if (!path.exists())
|
||||
throw NoSuchFileException(path.toFile())
|
||||
if (path.isDirectory())
|
||||
throw AccessDeniedException(path.toFile(), reason = "Not a file but a directory")
|
||||
if (!path.isReadable())
|
||||
throw AccessDeniedException(path.toFile(), reason = "Is not readable")
|
||||
val normalized = path.normalize()
|
||||
return object : SourceCode() {
|
||||
override val origin = normalized.absolutePathString()
|
||||
override fun getCharStream(): CharStream {
|
||||
return CharStreams.fromPath(normalized)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [origin]: `<res:/x/y/z.p8>` for a given `pathString` of "x/y/z.p8"
|
||||
*/
|
||||
fun fromResources(pathString: String): SourceCode {
|
||||
val path = Path.of(pathString).normalize()
|
||||
val sep = "/"
|
||||
val normalized = sep + path.toMutableList().joinToString(sep)
|
||||
val rscURL = object{}.javaClass.getResource(normalized)
|
||||
if (rscURL == null) {
|
||||
val rscRoot = object{}.javaClass.getResource("/")
|
||||
throw NoSuchFileException(
|
||||
File(normalized),
|
||||
reason = "looked in resources rooted at $rscRoot")
|
||||
}
|
||||
return object : SourceCode() {
|
||||
override val origin = "<res:$normalized>"
|
||||
override fun getCharStream(): CharStream {
|
||||
val inpStr = object{}.javaClass.getResourceAsStream(normalized)
|
||||
val chars = CharStreams.fromStream(inpStr)
|
||||
return chars
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: possibly more, like fromURL(..)
|
||||
/* // For `jar:..` URLs
|
||||
// see https://stackoverflow.com/questions/22605666/java-access-files-in-jar-causes-java-nio-file-filesystemnotfoundexception
|
||||
var url = URL("jar:file:/E:/x16/prog8(meisl)/compiler/build/libs/prog8compiler-7.0-BETA3-all.jar!/prog8lib/c64/textio.p8")
|
||||
val uri = url.toURI()
|
||||
val parts = uri.toString().split("!")
|
||||
val fs = FileSystems.newFileSystem(URI.create(parts[0]), mutableMapOf(Pair("", "")) )
|
||||
val path = fs.getPath(parts[1])
|
||||
*/
|
||||
}
|
||||
}
|
@ -1,5 +1,11 @@
|
||||
package prog8tests
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import kotlin.test.*
|
||||
import java.nio.file.Path // TODO: use kotlin.io.path.Path instead
|
||||
import kotlin.io.path.*
|
||||
|
||||
import prog8.ast.IBuiltinFunctions
|
||||
import prog8.ast.IMemSizer
|
||||
import prog8.ast.IStringEncoding
|
||||
@ -11,11 +17,6 @@ import prog8.ast.expressions.InferredTypes
|
||||
import prog8.ast.expressions.NumericLiteralValue
|
||||
import prog8.parser.ModuleImporter
|
||||
import prog8.parser.ParseError
|
||||
import java.nio.file.Path
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import kotlin.io.path.*
|
||||
import kotlin.test.*
|
||||
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
@ -51,7 +52,7 @@ class TestModuleImporter {
|
||||
val srcPath = Path.of("test", "fixtures", "i_do_not_exist")
|
||||
|
||||
assertFalse(srcPath.exists(), "sanity check: file should not exist")
|
||||
assertFailsWith<java.nio.file.NoSuchFileException> { importer.importModule(srcPath) }
|
||||
assertFailsWith<NoSuchFileException> { importer.importModule(srcPath) }
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -66,18 +67,7 @@ class TestModuleImporter {
|
||||
// fn importModule(Path) used to check *.isReadable()*, but NOT .isRegularFile():
|
||||
assertTrue(srcPath.isReadable(), "sanity check: should still be readable")
|
||||
|
||||
assertFailsWith<java.nio.file.AccessDeniedException> { importer.importModule(srcPath) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testImportLibraryModuleWithNonExistingPath() {
|
||||
val program = Program("foo", mutableListOf(), DummyFunctions, DummyMemsizer)
|
||||
val importer = ModuleImporter(program, DummyEncoding, "blah", listOf("./test/fixtures"))
|
||||
|
||||
val srcPath = Path.of("i_do_not_exist.p8")
|
||||
|
||||
assertFalse(srcPath.exists(), "sanity check: file should not exist")
|
||||
assertFailsWith<java.nio.file.NoSuchFileException> { importer.importLibraryModule(srcPath.nameWithoutExtension) }
|
||||
assertFailsWith<AccessDeniedException> { importer.importModule(srcPath) }
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -86,13 +76,14 @@ class TestModuleImporter {
|
||||
val importer = ModuleImporter(program, DummyEncoding, "blah", listOf("./test/fixtures"))
|
||||
|
||||
val filename = "file_with_syntax_error.p8"
|
||||
val act = { importer.importModule(Path.of("test", "fixtures", filename )) }
|
||||
val path = Path.of("test", "fixtures", filename)
|
||||
val act = { importer.importModule(path) }
|
||||
|
||||
assertFailsWith<ParseError> { act() }
|
||||
try {
|
||||
act()
|
||||
} catch (e: ParseError) {
|
||||
assertEquals(filename, e.position.file, "provenance; should be the path's filename, incl. extension '.p8'")
|
||||
assertEquals(path.absolutePathString(), e.position.file)
|
||||
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")
|
||||
@ -114,13 +105,28 @@ class TestModuleImporter {
|
||||
try {
|
||||
act()
|
||||
} catch (e: ParseError) {
|
||||
assertEquals(imported.fileName.toString(), e.position.file, "provenance; should be the importED file's filename, incl. extension '.p8'")
|
||||
val expectedProvenance = imported.absolutePathString()
|
||||
assertEquals(expectedProvenance, e.position.file)
|
||||
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 testImportLibraryModuleWithNonExistingName() {
|
||||
val program = Program("foo", mutableListOf(), DummyFunctions, DummyMemsizer)
|
||||
val importer = ModuleImporter(program, DummyEncoding, "blah", listOf("./test/fixtures"))
|
||||
val filenameNoExt = "i_do_not_exist"
|
||||
val filenameWithExt = filenameNoExt + ".p8"
|
||||
val srcPathNoExt = Path.of("test", "fixtures", filenameNoExt)
|
||||
val srcPathWithExt = Path.of("test", "fixtures", filenameWithExt)
|
||||
|
||||
assertFalse(srcPathNoExt.exists(), "sanity check: file should not exist")
|
||||
assertFalse(srcPathWithExt.exists(), "sanity check: file should not exist")
|
||||
assertFailsWith<NoSuchFileException> { importer.importLibraryModule(filenameNoExt) }
|
||||
assertFailsWith<NoSuchFileException> { importer.importLibraryModule(filenameWithExt) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testImportLibraryModuleWithSyntaxError() {
|
||||
@ -128,17 +134,16 @@ class TestModuleImporter {
|
||||
val importer = ModuleImporter(program, DummyEncoding, "blah", listOf("./test/fixtures"))
|
||||
|
||||
val filename = "file_with_syntax_error"
|
||||
|
||||
val act = { importer.importLibraryModule(filename) }
|
||||
|
||||
assertFailsWith<ParseError> { act() }
|
||||
try {
|
||||
act()
|
||||
} catch (e: ParseError) {
|
||||
assertEquals(
|
||||
filename + ".p8",
|
||||
e.position.file,
|
||||
"provenance; should be the path's filename, incl. extension '.p8'"
|
||||
)
|
||||
val expectedProvenance = Path.of("test", "fixtures", filename + ".p8")
|
||||
.absolutePathString()
|
||||
assertEquals(expectedProvenance, e.position.file)
|
||||
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")
|
||||
@ -148,7 +153,8 @@ class TestModuleImporter {
|
||||
@Test
|
||||
fun testImportLibraryModuleWithImportingBadModule() {
|
||||
val program = Program("foo", mutableListOf(), DummyFunctions, DummyMemsizer)
|
||||
val importer = ModuleImporter(program, DummyEncoding, "blah", listOf("./test/fixtures"))
|
||||
val libdirs = listOf("./test/fixtures")
|
||||
val importer = ModuleImporter(program, DummyEncoding, "blah", libdirs)
|
||||
|
||||
val importing = "import_file_with_syntax_error"
|
||||
val imported = "file_with_syntax_error"
|
||||
@ -158,11 +164,10 @@ class TestModuleImporter {
|
||||
try {
|
||||
act()
|
||||
} catch (e: ParseError) {
|
||||
assertEquals(
|
||||
imported + ".p8",
|
||||
e.position.file,
|
||||
"provenance; should be the importED file's name, incl. extension '.p8'"
|
||||
)
|
||||
val expectedProvenance = Path.of(libdirs[0], imported + ".p8")
|
||||
.normalize()
|
||||
.absolutePathString()
|
||||
assertEquals(expectedProvenance, e.position.file)
|
||||
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")
|
||||
|
@ -1,26 +1,24 @@
|
||||
package prog8tests
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.test.*
|
||||
import java.nio.file.Path // TODO: use kotlin.io.path.Path instead
|
||||
import kotlin.io.path.*
|
||||
import prog8.ast.statements.Block
|
||||
import prog8.parser.ParseError
|
||||
import prog8.parser.Prog8Parser
|
||||
import prog8.parser.Prog8Parser.parseModule
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.io.path.isDirectory
|
||||
import kotlin.io.path.isReadable
|
||||
import kotlin.io.path.isRegularFile
|
||||
import kotlin.test.*
|
||||
import prog8.parser.SourceCode
|
||||
|
||||
|
||||
class TestProg8Parser {
|
||||
|
||||
@Test
|
||||
fun testModuleSourceNeedNotEndWithNewline() {
|
||||
val nl = "\n" // say, Unix-style (different flavours tested elsewhere)
|
||||
val srcText = "foo {" + nl + "}" // source ends with '}' (= NO newline, issue #40)
|
||||
val src = SourceCode.of("foo {" + nl + "}") // source ends with '}' (= NO newline, issue #40)
|
||||
|
||||
// #45: Prog8ANTLRParser would report (throw) "missing <EOL> at '<EOF>'"
|
||||
val module = parseModule(srcText)
|
||||
val module = parseModule(src)
|
||||
assertEquals(1, module.statements.size)
|
||||
}
|
||||
|
||||
@ -28,7 +26,7 @@ class TestProg8Parser {
|
||||
fun testModuleSourceMayEndWithNewline() {
|
||||
val nl = "\n" // say, Unix-style (different flavours tested elsewhere)
|
||||
val srcText = "foo {" + nl + "}" + nl // source does end with a newline (issue #40)
|
||||
val module = parseModule(srcText)
|
||||
val module = parseModule(SourceCode.of(srcText))
|
||||
assertEquals(1, module.statements.size)
|
||||
}
|
||||
|
||||
@ -42,8 +40,8 @@ class TestProg8Parser {
|
||||
// GOOD: 2nd block `bar` does start on a new line; however, a nl at the very end ain't needed
|
||||
val srcGood = "foo {" + nl + "}" + nl + "bar {" + nl + "}"
|
||||
|
||||
assertFailsWith<ParseError> { parseModule(srcBad) }
|
||||
val module = parseModule(srcGood)
|
||||
assertFailsWith<ParseError> { parseModule(SourceCode.of(srcBad)) }
|
||||
val module = parseModule(SourceCode.of(srcGood))
|
||||
assertEquals(2, module.statements.size)
|
||||
}
|
||||
|
||||
@ -71,7 +69,7 @@ class TestProg8Parser {
|
||||
"}" +
|
||||
nlUnix // end with newline (see testModuleSourceNeedNotEndWithNewline)
|
||||
|
||||
val module = parseModule(srcText)
|
||||
val module = parseModule(SourceCode.of(srcText))
|
||||
assertEquals(2, module.statements.size)
|
||||
}
|
||||
|
||||
@ -86,7 +84,7 @@ class TestProg8Parser {
|
||||
blockA {
|
||||
}
|
||||
"""
|
||||
val module = parseModule(srcText)
|
||||
val module = parseModule(SourceCode.of(srcText))
|
||||
assertEquals(1, module.statements.size)
|
||||
}
|
||||
|
||||
@ -103,7 +101,7 @@ class TestProg8Parser {
|
||||
blockB {
|
||||
}
|
||||
"""
|
||||
val module = parseModule(srcText)
|
||||
val module = parseModule(SourceCode.of(srcText))
|
||||
assertEquals(2, module.statements.size)
|
||||
}
|
||||
|
||||
@ -118,7 +116,7 @@ class TestProg8Parser {
|
||||
; comment
|
||||
|
||||
"""
|
||||
val module = parseModule(srcText)
|
||||
val module = parseModule(SourceCode.of(srcText))
|
||||
assertEquals(1, module.statements.size)
|
||||
}
|
||||
|
||||
@ -127,66 +125,43 @@ class TestProg8Parser {
|
||||
// issue: #47
|
||||
|
||||
// block and block
|
||||
assertFailsWith<ParseError>{ parseModule("""
|
||||
assertFailsWith<ParseError>{ parseModule(SourceCode.of("""
|
||||
blockA {
|
||||
} blockB {
|
||||
}
|
||||
""") }
|
||||
""")) }
|
||||
|
||||
// block and directive
|
||||
assertFailsWith<ParseError>{ parseModule("""
|
||||
assertFailsWith<ParseError>{ parseModule(SourceCode.of("""
|
||||
blockB {
|
||||
} %import textio
|
||||
""") }
|
||||
""")) }
|
||||
|
||||
// The following two are bogus due to directive *args* expected to follow the directive name.
|
||||
// Leaving them in anyways.
|
||||
|
||||
// dir and block
|
||||
assertFailsWith<ParseError>{ parseModule("""
|
||||
assertFailsWith<ParseError>{ parseModule(SourceCode.of("""
|
||||
%import textio blockB {
|
||||
}
|
||||
""") }
|
||||
""")) }
|
||||
|
||||
assertFailsWith<ParseError>{ parseModule("""
|
||||
assertFailsWith<ParseError>{ parseModule(SourceCode.of("""
|
||||
%import textio %import syslib
|
||||
""") }
|
||||
""")) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testParseModuleWithDirectoryPath() {
|
||||
val srcPath = Path.of("test", "fixtures")
|
||||
assertTrue(srcPath.isDirectory(), "sanity check: should be a directory")
|
||||
assertFailsWith<java.nio.file.AccessDeniedException> { Prog8Parser.parseModule(srcPath) }
|
||||
}
|
||||
fun parseModuleShouldNotLookAtImports() {
|
||||
val imported = "i_do_not_exist"
|
||||
val pathNoExt = Path.of(imported).absolute()
|
||||
val pathWithExt = Path.of("${pathNoExt}.p8")
|
||||
val text = "%import $imported"
|
||||
|
||||
@Test
|
||||
fun testParseModuleWithNonExistingPath() {
|
||||
val srcPath = Path.of("test", "fixtures", "i_do_not_exist")
|
||||
assertFalse(srcPath.exists(), "sanity check: file should not exist")
|
||||
assertFailsWith<java.nio.file.NoSuchFileException> { Prog8Parser.parseModule(srcPath) }
|
||||
}
|
||||
assertFalse(pathNoExt.exists(), "sanity check: file should not exist: $pathNoExt")
|
||||
assertFalse(pathWithExt.exists(), "sanity check: file should not exist: $pathWithExt")
|
||||
|
||||
@Test
|
||||
fun testParseModuleWithPathMissingExtension_p8() {
|
||||
val srcPathWithoutExt = Path.of("test", "fixtures", "file_with_syntax_error")
|
||||
val srcPathWithExt = Path.of(srcPathWithoutExt.toString() + ".p8")
|
||||
assertTrue(srcPathWithExt.isRegularFile(), "sanity check: should be normal file")
|
||||
assertTrue(srcPathWithExt.isReadable(), "sanity check: should be readable")
|
||||
assertFailsWith<java.nio.file.NoSuchFileException> { Prog8Parser.parseModule(srcPathWithoutExt) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testParseModuleWithStringShouldNotLookAtImports() {
|
||||
val srcText = "%import i_do_not_exist"
|
||||
val module = Prog8Parser.parseModule(srcText)
|
||||
assertEquals(1, module.statements.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testParseModuleWithPathShouldNotLookAtImports() {
|
||||
val srcPath = Path.of("test", "fixtures", "import_nonexisting.p8")
|
||||
val module = Prog8Parser.parseModule(srcPath)
|
||||
val module = parseModule(SourceCode.of(text))
|
||||
assertEquals(1, module.statements.size)
|
||||
}
|
||||
|
||||
@ -194,9 +169,9 @@ class TestProg8Parser {
|
||||
fun testErrorLocationForSourceFromString() {
|
||||
val srcText = "bad * { }\n"
|
||||
|
||||
assertFailsWith<ParseError> { parseModule(srcText) }
|
||||
assertFailsWith<ParseError> { parseModule(SourceCode.of(srcText)) }
|
||||
try {
|
||||
parseModule(srcText)
|
||||
parseModule(SourceCode.of(srcText))
|
||||
} catch (e: ParseError) {
|
||||
// Note: assertContains expects *actual* value first
|
||||
assertContains(e.position.file, Regex("^<String@[0-9a-f]+>$"))
|
||||
@ -211,11 +186,11 @@ class TestProg8Parser {
|
||||
val filename = "file_with_syntax_error.p8"
|
||||
val path = Path.of("test", "fixtures", filename)
|
||||
|
||||
assertFailsWith<ParseError> { parseModule(path) }
|
||||
assertFailsWith<ParseError> { parseModule(SourceCode.fromPath(path)) }
|
||||
try {
|
||||
parseModule(path)
|
||||
parseModule(SourceCode.fromPath(path))
|
||||
} catch (e: ParseError) {
|
||||
assertEquals(filename, e.position.file, "provenance; should be the path's filename, incl. extension '.p8'")
|
||||
assertEquals(path.absolutePathString(), e.position.file, "provenance; should be the path's filename, incl. extension '.p8'")
|
||||
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")
|
||||
@ -224,13 +199,13 @@ class TestProg8Parser {
|
||||
|
||||
@Test
|
||||
fun testProg8Ast() {
|
||||
val module = parseModule("""
|
||||
main {
|
||||
sub start() {
|
||||
return
|
||||
}
|
||||
}
|
||||
""")
|
||||
val module = parseModule(SourceCode.of("""
|
||||
main {
|
||||
sub start() {
|
||||
return
|
||||
}
|
||||
}
|
||||
"""))
|
||||
assertIs<Block>(module.statements.first())
|
||||
assertEquals((module.statements.first() as Block).name, "main")
|
||||
}
|
||||
|
82
compilerAst/test/TestSourceCode.kt
Normal file
82
compilerAst/test/TestSourceCode.kt
Normal file
@ -0,0 +1,82 @@
|
||||
package prog8tests
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import kotlin.test.*
|
||||
import java.nio.file.Path // TODO: use kotlin.io.path.Path instead
|
||||
import kotlin.io.path.*
|
||||
|
||||
import prog8.parser.SourceCode
|
||||
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class TestSourceCode {
|
||||
|
||||
@Test
|
||||
fun testFactoryMethod_Of() {
|
||||
val text = """
|
||||
main { }
|
||||
""".trimIndent()
|
||||
val src = SourceCode.of(text)
|
||||
val actualText = src.getCharStream().toString()
|
||||
|
||||
assertContains(src.origin, Regex("^<String@[0-9a-f]+>$"))
|
||||
assertEquals(text, actualText)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFromPathWithNonExistingPath() {
|
||||
val filename = "i_do_not_exist.p8"
|
||||
val path = Path.of("test", "fixtures", filename)
|
||||
|
||||
assertFalse(path.exists(), "sanity check: file should not exist: ${path.absolute()}")
|
||||
assertFailsWith<NoSuchFileException> { SourceCode.fromPath(path) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFromPathWithMissingExtension_p8() {
|
||||
val pathWithoutExt = Path.of("test", "fixtures", "simple_main")
|
||||
val pathWithExt = Path.of(pathWithoutExt.toString() + ".p8")
|
||||
|
||||
assertTrue(pathWithExt.isRegularFile(), "sanity check: should be normal file: ${pathWithExt.absolute()}")
|
||||
assertTrue(pathWithExt.isReadable(), "sanity check: should be readable: ${pathWithExt.absolute()}")
|
||||
assertFailsWith<NoSuchFileException> { SourceCode.fromPath(pathWithoutExt) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFromPathWithDirectory() {
|
||||
val path = Path.of("test", "fixtures")
|
||||
|
||||
assertTrue(path.isDirectory(), "sanity check: should be a directory")
|
||||
assertFailsWith<AccessDeniedException> { SourceCode.fromPath(path) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFromPathWithExistingPath() {
|
||||
val filename = "simple_main.p8"
|
||||
val path = Path.of("test", "fixtures", filename)
|
||||
val src = SourceCode.fromPath(path)
|
||||
|
||||
val expectedOrigin = path.normalize().absolutePathString()
|
||||
assertEquals(expectedOrigin, src.origin)
|
||||
|
||||
val expectedSrcText = path.toFile().readText()
|
||||
val actualSrcText = src.getCharStream().toString()
|
||||
assertEquals(expectedSrcText, actualSrcText)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFromPathWithExistingNonNormalizedPath() {
|
||||
val filename = "simple_main.p8"
|
||||
val path = Path.of(".", "test", "..", "test", "fixtures", filename)
|
||||
val src = SourceCode.fromPath(path)
|
||||
|
||||
val expectedOrigin = path.normalize().absolutePathString()
|
||||
assertEquals(expectedOrigin, src.origin)
|
||||
|
||||
val expectedSrcText = path.toFile().readText()
|
||||
val actualSrcText = src.getCharStream().toString()
|
||||
assertEquals(expectedSrcText, actualSrcText)
|
||||
}
|
||||
|
||||
}
|
4
compilerAst/test/fixtures/simple_main.p8
vendored
Normal file
4
compilerAst/test/fixtures/simple_main.p8
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
main {
|
||||
sub start() {
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user