Note: Users running Windows should replace the colon (:) with a semicolon (;) in the classpath argument for all command listed below.
Run the following command to generate and compile your parser, and then run all the provided tests:
mvn clean package
java -cp "chocopy-ref.jar:target/assignment.jar" chocopy.ChocoPy --pass=s --test --dir src/test/data/pa1/sample/
In the starter code, only one test should pass. Your objective is to build a parser that passes all the provided tests and meets the assignment specifications.
To manually observe the output of your parser when run on a given input ChocoPy program, run the following command (replace the last argument to change the input file):
java -cp "chocopy-ref.jar:target/assignment.jar" chocopy.ChocoPy --pass=s src/test/data/pa1/sample/expr_plus.py
You can check the output produced by the staff-provided reference implementation on the same input file, as follows:
java -cp "chocopy-ref.jar:target/assignment.jar" chocopy.ChocoPy --pass=r src/test/data/pa1/sample/expr_plus.py
Try this with another input file as well, such as src/test/data/pa1/sample/coverage.py, to see what happens when the results disagree.
See the PA1 specification on the course website for a detailed specification of the assignment.
Refer to the ChocoPy Specification on the CS164 web site for the specification of the ChocoPy language.
Add the upstream repository remotes (you only need to do this once in your local clone):
git remote add upstream https://github.com/cs164berkeley/pa1-chocopy-parser.git
To sync with updates upstream:
git pull upstream master
Team member 1: Dário Chen Chen Ye
Team member 2: Fellipe Carvalho Pombo Lima
Team member 3: Leonardo Meato Fernandes
Projeto desenvolvido em 120 horas.
Respostas às perguntas:
-
O controle de identação do lexer foi feito no estado INDENT_CTRL, que vai da linha 172 à 235, sendo que o acesso a esse estado é feito na linha 94. Também há uma parte dos tokens que é emitida no EOF, da linha 238 à 242.
A estratégia foi a seguinte: toda vez que uma linha é quebrada (enquanto no estado principal do lexer), o analisador vai para o estado INDENT_CTRL, no qual os espaços em branco são contados. Na seção de declarações do jflex, há algumas variáveis declaradas, sendo a principal delas a pilha de identação, que começa com um 0. Após a contagem dos espaços em branco, compara-se o topo da pilha de identação com a quantidade contada: caso a quantidade seja maior que a do topo da pilha, essa quantidade é adicionada ao topo da pilha e é emitido um token INDENT;caso a quantidade seja igual, nada acontece; caso a quantidade seja menor, o topo da pilha é descartado e é emitido um token DEDENT. Em todos esses casos, logo após a operação descrita volta-se para o estado principal, a não ser que, após a emissão do DEDENT, a quantidade de espaços em branco ainda seja menor que o topo da pilha. Nesse caso, utiliza-se a função yypushback() para voltar ao começo do espaço em branco e não é feita mudança de estado, forçando o algoritmo descrito a ser executado novamente, até que a identação esteja correta.
No entanto, a contagem de espaços em branco só será feita caso haja espaços em branco e a regra {WhiteSpace} seja requisitada. Se esse não for o caso, é porque a identação é 0, então são emitidos quantos DEDENTs forem necessários, seguindo uma lógica parecida com a descrita acima, porém dando sempre um pushback(1) para não contar o caracter que não é um espaço em branco como consumido.
Além disso, é possível que o programa acabe com DEDENTs ainda para serem emitidos, porém sem uma quebra de linha no fim do programa. Nesse caso, o estado INDENT_CTRL não será acionado. Para isso, há um pequeno trecho no EOF para emitir os tokens restantes.
Há também mecanismos para eliminar NEWLINEs redundantes, para que não seja emitido DEDENT INDENT dentro de um mesmo bloco por conta de uma quebra de linha desnecessária, por exemplo.
-
A relação é muito grande. O algoritmo descrito pelo manual do ChocoPy foi seguido praticamente à risca, sendo a lógica inteiramente igual. No entanto, houve algumas adaptações que tiveram de ser feitas devido à ferramenta utilizada (JFlex), como, por exemplo, a emissão de vários tokens DEDENT. A emissão de mais de um token na mesma regra é impossível para o jflex, então, para contornarmos a situação, utilizamos a função yypushback(), para voltar na fita e permitir a correta execução do algoritmo de pilha descrito pelo manual.
-
A principal dificuldade em relação ao código foi aplicar a recursão, de acordo com a BNF apresentada no ChocoPy, sem gerar conflitos de Reduce-Reduce e Shift-Reduce. Além disso, o fato de ser extremamente difícil de se debuggar o código também trouxe grandes transtornos, visto que não sabiamos exatamente o que estava errado e como consertar quando alterávamos o código.