Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement semantic of the While statement #14

Open
wants to merge 3 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion src/main/kotlin/br/com/pnp/compiler/wat/WatGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
25 changes: 23 additions & 2 deletions src/main/kotlin/br/com/pnp/semantic/PnpContext.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ 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
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
Expand All @@ -37,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()
}
Expand Down Expand Up @@ -448,6 +449,26 @@ 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)
}

override fun exitWhileBlock(ctx: PnpParser.WhileBlockContext) {
analyser.endScope()
}

// endregion

// region type validation

private fun isNumber(type: Any): Boolean {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
procedimento principal
inicio
test1: booleano;
test1 <- true;

enquanto (test1) faca
test1 <- false;
fim
fim
28 changes: 28 additions & 0 deletions src/test/kotlin/br/com/pnp/compiler/wat/WatCompilerTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
27 changes: 27 additions & 0 deletions src/test/kotlin/br/com/pnp/compiler/wat/WatGeneratorTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -13,6 +14,7 @@ import org.junit.Test

class WatGeneratorTest : AppTest() {
override val subject = WatGenerator()
private val dollar = '$'

@Test
fun testConvert() {
Expand Down Expand Up @@ -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)
}
}
38 changes: 38 additions & 0 deletions src/test/kotlin/br/com/pnp/semantic/AnalyserTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<CompilationException> {
val testCode = """
procedimento principal
inicio
test: inteiro;
test <- 1;

enquanto (test) faca
test <- test + 1;
fim
fim
""".trimIndent()
subject.analyse(testCode)
}
}

// endregion
}