diff --git a/.github/workflows/classroom.yml b/.github/workflows/classroom.yml index e7c2a76..5575784 100644 --- a/.github/workflows/classroom.yml +++ b/.github/workflows/classroom.yml @@ -13,19 +13,19 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 - - name: Lexer e parser - id: lexer-e-parser + - name: expr + id: expr uses: classroom-resources/autograding-command-grader@v1 with: - test-name: Lexer e parser - setup-command: '' + test-name: expr + setup-command: mvn package command: java -cp "chocopy-ref.jar:target/assignment.jar" chocopy.ChocoPy - --pass=s --test --dir src/test/data/pa1/sample/ + --pass=s --test --dir src/test/data/pa1/sample/expr_plus.py timeout: 10 - max-score: 10 + max-score: 1 - name: Autograding Reporter uses: classroom-resources/autograding-grading-reporter@v1 env: - LEXER-E-PARSER_RESULTS: "${{steps.lexer-e-parser.outputs.result}}" + EXPR_RESULTS: "${{steps.expr.outputs.result}}" with: - runners: lexer-e-parser + runners: expr diff --git a/README.md b/README.md index 3f72893..533c2f2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/4nHL7_6-) +[![Open in Visual Studio Code](https://classroom.github.com/assets/open-in-vscode-2e0aaae1b6195c2367325f4f02e2d04e9abb55f0b24a779b69b11b9e10269abc.svg)](https://classroom.github.com/online_ide?assignment_repo_id=18897626&assignment_repo_type=AssignmentRepo) # CS 164: Programming Assignment 1 [PA1 Specification]: https://drive.google.com/open?id=1oYcJ5iv7Wt8oZNS1bEfswAklbMxDtwqB @@ -31,7 +33,7 @@ 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. +for the specification of the ChocoPy language. ## Receiving updates to this repository @@ -46,8 +48,10 @@ To sync with updates upstream: ## Submission writeup -Team member 1: +Team member 1: Elihofni Guirra Lima -Team member 2: +Team member 2: Henrique de Morais Porto + +Team member 3: Marco de Sousa Melo (Students should edit this section with their write-up) diff --git a/src/main/cup/chocopy/pa1/ChocoPy.cup b/src/main/cup/chocopy/pa1/ChocoPy.cup index d4ff444..1c6c5a3 100644 --- a/src/main/cup/chocopy/pa1/ChocoPy.cup +++ b/src/main/cup/chocopy/pa1/ChocoPy.cup @@ -130,6 +130,16 @@ action code {: return new ComplexSymbolFactory.Location(first.getLocation()[0], first.getLocation()[1]); } + /** Return the rightmost non-whitespace location in NODES, or null if NODES + * is empty. Assumes that the nodes of NODES are ordered left to right. */ + ComplexSymbolFactory.Location getRight(List nodes) { + if (nodes.isEmpty()) { + return null; + } + Node last = nodes.get(nodes.size() - 1); + return new ComplexSymbolFactory.Location(last.getLocation()[0], + last.getLocation()[1]); + } :} @@ -143,11 +153,83 @@ action code {: * in actions ( {: ... :} ). */ terminal NEWLINE; -terminal String PLUS; -terminal Integer NUMBER; + +/* Errors */ /* Returned by the lexer for erroneous tokens. Since it does not appear in * the grammar, it indicates a syntax error. */ -terminal UNRECOGNIZED; +terminal UNRECOGNIZED; +terminal INTEGER_OVERFLOW_LEXICAL_ERROR; + +/* Keywords. */ +terminal String FALSE; +terminal String NONE; +terminal String TRUE; +terminal String AND; +terminal String AS; +terminal String ASSERT; +terminal String ASYNC; +terminal String AWAIT; +terminal String BREAK; +terminal String CLASS; +terminal String CONTINUE; +terminal String DEF; +terminal String DEL; +terminal String ELIF; +terminal String ELSE; +terminal String EXCEPT; +terminal String FINALLY; +terminal String FOR; +terminal String FROM; +terminal String GLOBAL; +terminal String IF; +terminal String IMPORT; +terminal String IN; +terminal String IS; +terminal String LAMBDA; +terminal String NONLOCAL; +terminal String NOT; +terminal String OR; +terminal String PASS; +terminal String RAISE; +terminal String RETURN; +terminal String TRY; +terminal String WHILE; +terminal String WITH; +terminal String YIELD; +terminal String INDENT; // FIXME Validar se está na categoria correta +terminal String DEDENT; // FIXME Validar se está na categoria correta + +/* Literals. */ +terminal Integer NUMBER; +terminal String IDSTRING; +terminal String STRING; + +/* Operators. */ +terminal String PLUS; +terminal String MINUS; +terminal String UMINUS; +terminal String STAR; +terminal String DOUBLE_SLASH; +terminal String PERCENT; +terminal String LESS_THAN; +terminal String GREATER_THAN; +terminal String LESS_THAN_EQUAL; +terminal String GREATER_THAN_EQUAL; +terminal String EQUAL_EQUAL; +terminal String NOT_EQUALS; +terminal String ASSIGN; +terminal String LEFT_PAREN; +terminal String RIGHT_PAREN; +terminal String LEFT_BRACKET; +terminal String RIGHT_BRACKET; +terminal String COMMA; +terminal String COLON; +terminal String DOT; +terminal String RIGHT_ARROW; + +/* Identifiers. */ +terminal String IDENTIFIER; + /* Nonterminal symbols (defined in production rules below). * As for terminal symbols, @@ -162,7 +244,9 @@ non terminal Expr expr, binary_expr; /* Precedences (lowest to highest) for resolving what would otherwise be * ambiguities in the form of shift/reduce conflicts.. */ -precedence left PLUS; +precedence left PLUS, MINUS; +precedence left STAR; +precedence right UMINUS; /* The start symbol. */ start with program; @@ -205,10 +289,16 @@ expr ::= binary_expr:e {: RESULT = e; :} | NUMBER:n {: RESULT = new IntegerLiteral(nxleft, nxright, n); :} ; +expr ::= MINUS:op expr:e + {: RESULT = new UnaryExpr(opxleft, exright, op, e); :} + ; /* A binary expression, illustrating how to find the left and right * source position of a phrase. */ binary_expr ::= expr:e1 PLUS:op expr:e2 {: RESULT = new BinaryExpr(e1xleft, e2xright, e1, op, e2); :} - ; + | expr:e1 MINUS:op expr:e2 + {: RESULT = new BinaryExpr(e1xleft, e2xright, e1, op, e2); :} + | expr:e1 STAR:op expr:e2 + {: RESULT = new BinaryExpr(e1xleft, e2xright, e1, op, e2); :}; \ No newline at end of file diff --git a/src/main/jflex/chocopy/pa1/ChocoPy.jflex b/src/main/jflex/chocopy/pa1/ChocoPy.jflex index 9aafe7f..a16d362 100644 --- a/src/main/jflex/chocopy/pa1/ChocoPy.jflex +++ b/src/main/jflex/chocopy/pa1/ChocoPy.jflex @@ -16,6 +16,8 @@ import java_cup.runtime.*; %cup %cupdebug +%state LINE_START + %eofclose false /*** Do not change the flags above unless you know what you are doing. ***/ @@ -47,6 +49,26 @@ import java_cup.runtime.*; new ComplexSymbolFactory.Location(yyline + 1,yycolumn + yylength()), value); } + /*inicio da pilha de indentação no nivel zero*/ + private java.util.Stack indentStack = new java.util.Stack<>(); + private int pendingDedents = 0; + private boolean emitNewline = false; + private boolean startOfLine = true; + + { + indentStack.push(0); + } + /* contador de whitespaces no inicio da linha*/ + private int computeIndentation(String line) { + int count = 0; + for (int i = 0; i < line.length(); i++) { + char c = line.charAt(i); + if (c == ' ') count++; + else if (c == '\t') count += 8 - (count % 8); + else break; + } + return count; + } %} @@ -57,26 +79,214 @@ LineBreak = \r|\n|\r\n IntegerLiteral = 0 | [1-9][0-9]* +DoubleQuote = \" +Backslash = \\ +/* +Segundo ao item 3.4.1 da documentação, são aceitos os caracteres ASCII: [32-126, menos \ e "] +32 = \u0020 +126 = \u007E +\ = \u0022 +" = \u005C +*/ +AsciiChar = [\u0020-\u0021\u0023-\u005B\u005D-\u007E] + +/* +Ainda no item 3.4.1 da documentação, são aceitos somente as seguintes sequencias de escape: +\" = double quote +\n = newline +\t = tab +\\ = backslash +*/ +EscapedChar = {Backslash}([\"nt\\]) + +/* +Juntando temos a StringLiteral. +*/ +StringLiteral = {DoubleQuote}({AsciiChar}|{EscapedChar})*{DoubleQuote} + +/* +Identifier: o nome de variavel ou função +*/ +IdString = [_a-zA-Z][_a-z0-9A-Z]* + +/* +Comment: +No item 3.1.3 diz: A comment starts with a hash character (#) that is not part of a string literal, and ends at the end of the +physical line. Comments are ignored by the lexical analyzer; they are not emitted as tokens. +E seguindo o 3.1.1 que diz que uma linha pode ser \n, \r ou \r\n +*/ +Comment = #[^\n\r]* + +/* TODO +- NEWLINE, INDENT, DEDENT (usar variavel global, entender melhor no item 3.1.5). +*/ + %% { + // Gera dedents pendentes + if (pendingDedents > 0) { + pendingDedents--; + return symbol(ChocoPyTokens.DEDENT); + } + /* Whitespace. */ + {WhiteSpace} { /* ignore */ } - /* Delimiters. */ - {LineBreak} { return symbol(ChocoPyTokens.NEWLINE); } + /* Comment. */ + {Comment} { /* ignore */ } - /* Literals. */ - {IntegerLiteral} { return symbol(ChocoPyTokens.NUMBER, - Integer.parseInt(yytext())); } + /* Delimiters */ + {LineBreak} { + emitNewline = true; + startOfLine = true; + yybegin(LINE_START); + + } + + /* Operators (item 3.5 da documentação) */ + /* + - * // % < > <= >= == != = ( ) [ ] , : . -> */ + "+" { return symbol(ChocoPyTokens.PLUS, yytext()); } + "-" { return symbol(ChocoPyTokens.MINUS, yytext()); } + "*" { return symbol(ChocoPyTokens.STAR, yytext()); } + "//" { return symbol(ChocoPyTokens.DOUBLE_SLASH, yytext()); } + "%" { return symbol(ChocoPyTokens.PERCENT, yytext()); } + "<" { return symbol(ChocoPyTokens.LESS_THAN, yytext()); } + ">" { return symbol(ChocoPyTokens.GREATER_THAN, yytext()); } + "<=" { return symbol(ChocoPyTokens.LESS_THAN_EQUAL, yytext()); } + ">=" { return symbol(ChocoPyTokens.GREATER_THAN_EQUAL, yytext()); } + "==" { return symbol(ChocoPyTokens.EQUAL_EQUAL, yytext()); } + "!=" { return symbol(ChocoPyTokens.NOT_EQUALS, yytext()); } + "=" { return symbol(ChocoPyTokens.ASSIGN, yytext()); } + "(" { return symbol(ChocoPyTokens.LEFT_PAREN, yytext()); } + ")" { return symbol(ChocoPyTokens.RIGHT_PAREN, yytext()); } + "[" { return symbol(ChocoPyTokens.LEFT_BRACKET, yytext()); } + "]" { return symbol(ChocoPyTokens.RIGHT_BRACKET, yytext()); } + "," { return symbol(ChocoPyTokens.COMMA, yytext()); } + ":" { return symbol(ChocoPyTokens.COLON, yytext()); } + "." { return symbol(ChocoPyTokens.DOT, yytext()); } + "->" { return symbol(ChocoPyTokens.RIGHT_ARROW, yytext()); } + + + /* TODO Keywords (item 3.3 da documentação) */ + "False" { return symbol(ChocoPyTokens.FALSE, yytext()); } + "None" { return symbol(ChocoPyTokens.NONE, yytext()); } + "True" { return symbol(ChocoPyTokens.TRUE, yytext()); } + "and" { return symbol(ChocoPyTokens.AND, yytext()); } + "as" { return symbol(ChocoPyTokens.AS, yytext()); } + "assert" { return symbol(ChocoPyTokens.ASSERT, yytext()); } + "async" { return symbol(ChocoPyTokens.ASYNC, yytext()); } + "await" { return symbol(ChocoPyTokens.AWAIT, yytext()); } + "break" { return symbol(ChocoPyTokens.BREAK, yytext()); } + "class" { return symbol(ChocoPyTokens.CLASS, yytext()); } + "continue" { return symbol(ChocoPyTokens.CONTINUE, yytext()); } + "def" { return symbol(ChocoPyTokens.DEF, yytext()); } + "del" { return symbol(ChocoPyTokens.DEL, yytext()); } + "elif" { return symbol(ChocoPyTokens.ELIF, yytext()); } + "else" { return symbol(ChocoPyTokens.ELSE, yytext()); } + "except" { return symbol(ChocoPyTokens.EXCEPT, yytext()); } + "finally" { return symbol(ChocoPyTokens.FINALLY, yytext()); } + "for" { return symbol(ChocoPyTokens.FOR, yytext()); } + "from" { return symbol(ChocoPyTokens.FROM, yytext()); } + "global" { return symbol(ChocoPyTokens.GLOBAL, yytext()); } + "if" { return symbol(ChocoPyTokens.IF, yytext()); } + "import" { return symbol(ChocoPyTokens.IMPORT, yytext()); } + "in" { return symbol(ChocoPyTokens.IN, yytext()); } + "is" { return symbol(ChocoPyTokens.IS, yytext()); } + "lambda" { return symbol(ChocoPyTokens.LAMBDA, yytext()); } + "nonlocal" { return symbol(ChocoPyTokens.NONLOCAL, yytext()); } + "not" { return symbol(ChocoPyTokens.NOT, yytext()); } + "or" { return symbol(ChocoPyTokens.OR, yytext()); } + "pass" { return symbol(ChocoPyTokens.PASS, yytext()); } + "raise" { return symbol(ChocoPyTokens.RAISE, yytext()); } + "return" { return symbol(ChocoPyTokens.RETURN, yytext()); } + "try" { return symbol(ChocoPyTokens.TRY, yytext()); } + "while" { return symbol(ChocoPyTokens.WHILE, yytext()); } + "with" { return symbol(ChocoPyTokens.WITH, yytext()); } + "yield" { return symbol(ChocoPyTokens.YIELD, yytext()); } - /* Operators. */ - "+" { return symbol(ChocoPyTokens.PLUS, yytext()); } + /* + Literal: to denote a constant literal such as an integer literal, a string literal. + */ + {IntegerLiteral} { return symbol(ChocoPyTokens.NUMBER, Integer.parseInt(yytext())); } + {StringLiteral} { return symbol(ChocoPyTokens.STRING, yytext()); } + + /* Identifiers */ + {IdString} { return symbol(ChocoPyTokens.IDENTIFIER, yytext()); } - /* Whitespace. */ - {WhiteSpace} { /* ignore */ } } + { + {WhiteSpace}* { + String indentText = yytext(); + int currentIndent = computeIndentation(indentText); -<> { return symbol(ChocoPyTokens.EOF); } + int lookahead; + try { + lookahead = yycharat(yylength()); + } catch (Exception e) { + lookahead = -1; + } + // Leia o próximo caractere para ver se a linha é vazia/comentário + if (lookahead == '#' || lookahead == '\n' || lookahead == '\r' || lookahead == -1) { + startOfLine = false; + yybegin(YYINITIAL); + return symbol(ChocoPyTokens.NEWLINE); + } else { + int prevIndent = indentStack.peek(); + if (currentIndent == prevIndent) { + if (emitNewline) { + emitNewline = false; + yybegin(YYINITIAL); + return symbol(ChocoPyTokens.NEWLINE); + } + yybegin(YYINITIAL); + return symbol(ChocoPyTokens.NEWLINE); + } else if (currentIndent > prevIndent) { + indentStack.push(currentIndent); + yybegin(YYINITIAL); + return symbol(ChocoPyTokens.INDENT); + } else { + pendingDedents = 0; + while (indentStack.peek() > currentIndent) { + indentStack.pop(); + pendingDedents++; + } + if (indentStack.peek() != currentIndent) { + throw new Error("Indentation Error"); + } + if (emitNewline) { + emitNewline = false; + return symbol(ChocoPyTokens.NEWLINE); + } + if (pendingDedents > 0) { + pendingDedents--; + return symbol(ChocoPyTokens.DEDENT); + } + yybegin(YYINITIAL); + } + } + } +} +<> { + if (emitNewline) { + emitNewline = false; + return symbol(ChocoPyTokens.NEWLINE); + } + while (indentStack.size() > 1) { + indentStack.pop(); + return symbol(ChocoPyTokens.DEDENT); + } + return symbol(ChocoPyTokens.EOF); +} /* Error fallback. */ [^] { return symbol(ChocoPyTokens.UNRECOGNIZED); } + + +/* +links importantes: +https://jflex.de/manual.html +https://classroom.google.com/c/NzU5OTA0OTQ1NDA1/m/NzU5OTM1NTExMzk2/details +https://docs.google.com/document/d/1wqPQzb4nLzOB8LDCB8lRMzFOSLDb0n4tsnLgZQOu8AY/edit?tab=t.0 +https://github.com/compilers-uff/lexer-e-parser-grupo-13 +*/ \ No newline at end of file