From 034eae07224674e973c83f685b0082d5feeb886c Mon Sep 17 00:00:00 2001 From: Mirella de Medeiros Date: Thu, 19 Dec 2019 16:00:12 -0300 Subject: [PATCH 1/3] while statement semantic + conversion to wat --- .../br/com/pnp/compiler/wat/WatGenerator.kt | 28 +++++++++++++- .../kotlin/br/com/pnp/semantic/PnpContext.kt | 17 +++++++++ .../testCodes/statement/whileStatement.pnp | 9 +++++ .../com/pnp/compiler/wat/WatCompilerTest.kt | 28 ++++++++++++++ .../com/pnp/compiler/wat/WatGeneratorTest.kt | 27 +++++++++++++ .../br/com/pnp/semantic/AnalyserTest.kt | 38 +++++++++++++++++++ 6 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 src/test/kotlin/br/com/pnp/compiler/testCodes/statement/whileStatement.pnp diff --git a/src/main/kotlin/br/com/pnp/compiler/wat/WatGenerator.kt b/src/main/kotlin/br/com/pnp/compiler/wat/WatGenerator.kt index 0e42e62..c040e01 100644 --- a/src/main/kotlin/br/com/pnp/compiler/wat/WatGenerator.kt +++ b/src/main/kotlin/br/com/pnp/compiler/wat/WatGenerator.kt @@ -21,6 +21,14 @@ class WatGenerator : Generator() { private val INTEGER_PREFIX = "i32" private val FLOAT_PREFIX = "f32" private val INTEGER_OPERATOR_SUFFIX = "_s" + // some conversions to wat need a unique identifier + // this variable with autoincrement is for these conversions cases + private var IDENTIFIER_COUNTER: Long = 0 + get() { + val value = field + field++ + return value + } override fun convert(symbols: SymbolTable): String { val wat = StringBuilder() @@ -130,7 +138,25 @@ class WatGenerator : Generator() { } override fun convertWhile(statement: WhileStatement): String { - TODO("not implemented") + val conditionNegation = UnaryOperation(Operator.NOT, statement.condition, PrimitiveType.boolean) + + return convertExpression(statement.condition)?.let { exitCondition -> + convertExpression(conditionNegation)?.let { enterCondition -> + val body = convertBody(statement.block.instructions) + val blockRef = "\$B$IDENTIFIER_COUNTER" // has auto increment, so the next access has + val loopRef = "\$L$IDENTIFIER_COUNTER" // a different value and has to be stored + + "block $blockRef\n" + + "$enterCondition\n" + + "br_if $blockRef\n" + + "loop $loopRef" + + body + + "$exitCondition\n" + + "br_if $loopRef\n" + + "end\n" + + "end" + } + } ?: "" } override fun convertDoWhile(statement: DoWhileStatement): String { diff --git a/src/main/kotlin/br/com/pnp/semantic/PnpContext.kt b/src/main/kotlin/br/com/pnp/semantic/PnpContext.kt index 739cb56..a012fe3 100644 --- a/src/main/kotlin/br/com/pnp/semantic/PnpContext.kt +++ b/src/main/kotlin/br/com/pnp/semantic/PnpContext.kt @@ -11,6 +11,7 @@ import br.com.pnp.grammar.antlr.PnpBaseListener import br.com.pnp.grammar.antlr.PnpParser import br.com.pnp.model.construct.Procedure import br.com.pnp.model.construct.Variable +import br.com.pnp.model.construct.statement.WhileStatement import br.com.pnp.model.construct.type.Type import br.com.pnp.model.construct.type.primitive.PrimitiveType import br.com.pnp.model.expression.Expression @@ -448,6 +449,22 @@ class PnpContext(val analyser: Analyser) : PnpBaseListener() { // endregion + // region statement + + override fun exitWhileStart(ctx: PnpParser.WhileStartContext) { + analyser.tryPop()?.let { condition -> + if (!isBoolean(condition)) { + throw IncompatibleTypeException(ctx.start, condition.type, PrimitiveType.boolean) + } + + val statement = WhileStatement(condition) + analyser.newInstruction(statement) + analyser.newScope(statement.block) + } ?: throw UnknownSemanticException(ctx.start) + } + + // endregion + // region type validation private fun isNumber(type: Any): Boolean { diff --git a/src/test/kotlin/br/com/pnp/compiler/testCodes/statement/whileStatement.pnp b/src/test/kotlin/br/com/pnp/compiler/testCodes/statement/whileStatement.pnp new file mode 100644 index 0000000..b6c73a0 --- /dev/null +++ b/src/test/kotlin/br/com/pnp/compiler/testCodes/statement/whileStatement.pnp @@ -0,0 +1,9 @@ +procedimento principal +inicio + test1: booleano; + test1 <- true; + + enquanto (test1) faca + test1 <- false; + fim +fim diff --git a/src/test/kotlin/br/com/pnp/compiler/wat/WatCompilerTest.kt b/src/test/kotlin/br/com/pnp/compiler/wat/WatCompilerTest.kt index 7133a3e..34135b3 100644 --- a/src/test/kotlin/br/com/pnp/compiler/wat/WatCompilerTest.kt +++ b/src/test/kotlin/br/com/pnp/compiler/wat/WatCompilerTest.kt @@ -260,4 +260,32 @@ class WatCompilerTest : AppTest() { subject.analyse(sourceFile) val result = subject.generateCode() } + + @Test + fun testWhileStatement() { + val relativePath = "src/test/kotlin/br/com/pnp/compiler/testCodes/statement/whileStatement.pnp" + val sourceFile = File(relativePath) + + val expectedResult = """ + (module + (func (export "main") (local ${dollar}test1 i32) + i32.const 1 + block ${dollar}B0 + get_local ${dollar}test1 + i32.eqz + br_if ${dollar}B0 + loop ${dollar}L1 + i32.const 0 + get_local ${dollar}test1 + br_if ${dollar}L1 + end + end + )) + """.trimIndent() + + subject.analyse(sourceFile) + val result = subject.generateCode() + + assertEquals(expectedResult, result) + } } diff --git a/src/test/kotlin/br/com/pnp/compiler/wat/WatGeneratorTest.kt b/src/test/kotlin/br/com/pnp/compiler/wat/WatGeneratorTest.kt index 1ec4afc..20b7117 100644 --- a/src/test/kotlin/br/com/pnp/compiler/wat/WatGeneratorTest.kt +++ b/src/test/kotlin/br/com/pnp/compiler/wat/WatGeneratorTest.kt @@ -3,6 +3,7 @@ package br.com.pnp.compiler.wat import br.com.pnp.AppTest import br.com.pnp.model.construct.Procedure import br.com.pnp.model.construct.Variable +import br.com.pnp.model.construct.statement.WhileStatement import br.com.pnp.model.construct.type.Type import br.com.pnp.model.construct.type.primitive.PrimitiveType import br.com.pnp.model.expression.operation.BinaryOperation @@ -13,6 +14,7 @@ import org.junit.Test class WatGeneratorTest : AppTest() { override val subject = WatGenerator() + private val dollar = '$' @Test fun testConvert() { @@ -533,4 +535,29 @@ class WatGeneratorTest : AppTest() { } // endregion + + @Test + fun testConvertWhile() { + val counter = getPrivateAttribute("IDENTIFIER_COUNTER") as Long + val convertWhileMethod = getPrivateMethod("convertWhile", WhileStatement::class.java) + + val blockID = counter + val loopID = counter + 1 + val expectedResult = """ + block ${dollar}B$blockID + i32.const 1 + i32.eqz + br_if ${dollar}B$blockID + loop ${dollar}L$loopID + i32.const 1 + br_if ${dollar}L$loopID + end + end + """.trimIndent() + + val statement = WhileStatement(Variable.literalBoolean(true)) + val result = convertWhileMethod?.invoke(subject, statement) as String + + assertEquals(expectedResult, result) + } } diff --git a/src/test/kotlin/br/com/pnp/semantic/AnalyserTest.kt b/src/test/kotlin/br/com/pnp/semantic/AnalyserTest.kt index 363af62..5417b42 100644 --- a/src/test/kotlin/br/com/pnp/semantic/AnalyserTest.kt +++ b/src/test/kotlin/br/com/pnp/semantic/AnalyserTest.kt @@ -969,4 +969,42 @@ class AnalyserTest : AppTest() { } // endregion + + // region statements + + @Test + fun testAnalyseWhileStatementSuccess() { + val testCode = """ + procedimento principal + inicio + test: inteiro; + test <- 1; + + enquanto (test < 10) faca + test <- test + 1; + fim + fim + """.trimIndent() + subject.analyse(testCode) + } + + @Test + fun testAnalyseWhileStatementFailConditionNotBoolean() { + assertFailsWith { + val testCode = """ + procedimento principal + inicio + test: inteiro; + test <- 1; + + enquanto (test) faca + test <- test + 1; + fim + fim + """.trimIndent() + subject.analyse(testCode) + } + } + + // endregion } From 5c0a0415c92e69ac14f5bbe13fcbc6520a79f08d Mon Sep 17 00:00:00 2001 From: Mirella de Medeiros Date: Thu, 19 Dec 2019 16:18:33 -0300 Subject: [PATCH 2/3] rename exception --- ...utputAssignment.kt => MissingOutputAssignmentException.kt} | 2 +- src/main/kotlin/br/com/pnp/semantic/PnpContext.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/main/kotlin/br/com/pnp/exception/{MissingOutputAssignment.kt => MissingOutputAssignmentException.kt} (70%) diff --git a/src/main/kotlin/br/com/pnp/exception/MissingOutputAssignment.kt b/src/main/kotlin/br/com/pnp/exception/MissingOutputAssignmentException.kt similarity index 70% rename from src/main/kotlin/br/com/pnp/exception/MissingOutputAssignment.kt rename to src/main/kotlin/br/com/pnp/exception/MissingOutputAssignmentException.kt index d4dad4a..e82317b 100644 --- a/src/main/kotlin/br/com/pnp/exception/MissingOutputAssignment.kt +++ b/src/main/kotlin/br/com/pnp/exception/MissingOutputAssignmentException.kt @@ -2,5 +2,5 @@ package br.com.pnp.exception import org.antlr.v4.runtime.Token -class MissingOutputAssignment(token: Token) : +class MissingOutputAssignmentException(token: Token) : SemanticException(token, "Missing output variable assignment") diff --git a/src/main/kotlin/br/com/pnp/semantic/PnpContext.kt b/src/main/kotlin/br/com/pnp/semantic/PnpContext.kt index a012fe3..58e90f7 100644 --- a/src/main/kotlin/br/com/pnp/semantic/PnpContext.kt +++ b/src/main/kotlin/br/com/pnp/semantic/PnpContext.kt @@ -3,7 +3,7 @@ package br.com.pnp.semantic import br.com.pnp.exception.ConflictDeclarationException import br.com.pnp.exception.IncompatibleTypeException import br.com.pnp.exception.MismatchedInputException -import br.com.pnp.exception.MissingOutputAssignment +import br.com.pnp.exception.MissingOutputAssignmentException import br.com.pnp.exception.OperatorNotApplicableException import br.com.pnp.exception.UnknownSemanticException import br.com.pnp.exception.UnresolvedReferenceException @@ -38,7 +38,7 @@ class PnpContext(val analyser: Analyser) : PnpBaseListener() { val procedure = analyser.tryGet(identifier) as? Procedure ?: throw UnknownSemanticException(ctx.start) if (!procedure.isOutputAssigned()) { - throw MissingOutputAssignment(ctx.procedureBody().procedureOutput().start) + throw MissingOutputAssignmentException(ctx.procedureBody().procedureOutput().start) } analyser.endScope() } From d4646aabee23876ab0ec056e0d56ae27e8019246 Mon Sep 17 00:00:00 2001 From: Mirella de Medeiros Date: Sun, 22 Dec 2019 17:00:19 -0300 Subject: [PATCH 3/3] fix scopes --- src/main/kotlin/br/com/pnp/semantic/PnpContext.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/kotlin/br/com/pnp/semantic/PnpContext.kt b/src/main/kotlin/br/com/pnp/semantic/PnpContext.kt index 58e90f7..4d6e77e 100644 --- a/src/main/kotlin/br/com/pnp/semantic/PnpContext.kt +++ b/src/main/kotlin/br/com/pnp/semantic/PnpContext.kt @@ -463,6 +463,10 @@ class PnpContext(val analyser: Analyser) : PnpBaseListener() { } ?: throw UnknownSemanticException(ctx.start) } + override fun exitWhileBlock(ctx: PnpParser.WhileBlockContext) { + analyser.endScope() + } + // endregion // region type validation