* structure TestProg8Parser with @Nested

This commit is contained in:
meisl 2021-08-02 14:52:46 +02:00
parent ef0c4797bb
commit c2986eaf47

View File

@ -4,6 +4,7 @@ import prog8tests.helpers.*
import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Nested
import kotlin.test.* import kotlin.test.*
import kotlin.io.path.* import kotlin.io.path.*
@ -19,452 +20,511 @@ import prog8.ast.expressions.*
@TestInstance(TestInstance.Lifecycle.PER_CLASS) @TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestProg8Parser { class TestProg8Parser {
@Test @Nested
fun testModuleSourceNeedNotEndWithNewline() { inner class Newline {
val nl = "\n" // say, Unix-style (different flavours tested elsewhere)
val src = SourceCode.of("foo {" + nl + "}") // source ends with '}' (= NO newline, issue #40)
// #45: Prog8ANTLRParser would report (throw) "missing <EOL> at '<EOF>'" @Nested
val module = parseModule(src) inner class AtEnd {
assertEquals(1, module.statements.size)
}
@Test @Test
fun testModuleSourceMayEndWithNewline() { fun testModuleSourceNeedNotEndWithNewline() {
val nl = "\n" // say, Unix-style (different flavours tested elsewhere) val nl = "\n" // say, Unix-style (different flavours tested elsewhere)
val srcText = "foo {" + nl + "}" + nl // source does end with a newline (issue #40) val src = SourceCode.of("foo {" + nl + "}") // source ends with '}' (= NO newline, issue #40)
val module = parseModule(SourceCode.of(srcText))
assertEquals(1, module.statements.size)
}
@Test // #45: Prog8ANTLRParser would report (throw) "missing <EOL> at '<EOF>'"
fun testAllBlocksButLastMustEndWithNewline() { val module = parseModule(src)
val nl = "\n" // say, Unix-style (different flavours tested elsewhere) assertEquals(1, module.statements.size)
// BAD: 2nd block `bar` does NOT start on new line; however, there's is a nl at the very end
val srcBad = "foo {" + nl + "}" + " bar {" + nl + "}" + nl
// 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(SourceCode.of(srcBad)) }
val module = parseModule(SourceCode.of(srcGood))
assertEquals(2, module.statements.size)
}
@Test
fun testWindowsAndMacNewlinesAreAlsoFine() {
val nlWin = "\r\n"
val nlUnix = "\n"
val nlMac = "\r"
//parseModule(Paths.get("test", "fixtures", "mac_newlines.p8").toAbsolutePath())
// a good mix of all kinds of newlines:
val srcText =
"foo {" +
nlMac +
nlWin +
"}" +
nlMac + // <-- do test a single \r (!) where an EOL is expected
"bar {" +
nlUnix +
"}" +
nlUnix + nlMac // both should be "eaten up" by just one EOL token
"combi {" +
nlMac + nlWin + nlUnix // all three should be "eaten up" by just one EOL token
"}" +
nlUnix // end with newline (see testModuleSourceNeedNotEndWithNewline)
val module = parseModule(SourceCode.of(srcText))
assertEquals(2, module.statements.size)
}
@Test
fun testInterleavedEolAndCommentBeforeFirstBlock() {
// issue: #47
val srcText = """
; comment
; comment
blockA {
} }
"""
val module = parseModule(SourceCode.of(srcText))
assertEquals(1, module.statements.size)
}
@Test @Test
fun testInterleavedEolAndCommentBetweenBlocks() { fun testModuleSourceMayEndWithNewline() {
// issue: #47 val nl = "\n" // say, Unix-style (different flavours tested elsewhere)
val srcText = """ val srcText = "foo {" + nl + "}" + nl // source does end with a newline (issue #40)
blockA { val module = parseModule(SourceCode.of(srcText))
assertEquals(1, module.statements.size)
} }
; comment }
; comment
blockB {
}
"""
val module = parseModule(SourceCode.of(srcText))
assertEquals(2, module.statements.size)
}
@Test @Test
fun testInterleavedEolAndCommentAfterLastBlock() { fun testAllBlocksButLastMustEndWithNewline() {
// issue: #47 val nl = "\n" // say, Unix-style (different flavours tested elsewhere)
val srcText = """
blockA {
}
; comment
; comment
"""
val module = parseModule(SourceCode.of(srcText))
assertEquals(1, module.statements.size)
}
@Test // BAD: 2nd block `bar` does NOT start on new line; however, there's is a nl at the very end
fun testNewlineBetweenTwoBlocksOrDirectivesStillRequired() { val srcBad = "foo {" + nl + "}" + " bar {" + nl + "}" + nl
// issue: #47
// block and block // GOOD: 2nd block `bar` does start on a new line; however, a nl at the very end ain't needed
assertFailsWith<ParseError>{ parseModule(SourceCode.of(""" val srcGood = "foo {" + nl + "}" + nl + "bar {" + nl + "}"
blockA {
} blockB {
}
""")) }
// block and directive assertFailsWith<ParseError> { parseModule(SourceCode.of(srcBad)) }
assertFailsWith<ParseError>{ parseModule(SourceCode.of(""" val module = parseModule(SourceCode.of(srcGood))
blockB { assertEquals(2, module.statements.size)
} %import textio }
""")) }
// The following two are bogus due to directive *args* expected to follow the directive name. @Test
// Leaving them in anyways. fun testNewlineBetweenTwoBlocksOrDirectivesStillRequired() {
// issue: #47
// dir and block // block and block
assertFailsWith<ParseError>{ parseModule(SourceCode.of(""" assertFailsWith<ParseError>{ parseModule(SourceCode.of("""
%import textio blockB { blockA {
} } blockB {
""")) } }
""")) }
assertFailsWith<ParseError>{ parseModule(SourceCode.of(""" // block and directive
%import textio %import syslib assertFailsWith<ParseError>{ parseModule(SourceCode.of("""
""")) } blockB {
} } %import textio
""")) }
@Test // The following two are bogus due to directive *args* expected to follow the directive name.
fun parseModuleShouldNotLookAtImports() { // Leaving them in anyways.
val importedNoExt = assumeNotExists(fixturesDir, "i_do_not_exist")
assumeNotExists(fixturesDir, "i_do_not_exist.p8")
val text = "%import ${importedNoExt.name}"
val module = parseModule(SourceCode.of(text))
assertEquals(1, module.statements.size) // dir and block
} assertFailsWith<ParseError>{ parseModule(SourceCode.of("""
%import textio blockB {
}
""")) }
assertFailsWith<ParseError>{ parseModule(SourceCode.of("""
%import textio %import syslib
""")) }
}
@Test @Test
fun testParseModuleWithEmptyString() { fun testWindowsAndMacNewlinesAreAlsoFine() {
val module = parseModule(SourceCode.of("")) val nlWin = "\r\n"
assertEquals(0, module.statements.size) val nlUnix = "\n"
} val nlMac = "\r"
@Test //parseModule(Paths.get("test", "fixtures", "mac_newlines.p8").toAbsolutePath())
fun testParseModuleWithEmptyFile() {
val path = assumeReadableFile(fixturesDir,"empty.p8")
val module = parseModule(SourceCode.fromPath(path))
assertEquals(0, module.statements.size)
}
@Test // a good mix of all kinds of newlines:
fun testModuleNameForSourceFromString() { val srcText =
val srcText = """ "foo {" +
main { nlMac +
} nlWin +
""".trimIndent() "}" +
val module = parseModule(SourceCode.of(srcText)) nlMac + // <-- do test a single \r (!) where an EOL is expected
"bar {" +
nlUnix +
"}" +
nlUnix + nlMac // both should be "eaten up" by just one EOL token
"combi {" +
nlMac + nlWin + nlUnix // all three should be "eaten up" by just one EOL token
"}" +
nlUnix // end with newline (see testModuleSourceNeedNotEndWithNewline)
// Note: assertContains has *actual* as first param val module = parseModule(SourceCode.of(srcText))
assertContains(module.name, Regex("^anonymous_[0-9a-f]+$")) assertEquals(2, module.statements.size)
}
@Test
fun testModuleNameForSourceFromPath() {
val path = assumeReadableFile(fixturesDir,"simple_main.p8")
val module = parseModule(SourceCode.fromPath(path))
assertEquals(path.nameWithoutExtension, module.name)
}
fun assertPosition(actual: Position, expFile: String? = null, expLine: Int? = null, expStartCol: Int? = null, expEndCol: Int? = null) {
require(!listOf(expLine, expStartCol, expEndCol).all { it == null })
if (expLine != null) assertEquals(expLine, actual.line, ".position.line (1-based)")
if (expStartCol != null) assertEquals(expStartCol, actual.startCol, ".position.startCol (0-based)" )
if (expEndCol != null) assertEquals(expEndCol, actual.endCol, ".position.endCol (0-based)")
if (expFile != null) assertEquals(expFile, actual.file, ".position.file")
}
fun assertPosition(actual: Position, expFile: Regex? = null, expLine: Int? = null, expStartCol: Int? = null, expEndCol: Int? = null) {
require(!listOf(expLine, expStartCol, expEndCol).all { it == null })
if (expLine != null) assertEquals(expLine, actual.line, ".position.line (1-based)")
if (expStartCol != null) assertEquals(expStartCol, actual.startCol, ".position.startCol (0-based)" )
if (expEndCol != null) assertEquals(expEndCol, actual.endCol, ".position.endCol (0-based)")
// Note: assertContains expects *actual* value first
if (expFile != null) assertContains(actual.file, expFile, ".position.file")
}
fun assertPositionOf(actual: Node, expFile: String? = null, expLine: Int? = null, expStartCol: Int? = null, expEndCol: Int? = null) =
assertPosition(actual.position, expFile, expLine, expStartCol, expEndCol)
fun assertPositionOf(actual: Node, expFile: Regex? = null, expLine: Int? = null, expStartCol: Int? = null, expEndCol: Int? = null) =
assertPosition(actual.position, expFile, expLine, expStartCol, expEndCol)
@Test
fun testErrorLocationForSourceFromString() {
val srcText = "bad * { }\n"
assertFailsWith<ParseError> { parseModule(SourceCode.of(srcText)) }
try {
parseModule(SourceCode.of(srcText))
} catch (e: ParseError) {
assertPosition(e.position, Regex("^<String@[0-9a-f]+>$"), 1, 4, 4)
} }
} }
@Test @Nested
fun testErrorLocationForSourceFromPath() { inner class EOLsInterleavedWithComments {
val path = assumeReadableFile(fixturesDir, "file_with_syntax_error.p8")
assertFailsWith<ParseError> { parseModule(SourceCode.fromPath(path)) } @Test
try { fun testInterleavedEolAndCommentBeforeFirstBlock() {
parseModule(SourceCode.fromPath(path)) // issue: #47
} catch (e: ParseError) { val srcText = """
assertPosition(e.position, path.absolutePathString(), 2, 6) // TODO: endCol wrong ; comment
; comment
blockA {
}
"""
val module = parseModule(SourceCode.of(srcText))
assertEquals(1, module.statements.size)
}
@Test
fun testInterleavedEolAndCommentBetweenBlocks() {
// issue: #47
val srcText = """
blockA {
}
; comment
; comment
blockB {
}
"""
val module = parseModule(SourceCode.of(srcText))
assertEquals(2, module.statements.size)
}
@Test
fun testInterleavedEolAndCommentAfterLastBlock() {
// issue: #47
val srcText = """
blockA {
}
; comment
; comment
"""
val module = parseModule(SourceCode.of(srcText))
assertEquals(1, module.statements.size)
} }
} }
@Test
fun testModulePositionForSourceFromString() { @Nested
val srcText = """ inner class ImportDirectives {
main { @Test
fun parseModuleShouldNotLookAtImports() {
val importedNoExt = assumeNotExists(fixturesDir, "i_do_not_exist")
assumeNotExists(fixturesDir, "i_do_not_exist.p8")
val text = "%import ${importedNoExt.name}"
val module = parseModule(SourceCode.of(text))
assertEquals(1, module.statements.size)
}
}
@Nested
inner class EmptySourcecode {
@Test
fun testParseModuleWithEmptyString() {
val module = parseModule(SourceCode.of(""))
assertEquals(0, module.statements.size)
}
@Test
fun testParseModuleWithEmptyFile() {
val path = assumeReadableFile(fixturesDir, "empty.p8")
val module = parseModule(SourceCode.fromPath(path))
assertEquals(0, module.statements.size)
}
}
@Nested
inner class NameOfModule {
@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 = assumeReadableFile(fixturesDir, "simple_main.p8")
val module = parseModule(SourceCode.fromPath(path))
assertEquals(path.nameWithoutExtension, module.name)
}
}
@Nested
inner class PositionOfAstNodesAndParseErrors {
fun assertPosition(
actual: Position,
expFile: String? = null,
expLine: Int? = null,
expStartCol: Int? = null,
expEndCol: Int? = null
) {
require(!listOf(expLine, expStartCol, expEndCol).all { it == null })
if (expLine != null) assertEquals(expLine, actual.line, ".position.line (1-based)")
if (expStartCol != null) assertEquals(expStartCol, actual.startCol, ".position.startCol (0-based)")
if (expEndCol != null) assertEquals(expEndCol, actual.endCol, ".position.endCol (0-based)")
if (expFile != null) assertEquals(expFile, actual.file, ".position.file")
}
fun assertPosition(
actual: Position,
expFile: Regex? = null,
expLine: Int? = null,
expStartCol: Int? = null,
expEndCol: Int? = null
) {
require(!listOf(expLine, expStartCol, expEndCol).all { it == null })
if (expLine != null) assertEquals(expLine, actual.line, ".position.line (1-based)")
if (expStartCol != null) assertEquals(expStartCol, actual.startCol, ".position.startCol (0-based)")
if (expEndCol != null) assertEquals(expEndCol, actual.endCol, ".position.endCol (0-based)")
// Note: assertContains expects *actual* value first
if (expFile != null) assertContains(actual.file, expFile, ".position.file")
}
fun assertPositionOf(
actual: Node,
expFile: String? = null,
expLine: Int? = null,
expStartCol: Int? = null,
expEndCol: Int? = null
) =
assertPosition(actual.position, expFile, expLine, expStartCol, expEndCol)
fun assertPositionOf(
actual: Node,
expFile: Regex? = null,
expLine: Int? = null,
expStartCol: Int? = null,
expEndCol: Int? = null
) =
assertPosition(actual.position, expFile, expLine, expStartCol, expEndCol)
@Test
fun testErrorLocationForSourceFromString() {
val srcText = "bad * { }\n"
assertFailsWith<ParseError> { parseModule(SourceCode.of(srcText)) }
try {
parseModule(SourceCode.of(srcText))
} catch (e: ParseError) {
assertPosition(e.position, Regex("^<String@[0-9a-f]+>$"), 1, 4, 4)
} }
""".trimIndent() }
val module = parseModule(SourceCode.of(srcText))
assertPositionOf(module, Regex("^<String@[0-9a-f]+>$"), 1, 0) // TODO: endCol wrong
}
@Test @Test
fun testModulePositionForSourceFromPath() { fun testErrorLocationForSourceFromPath() {
val path = assumeReadableFile(fixturesDir,"simple_main.p8") val path = assumeReadableFile(fixturesDir, "file_with_syntax_error.p8")
val module = parseModule(SourceCode.fromPath(path)) assertFailsWith<ParseError> { parseModule(SourceCode.fromPath(path)) }
assertPositionOf(module, path.absolutePathString(), 1, 0) // TODO: endCol wrong try {
} parseModule(SourceCode.fromPath(path))
} catch (e: ParseError) {
assertPosition(e.position, path.absolutePathString(), 2, 6) // TODO: endCol wrong
}
}
@Test @Test
fun testInnerNodePositionsForSourceFromPath() { fun testModulePositionForSourceFromString() {
val path = assumeReadableFile(fixturesDir,"simple_main.p8") val srcText = """
main {
}
""".trimIndent()
val module = parseModule(SourceCode.of(srcText))
assertPositionOf(module, Regex("^<String@[0-9a-f]+>$"), 1, 0) // TODO: endCol wrong
}
val module = parseModule(SourceCode.fromPath(path)) @Test
val mpf = module.position.file fun testModulePositionForSourceFromPath() {
val path = assumeReadableFile(fixturesDir, "simple_main.p8")
assertPositionOf(module, path.absolutePathString(), 1, 0) // TODO: endCol wrong val module = parseModule(SourceCode.fromPath(path))
val mainBlock = module.statements.filterIsInstance<Block>()[0] assertPositionOf(module, path.absolutePathString(), 1, 0) // TODO: endCol wrong
assertPositionOf(mainBlock, mpf, 1, 0) // TODO: endCol wrong! }
val startSub = mainBlock.statements.filterIsInstance<Subroutine>()[0]
assertPositionOf(startSub, mpf, 2, 4) // TODO: endCol wrong!
}
/** @Test
* TODO: this test is testing way too much at once fun testInnerNodePositionsForSourceFromPath() {
*/ val path = assumeReadableFile(fixturesDir, "simple_main.p8")
@Test
@Disabled("TODO: fix .position of nodes below Module - step 8, 'refactor AST gen'") val module = parseModule(SourceCode.fromPath(path))
fun testInnerNodePositionsForSourceFromString() { val mpf = module.position.file
val srcText = """
%target 16, "abc" ; DirectiveArg directly inherits from Node - neither an Expression nor a Statement..? assertPositionOf(module, path.absolutePathString(), 1, 0) // TODO: endCol wrong
main { val mainBlock = module.statements.filterIsInstance<Block>()[0]
sub start() { assertPositionOf(mainBlock, mpf, 1, 0) // TODO: endCol wrong!
ubyte foo = 42 val startSub = mainBlock.statements.filterIsInstance<Subroutine>()[0]
ubyte bar assertPositionOf(startSub, mpf, 2, 4) // TODO: endCol wrong!
when (foo) { }
23 -> bar = 'x' ; WhenChoice, also directly inheriting Node
42 -> bar = 'y'
else -> bar = 'z' /**
* TODO: this test is testing way too much at once
*/
@Test
@Disabled("TODO: fix .position of nodes below Module - step 8, 'refactor AST gen'")
fun testInnerNodePositionsForSourceFromString() {
val srcText = """
%target 16, "abc" ; DirectiveArg directly inherits from Node - neither an Expression nor a Statement..?
main {
sub start() {
ubyte foo = 42
ubyte bar
when (foo) {
23 -> bar = 'x' ; WhenChoice, also directly inheriting Node
42 -> bar = 'y'
else -> bar = 'z'
}
} }
} }
} """.trimIndent()
""".trimIndent() val module = parseModule(SourceCode.of(srcText))
val module = parseModule(SourceCode.of(srcText)) val mpf = module.position.file
val mpf = module.position.file
val targetDirective = module.statements.filterIsInstance<Directive>()[0] val targetDirective = module.statements.filterIsInstance<Directive>()[0]
assertPositionOf(targetDirective, mpf, 1, 0) // TODO: endCol wrong! assertPositionOf(targetDirective, mpf, 1, 0) // TODO: endCol wrong!
val mainBlock = module.statements.filterIsInstance<Block>()[0] val mainBlock = module.statements.filterIsInstance<Block>()[0]
assertPositionOf(mainBlock, mpf, 2, 0) // TODO: endCol wrong! assertPositionOf(mainBlock, mpf, 2, 0) // TODO: endCol wrong!
val startSub = mainBlock.statements.filterIsInstance<Subroutine>()[0] val startSub = mainBlock.statements.filterIsInstance<Subroutine>()[0]
assertPositionOf(startSub, mpf, 3, 4) // TODO: endCol wrong! assertPositionOf(startSub, mpf, 3, 4) // TODO: endCol wrong!
val declFoo = startSub.statements.filterIsInstance<VarDecl>()[0] val declFoo = startSub.statements.filterIsInstance<VarDecl>()[0]
assertPositionOf(declFoo, mpf, 4, 8) // TODO: endCol wrong! assertPositionOf(declFoo, mpf, 4, 8) // TODO: endCol wrong!
val rhsFoo = declFoo.value!! val rhsFoo = declFoo.value!!
assertPositionOf(rhsFoo, mpf, 4, 20) // TODO: endCol wrong! assertPositionOf(rhsFoo, mpf, 4, 20) // TODO: endCol wrong!
val declBar = startSub.statements.filterIsInstance<VarDecl>()[1] val declBar = startSub.statements.filterIsInstance<VarDecl>()[1]
assertPositionOf(declBar, mpf, 5, 8) // TODO: endCol wrong! assertPositionOf(declBar, mpf, 5, 8) // TODO: endCol wrong!
val whenStmt = startSub.statements.filterIsInstance<WhenStatement>()[0] val whenStmt = startSub.statements.filterIsInstance<WhenStatement>()[0]
assertPositionOf(whenStmt, mpf, 6, 8) // TODO: endCol wrong! assertPositionOf(whenStmt, mpf, 6, 8) // TODO: endCol wrong!
assertPositionOf(whenStmt.choices[0], mpf, 7, 12) // TODO: endCol wrong! assertPositionOf(whenStmt.choices[0], mpf, 7, 12) // TODO: endCol wrong!
assertPositionOf(whenStmt.choices[1], mpf, 8, 12) // TODO: endCol wrong! assertPositionOf(whenStmt.choices[1], mpf, 8, 12) // TODO: endCol wrong!
assertPositionOf(whenStmt.choices[2], mpf, 9, 12) // TODO: endCol wrong! assertPositionOf(whenStmt.choices[2], mpf, 9, 12) // TODO: endCol wrong!
}
} }
@Test @Nested
fun testCharLitAsArg() { inner class CharLiterals {
val src = SourceCode.of("""
main { @Test
sub start() { fun testCharLitAsArg() {
chrout('\n') val src = SourceCode.of("""
main {
sub start() {
chrout('\n')
}
} }
} """)
""") val module = parseModule(src)
val module = parseModule(src)
val startSub = module val startSub = module
.statements.filterIsInstance<Block>()[0] .statements.filterIsInstance<Block>()[0]
.statements.filterIsInstance<Subroutine>()[0] .statements.filterIsInstance<Subroutine>()[0]
val funCall = startSub.statements.filterIsInstance<IFunctionCall>().first() val funCall = startSub.statements.filterIsInstance<IFunctionCall>().first()
assertIs<CharLiteral>(funCall.args[0]) assertIs<CharLiteral>(funCall.args[0])
val char = funCall.args[0] as CharLiteral val char = funCall.args[0] as CharLiteral
assertEquals('\n', char.value) assertEquals('\n', char.value)
} }
@Test @Test
fun testBlockLevelVarDeclWithCharLiteral_noAltEnc() { fun testBlockLevelVarDeclWithCharLiteral_noAltEnc() {
val src = SourceCode.of(""" val src = SourceCode.of("""
main { main {
ubyte c = 'x'
}
""")
val module = parseModule(src)
val decl = module
.statements.filterIsInstance<Block>()[0]
.statements.filterIsInstance<VarDecl>()[0]
val rhs = decl.value as CharLiteral
assertEquals('x', rhs.value, "char literal's .value")
assertEquals(false, rhs.altEncoding, "char literal's .altEncoding")
}
@Test
fun testBlockLevelConstDeclWithCharLiteral_withAltEnc() {
val src = SourceCode.of("""
main {
const ubyte c = @'x'
}
""")
val module = parseModule(src)
val decl = module
.statements.filterIsInstance<Block>()[0]
.statements.filterIsInstance<VarDecl>()[0]
val rhs = decl.value as CharLiteral
assertEquals('x', rhs.value, "char literal's .value")
assertEquals(true, rhs.altEncoding, "char literal's .altEncoding")
}
@Test
fun testSubRoutineLevelVarDeclWithCharLiteral_noAltEnc() {
val src = SourceCode.of("""
main {
sub start() {
ubyte c = 'x' ubyte c = 'x'
} }
} """)
""") val module = parseModule(src)
val module = parseModule(src) val decl = module
val decl = module .statements.filterIsInstance<Block>()[0]
.statements.filterIsInstance<Block>()[0] .statements.filterIsInstance<VarDecl>()[0]
.statements.filterIsInstance<Subroutine>()[0]
.statements.filterIsInstance<VarDecl>()[0]
val rhs = decl.value as CharLiteral val rhs = decl.value as CharLiteral
assertEquals('x', rhs.value, "char literal's .value") assertEquals('x', rhs.value, "char literal's .value")
assertEquals(false, rhs.altEncoding, "char literal's .altEncoding") assertEquals(false, rhs.altEncoding, "char literal's .altEncoding")
} }
@Test @Test
fun testSubRoutineLevelConstDeclWithCharLiteral_withAltEnc() { fun testBlockLevelConstDeclWithCharLiteral_withAltEnc() {
val src = SourceCode.of(""" val src = SourceCode.of("""
main { main {
sub start() {
const ubyte c = @'x' const ubyte c = @'x'
} }
} """)
""") val module = parseModule(src)
val module = parseModule(src) val decl = module
val decl = module .statements.filterIsInstance<Block>()[0]
.statements.filterIsInstance<Block>()[0] .statements.filterIsInstance<VarDecl>()[0]
.statements.filterIsInstance<Subroutine>()[0]
.statements.filterIsInstance<VarDecl>()[0]
val rhs = decl.value as CharLiteral val rhs = decl.value as CharLiteral
assertEquals('x', rhs.value, "char literal's .value") assertEquals('x', rhs.value, "char literal's .value")
assertEquals(true, rhs.altEncoding, "char literal's .altEncoding") assertEquals(true, rhs.altEncoding, "char literal's .altEncoding")
} }
@Test
@Test fun testSubRoutineLevelVarDeclWithCharLiteral_noAltEnc() {
fun testForloop() { val src = SourceCode.of("""
val module = parseModule(SourceCode.of(""" main {
main { sub start() {
sub start() { ubyte c = 'x'
ubyte ub
for ub in "start" downto "end" { ; #0
}
for ub in "something" { ; #1
}
for ub in @'a' to 'f' { ; #2
}
for ub in false to true { ; #3
}
for ub in 9 to 1 { ; #4 - yes, *parser* should NOT check!
} }
} }
} """)
""")) val module = parseModule(src)
val iterables = module val decl = module
.statements.filterIsInstance<Block>()[0] .statements.filterIsInstance<Block>()[0]
.statements.filterIsInstance<Subroutine>()[0] .statements.filterIsInstance<Subroutine>()[0]
.statements.filterIsInstance<ForLoop>() .statements.filterIsInstance<VarDecl>()[0]
.map { it.iterable }
assertEquals(5, iterables.size) val rhs = decl.value as CharLiteral
assertEquals('x', rhs.value, "char literal's .value")
assertEquals(false, rhs.altEncoding, "char literal's .altEncoding")
}
val it0 = iterables[0] as RangeExpr @Test
assertIs<StringLiteralValue>(it0.from, "parser should leave it as is") fun testSubRoutineLevelConstDeclWithCharLiteral_withAltEnc() {
assertIs<StringLiteralValue>(it0.to, "parser should leave it as is") val src = SourceCode.of("""
main {
sub start() {
const ubyte c = @'x'
}
}
""")
val module = parseModule(src)
val decl = module
.statements.filterIsInstance<Block>()[0]
.statements.filterIsInstance<Subroutine>()[0]
.statements.filterIsInstance<VarDecl>()[0]
val it1 = iterables[1] as StringLiteralValue val rhs = decl.value as CharLiteral
assertEquals("something", it1.value, "parser should leave it as is") assertEquals('x', rhs.value, "char literal's .value")
assertEquals(true, rhs.altEncoding, "char literal's .altEncoding")
val it2 = iterables[2] as RangeExpr }
assertIs<CharLiteral>(it2.from, "parser should leave it as is")
assertIs<CharLiteral>(it2.to, "parser should leave it as is")
val it3 = iterables[3] as RangeExpr
// TODO: intro BoolLiteral
assertIs<NumericLiteralValue>(it3.from, "parser should leave it as is")
assertIs<NumericLiteralValue>(it3.to, "parser should leave it as is")
val it4 = iterables[4] as RangeExpr
assertIs<NumericLiteralValue>(it4.from, "parser should leave it as is")
assertIs<NumericLiteralValue>(it4.to, "parser should leave it as is")
} }
@Nested
inner class Ranges {
@Test
fun testForloop() {
val module = parseModule(SourceCode.of("""
main {
sub start() {
ubyte ub
for ub in "start" downto "end" { ; #0
}
for ub in "something" { ; #1
}
for ub in @'a' to 'f' { ; #2
}
for ub in false to true { ; #3
}
for ub in 9 to 1 { ; #4 - yes, *parser* should NOT check!
}
}
}
"""))
val iterables = module
.statements.filterIsInstance<Block>()[0]
.statements.filterIsInstance<Subroutine>()[0]
.statements.filterIsInstance<ForLoop>()
.map { it.iterable }
assertEquals(5, iterables.size)
val it0 = iterables[0] as RangeExpr
assertIs<StringLiteralValue>(it0.from, "parser should leave it as is")
assertIs<StringLiteralValue>(it0.to, "parser should leave it as is")
val it1 = iterables[1] as StringLiteralValue
assertEquals("something", it1.value, "parser should leave it as is")
val it2 = iterables[2] as RangeExpr
assertIs<CharLiteral>(it2.from, "parser should leave it as is")
assertIs<CharLiteral>(it2.to, "parser should leave it as is")
val it3 = iterables[3] as RangeExpr
// TODO: intro BoolLiteral
assertIs<NumericLiteralValue>(it3.from, "parser should leave it as is")
assertIs<NumericLiteralValue>(it3.to, "parser should leave it as is")
val it4 = iterables[4] as RangeExpr
assertIs<NumericLiteralValue>(it4.from, "parser should leave it as is")
assertIs<NumericLiteralValue>(it4.to, "parser should leave it as is")
}
}
} }