From 4ad21d1b82e2de9735371a6136487651817d78f9 Mon Sep 17 00:00:00 2001 From: Pedro Vinicius Coelho belem Date: Tue, 2 Apr 2024 15:10:53 -0300 Subject: [PATCH 01/15] feat: implement ASTVisitor --- .../java/org/example/visitor/ASTVisitor.java | 4 - .../kotlin/org/example/visitor/ASTVisitor.kt | 4 + .../org/example/visitor/ASTVisitorImpl.kt | 144 ++++++++++++++++++ 3 files changed, 148 insertions(+), 4 deletions(-) delete mode 100644 app/src/main/java/org/example/visitor/ASTVisitor.java create mode 100644 app/src/main/kotlin/org/example/visitor/ASTVisitor.kt create mode 100644 app/src/main/kotlin/org/example/visitor/ASTVisitorImpl.kt diff --git a/app/src/main/java/org/example/visitor/ASTVisitor.java b/app/src/main/java/org/example/visitor/ASTVisitor.java deleted file mode 100644 index e29852c..0000000 --- a/app/src/main/java/org/example/visitor/ASTVisitor.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.example.visitor; - -public interface ASTVisitor extends Visitor{ -} diff --git a/app/src/main/kotlin/org/example/visitor/ASTVisitor.kt b/app/src/main/kotlin/org/example/visitor/ASTVisitor.kt new file mode 100644 index 0000000..c991557 --- /dev/null +++ b/app/src/main/kotlin/org/example/visitor/ASTVisitor.kt @@ -0,0 +1,4 @@ +package org.example.visitor; + +interface ASTVisitor : Visitor + diff --git a/app/src/main/kotlin/org/example/visitor/ASTVisitorImpl.kt b/app/src/main/kotlin/org/example/visitor/ASTVisitorImpl.kt new file mode 100644 index 0000000..a701c0c --- /dev/null +++ b/app/src/main/kotlin/org/example/visitor/ASTVisitorImpl.kt @@ -0,0 +1,144 @@ +package org.example.visitor + +import jdk.jshell.spi.ExecutionControl.NotImplementedException +import org.example.ast.* + +object ASTVisitorImpl : ASTVisitor { + private fun acceptThis(block: A.(ASTVisitor) -> Unit): A.() -> Unit = + { block(ASTVisitorImpl) } + + + override fun visit(program: Program?): Unit = program!!.run { + acceptThis(MainClass::accept)(mainClass) + classes.classDecls + .forEach(acceptThis(ClassDecl::accept)) + } + + override fun visit(a: And?): Unit = a!!.run { + acceptThis(Expression::accept)(lhe) + acceptThis(Expression::accept)(rhe) + } + + override fun visit(b: BooleanType?) { + TODO("Not yet implemented") + } + + override fun visit(n: Not?): Unit = n!!.run { + acceptThis(Expression::accept)(e) + } + + override fun visit(t: True?): Unit = Unit + + override fun visit(f: False?): Unit = Unit + + override fun visit(i: Identifier?): Unit = Unit + + override fun visit(c: Call?): Unit = c!!.run { + acceptThis(Expression::accept)(owner) + acceptThis(Identifier::accept)(method) + expressionList.list.forEach(acceptThis(Expression::accept)) + } + + override fun visit(i: IdentifierExpression?): Unit = Unit + + override fun visit(i: IdentifierType?): Unit = + throw Exception("Wrong Visitor Exception") + + override fun visit(n: NewObject?): Unit = n!!.run { + acceptThis(Identifier::accept)(identifier) + } + + override fun visit(t: This?): Unit = Unit + + override fun visit(a: ArrayLookup?): Unit = a!!.run { + acceptThis(Expression::accept)(array) + acceptThis(Expression::accept)(idx) + } + + override fun visit(a: ArrayAssign?): Unit = a!!.run { + acceptThis(Identifier::accept)(identifier) + acceptThis(Expression::accept)(index) + acceptThis(Expression::accept)(value) + } + + override fun visit(a: ArrayLength?): Unit { + TODO("Not yet implemented") + } + + override fun visit(p: Plus?): Unit { + TODO("Not yet implemented") + } + + override fun visit(m: Minus?): Unit { + TODO("Not yet implemented") + } + + override fun visit(t: Times?): Unit { + TODO("Not yet implemented") + } + + override fun visit(i: IntegerLiteral?): Unit { + TODO("Not yet implemented") + } + + override fun visit(i: IntegerType?): Unit = throw Exception("Wrong Visitor Exception") + + override fun visit(i: IntArrayType?): Unit = throw Exception("Wrong Visitor Exception") + + override fun visit(l: LessThan?): Unit = l!!.run { + acceptThis(Expression::accept)(lhe) + acceptThis(Expression::accept)(rhe) + } + + override fun visit(n: NewArray?): Unit = n!!.run { + acceptThis(Expression::accept)(size) + } + + override fun visit(w: While?): Unit = w!!.run { + acceptThis(Expression::accept)(condition) + acceptThis(Statement::accept)(body) + } + + override fun visit(i: If?): Unit = i!!.run { + acceptThis(Expression::accept)(condition) + acceptThis(Statement::accept)(thenBranch) + acceptThis(Statement::accept)(elseBranch) + } + + override fun visit(a: Assign?): Unit = a!!.run { + acceptThis(Identifier::accept)(identifier) + acceptThis(Expression::accept)(value) + } + + override fun visit(s: Sout?): Unit = s!!.run { + acceptThis(Expression::accept)(expression) + } + + override fun visit(b: Block?): Unit = b!!.run { + statements.statements + .forEach(acceptThis(Statement::accept)) + } + + override fun visit(m: MainClass?): Unit = TODO() + + + override fun visit(c: ClassDeclSimple?): Unit { + TODO("Not yet implemented") + } + + override fun visit(c: ClassDeclExtends?): Unit { + TODO("Not yet implemented") + } + + override fun visit(m: MethodDecl?): Unit { + TODO("Not yet implemented") + } + + override fun visit(v: VarDecl?): Unit { + TODO("Not yet implemented") + } + + override fun visit(f: Formal?): Unit { + TODO("Not yet implemented") + } +} \ No newline at end of file From 396890c5bb4f911606c872def14c616eec6bcbd9 Mon Sep 17 00:00:00 2001 From: Pedro Vinicius Coelho belem Date: Thu, 4 Apr 2024 12:38:18 -0300 Subject: [PATCH 02/15] feat: add SymbolVisitor interface --- app/build.gradle.kts | 5 ++ .../org/example/visitor/ClassDeclVisitor.kt | 29 +++++++++ .../org/example/visitor/SymbolVisitor.kt | 59 +++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 app/src/main/kotlin/org/example/visitor/ClassDeclVisitor.kt create mode 100644 app/src/main/kotlin/org/example/visitor/SymbolVisitor.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5090a1a..4596945 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -26,10 +26,12 @@ dependencies { implementation(kotlin("stdlib-jdk8")) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1-Beta") testImplementation("org.junit.jupiter:junit-jupiter-params") + implementation("io.arrow-kt:arrow-core:1.2.1") } tasks { compileJavacc { + dependsOn(compileKotlin) inputDirectory = file("src/main/javacc") outputDirectory = file(layout.buildDirectory.dir("generated/javacc")) } @@ -45,6 +47,9 @@ sourceSets { java { srcDir(file(layout.buildDirectory.dir("generated/javacc"))) } + kotlin { + srcDir("src/main/kotlin") + } } } diff --git a/app/src/main/kotlin/org/example/visitor/ClassDeclVisitor.kt b/app/src/main/kotlin/org/example/visitor/ClassDeclVisitor.kt new file mode 100644 index 0000000..c108176 --- /dev/null +++ b/app/src/main/kotlin/org/example/visitor/ClassDeclVisitor.kt @@ -0,0 +1,29 @@ +package org.example.ast2.org.example.visitor + +import arrow.core.Either +import arrow.core.raise.either +import arrow.core.raise.ensure +import org.example.ast.ClassDeclSimple +import org.example.visitor.* + +class ClassDeclVisitor( + override var table: Table +) : SymbolVisitor { + override fun visit(entity: ClassDeclSimple): Either = either { + ensure(!table.contains(entity.className)) { + Error("ClassDeclVisitor: ClassDecl must have a unique name") + } + + table + Table( + ClassData( + name = entity.className, + fields = VarDeclListVisitor(Table()) + .visit(entity.fields.varDecls) + .bind(), + methods = Table( + + ) + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/example/visitor/SymbolVisitor.kt b/app/src/main/kotlin/org/example/visitor/SymbolVisitor.kt new file mode 100644 index 0000000..f87be92 --- /dev/null +++ b/app/src/main/kotlin/org/example/visitor/SymbolVisitor.kt @@ -0,0 +1,59 @@ +package org.example.visitor; + +import arrow.core.* +import org.example.ast.* + +interface ASTVisitor : Visitor + +sealed interface Scope { + val name: Identifier +} + +data class ClassData( + override val name: Identifier, + val fields: Table, + val methods: Table +) : Scope + +data class MethodData( + override val name: Identifier, + val args: Table, + val locals: Table, +) : Scope + +data class ParamData( + override val name: Identifier, + val type: Type +) : Scope + +data class FieldData( + override val name: Identifier, + val type: Type +) : Scope + +data class Table(private val map: Map) { + constructor( + vararg pairs: Scope + ) : this(listOf(*pairs).associateBy { it.name }) + + constructor( + list: ArrayList + ) : this(list.associateBy { it.name }) + + operator fun plus(table: Table): Table = Table(map + table.map) + operator fun plus(pair: Scope): Table = Table(map + (pair.name to pair)) + operator fun get(key: Identifier): Scope? = map[key] + fun contains(key: Identifier): Boolean = map.containsKey(key) + + override fun toString(): String = map.entries + .joinToString(prefix = "{ ", separator = ", ", postfix = " }") { (k, v) -> "$k -> $v" } +} + +@JvmInline +value class Error(val message: String) + +interface SymbolVisitor { + var table : Table + fun visit(entity: T): Either +} + From ab2c1f6edaa4918117216c8689c50a0030090717 Mon Sep 17 00:00:00 2001 From: Pedro Vinicius Coelho belem Date: Thu, 4 Apr 2024 12:39:00 -0300 Subject: [PATCH 03/15] feat: remove dead code --- .../kotlin/org/example/visitor/ASTVisitor.kt | 4 - .../org/example/visitor/ASTVisitorImpl.kt | 144 ------------------ 2 files changed, 148 deletions(-) delete mode 100644 app/src/main/kotlin/org/example/visitor/ASTVisitor.kt delete mode 100644 app/src/main/kotlin/org/example/visitor/ASTVisitorImpl.kt diff --git a/app/src/main/kotlin/org/example/visitor/ASTVisitor.kt b/app/src/main/kotlin/org/example/visitor/ASTVisitor.kt deleted file mode 100644 index c991557..0000000 --- a/app/src/main/kotlin/org/example/visitor/ASTVisitor.kt +++ /dev/null @@ -1,4 +0,0 @@ -package org.example.visitor; - -interface ASTVisitor : Visitor - diff --git a/app/src/main/kotlin/org/example/visitor/ASTVisitorImpl.kt b/app/src/main/kotlin/org/example/visitor/ASTVisitorImpl.kt deleted file mode 100644 index a701c0c..0000000 --- a/app/src/main/kotlin/org/example/visitor/ASTVisitorImpl.kt +++ /dev/null @@ -1,144 +0,0 @@ -package org.example.visitor - -import jdk.jshell.spi.ExecutionControl.NotImplementedException -import org.example.ast.* - -object ASTVisitorImpl : ASTVisitor { - private fun acceptThis(block: A.(ASTVisitor) -> Unit): A.() -> Unit = - { block(ASTVisitorImpl) } - - - override fun visit(program: Program?): Unit = program!!.run { - acceptThis(MainClass::accept)(mainClass) - classes.classDecls - .forEach(acceptThis(ClassDecl::accept)) - } - - override fun visit(a: And?): Unit = a!!.run { - acceptThis(Expression::accept)(lhe) - acceptThis(Expression::accept)(rhe) - } - - override fun visit(b: BooleanType?) { - TODO("Not yet implemented") - } - - override fun visit(n: Not?): Unit = n!!.run { - acceptThis(Expression::accept)(e) - } - - override fun visit(t: True?): Unit = Unit - - override fun visit(f: False?): Unit = Unit - - override fun visit(i: Identifier?): Unit = Unit - - override fun visit(c: Call?): Unit = c!!.run { - acceptThis(Expression::accept)(owner) - acceptThis(Identifier::accept)(method) - expressionList.list.forEach(acceptThis(Expression::accept)) - } - - override fun visit(i: IdentifierExpression?): Unit = Unit - - override fun visit(i: IdentifierType?): Unit = - throw Exception("Wrong Visitor Exception") - - override fun visit(n: NewObject?): Unit = n!!.run { - acceptThis(Identifier::accept)(identifier) - } - - override fun visit(t: This?): Unit = Unit - - override fun visit(a: ArrayLookup?): Unit = a!!.run { - acceptThis(Expression::accept)(array) - acceptThis(Expression::accept)(idx) - } - - override fun visit(a: ArrayAssign?): Unit = a!!.run { - acceptThis(Identifier::accept)(identifier) - acceptThis(Expression::accept)(index) - acceptThis(Expression::accept)(value) - } - - override fun visit(a: ArrayLength?): Unit { - TODO("Not yet implemented") - } - - override fun visit(p: Plus?): Unit { - TODO("Not yet implemented") - } - - override fun visit(m: Minus?): Unit { - TODO("Not yet implemented") - } - - override fun visit(t: Times?): Unit { - TODO("Not yet implemented") - } - - override fun visit(i: IntegerLiteral?): Unit { - TODO("Not yet implemented") - } - - override fun visit(i: IntegerType?): Unit = throw Exception("Wrong Visitor Exception") - - override fun visit(i: IntArrayType?): Unit = throw Exception("Wrong Visitor Exception") - - override fun visit(l: LessThan?): Unit = l!!.run { - acceptThis(Expression::accept)(lhe) - acceptThis(Expression::accept)(rhe) - } - - override fun visit(n: NewArray?): Unit = n!!.run { - acceptThis(Expression::accept)(size) - } - - override fun visit(w: While?): Unit = w!!.run { - acceptThis(Expression::accept)(condition) - acceptThis(Statement::accept)(body) - } - - override fun visit(i: If?): Unit = i!!.run { - acceptThis(Expression::accept)(condition) - acceptThis(Statement::accept)(thenBranch) - acceptThis(Statement::accept)(elseBranch) - } - - override fun visit(a: Assign?): Unit = a!!.run { - acceptThis(Identifier::accept)(identifier) - acceptThis(Expression::accept)(value) - } - - override fun visit(s: Sout?): Unit = s!!.run { - acceptThis(Expression::accept)(expression) - } - - override fun visit(b: Block?): Unit = b!!.run { - statements.statements - .forEach(acceptThis(Statement::accept)) - } - - override fun visit(m: MainClass?): Unit = TODO() - - - override fun visit(c: ClassDeclSimple?): Unit { - TODO("Not yet implemented") - } - - override fun visit(c: ClassDeclExtends?): Unit { - TODO("Not yet implemented") - } - - override fun visit(m: MethodDecl?): Unit { - TODO("Not yet implemented") - } - - override fun visit(v: VarDecl?): Unit { - TODO("Not yet implemented") - } - - override fun visit(f: Formal?): Unit { - TODO("Not yet implemented") - } -} \ No newline at end of file From fb3b2a85f0204a31a9c6b8d0d66d1bb0ca308ac1 Mon Sep 17 00:00:00 2001 From: Pedro Vinicius Coelho belem Date: Thu, 4 Apr 2024 12:39:11 -0300 Subject: [PATCH 04/15] feat: add MainClassVisitor --- .../org/example/visitor/MainClassVisitor.kt | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 app/src/main/kotlin/org/example/visitor/MainClassVisitor.kt diff --git a/app/src/main/kotlin/org/example/visitor/MainClassVisitor.kt b/app/src/main/kotlin/org/example/visitor/MainClassVisitor.kt new file mode 100644 index 0000000..bcb9892 --- /dev/null +++ b/app/src/main/kotlin/org/example/visitor/MainClassVisitor.kt @@ -0,0 +1,36 @@ +package org.example.ast2.org.example.visitor + +import arrow.core.Either +import arrow.core.raise.either +import arrow.core.raise.ensure +import org.example.ast.Identifier +import org.example.ast.IdentifierType +import org.example.ast.MainClass +import org.example.visitor.* + +class MainClassVisitor(override var table: Table) : SymbolVisitor { + override fun visit(entity: MainClass): Either = either { + ensure(!table.contains(entity.className)) { + Error("MainClassVisitor: MainClass must have a unique name") + } + + Table( + ClassData( + name = entity.className, + fields = Table(), + methods = Table( + MethodData( + name = Identifier("main"), + args = Table( + ParamData( + name = Identifier("args"), + type = IdentifierType("String[]") + ) + ), + locals = Table() + ) + ) + ) + ) + } +} \ No newline at end of file From 4ea65a3027564c81cf2ce6cfede525178d6195af Mon Sep 17 00:00:00 2001 From: Pedro Vinicius Coelho belem Date: Thu, 4 Apr 2024 12:39:28 -0300 Subject: [PATCH 05/15] feat: add VarDeclVisitor --- .../org/example/visitor/VarDeclVisitor.kt | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 app/src/main/kotlin/org/example/visitor/VarDeclVisitor.kt diff --git a/app/src/main/kotlin/org/example/visitor/VarDeclVisitor.kt b/app/src/main/kotlin/org/example/visitor/VarDeclVisitor.kt new file mode 100644 index 0000000..8f4e5c5 --- /dev/null +++ b/app/src/main/kotlin/org/example/visitor/VarDeclVisitor.kt @@ -0,0 +1,39 @@ +package org.example.ast2.org.example.visitor + +import arrow.core.Either +import arrow.core.mapOrAccumulate +import arrow.core.raise.either +import arrow.core.raise.ensure +import arrow.core.raise.zipOrAccumulate +import arrow.core.right +import org.example.ast.Identifier +import org.example.ast.VarDecl +import org.example.visitor.* + +class VarDeclListVisitor(override var table: Table) : SymbolVisitor> { + override fun visit(entity: List): Either = either { + VarDeclVisitor(table).run { + entity.forEach { + ensure(!table.contains(Identifier(it.name))) { + Error("VarDeclListVisitor: VarDecl must have a unique name") + } + + table += visit(it).bind() + } + + this@VarDeclListVisitor.table = table + } + + table + } +} + +class VarDeclVisitor(override var table: Table) : SymbolVisitor { + override fun visit(entity: VarDecl): Either = either { + ensure(!table.contains(Identifier(entity.name))) { + Error("VarDeclVisitor: VarDecl must have a unique name") + } + + Table(FieldData(Identifier(entity.name), entity.type)) + } +} \ No newline at end of file From 421f7c5eb8b2ab92e1e4f33ce5c7219e10ee9213 Mon Sep 17 00:00:00 2001 From: Pedro Vinicius Coelho belem Date: Thu, 4 Apr 2024 13:31:33 -0300 Subject: [PATCH 06/15] fix: type matching --- .../org/example/visitor/ClassDeclVisitor.kt | 22 +++-- .../org/example/visitor/MainClassVisitor.kt | 17 ++-- .../org/example/visitor/MethodDeclVisitor.kt | 80 +++++++++++++++++++ .../org/example/visitor/SymbolVisitor.kt | 39 +++++---- .../org/example/visitor/VarDeclVisitor.kt | 26 +++--- 5 files changed, 132 insertions(+), 52 deletions(-) create mode 100644 app/src/main/kotlin/org/example/visitor/MethodDeclVisitor.kt diff --git a/app/src/main/kotlin/org/example/visitor/ClassDeclVisitor.kt b/app/src/main/kotlin/org/example/visitor/ClassDeclVisitor.kt index c108176..e79009d 100644 --- a/app/src/main/kotlin/org/example/visitor/ClassDeclVisitor.kt +++ b/app/src/main/kotlin/org/example/visitor/ClassDeclVisitor.kt @@ -1,28 +1,26 @@ -package org.example.ast2.org.example.visitor +package org.example.visitor import arrow.core.Either import arrow.core.raise.either import arrow.core.raise.ensure import org.example.ast.ClassDeclSimple -import org.example.visitor.* +import org.example.ast2.org.example.visitor.MethodDeclListVisitor -class ClassDeclVisitor( - override var table: Table -) : SymbolVisitor { +class ClassDeclVisitor : SymbolVisitor() { override fun visit(entity: ClassDeclSimple): Either = either { - ensure(!table.contains(entity.className)) { + ensure(!table.contains(entity.className.s)) { Error("ClassDeclVisitor: ClassDecl must have a unique name") } table + Table( ClassData( - name = entity.className, - fields = VarDeclListVisitor(Table()) - .visit(entity.fields.varDecls) + name = entity.className.s, + fields = VarDeclListVisitor() + .visit(entity.fields) .bind(), - methods = Table( - - ) + methods = MethodDeclListVisitor() + .visit(entity.methods) + .bind() ) ) } diff --git a/app/src/main/kotlin/org/example/visitor/MainClassVisitor.kt b/app/src/main/kotlin/org/example/visitor/MainClassVisitor.kt index bcb9892..a5e0a4c 100644 --- a/app/src/main/kotlin/org/example/visitor/MainClassVisitor.kt +++ b/app/src/main/kotlin/org/example/visitor/MainClassVisitor.kt @@ -1,30 +1,27 @@ -package org.example.ast2.org.example.visitor +package org.example.visitor import arrow.core.Either import arrow.core.raise.either import arrow.core.raise.ensure -import org.example.ast.Identifier -import org.example.ast.IdentifierType import org.example.ast.MainClass -import org.example.visitor.* -class MainClassVisitor(override var table: Table) : SymbolVisitor { +class MainClassVisitor : SymbolVisitor() { override fun visit(entity: MainClass): Either = either { - ensure(!table.contains(entity.className)) { + ensure(!table.contains(entity.className.s)) { Error("MainClassVisitor: MainClass must have a unique name") } Table( ClassData( - name = entity.className, + name = entity.className.s, fields = Table(), methods = Table( MethodData( - name = Identifier("main"), + name = entity.className.s, args = Table( ParamData( - name = Identifier("args"), - type = IdentifierType("String[]") + name = entity.argsName.s, + type = "String[]" ) ), locals = Table() diff --git a/app/src/main/kotlin/org/example/visitor/MethodDeclVisitor.kt b/app/src/main/kotlin/org/example/visitor/MethodDeclVisitor.kt new file mode 100644 index 0000000..278592c --- /dev/null +++ b/app/src/main/kotlin/org/example/visitor/MethodDeclVisitor.kt @@ -0,0 +1,80 @@ +package org.example.ast2.org.example.visitor + +import arrow.core.Either +import arrow.core.raise.either +import arrow.core.raise.ensure +import org.example.ast.Formal +import org.example.ast.FormalList +import org.example.ast.Identifier +import org.example.ast.MethodDecl +import org.example.ast.MethodDeclList +import org.example.visitor.* + +class FormalsListVisitor : SymbolVisitor() { + override fun visit(entity: FormalList): Either = either { + FormalsVisitor().run { + entity.formals.forEach { + ensure(!table.contains(it.name)) { + Error("FormalsListVisitor: Formals must have unique names") + } + + table += visit(it).bind() + } + + this@FormalsListVisitor.table = table + } + + table + } +} + +class FormalsVisitor : SymbolVisitor() { + override fun visit(entity: Formal): Either = either { + ensure(!table.contains(entity.name)) { + Error("FormalsVisitor: Formals must have unique names") + } + + Table( + FormalData( + name = entity.name, + type = entity.type.toString() + ) + ) + } +} + +class MethodDeclListVisitor : SymbolVisitor() { + override fun visit(entity: MethodDeclList): Either = either { + MethodDeclVisitor().run { + entity.methodDecls.forEach { + ensure(!table.contains(it.identifier)) { + Error("MethodDeclListVisitor: MethodDecl must have a unique name") + } + + table += visit(it).bind() + } + } + + table + } +} + +class MethodDeclVisitor : SymbolVisitor() { + override fun visit(entity: MethodDecl): Either = either { + ensure(!table.contains(entity.identifier)) { + Error("MethodDeclVisitor: MethodDecl must have a unique name") + } + + Table( + MethodData( + name = entity.identifier, + args = FormalsListVisitor() + .visit(entity.formals) + .bind(), + locals = VarDeclListVisitor() + .visit(entity.varDecls) + .bind() + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/example/visitor/SymbolVisitor.kt b/app/src/main/kotlin/org/example/visitor/SymbolVisitor.kt index f87be92..f96ee76 100644 --- a/app/src/main/kotlin/org/example/visitor/SymbolVisitor.kt +++ b/app/src/main/kotlin/org/example/visitor/SymbolVisitor.kt @@ -5,33 +5,38 @@ import org.example.ast.* interface ASTVisitor : Visitor +@JvmInline +value class Id(val name: String) { + constructor(name: Identifier) : this(name.s) +} + sealed interface Scope { - val name: Identifier + val name: String } data class ClassData( - override val name: Identifier, + override val name: String, val fields: Table, val methods: Table ) : Scope +data class ParamData( + override val name: String, + val type: String +) : Scope + data class MethodData( - override val name: Identifier, + override val name: String, val args: Table, val locals: Table, ) : Scope -data class ParamData( - override val name: Identifier, - val type: Type -) : Scope - -data class FieldData( - override val name: Identifier, - val type: Type +data class FormalData( + override val name: String, + val type: String ) : Scope -data class Table(private val map: Map) { +data class Table(private val map: Map) { constructor( vararg pairs: Scope ) : this(listOf(*pairs).associateBy { it.name }) @@ -42,8 +47,8 @@ data class Table(private val map: Map) { operator fun plus(table: Table): Table = Table(map + table.map) operator fun plus(pair: Scope): Table = Table(map + (pair.name to pair)) - operator fun get(key: Identifier): Scope? = map[key] - fun contains(key: Identifier): Boolean = map.containsKey(key) + operator fun get(key: String): Scope? = map[key] + fun contains(key: String): Boolean = map.containsKey(key) override fun toString(): String = map.entries .joinToString(prefix = "{ ", separator = ", ", postfix = " }") { (k, v) -> "$k -> $v" } @@ -52,8 +57,8 @@ data class Table(private val map: Map) { @JvmInline value class Error(val message: String) -interface SymbolVisitor { - var table : Table - fun visit(entity: T): Either +abstract class SymbolVisitor { + var table : Table = Table() + abstract fun visit(entity: T): Either } diff --git a/app/src/main/kotlin/org/example/visitor/VarDeclVisitor.kt b/app/src/main/kotlin/org/example/visitor/VarDeclVisitor.kt index 8f4e5c5..4fd61be 100644 --- a/app/src/main/kotlin/org/example/visitor/VarDeclVisitor.kt +++ b/app/src/main/kotlin/org/example/visitor/VarDeclVisitor.kt @@ -1,20 +1,17 @@ -package org.example.ast2.org.example.visitor +package org.example.visitor import arrow.core.Either -import arrow.core.mapOrAccumulate import arrow.core.raise.either import arrow.core.raise.ensure -import arrow.core.raise.zipOrAccumulate -import arrow.core.right import org.example.ast.Identifier import org.example.ast.VarDecl -import org.example.visitor.* +import org.example.ast.VarDeclList -class VarDeclListVisitor(override var table: Table) : SymbolVisitor> { - override fun visit(entity: List): Either = either { - VarDeclVisitor(table).run { - entity.forEach { - ensure(!table.contains(Identifier(it.name))) { +class VarDeclListVisitor : SymbolVisitor() { + override fun visit(entity: VarDeclList): Either = either { + VarDeclVisitor().run { + entity.varDecls.forEach { + ensure(!table.contains(it.name)) { Error("VarDeclListVisitor: VarDecl must have a unique name") } @@ -28,12 +25,15 @@ class VarDeclListVisitor(override var table: Table) : SymbolVisitor { +class VarDeclVisitor : SymbolVisitor() { override fun visit(entity: VarDecl): Either = either { - ensure(!table.contains(Identifier(entity.name))) { + ensure(!table.contains(entity.name)) { Error("VarDeclVisitor: VarDecl must have a unique name") } - Table(FieldData(Identifier(entity.name), entity.type)) + table + FormalData( + name = entity.name, + type = entity.type.toString() + ) } } \ No newline at end of file From 590c4a24fcb9e6e269707857ab0ebb359f60ef01 Mon Sep 17 00:00:00 2001 From: Pedro Vinicius Coelho belem Date: Thu, 4 Apr 2024 13:32:08 -0300 Subject: [PATCH 07/15] crime: private -> public --- app/src/main/java/org/example/ast/ClassDeclSimple.java | 6 +++--- app/src/main/java/org/example/ast/Formal.java | 4 ++-- app/src/main/java/org/example/ast/FormalList.java | 2 +- app/src/main/java/org/example/ast/Identifier.java | 2 +- app/src/main/java/org/example/ast/MainClass.java | 4 ++-- app/src/main/java/org/example/ast/MethodDecl.java | 8 ++++---- app/src/main/java/org/example/ast/MethodDeclList.java | 2 +- app/src/main/java/org/example/ast/VarDecl.java | 4 ++-- app/src/main/java/org/example/ast/VarDeclList.java | 2 +- 9 files changed, 17 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/org/example/ast/ClassDeclSimple.java b/app/src/main/java/org/example/ast/ClassDeclSimple.java index 26b0ebc..3e5c851 100644 --- a/app/src/main/java/org/example/ast/ClassDeclSimple.java +++ b/app/src/main/java/org/example/ast/ClassDeclSimple.java @@ -12,11 +12,11 @@ @Builder @AllArgsConstructor public class ClassDeclSimple extends ClassDecl { - private Identifier className; + public Identifier className; @Builder.Default - private VarDeclList fields = new VarDeclList(); + public VarDeclList fields = new VarDeclList(); @Builder.Default - private MethodDeclList methods = new MethodDeclList(); + public MethodDeclList methods = new MethodDeclList(); @Override public void accept(ASTVisitor v) { diff --git a/app/src/main/java/org/example/ast/Formal.java b/app/src/main/java/org/example/ast/Formal.java index 9239d51..70085b8 100644 --- a/app/src/main/java/org/example/ast/Formal.java +++ b/app/src/main/java/org/example/ast/Formal.java @@ -12,8 +12,8 @@ @Builder @AllArgsConstructor public class Formal extends Node { - private Type type; - private String name; + public Type type; + public String name; @Override public void accept(ASTVisitor v) { diff --git a/app/src/main/java/org/example/ast/FormalList.java b/app/src/main/java/org/example/ast/FormalList.java index 092ed33..02d1276 100644 --- a/app/src/main/java/org/example/ast/FormalList.java +++ b/app/src/main/java/org/example/ast/FormalList.java @@ -11,7 +11,7 @@ @EqualsAndHashCode public class FormalList { @Builder.Default - private ArrayList formals = new ArrayList<>(); + public ArrayList formals = new ArrayList<>(); public void addFormal(Formal formal) { formals.add(formal); diff --git a/app/src/main/java/org/example/ast/Identifier.java b/app/src/main/java/org/example/ast/Identifier.java index d0315ec..280ffd9 100644 --- a/app/src/main/java/org/example/ast/Identifier.java +++ b/app/src/main/java/org/example/ast/Identifier.java @@ -12,7 +12,7 @@ @Data @AllArgsConstructor public class Identifier extends Expression { - private String s; + public String s; @Override public void accept(ASTVisitor v) { v.visit(this); diff --git a/app/src/main/java/org/example/ast/MainClass.java b/app/src/main/java/org/example/ast/MainClass.java index 37a8eee..dc4f504 100644 --- a/app/src/main/java/org/example/ast/MainClass.java +++ b/app/src/main/java/org/example/ast/MainClass.java @@ -12,8 +12,8 @@ @Builder @AllArgsConstructor public class MainClass extends Node { - private Identifier className; - private Identifier argsName; + public Identifier className; + public Identifier argsName; private StatementList statements; @Override diff --git a/app/src/main/java/org/example/ast/MethodDecl.java b/app/src/main/java/org/example/ast/MethodDecl.java index 8f10193..78cf768 100644 --- a/app/src/main/java/org/example/ast/MethodDecl.java +++ b/app/src/main/java/org/example/ast/MethodDecl.java @@ -12,10 +12,10 @@ @Builder @AllArgsConstructor public class MethodDecl extends Node { - private Type type; - private String identifier; - private FormalList formals; - private VarDeclList varDecls; + public Type type; + public String identifier; + public FormalList formals; + public VarDeclList varDecls; private StatementList statements; private Expression returnExpression; diff --git a/app/src/main/java/org/example/ast/MethodDeclList.java b/app/src/main/java/org/example/ast/MethodDeclList.java index abb5c83..6408840 100644 --- a/app/src/main/java/org/example/ast/MethodDeclList.java +++ b/app/src/main/java/org/example/ast/MethodDeclList.java @@ -11,7 +11,7 @@ @EqualsAndHashCode public class MethodDeclList { @Builder.Default - private ArrayList methodDecls = new ArrayList<>(); + public ArrayList methodDecls = new ArrayList<>(); public void addMethodDecl(MethodDecl methodDecl) { methodDecls.add(methodDecl); diff --git a/app/src/main/java/org/example/ast/VarDecl.java b/app/src/main/java/org/example/ast/VarDecl.java index d7a088f..07d0ce3 100644 --- a/app/src/main/java/org/example/ast/VarDecl.java +++ b/app/src/main/java/org/example/ast/VarDecl.java @@ -12,8 +12,8 @@ @Builder @AllArgsConstructor public class VarDecl extends Node { - private Type type; - private String name; + public Type type; + public String name; @Override public void accept(ASTVisitor v) { diff --git a/app/src/main/java/org/example/ast/VarDeclList.java b/app/src/main/java/org/example/ast/VarDeclList.java index 046d6b3..2609de4 100644 --- a/app/src/main/java/org/example/ast/VarDeclList.java +++ b/app/src/main/java/org/example/ast/VarDeclList.java @@ -13,7 +13,7 @@ @NoArgsConstructor public class VarDeclList { @Builder.Default - private ArrayList varDecls = new ArrayList<>(); + public ArrayList varDecls = new ArrayList<>(); public void addVarDecl(VarDecl varDecl) { varDecls.add(varDecl); From 86246c158e53d2bc8380340b6b9cb8daa669f242 Mon Sep 17 00:00:00 2001 From: Pedro Vinicius Coelho belem Date: Sun, 7 Apr 2024 09:34:48 -0300 Subject: [PATCH 08/15] feat: add mockk dependency --- app/build.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4596945..b53593b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -27,6 +27,8 @@ dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1-Beta") testImplementation("org.junit.jupiter:junit-jupiter-params") implementation("io.arrow-kt:arrow-core:1.2.1") + + testImplementation("io.mockk:mockk:1.12.0") } tasks { From f992f0e0c99bdf992bf25167bd80aca7000ded82 Mon Sep 17 00:00:00 2001 From: Pedro Vinicius Coelho belem Date: Sun, 7 Apr 2024 09:36:53 -0300 Subject: [PATCH 09/15] refactor: make visitor stateless --- .../org/example/visitor/ClassDeclVisitor.kt | 82 +++++++++++++++---- .../org/example/visitor/MainClassVisitor.kt | 6 +- .../org/example/visitor/MethodDeclVisitor.kt | 64 ++++++--------- .../org/example/visitor/ProgramVisitor.kt | 18 ++++ .../org/example/visitor/SymbolVisitor.kt | 9 +- .../org/example/visitor/VarDeclVisitor.kt | 30 +++---- 6 files changed, 131 insertions(+), 78 deletions(-) create mode 100644 app/src/main/kotlin/org/example/visitor/ProgramVisitor.kt diff --git a/app/src/main/kotlin/org/example/visitor/ClassDeclVisitor.kt b/app/src/main/kotlin/org/example/visitor/ClassDeclVisitor.kt index e79009d..b4dc8e8 100644 --- a/app/src/main/kotlin/org/example/visitor/ClassDeclVisitor.kt +++ b/app/src/main/kotlin/org/example/visitor/ClassDeclVisitor.kt @@ -3,25 +3,79 @@ package org.example.visitor import arrow.core.Either import arrow.core.raise.either import arrow.core.raise.ensure -import org.example.ast.ClassDeclSimple +import org.example.ast.* import org.example.ast2.org.example.visitor.MethodDeclListVisitor -class ClassDeclVisitor : SymbolVisitor() { - override fun visit(entity: ClassDeclSimple): Either = either { - ensure(!table.contains(entity.className.s)) { +object ClassDeclListVisitor : SymbolVisitor() { + override fun Table.visit(entity: ClassDeclList): Either = + ClassDeclVisitor.fold( + entity.classDecls.toList() + ) { visit(entity) } +} + +object ClassDeclVisitor : SymbolVisitor() { + private fun extractName(entity: ClassDecl): String = + when (entity) { + is ClassDeclSimple -> entity.getClassName().getS() + is ClassDeclExtends -> entity.getClassName().getS() + else -> throw IllegalArgumentException( + "ClassDeclVisitor: ClassDecl must be either ClassDeclSimple or ClassDeclExtends" + ) + } + + object ClassDeclSimpleVisitor : SymbolVisitor() { + override fun Table.visit(entity: ClassDeclSimple): Either = either { + this@visit + Table( + ClassData( + name = extractName(entity), + fields = with(VarDeclListVisitor) { + visit(entity.fields).bind() + }, + methods = with(MethodDeclListVisitor) { + visit(entity.methods).bind() + } + ) + ) + } + } + + object ClassDeclExtendsVisitor : SymbolVisitor() { + override fun Table.visit(entity: ClassDeclExtends): Either = either { + this@visit + Table( + ClassData( + name = extractName(entity), + fields = with(VarDeclListVisitor) { + visit(entity.fields).bind() + }, + methods = with(MethodDeclListVisitor) { + visit(entity.methods).bind() + } + ) + ) + } + } + + override fun Table.visit(entity: ClassDecl): Either = either { + val name = extractName(entity) + + ensure(!this@visit.contains(name)) { Error("ClassDeclVisitor: ClassDecl must have a unique name") } - table + Table( - ClassData( - name = entity.className.s, - fields = VarDeclListVisitor() - .visit(entity.fields) - .bind(), - methods = MethodDeclListVisitor() - .visit(entity.methods) - .bind() + val newTable = when (entity) { + is ClassDeclSimple -> + with(ClassDeclSimpleVisitor) { + visit(entity).bind() + } + is ClassDeclExtends -> + with(ClassDeclExtendsVisitor) { + visit(entity).bind() + } + else -> throw IllegalArgumentException( + "ClassDeclVisitor: ClassDecl must be either ClassDeclSimple or ClassDeclExtends" ) - ) + } + + this@visit + newTable } } \ No newline at end of file diff --git a/app/src/main/kotlin/org/example/visitor/MainClassVisitor.kt b/app/src/main/kotlin/org/example/visitor/MainClassVisitor.kt index a5e0a4c..d7977e9 100644 --- a/app/src/main/kotlin/org/example/visitor/MainClassVisitor.kt +++ b/app/src/main/kotlin/org/example/visitor/MainClassVisitor.kt @@ -5,9 +5,9 @@ import arrow.core.raise.either import arrow.core.raise.ensure import org.example.ast.MainClass -class MainClassVisitor : SymbolVisitor() { - override fun visit(entity: MainClass): Either = either { - ensure(!table.contains(entity.className.s)) { +object MainClassVisitor : SymbolVisitor() { + override fun Table.visit(entity: MainClass): Either = either { + ensure(!contains(entity.className.s)) { Error("MainClassVisitor: MainClass must have a unique name") } diff --git a/app/src/main/kotlin/org/example/visitor/MethodDeclVisitor.kt b/app/src/main/kotlin/org/example/visitor/MethodDeclVisitor.kt index 278592c..99843f2 100644 --- a/app/src/main/kotlin/org/example/visitor/MethodDeclVisitor.kt +++ b/app/src/main/kotlin/org/example/visitor/MethodDeclVisitor.kt @@ -5,32 +5,25 @@ import arrow.core.raise.either import arrow.core.raise.ensure import org.example.ast.Formal import org.example.ast.FormalList -import org.example.ast.Identifier import org.example.ast.MethodDecl import org.example.ast.MethodDeclList import org.example.visitor.* -class FormalsListVisitor : SymbolVisitor() { - override fun visit(entity: FormalList): Either = either { - FormalsVisitor().run { - entity.formals.forEach { - ensure(!table.contains(it.name)) { - Error("FormalsListVisitor: Formals must have unique names") - } - table += visit(it).bind() - } - this@FormalsListVisitor.table = table - } - table - } +object FormalsListVisitor : SymbolVisitor() { + override fun Table.visit(entity: FormalList): Either = + with(FormalsVisitor) { + fold( + entity.formals.toList() + ) { visit(it) } + } } -class FormalsVisitor : SymbolVisitor() { - override fun visit(entity: Formal): Either = either { - ensure(!table.contains(entity.name)) { +object FormalsVisitor : SymbolVisitor() { + override fun Table.visit(entity: Formal): Either = either { + ensure(!contains(entity.name)) { Error("FormalsVisitor: Formals must have unique names") } @@ -43,37 +36,30 @@ class FormalsVisitor : SymbolVisitor() { } } -class MethodDeclListVisitor : SymbolVisitor() { - override fun visit(entity: MethodDeclList): Either = either { - MethodDeclVisitor().run { - entity.methodDecls.forEach { - ensure(!table.contains(it.identifier)) { - Error("MethodDeclListVisitor: MethodDecl must have a unique name") - } - - table += visit(it).bind() - } +object MethodDeclListVisitor : SymbolVisitor() { + override fun Table.visit(entity: MethodDeclList): Either = + with(MethodDeclVisitor) { + fold( + entity.methodDecls.toList() + ) { this@visit.visit(it) } } - - table - } } -class MethodDeclVisitor : SymbolVisitor() { - override fun visit(entity: MethodDecl): Either = either { - ensure(!table.contains(entity.identifier)) { +object MethodDeclVisitor : SymbolVisitor() { + override fun Table.visit(entity: MethodDecl): Either = either { + ensure(!contains(entity.identifier)) { Error("MethodDeclVisitor: MethodDecl must have a unique name") } Table( MethodData( name = entity.identifier, - args = FormalsListVisitor() - .visit(entity.formals) - .bind(), - locals = VarDeclListVisitor() - .visit(entity.varDecls) - .bind() + args = with(FormalsListVisitor) { + visit(entity.formals) + }.bind(), + locals = with(VarDeclListVisitor) { + visit(entity.varDecls) + }.bind() ) ) } diff --git a/app/src/main/kotlin/org/example/visitor/ProgramVisitor.kt b/app/src/main/kotlin/org/example/visitor/ProgramVisitor.kt new file mode 100644 index 0000000..477ab51 --- /dev/null +++ b/app/src/main/kotlin/org/example/visitor/ProgramVisitor.kt @@ -0,0 +1,18 @@ +package org.example.visitor + +import arrow.core.Either +import arrow.core.raise.either +import org.example.ast.Program + +object ProgramVisitor : SymbolVisitor() { + override fun Table.visit(entity: Program): Either = either { + val mainClassTable = with(MainClassVisitor) { + visit(entity.mainClass) + }.bind() + val classDeclListTable = with(ClassDeclListVisitor) { + visit(entity.classes) + }.bind() + + this@visit + mainClassTable + classDeclListTable + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/example/visitor/SymbolVisitor.kt b/app/src/main/kotlin/org/example/visitor/SymbolVisitor.kt index f96ee76..fafe6f6 100644 --- a/app/src/main/kotlin/org/example/visitor/SymbolVisitor.kt +++ b/app/src/main/kotlin/org/example/visitor/SymbolVisitor.kt @@ -58,7 +58,12 @@ data class Table(private val map: Map) { value class Error(val message: String) abstract class SymbolVisitor { - var table : Table = Table() - abstract fun visit(entity: T): Either + fun fold(entityList: List, f: (T) -> Either): Either = + entityList.fold(Table().right()) { + acc: Either, entity -> f(entity) + .flatMap { table -> acc.map { it + table } } + } + + abstract fun Table.visit(entity: T): Either } diff --git a/app/src/main/kotlin/org/example/visitor/VarDeclVisitor.kt b/app/src/main/kotlin/org/example/visitor/VarDeclVisitor.kt index 4fd61be..d0539e9 100644 --- a/app/src/main/kotlin/org/example/visitor/VarDeclVisitor.kt +++ b/app/src/main/kotlin/org/example/visitor/VarDeclVisitor.kt @@ -3,35 +3,25 @@ package org.example.visitor import arrow.core.Either import arrow.core.raise.either import arrow.core.raise.ensure -import org.example.ast.Identifier import org.example.ast.VarDecl import org.example.ast.VarDeclList -class VarDeclListVisitor : SymbolVisitor() { - override fun visit(entity: VarDeclList): Either = either { - VarDeclVisitor().run { - entity.varDecls.forEach { - ensure(!table.contains(it.name)) { - Error("VarDeclListVisitor: VarDecl must have a unique name") - } - - table += visit(it).bind() - } - - this@VarDeclListVisitor.table = table +object VarDeclListVisitor : SymbolVisitor() { + override fun Table.visit(entity: VarDeclList): Either = + with(VarDeclVisitor) { + fold( + entity.varDecls.toList() + ) { visit(it) } } - - table - } } -class VarDeclVisitor : SymbolVisitor() { - override fun visit(entity: VarDecl): Either = either { - ensure(!table.contains(entity.name)) { +object VarDeclVisitor : SymbolVisitor() { + override fun Table.visit(entity: VarDecl): Either = either { + ensure(!contains(entity.name)) { Error("VarDeclVisitor: VarDecl must have a unique name") } - table + FormalData( + this@visit + FormalData( name = entity.name, type = entity.type.toString() ) From c42dc8c474a025fa0a0725bfb3542b2f648e2fb6 Mon Sep 17 00:00:00 2001 From: Pedro Vinicius Coelho belem Date: Sun, 7 Apr 2024 10:23:59 -0300 Subject: [PATCH 10/15] refactor: improve java-kotlin compatibility --- app/src/main/java/org/example/ast/ClassDeclExtends.java | 8 ++++---- app/src/main/java/org/example/ast/ClassDeclList.java | 2 +- app/src/main/java/org/example/ast/ExpressionList.java | 2 +- app/src/main/java/org/example/ast/Identifier.java | 6 ++---- .../main/java/org/example/ast/IdentifierExpression.java | 2 +- app/src/main/java/org/example/ast/IdentifierType.java | 2 +- app/src/main/java/org/example/ast/IntegerType.java | 1 + app/src/main/java/org/example/ast/MainClass.java | 2 +- app/src/main/java/org/example/ast/MethodDecl.java | 4 ++-- app/src/main/java/org/example/ast/Program.java | 4 ++-- app/src/main/java/org/example/ast/Type.java | 2 ++ 11 files changed, 18 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/org/example/ast/ClassDeclExtends.java b/app/src/main/java/org/example/ast/ClassDeclExtends.java index ba7a548..1dbbb63 100644 --- a/app/src/main/java/org/example/ast/ClassDeclExtends.java +++ b/app/src/main/java/org/example/ast/ClassDeclExtends.java @@ -12,12 +12,12 @@ @Builder @AllArgsConstructor public class ClassDeclExtends extends ClassDecl { - private Identifier className; - private Identifier parent; + public Identifier className; + public Identifier parent; @Builder.Default - private VarDeclList fields = new VarDeclList(); + public VarDeclList fields = new VarDeclList(); @Builder.Default - private MethodDeclList methods = new MethodDeclList(); + public MethodDeclList methods = new MethodDeclList(); @Override public void accept(ASTVisitor v) { diff --git a/app/src/main/java/org/example/ast/ClassDeclList.java b/app/src/main/java/org/example/ast/ClassDeclList.java index 2df0cb8..52eb5a2 100644 --- a/app/src/main/java/org/example/ast/ClassDeclList.java +++ b/app/src/main/java/org/example/ast/ClassDeclList.java @@ -11,7 +11,7 @@ @EqualsAndHashCode public class ClassDeclList { @Builder.Default - private ArrayList classDecls = new ArrayList<>(); + public ArrayList classDecls = new ArrayList<>(); public void addClassDecl(ClassDecl classDecl) { classDecls.add(classDecl); diff --git a/app/src/main/java/org/example/ast/ExpressionList.java b/app/src/main/java/org/example/ast/ExpressionList.java index 68b8851..100a4d3 100644 --- a/app/src/main/java/org/example/ast/ExpressionList.java +++ b/app/src/main/java/org/example/ast/ExpressionList.java @@ -11,7 +11,7 @@ @AllArgsConstructor @NoArgsConstructor public class ExpressionList { - private ArrayList list = new ArrayList<>(); + public ArrayList list = new ArrayList<>(); public void addExpression(Expression expression) { list.add(expression); diff --git a/app/src/main/java/org/example/ast/Identifier.java b/app/src/main/java/org/example/ast/Identifier.java index 280ffd9..59fbab8 100644 --- a/app/src/main/java/org/example/ast/Identifier.java +++ b/app/src/main/java/org/example/ast/Identifier.java @@ -1,9 +1,6 @@ package org.example.ast; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; +import lombok.*; import org.example.visitor.ASTVisitor; import org.example.visitor.TypeVisitor; @@ -11,6 +8,7 @@ @ToString @Data @AllArgsConstructor +@Builder public class Identifier extends Expression { public String s; @Override diff --git a/app/src/main/java/org/example/ast/IdentifierExpression.java b/app/src/main/java/org/example/ast/IdentifierExpression.java index 9c6d807..91e7287 100644 --- a/app/src/main/java/org/example/ast/IdentifierExpression.java +++ b/app/src/main/java/org/example/ast/IdentifierExpression.java @@ -12,7 +12,7 @@ @Builder @AllArgsConstructor public class IdentifierExpression extends Expression { - private String id; + public String id; @Override public void accept(ASTVisitor v) { diff --git a/app/src/main/java/org/example/ast/IdentifierType.java b/app/src/main/java/org/example/ast/IdentifierType.java index 1798dbe..9bfe3ed 100644 --- a/app/src/main/java/org/example/ast/IdentifierType.java +++ b/app/src/main/java/org/example/ast/IdentifierType.java @@ -10,7 +10,7 @@ @Data @AllArgsConstructor public class IdentifierType extends Type { - private String s; + public String s; @Override public void accept(ASTVisitor v) { diff --git a/app/src/main/java/org/example/ast/IntegerType.java b/app/src/main/java/org/example/ast/IntegerType.java index d71367a..339f9f4 100644 --- a/app/src/main/java/org/example/ast/IntegerType.java +++ b/app/src/main/java/org/example/ast/IntegerType.java @@ -1,5 +1,6 @@ package org.example.ast; +import lombok.Builder; import lombok.EqualsAndHashCode; import org.example.visitor.ASTVisitor; import org.example.visitor.TypeVisitor; diff --git a/app/src/main/java/org/example/ast/MainClass.java b/app/src/main/java/org/example/ast/MainClass.java index dc4f504..aaab08b 100644 --- a/app/src/main/java/org/example/ast/MainClass.java +++ b/app/src/main/java/org/example/ast/MainClass.java @@ -14,7 +14,7 @@ public class MainClass extends Node { public Identifier className; public Identifier argsName; - private StatementList statements; + public StatementList statements; @Override public void accept(ASTVisitor v) { diff --git a/app/src/main/java/org/example/ast/MethodDecl.java b/app/src/main/java/org/example/ast/MethodDecl.java index 78cf768..a236bee 100644 --- a/app/src/main/java/org/example/ast/MethodDecl.java +++ b/app/src/main/java/org/example/ast/MethodDecl.java @@ -16,8 +16,8 @@ public class MethodDecl extends Node { public String identifier; public FormalList formals; public VarDeclList varDecls; - private StatementList statements; - private Expression returnExpression; + public StatementList statements; + public Expression returnExpression; @Override public void accept(ASTVisitor v) { diff --git a/app/src/main/java/org/example/ast/Program.java b/app/src/main/java/org/example/ast/Program.java index d1a4a5f..f6dec84 100644 --- a/app/src/main/java/org/example/ast/Program.java +++ b/app/src/main/java/org/example/ast/Program.java @@ -12,8 +12,8 @@ @Builder @AllArgsConstructor public class Program extends Node { - private MainClass mainClass; - private ClassDeclList classes; + public MainClass mainClass; + public ClassDeclList classes; @Override public void accept(ASTVisitor v) { diff --git a/app/src/main/java/org/example/ast/Type.java b/app/src/main/java/org/example/ast/Type.java index dacbe82..7c7ee37 100644 --- a/app/src/main/java/org/example/ast/Type.java +++ b/app/src/main/java/org/example/ast/Type.java @@ -1,4 +1,6 @@ package org.example.ast; +import lombok.Builder; + public abstract class Type extends Node { } From 56918060cf6eeed7b585630e3d968563b431eeee Mon Sep 17 00:00:00 2001 From: Pedro Vinicius Coelho belem Date: Sun, 7 Apr 2024 10:24:40 -0300 Subject: [PATCH 11/15] feat: add assertThat dependency --- app/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b53593b..c0bad9f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -29,6 +29,7 @@ dependencies { implementation("io.arrow-kt:arrow-core:1.2.1") testImplementation("io.mockk:mockk:1.12.0") + testImplementation("org.assertj:assertj-core:3.25.1") } tasks { From d8ca793177cd112cfc7d638265f3bb9faf4dbf11 Mon Sep 17 00:00:00 2001 From: Pedro Vinicius Coelho belem Date: Sun, 7 Apr 2024 12:10:47 -0300 Subject: [PATCH 12/15] refactor: improve visitor mock-ability --- app/build.gradle.kts | 2 +- .../org/example/visitor/ClassDeclVisitor.kt | 17 +++++------ .../org/example/visitor/MainClassVisitor.kt | 7 ++++- .../org/example/visitor/MethodDeclVisitor.kt | 29 +++++++++---------- .../org/example/visitor/ProgramVisitor.kt | 16 +++++----- .../org/example/visitor/SymbolVisitor.kt | 23 +++++++++++++-- .../org/example/visitor/VarDeclVisitor.kt | 4 +-- 7 files changed, 59 insertions(+), 39 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c0bad9f..329ff2a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -28,7 +28,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-params") implementation("io.arrow-kt:arrow-core:1.2.1") - testImplementation("io.mockk:mockk:1.12.0") + testImplementation("io.mockk:mockk:1.13.10") testImplementation("org.assertj:assertj-core:3.25.1") } diff --git a/app/src/main/kotlin/org/example/visitor/ClassDeclVisitor.kt b/app/src/main/kotlin/org/example/visitor/ClassDeclVisitor.kt index b4dc8e8..e75d71f 100644 --- a/app/src/main/kotlin/org/example/visitor/ClassDeclVisitor.kt +++ b/app/src/main/kotlin/org/example/visitor/ClassDeclVisitor.kt @@ -5,32 +5,31 @@ import arrow.core.raise.either import arrow.core.raise.ensure import org.example.ast.* import org.example.ast2.org.example.visitor.MethodDeclListVisitor +import org.example.visitor.SymbolVisitor.Companion.dispatch -object ClassDeclListVisitor : SymbolVisitor() { +object ClassDeclListVisitor : SymbolVisitor { override fun Table.visit(entity: ClassDeclList): Either = ClassDeclVisitor.fold( entity.classDecls.toList() ) { visit(entity) } } -object ClassDeclVisitor : SymbolVisitor() { +object ClassDeclVisitor : SymbolVisitor { private fun extractName(entity: ClassDecl): String = when (entity) { - is ClassDeclSimple -> entity.getClassName().getS() - is ClassDeclExtends -> entity.getClassName().getS() + is ClassDeclSimple -> entity.className.s + is ClassDeclExtends -> entity.className.s else -> throw IllegalArgumentException( "ClassDeclVisitor: ClassDecl must be either ClassDeclSimple or ClassDeclExtends" ) } - object ClassDeclSimpleVisitor : SymbolVisitor() { + object ClassDeclSimpleVisitor : SymbolVisitor { override fun Table.visit(entity: ClassDeclSimple): Either = either { this@visit + Table( ClassData( name = extractName(entity), - fields = with(VarDeclListVisitor) { - visit(entity.fields).bind() - }, + fields = dispatch(entity.fields, table = this@visit).bind(), methods = with(MethodDeclListVisitor) { visit(entity.methods).bind() } @@ -39,7 +38,7 @@ object ClassDeclVisitor : SymbolVisitor() { } } - object ClassDeclExtendsVisitor : SymbolVisitor() { + object ClassDeclExtendsVisitor : SymbolVisitor { override fun Table.visit(entity: ClassDeclExtends): Either = either { this@visit + Table( ClassData( diff --git a/app/src/main/kotlin/org/example/visitor/MainClassVisitor.kt b/app/src/main/kotlin/org/example/visitor/MainClassVisitor.kt index d7977e9..22e4618 100644 --- a/app/src/main/kotlin/org/example/visitor/MainClassVisitor.kt +++ b/app/src/main/kotlin/org/example/visitor/MainClassVisitor.kt @@ -5,11 +5,14 @@ import arrow.core.raise.either import arrow.core.raise.ensure import org.example.ast.MainClass -object MainClassVisitor : SymbolVisitor() { +object MainClassVisitor : SymbolVisitor { override fun Table.visit(entity: MainClass): Either = either { ensure(!contains(entity.className.s)) { Error("MainClassVisitor: MainClass must have a unique name") } + ensure(entity.argsName.s == "args") { + Error("MainClassVisitor: MainClass args must be named 'args'") + } Table( ClassData( @@ -30,4 +33,6 @@ object MainClassVisitor : SymbolVisitor() { ) ) } + + } \ No newline at end of file diff --git a/app/src/main/kotlin/org/example/visitor/MethodDeclVisitor.kt b/app/src/main/kotlin/org/example/visitor/MethodDeclVisitor.kt index 99843f2..0917db4 100644 --- a/app/src/main/kotlin/org/example/visitor/MethodDeclVisitor.kt +++ b/app/src/main/kotlin/org/example/visitor/MethodDeclVisitor.kt @@ -8,20 +8,20 @@ import org.example.ast.FormalList import org.example.ast.MethodDecl import org.example.ast.MethodDeclList import org.example.visitor.* +import org.example.visitor.SymbolVisitor.Companion.dispatch - - -object FormalsListVisitor : SymbolVisitor() { +object FormalsListVisitor : SymbolVisitor { override fun Table.visit(entity: FormalList): Either = with(FormalsVisitor) { fold( - entity.formals.toList() - ) { visit(it) } + entity.formals.toList(), + ::dispatch + ) } } -object FormalsVisitor : SymbolVisitor() { +object FormalsVisitor : SymbolVisitor { override fun Table.visit(entity: Formal): Either = either { ensure(!contains(entity.name)) { Error("FormalsVisitor: Formals must have unique names") @@ -36,16 +36,17 @@ object FormalsVisitor : SymbolVisitor() { } } -object MethodDeclListVisitor : SymbolVisitor() { +object MethodDeclListVisitor : SymbolVisitor { override fun Table.visit(entity: MethodDeclList): Either = with(MethodDeclVisitor) { fold( - entity.methodDecls.toList() - ) { this@visit.visit(it) } + entity.methodDecls.toList(), + ::dispatch + ) } } -object MethodDeclVisitor : SymbolVisitor() { +object MethodDeclVisitor : SymbolVisitor { override fun Table.visit(entity: MethodDecl): Either = either { ensure(!contains(entity.identifier)) { Error("MethodDeclVisitor: MethodDecl must have a unique name") @@ -54,12 +55,8 @@ object MethodDeclVisitor : SymbolVisitor() { Table( MethodData( name = entity.identifier, - args = with(FormalsListVisitor) { - visit(entity.formals) - }.bind(), - locals = with(VarDeclListVisitor) { - visit(entity.varDecls) - }.bind() + args = dispatch(entity.formals).bind(), + locals = dispatch(entity.varDecls).bind() ) ) } diff --git a/app/src/main/kotlin/org/example/visitor/ProgramVisitor.kt b/app/src/main/kotlin/org/example/visitor/ProgramVisitor.kt index 477ab51..0a9ba5d 100644 --- a/app/src/main/kotlin/org/example/visitor/ProgramVisitor.kt +++ b/app/src/main/kotlin/org/example/visitor/ProgramVisitor.kt @@ -1,18 +1,18 @@ package org.example.visitor import arrow.core.Either +import arrow.core.flatMap +import arrow.core.left import arrow.core.raise.either import org.example.ast.Program +import org.example.visitor.SymbolVisitor.Companion.dispatch -object ProgramVisitor : SymbolVisitor() { +object ProgramVisitor : SymbolVisitor { override fun Table.visit(entity: Program): Either = either { - val mainClassTable = with(MainClassVisitor) { - visit(entity.mainClass) - }.bind() - val classDeclListTable = with(ClassDeclListVisitor) { - visit(entity.classes) - }.bind() + val mainClassTable = dispatch(entity.mainClass, this@visit).bind() - this@visit + mainClassTable + classDeclListTable + val fullTable = dispatch(entity.classes, mainClassTable).bind() + + fullTable } } \ No newline at end of file diff --git a/app/src/main/kotlin/org/example/visitor/SymbolVisitor.kt b/app/src/main/kotlin/org/example/visitor/SymbolVisitor.kt index fafe6f6..fd1b708 100644 --- a/app/src/main/kotlin/org/example/visitor/SymbolVisitor.kt +++ b/app/src/main/kotlin/org/example/visitor/SymbolVisitor.kt @@ -2,6 +2,9 @@ package org.example.visitor; import arrow.core.* import org.example.ast.* +import org.example.ast2.org.example.visitor.FormalsListVisitor +import org.example.ast2.org.example.visitor.FormalsVisitor +import org.example.ast2.org.example.visitor.MethodDeclVisitor interface ASTVisitor : Visitor @@ -29,6 +32,7 @@ data class MethodData( override val name: String, val args: Table, val locals: Table, + val returnType: String? = null, ) : Scope data class FormalData( @@ -57,13 +61,28 @@ data class Table(private val map: Map) { @JvmInline value class Error(val message: String) -abstract class SymbolVisitor { +interface SymbolVisitor { + companion object { + fun dispatch(entity: T, table: Table = Table()): Either = + when (entity) { + is MainClass -> MainClassVisitor.run { table.visit(entity) } + is ClassDecl -> ClassDeclVisitor.run { table.visit(entity) } + is VarDecl -> VarDeclVisitor.run { table.visit(entity) } + is MethodDecl -> MethodDeclVisitor.run { table.visit(entity) } + is FormalList -> FormalsListVisitor.run { table.visit(entity) } + is Formal -> FormalsVisitor.run { table.visit(entity) } + is Program -> ProgramVisitor.run { table.visit(entity) } + else -> throw IllegalArgumentException("SymbolVisitor: Unknown entity type") + } + } + fun fold(entityList: List, f: (T) -> Either): Either = entityList.fold(Table().right()) { acc: Either, entity -> f(entity) .flatMap { table -> acc.map { it + table } } } - abstract fun Table.visit(entity: T): Either + fun Table.visit(entity: T): Either } + diff --git a/app/src/main/kotlin/org/example/visitor/VarDeclVisitor.kt b/app/src/main/kotlin/org/example/visitor/VarDeclVisitor.kt index d0539e9..eb6c147 100644 --- a/app/src/main/kotlin/org/example/visitor/VarDeclVisitor.kt +++ b/app/src/main/kotlin/org/example/visitor/VarDeclVisitor.kt @@ -6,7 +6,7 @@ import arrow.core.raise.ensure import org.example.ast.VarDecl import org.example.ast.VarDeclList -object VarDeclListVisitor : SymbolVisitor() { +object VarDeclListVisitor : SymbolVisitor { override fun Table.visit(entity: VarDeclList): Either = with(VarDeclVisitor) { fold( @@ -15,7 +15,7 @@ object VarDeclListVisitor : SymbolVisitor() { } } -object VarDeclVisitor : SymbolVisitor() { +object VarDeclVisitor : SymbolVisitor { override fun Table.visit(entity: VarDecl): Either = either { ensure(!contains(entity.name)) { Error("VarDeclVisitor: VarDecl must have a unique name") From f34ee0c3435d77be6ef3907272cb239768700ed0 Mon Sep 17 00:00:00 2001 From: Pedro Vinicius Coelho belem Date: Sun, 7 Apr 2024 12:13:48 -0300 Subject: [PATCH 13/15] test: add ProgramVisitorTest and MainClassVisitorTest --- .../example/visitor/MainClassVisitorTest.kt | 81 +++++++++++++++++++ .../org/example/visitor/ProgramVisitorTest.kt | 78 ++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 app/src/test/kotlin/org/example/visitor/MainClassVisitorTest.kt create mode 100644 app/src/test/kotlin/org/example/visitor/ProgramVisitorTest.kt diff --git a/app/src/test/kotlin/org/example/visitor/MainClassVisitorTest.kt b/app/src/test/kotlin/org/example/visitor/MainClassVisitorTest.kt new file mode 100644 index 0000000..84a0cb4 --- /dev/null +++ b/app/src/test/kotlin/org/example/visitor/MainClassVisitorTest.kt @@ -0,0 +1,81 @@ +package org.example.visitor + +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkObject +import io.mockk.unmockkObject +import org.assertj.core.api.Assertions +import org.example.ast.Identifier +import org.example.ast.MainClass +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test + +class MainClassVisitorTest { + fun tearDown(): Unit { + unmockkObject(MainClassVisitor) + } + + @Test + fun `should visit main class`(): Unit = MainClassVisitor.run { + // Arrange + val table = Table() + val mainClass = MainClass.builder() + .className(Identifier.builder().s("Main").build()) + .argsName(Identifier.builder().s("args").build()) + .build() + val expectedTable = Companion.defaultMainTable + + // Act + val result = table.visit(mainClass) + + // Assert + result.fold( + ifLeft = { Assertions.fail("Should not throw an error") }, + ifRight = { Assertions.assertThat(it).isEqualTo(expectedTable) } + ) + } + + @Test + fun `Should visit main class with error`(): Unit = MainClassVisitor.run { + // Arrange + val table = Table() + val mainClass = MainClass.builder() + .className(Identifier.builder().s("Main").build()) + .argsName(Identifier.builder().s("wrong").build()) + .build() + + // Act + val result = table.visit(mainClass) + + // Assert + result.fold( + ifLeft = { Assertions.assertThat(it.message).isEqualTo("MainClassVisitor: MainClass args must be named 'args'") }, + ifRight = { Assertions.fail("Should throw an error") } + ) + } + + companion object { + val defaultClassData = + ClassData( + name = "Main", + fields = Table(), + methods = Table( + MethodData( + name = "Main", + args = Table( + ParamData( + name = "args", + type = "String[]" + ) + ), + locals = Table(), + returnType = null + ) + ) + ) + val defaultMainTable = Table( + Companion.defaultClassData + ) + } +} \ No newline at end of file diff --git a/app/src/test/kotlin/org/example/visitor/ProgramVisitorTest.kt b/app/src/test/kotlin/org/example/visitor/ProgramVisitorTest.kt new file mode 100644 index 0000000..ea92fc1 --- /dev/null +++ b/app/src/test/kotlin/org/example/visitor/ProgramVisitorTest.kt @@ -0,0 +1,78 @@ +package org.example.visitor + + +import arrow.core.* +import io.mockk.* +import org.assertj.core.api.Assertions.assertThat +import org.example.ast.* +import org.example.visitor.SymbolVisitor.Companion.dispatch +import org.junit.jupiter.api.Test + +class ProgramVisitorTest { + fun tearDown() { + unmockkAll() + } + + @Test + fun `should visit program`(): Unit = ProgramVisitor.run { + // Arrange + val expectedMainTable = MainClassVisitorTest.defaultMainTable + val expectedClassTable = expectedMainTable + Table( + MainClassVisitorTest.defaultClassData.copy( + name = "otherClass" + ) + ) + val expectedFullTable = expectedClassTable + Table( + MainClassVisitorTest.defaultClassData, + MainClassVisitorTest.defaultClassData.copy( + name = "otherClass" + ) + ) + val table = Table() + val program = slot() + mockkObject(SymbolVisitor.Companion) { + every { dispatch(any(MainClass::class), table) } returns expectedMainTable.right() + every { dispatch(any(ClassDeclList::class), table) } returns expectedClassTable.right() + + // Act + val result = dispatch(program, table) + + // Assert + assertThat(result).isEqualTo(expectedFullTable.right()) + } + } + + @Test + fun `should visit program with error`(): Unit = ProgramVisitor.run { + // Arrange + val table = Table() + val program = slot() + mockkObject(SymbolVisitor.Companion) { + every { dispatch(any(MainClass::class), table) } returns Error("MainClassVisitor: MainClass must have a unique name").left() + + // Act + val result = dispatch(program, table) + + // Assert + assertThat(result).isEqualTo(Error("MainClassVisitor: MainClass must have a unique name").left()) + } + } + + @Test + fun `should visit program with error in class decl list`(): Unit = ProgramVisitor.run { + // Arrange + val table = Table() + val expectedMainTable = MainClassVisitorTest.defaultMainTable + val program = slot() + mockkObject(SymbolVisitor.Companion) { + every { dispatch(any(MainClass::class), table) } returns expectedMainTable.right() + every { dispatch(any(ClassDeclList::class), table) } returns Error("ClassDeclVisitor: ClassDecl must have a unique name").left() + + // Act + val result = dispatch(program, table) + + // Assert + assertThat(result).isEqualTo(Error("ClassDeclVisitor: ClassDecl must have a unique name").left()) + } + } +} \ No newline at end of file From 7b5ba5f2aac471a06b049970a2f93fa020712f97 Mon Sep 17 00:00:00 2001 From: Pedro Vinicius Coelho belem Date: Sun, 7 Apr 2024 16:15:43 -0300 Subject: [PATCH 14/15] fix: match types --- .../org/example/visitor/ClassDeclVisitor.kt | 30 ++----- .../org/example/visitor/MainClassVisitor.kt | 8 +- .../org/example/visitor/MethodDeclVisitor.kt | 4 +- .../org/example/visitor/SymbolVisitor.kt | 13 ++- .../org/example/visitor/VarDeclVisitor.kt | 2 +- .../visitor/ClassDeclSimpleVisitorTest.kt | 87 +++++++++++++++++++ .../example/visitor/MainClassVisitorTest.kt | 9 +- .../org/example/visitor/ProgramVisitorTest.kt | 53 +++++++++++ 8 files changed, 169 insertions(+), 37 deletions(-) create mode 100644 app/src/test/kotlin/org/example/visitor/ClassDeclSimpleVisitorTest.kt diff --git a/app/src/main/kotlin/org/example/visitor/ClassDeclVisitor.kt b/app/src/main/kotlin/org/example/visitor/ClassDeclVisitor.kt index e75d71f..1f892e0 100644 --- a/app/src/main/kotlin/org/example/visitor/ClassDeclVisitor.kt +++ b/app/src/main/kotlin/org/example/visitor/ClassDeclVisitor.kt @@ -11,7 +11,7 @@ object ClassDeclListVisitor : SymbolVisitor { override fun Table.visit(entity: ClassDeclList): Either = ClassDeclVisitor.fold( entity.classDecls.toList() - ) { visit(entity) } + ) { dispatch(it) } } object ClassDeclVisitor : SymbolVisitor { @@ -29,10 +29,8 @@ object ClassDeclVisitor : SymbolVisitor { this@visit + Table( ClassData( name = extractName(entity), - fields = dispatch(entity.fields, table = this@visit).bind(), - methods = with(MethodDeclListVisitor) { - visit(entity.methods).bind() - } + fields = dispatch(entity.fields).bind(), + methods = dispatch(entity.methods).bind() ) ) } @@ -43,12 +41,8 @@ object ClassDeclVisitor : SymbolVisitor { this@visit + Table( ClassData( name = extractName(entity), - fields = with(VarDeclListVisitor) { - visit(entity.fields).bind() - }, - methods = with(MethodDeclListVisitor) { - visit(entity.methods).bind() - } + fields = dispatch(entity.fields).bind(), + methods = dispatch(entity.methods).bind() ) ) } @@ -61,19 +55,7 @@ object ClassDeclVisitor : SymbolVisitor { Error("ClassDeclVisitor: ClassDecl must have a unique name") } - val newTable = when (entity) { - is ClassDeclSimple -> - with(ClassDeclSimpleVisitor) { - visit(entity).bind() - } - is ClassDeclExtends -> - with(ClassDeclExtendsVisitor) { - visit(entity).bind() - } - else -> throw IllegalArgumentException( - "ClassDeclVisitor: ClassDecl must be either ClassDeclSimple or ClassDeclExtends" - ) - } + val newTable = dispatch(entity, table = this@visit).bind() this@visit + newTable } diff --git a/app/src/main/kotlin/org/example/visitor/MainClassVisitor.kt b/app/src/main/kotlin/org/example/visitor/MainClassVisitor.kt index 22e4618..396d60c 100644 --- a/app/src/main/kotlin/org/example/visitor/MainClassVisitor.kt +++ b/app/src/main/kotlin/org/example/visitor/MainClassVisitor.kt @@ -3,6 +3,8 @@ package org.example.visitor import arrow.core.Either import arrow.core.raise.either import arrow.core.raise.ensure +import org.example.ast.IntegerLiteral +import org.example.ast.IntegerType import org.example.ast.MainClass object MainClassVisitor : SymbolVisitor { @@ -24,10 +26,12 @@ object MainClassVisitor : SymbolVisitor { args = Table( ParamData( name = entity.argsName.s, - type = "String[]" + type = IntegerType() ) ), - locals = Table() + varDeclList = Table( + + ), ) ) ) diff --git a/app/src/main/kotlin/org/example/visitor/MethodDeclVisitor.kt b/app/src/main/kotlin/org/example/visitor/MethodDeclVisitor.kt index 0917db4..75364c2 100644 --- a/app/src/main/kotlin/org/example/visitor/MethodDeclVisitor.kt +++ b/app/src/main/kotlin/org/example/visitor/MethodDeclVisitor.kt @@ -30,7 +30,7 @@ object FormalsVisitor : SymbolVisitor { Table( FormalData( name = entity.name, - type = entity.type.toString() + type = entity.type ) ) } @@ -56,7 +56,7 @@ object MethodDeclVisitor : SymbolVisitor { MethodData( name = entity.identifier, args = dispatch(entity.formals).bind(), - locals = dispatch(entity.varDecls).bind() + varDeclList = dispatch(entity.varDecls).bind() ) ) } diff --git a/app/src/main/kotlin/org/example/visitor/SymbolVisitor.kt b/app/src/main/kotlin/org/example/visitor/SymbolVisitor.kt index fd1b708..56e87d9 100644 --- a/app/src/main/kotlin/org/example/visitor/SymbolVisitor.kt +++ b/app/src/main/kotlin/org/example/visitor/SymbolVisitor.kt @@ -4,7 +4,9 @@ import arrow.core.* import org.example.ast.* import org.example.ast2.org.example.visitor.FormalsListVisitor import org.example.ast2.org.example.visitor.FormalsVisitor +import org.example.ast2.org.example.visitor.MethodDeclListVisitor import org.example.ast2.org.example.visitor.MethodDeclVisitor +import org.example.visitor.ClassDeclListVisitor.visit interface ASTVisitor : Visitor @@ -25,19 +27,19 @@ data class ClassData( data class ParamData( override val name: String, - val type: String + val type: Type ) : Scope data class MethodData( override val name: String, val args: Table, - val locals: Table, + val varDeclList: Table, val returnType: String? = null, ) : Scope data class FormalData( override val name: String, - val type: String + val type: Type ) : Scope data class Table(private val map: Map) { @@ -66,9 +68,12 @@ interface SymbolVisitor { fun dispatch(entity: T, table: Table = Table()): Either = when (entity) { is MainClass -> MainClassVisitor.run { table.visit(entity) } - is ClassDecl -> ClassDeclVisitor.run { table.visit(entity) } + is ClassDeclSimple -> ClassDeclVisitor.ClassDeclSimpleVisitor.run { table.visit(entity) } + is ClassDeclExtends -> ClassDeclVisitor.ClassDeclExtendsVisitor.run { table.visit(entity) } is VarDecl -> VarDeclVisitor.run { table.visit(entity) } + is VarDeclList -> VarDeclListVisitor.run { table.visit(entity) } is MethodDecl -> MethodDeclVisitor.run { table.visit(entity) } + is MethodDeclList -> MethodDeclListVisitor.run { table.visit(entity) } is FormalList -> FormalsListVisitor.run { table.visit(entity) } is Formal -> FormalsVisitor.run { table.visit(entity) } is Program -> ProgramVisitor.run { table.visit(entity) } diff --git a/app/src/main/kotlin/org/example/visitor/VarDeclVisitor.kt b/app/src/main/kotlin/org/example/visitor/VarDeclVisitor.kt index eb6c147..d894764 100644 --- a/app/src/main/kotlin/org/example/visitor/VarDeclVisitor.kt +++ b/app/src/main/kotlin/org/example/visitor/VarDeclVisitor.kt @@ -23,7 +23,7 @@ object VarDeclVisitor : SymbolVisitor { this@visit + FormalData( name = entity.name, - type = entity.type.toString() + type = entity.type ) } } \ No newline at end of file diff --git a/app/src/test/kotlin/org/example/visitor/ClassDeclSimpleVisitorTest.kt b/app/src/test/kotlin/org/example/visitor/ClassDeclSimpleVisitorTest.kt new file mode 100644 index 0000000..6f51f36 --- /dev/null +++ b/app/src/test/kotlin/org/example/visitor/ClassDeclSimpleVisitorTest.kt @@ -0,0 +1,87 @@ +package org.example.visitor + +import arrow.core.getOrElse +import arrow.core.right +import io.mockk.unmockkAll +import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.fail +import org.example.ast.* +import org.example.visitor.SymbolVisitor.Companion.dispatch +import org.junit.jupiter.api.Test + +class ClassDeclSimpleVisitorTest { + fun tearDown() { + unmockkAll() + } + + @Test + fun `should visit class decl`(): Unit = ClassDeclVisitor.ClassDeclSimpleVisitor.run { + // Arrange + val table = Table() + val varDeclList = VarDeclList( + ArrayList( + listOf( + VarDecl(IntegerType(), "field1"), + VarDecl(IntegerType(), "field2") + ) + ) + ) + val statementList = StatementList( + ArrayList( + listOf( + Assign( + Identifier("local1"), + IntegerLiteral(1) + ) + ) + ) + ) + val expression = And(True(), False()) + val formalList = FormalList( + ArrayList( + listOf( + Formal(IntegerType(), "arg1") + ) + ) + ) + val methodDeclList = MethodDeclList( + ArrayList( + listOf( + MethodDecl( + IntegerType(), + "method1", + formalList, + varDeclList, + statementList, + expression + ) + ) + ) + ) + val classDecl: ClassDeclSimple = ClassDeclSimple( + Identifier("Main"), + varDeclList, + methodDeclList + ) + val expectedClass = ClassData( + name = "Main", + fields = dispatch(varDeclList).getOrElse { fail("Should not fail") }, + methods = Table( + MethodData( + name = "method1", + args = dispatch(formalList).getOrElse { fail("Should not fail") }, + varDeclList = dispatch(varDeclList).getOrElse { fail("Should not fail") }, + ) + ) + ) + val expectedTable = Table( + expectedClass + ) + + // Act + val result = dispatch(classDecl, table) + + // Assert + Assertions.assertThat(result).isEqualTo(expectedTable.right()) + } +} \ No newline at end of file diff --git a/app/src/test/kotlin/org/example/visitor/MainClassVisitorTest.kt b/app/src/test/kotlin/org/example/visitor/MainClassVisitorTest.kt index 84a0cb4..960e22e 100644 --- a/app/src/test/kotlin/org/example/visitor/MainClassVisitorTest.kt +++ b/app/src/test/kotlin/org/example/visitor/MainClassVisitorTest.kt @@ -6,6 +6,7 @@ import io.mockk.mockkObject import io.mockk.unmockkObject import org.assertj.core.api.Assertions import org.example.ast.Identifier +import org.example.ast.IntegerType import org.example.ast.MainClass import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.BeforeAll @@ -66,16 +67,16 @@ class MainClassVisitorTest { args = Table( ParamData( name = "args", - type = "String[]" + type = IntegerType() ) ), - locals = Table(), + varDeclList = Table(), returnType = null ) ) ) val defaultMainTable = Table( - Companion.defaultClassData - ) + defaultClassData + ) } } \ No newline at end of file diff --git a/app/src/test/kotlin/org/example/visitor/ProgramVisitorTest.kt b/app/src/test/kotlin/org/example/visitor/ProgramVisitorTest.kt index ea92fc1..de2debf 100644 --- a/app/src/test/kotlin/org/example/visitor/ProgramVisitorTest.kt +++ b/app/src/test/kotlin/org/example/visitor/ProgramVisitorTest.kt @@ -8,6 +8,59 @@ import org.example.ast.* import org.example.visitor.SymbolVisitor.Companion.dispatch import org.junit.jupiter.api.Test +//class ClassDeclVisitorTest { +// fun tearDown() { +// unmockkAll() +// } +// +// +// @Test +// fun `should visit class decl`(): Unit = ClassDeclVisitor.run { +// // Arrange +// val table = Table() +// val classDecl = ClassDecl.builder() +// .className(Identifier.builder().s("Main").build()) +// .varDecls(VarDeclList.builder().build()) +// .methodDecls(MethodDeclList.builder().build()) +// .build() +// val expectedTable = Table( +// ClassData( +// name = "Main" +// ) +// ) +// +// // Act +// val result = table.visit(classDecl) +// +// // Assert +// assertThat(result).isEqualTo(expectedTable.right()) +// } +// +// @Test +// fun `should visit class decl with error`(): Unit = ClassDeclVisitor.run { +// // Arrange +// val table = Table() +// val classDecl = ClassDecl.builder() +// .className(Identifier.builder().s("Main").build()) +// .varDecls(VarDeclList.builder().build()) +// .methodDecls(MethodDeclList.builder().build()) +// .build() +// val expectedTable = Table( +// ClassData( +// name = "Main" +// ) +// ) +// val expectedError = Error("ClassDeclVisitor: ClassDecl must have a unique name") +// +// // Act +// val result = table.visit(classDecl) +// +// // Assert +// assertThat(result).isEqualTo(expectedTable.right()) +// } +// +//} + class ProgramVisitorTest { fun tearDown() { unmockkAll() From 616a4bf00736097359ab3af3f4c1d0b40f25efd1 Mon Sep 17 00:00:00 2001 From: Pedro Vinicius Coelho belem Date: Sun, 7 Apr 2024 16:27:35 -0300 Subject: [PATCH 15/15] test: add ClassDeclExtensionTest --- .../example/visitor/ClassDeclExtensionTest.kt | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 app/src/test/kotlin/org/example/visitor/ClassDeclExtensionTest.kt diff --git a/app/src/test/kotlin/org/example/visitor/ClassDeclExtensionTest.kt b/app/src/test/kotlin/org/example/visitor/ClassDeclExtensionTest.kt new file mode 100644 index 0000000..eb75710 --- /dev/null +++ b/app/src/test/kotlin/org/example/visitor/ClassDeclExtensionTest.kt @@ -0,0 +1,96 @@ +package org.example.visitor + +import arrow.core.flatMap +import arrow.core.getOrElse +import arrow.core.right +import io.mockk.mockk +import io.mockk.unmockkAll +import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.fail +import org.example.ast.* +import org.example.visitor.SymbolVisitor.Companion.dispatch +import org.junit.jupiter.api.Test + +class ClassDeclExtensionTest { + fun tearDown() { + unmockkAll() + } + + @Test + fun `should visit class decl`(): Unit = ClassDeclVisitor.run { + // Arrange + val table = Table() + val varDeclList = VarDeclList( + ArrayList( + listOf( + VarDecl(IntegerType(), "field1"), + VarDecl(IntegerType(), "field2") + ) + ) + ) + val statementList = StatementList( + ArrayList( + listOf( + Assign( + Identifier("local1"), + IntegerLiteral(1) + ) + ) + ) + ) + val expression = And(True(), False()) + val formalList = FormalList( + ArrayList( + listOf( + Formal(IntegerType(), "arg1") + ) + ) + ) + val methodDeclList = MethodDeclList( + ArrayList( + listOf( + MethodDecl( + IntegerType(), + "method1", + formalList, + varDeclList, + statementList, + expression + ) + ) + ) + ) + val superClass = ClassDeclSimple( + Identifier("Super"), + varDeclList, + methodDeclList + ) + val classDecl = ClassDeclExtends( + Identifier("Main"), + Identifier("Super"), + varDeclList, + methodDeclList + ) + val expectedClass = ClassData( + name = "Main", + fields = dispatch(varDeclList).getOrElse { fail("Should not fail") }, + methods = dispatch(methodDeclList).getOrElse { fail("Should not fail") } + ) + val expectedSuperClass = ClassData( + name = "Super", + fields = dispatch(varDeclList).getOrElse { fail("Should not fail") }, + methods = dispatch(methodDeclList).getOrElse { fail("Should not fail") } + ) + val expectedTable = Table( + expectedClass, + expectedSuperClass + ) + + // Act + val result = dispatch(superClass, table = table) + .flatMap { dispatch(classDecl, table = it) } + + // Assert + Assertions.assertThat(result).isEqualTo(expectedTable.right()) + } +} \ No newline at end of file