Feat add symbol table visitor#31
Conversation
| @AllArgsConstructor | ||
| public class ClassDeclSimple extends ClassDecl { | ||
| private Identifier className; | ||
| public Identifier className; |
There was a problem hiding this comment.
Utilize o getters e setters do lombok, não deixe todos os campos da AST publico
| import org.example.ast.ClassDeclSimple | ||
| import org.example.ast2.org.example.visitor.MethodDeclListVisitor | ||
|
|
||
| class ClassDeclVisitor : SymbolVisitor<ClassDeclSimple>() { |
There was a problem hiding this comment.
Falta os casos de herança nas classes
| import org.example.ast.VarDecl | ||
| import org.example.ast.VarDeclList | ||
|
|
||
| class VarDeclListVisitor : SymbolVisitor<VarDeclList>() { |
There was a problem hiding this comment.
Onde está os testes que comprovam que a sua implementação funciona na AST de verdade?
|
|
||
| abstract class SymbolVisitor<T> { | ||
| var table : Table = Table() | ||
| abstract fun visit(entity: T): Either<Error, Table> |
There was a problem hiding this comment.
É interessante ter uma coleção de erros para reportar ao usuário como o Heron falou na aula
There was a problem hiding this comment.
Isso é bem tranquilo de mudar é só alterar de Either<Error, *> para EitherNel<Error, *>. Mas não quero mudar até fechar a implementação.
| object ClassDeclVisitor : SymbolVisitor<ClassDecl> { | ||
| private fun extractName(entity: ClassDecl): String = | ||
| when (entity) { | ||
| is ClassDeclSimple -> entity.className.s | ||
| is ClassDeclExtends -> entity.className.s | ||
| else -> throw IllegalArgumentException( | ||
| "ClassDeclVisitor: ClassDecl must be either ClassDeclSimple or ClassDeclExtends" | ||
| ) | ||
| } | ||
|
|
||
| object ClassDeclSimpleVisitor : SymbolVisitor<ClassDeclSimple> { | ||
| override fun Table.visit(entity: ClassDeclSimple): Either<Error, Table> = either { | ||
| this@visit + Table( | ||
| ClassData( | ||
| name = extractName(entity), | ||
| fields = dispatch(entity.fields).bind(), | ||
| methods = dispatch(entity.methods).bind() | ||
| ) | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| object ClassDeclExtendsVisitor : SymbolVisitor<ClassDeclExtends> { | ||
| override fun Table.visit(entity: ClassDeclExtends): Either<Error, Table> = either { | ||
| this@visit + Table( | ||
| ClassData( | ||
| name = extractName(entity), | ||
| fields = dispatch(entity.fields).bind(), | ||
| methods = dispatch(entity.methods).bind() | ||
| ) | ||
| ) | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
Como você planeja resolver os simbolos de herança da super classe que a subclasse herda?
class Super {
public int a;
}
class Sub extends Super {
}Onde essa classe sub terá o valor a como padrão na implementação. Como você irá resolver os casos em que há redeclaração de variável e de métodos? E se essa redeclaração alterar o tipo como você resolve?
| fields = Table(), | ||
| methods = Table( | ||
| MethodData( | ||
| name = entity.className.s, |
| @JvmInline | ||
| value class Error(val message: String) | ||
|
|
||
| interface SymbolVisitor<T> { | ||
| companion object { | ||
| fun <T> dispatch(entity: T, table: Table = Table()): Either<Error, Table> = | ||
| when (entity) { | ||
| is MainClass -> MainClassVisitor.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) } | ||
| else -> throw IllegalArgumentException("SymbolVisitor: Unknown entity type") | ||
| } | ||
| } |
There was a problem hiding this comment.
Uma das coisas que o Visitor Pattern busca resolver é exatamente esse monte de condicional para resolver que operação aplicar em determinada estrutura usando Double Dispatch.
Tanto que toda estrutura que utiliza visitor o inicio das chamadas não é visitor.visit e sim struct.accept, porque a estrutura que injeta o algorítimo nela
| fields = dispatch(varDeclList).getOrElse { fail("Should not fail") }, | ||
| methods = dispatch(methodDeclList).getOrElse { fail("Should not fail") } |
There was a problem hiding this comment.
Não sei se eu entendi direito, mas você está aplicando seu algorítmo no resultado experado? Se sim, porque não usar o resultado esperado na mão, já que não seria um teste que a implementação é comparada com ela mesma
| //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()) | ||
| // } | ||
| // | ||
| //} |
| 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<Program>() | ||
| 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()) | ||
| } | ||
| } |
There was a problem hiding this comment.
Não entendi muito bem esse código, poderia explicar no zap depois?



Descrição
Closes #
Tipo de mudança
Checklist: