diff --git a/compilerAst/src/prog8/parser/ModuleParsing.kt b/compilerAst/src/prog8/parser/ModuleParsing.kt index 1689fc03e..69dd07820 100644 --- a/compilerAst/src/prog8/parser/ModuleParsing.kt +++ b/compilerAst/src/prog8/parser/ModuleParsing.kt @@ -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? { - 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 } diff --git a/compilerAst/src/prog8/parser/Prog8Parser.kt b/compilerAst/src/prog8/parser/Prog8Parser.kt index d3d10eea4..45c684d60 100644 --- a/compilerAst/src/prog8/parser/Prog8Parser.kt +++ b/compilerAst/src/prog8/parser/Prog8Parser.kt @@ -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), "") - } - - 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) diff --git a/compilerAst/src/prog8/parser/SourceCode.kt b/compilerAst/src/prog8/parser/SourceCode.kt new file mode 100644 index 000000000..02b3f721b --- /dev/null +++ b/compilerAst/src/prog8/parser/SourceCode.kt @@ -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]) + * * `` if was created via [of] + * * `` 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 ``. + */ + fun of(text: String): SourceCode { + return object : SourceCode() { + override val origin = "" + 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]: `` 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 = "" + 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]) +*/ + } +} diff --git a/compilerAst/test/TestModuleImporter.kt b/compilerAst/test/TestModuleImporter.kt index a1674dfe3..6fe9b6a73 100644 --- a/compilerAst/test/TestModuleImporter.kt +++ b/compilerAst/test/TestModuleImporter.kt @@ -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 { importer.importModule(srcPath) } + assertFailsWith { 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 { 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 { importer.importLibraryModule(srcPath.nameWithoutExtension) } + assertFailsWith { 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 { 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 { importer.importLibraryModule(filenameNoExt) } + assertFailsWith { 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 { 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") diff --git a/compilerAst/test/TestProg8Parser.kt b/compilerAst/test/TestProg8Parser.kt index 749c1c7d1..381feffa6 100644 --- a/compilerAst/test/TestProg8Parser.kt +++ b/compilerAst/test/TestProg8Parser.kt @@ -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 at ''" - 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 { parseModule(srcBad) } - val module = parseModule(srcGood) + assertFailsWith { 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{ parseModule(""" + assertFailsWith{ parseModule(SourceCode.of(""" blockA { } blockB { } - """) } + """)) } // block and directive - assertFailsWith{ parseModule(""" + assertFailsWith{ 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{ parseModule(""" + assertFailsWith{ parseModule(SourceCode.of(""" %import textio blockB { } - """) } + """)) } - assertFailsWith{ parseModule(""" + assertFailsWith{ 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 { 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 { 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 { 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 { parseModule(srcText) } + assertFailsWith { 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("^$")) @@ -211,11 +186,11 @@ class TestProg8Parser { val filename = "file_with_syntax_error.p8" val path = Path.of("test", "fixtures", filename) - assertFailsWith { parseModule(path) } + assertFailsWith { 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(module.statements.first()) assertEquals((module.statements.first() as Block).name, "main") } diff --git a/compilerAst/test/TestSourceCode.kt b/compilerAst/test/TestSourceCode.kt new file mode 100644 index 000000000..7a20767f2 --- /dev/null +++ b/compilerAst/test/TestSourceCode.kt @@ -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("^$")) + 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 { 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 { SourceCode.fromPath(pathWithoutExt) } + } + + @Test + fun testFromPathWithDirectory() { + val path = Path.of("test", "fixtures") + + assertTrue(path.isDirectory(), "sanity check: should be a directory") + assertFailsWith { 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) + } + +} diff --git a/compilerAst/test/fixtures/simple_main.p8 b/compilerAst/test/fixtures/simple_main.p8 new file mode 100644 index 000000000..afaa79f93 --- /dev/null +++ b/compilerAst/test/fixtures/simple_main.p8 @@ -0,0 +1,4 @@ +main { + sub start() { + } +}