pns/src/pins25/phase/SynAn.java
2025-03-21 15:28:24 +01:00

545 lines
17 KiB
Java

package pins25.phase;
import pins25.common.*;
import java.util.Objects;
/**
* Sintaksni analizator.
*/
public class SynAn implements AutoCloseable {
/**
* Leksikalni analizator.
*/
private final LexAn lexAn;
/**
* Ustvari nov sintaksni analizator.
*
* @param srcFileName Ime izvorne datoteke.
*/
public SynAn(final String srcFileName) {
this.lexAn = new LexAn(srcFileName);
}
@Override
public void close() {
lexAn.close();
}
/**
* Prevzame leksikalni analizator od leksikalnega analizatorja in preveri, ali
* je prave vrste.
*
* @param symbol Pričakovana vrsta leksikalnega simbola.
* @return Prevzeti leksikalni simbol.
*/
private Token check(Token.Symbol symbol) {
final Token token = lexAn.takeToken();
if (token.symbol() != symbol)
throw new Report.Error(token, "Unexpected symbol '" + token.lexeme() + "', expected " + symbol.prettyPrint() + ".");
return token;
}
/**
* Opravi sintaksno analizo.
*/
public void parse() {
parseProgram();
if (lexAn.peekToken().symbol() != Token.Symbol.EOF)
Report.warning(lexAn.peekToken(),
"Unexpected text '" + lexAn.peekToken().lexeme() + "...' at the end of the program.");
}
private void parseProgram() {
System.out.println("program -> definition restdefs");
parseDefinition();
parseRestDefinitions();
}
private void parseRestDefinitions() {
if (lexAn.peekToken().symbol() != Token.Symbol.EOF) {
System.out.println("restdefs -> definition restdefs");
parseDefinition();
parseRestDefinitions();
} else {
System.out.println("restdefs -> ε");
}
}
private void parseDefinition() {
Token token = lexAn.peekToken();
switch (token.symbol()) {
case FUN:
System.out.println("definition -> FUN IDENTIFIER LPAREN parameters RPAREN funcassign");
check(Token.Symbol.FUN);
check(Token.Symbol.IDENTIFIER);
check(Token.Symbol.LPAREN);
parseParameters();
check(Token.Symbol.RPAREN);
parseFunctionAssignment();
break;
case VAR:
System.out.println("definition -> VAR IDENTIFIER ASSIGN initializers");
check(Token.Symbol.VAR);
check(Token.Symbol.IDENTIFIER);
check(Token.Symbol.ASSIGN);
parseInitializers();
break;
default:
throw new Report.Error(token, "Unexpected symbol '" + token.lexeme() + "', expected FUN or VAR.");
}
}
private void parseFunctionAssignment() {
Token token = lexAn.peekToken();
if (token.symbol() == Token.Symbol.ASSIGN) {
System.out.println("funcassign -> ASSIGN statements");
check(Token.Symbol.ASSIGN);
parseStatements();
} else {
System.out.println("funcassign -> ε");
}
}
private void parseParameters() {
Token token = lexAn.peekToken();
if (token.symbol() == Token.Symbol.IDENTIFIER) {
System.out.println("parameters -> IDENTIFIER restparams");
check(Token.Symbol.IDENTIFIER);
parseRestParameters();
} else {
System.out.println("parameters -> ε");
}
}
private void parseRestParameters() {
Token token = lexAn.peekToken();
if (token.symbol() == Token.Symbol.COMMA) {
System.out.println("restparams -> COMMA IDENTIFIER restparams");
check(Token.Symbol.COMMA);
check(Token.Symbol.IDENTIFIER);
parseRestParameters();
} else {
System.out.println("restparams -> ε");
}
}
private void parseStatements() {
System.out.println("statements -> statement reststmts");
parseStatement();
parseRestStatements();
}
private void parseExpressionAssign() {
if (lexAn.peekToken().symbol() == Token.Symbol.ASSIGN) {
System.out.println("exprassign -> ASSIGN expression");
check(Token.Symbol.ASSIGN);
parseExpression();
} else {
System.out.println("exprassign -> ε");
}
}
private void parseElseStatement() {
if (lexAn.peekToken().symbol() == Token.Symbol.ELSE) {
System.out.println("elsestmt -> ELSE statements");
check(Token.Symbol.ELSE);
parseStatements();
} else {
System.out.println("elsestmt -> ε");
}
}
private void parseRestStatements() {
if (lexAn.peekToken().symbol() == Token.Symbol.COMMA) {
System.out.println("reststmts -> COMMA statement reststmts");
check(Token.Symbol.COMMA);
parseStatement();
parseRestStatements();
} else {
System.out.println("reststmts -> ε");
}
}
private void parseStatement() {
Token token = lexAn.peekToken();
switch (token.symbol()) {
case IF:
System.out.println("statement -> if expression then statements elsestmt end");
check(Token.Symbol.IF);
parseExpression();
check(Token.Symbol.THEN);
parseStatements();
parseElseStatement();
check(Token.Symbol.END);
break;
case WHILE:
System.out.println("statement -> while expression do statements end");
check(Token.Symbol.WHILE);
parseExpression();
check(Token.Symbol.DO);
parseStatements();
check(Token.Symbol.END);
break;
case LET:
System.out.println("statement -> let definition reststmtdefs in statements end");
check(Token.Symbol.LET);
parseDefinition();
parseRestStatementDefinitions();
check(Token.Symbol.IN);
parseStatements();
check(Token.Symbol.END);
break;
default:
System.out.println("statement -> expression exprassign");
parseExpression();
parseExpressionAssign();
}
}
private void parseRestStatementDefinitions() {
switch (lexAn.peekToken().symbol()) {
case IN:
System.out.println("reststmtdefs -> ε");
break;
case FUN:
case VAR:
System.out.println("reststmtdefs -> definition reststmtdefs");
parseDefinition();
parseRestStatementDefinitions();
break;
}
}
private void parseInitializers() {
switch (lexAn.peekToken().symbol()) {
case INTCONST:
case CHARCONST:
case STRINGCONST:
System.out.println("initializers -> initializer restinits");
parseInitializer();
parseRestInitializers();
break;
default:
System.out.println("initializers -> ε");
break;
}
}
private void parseInitializer() {
Token token = lexAn.peekToken();
switch (token.symbol()) {
case INTCONST:
System.out.println("initializer -> INTCONST intconstmult");
check(Token.Symbol.INTCONST);
parseIntegerConstantMultiplier();
break;
case CHARCONST:
System.out.println("initializer -> CHARCONST");
check(Token.Symbol.CHARCONST);
break;
case STRINGCONST:
System.out.println("initializer -> STRINGCONST");
check(Token.Symbol.STRINGCONST);
break;
default:
throw new Report.Error(token, "Unexpected symbol '" + token.lexeme() + "', expected integer, character or string constant.");
}
}
private void parseRestInitializers() {
Token token = lexAn.peekToken();
if (token.symbol() == Token.Symbol.COMMA) {
System.out.println("restinits -> COMMA initializer restinits");
check(Token.Symbol.COMMA);
parseInitializer();
parseRestInitializers();
} else {
System.out.println("restinits -> ε");
}
}
private void parseIntegerConstantMultiplier() {
Token token = lexAn.peekToken();
if (token.symbol() == Token.Symbol.MUL) {
System.out.println("intconstmult -> MUL const");
check(Token.Symbol.MUL);
parseConstant();
} else {
System.out.println("intconstmult -> ε");
}
}
private void parseConstant() {
Token token = lexAn.peekToken();
switch (token.symbol()) {
case INTCONST:
System.out.println("const -> INTCONST");
check(Token.Symbol.INTCONST);
break;
case CHARCONST:
System.out.println("const -> CHARCONST");
check(Token.Symbol.CHARCONST);
break;
case STRINGCONST:
System.out.println("const -> STRINGCONST");
check(Token.Symbol.STRINGCONST);
break;
default:
throw new Report.Error(token, "Unexpected symbol '" + token.lexeme() + "', expected integer, character or string constant.");
}
}
private void parseExpression() {
System.out.println("expression -> conjexpr restdisj");
parseConjunctionExpression();
parseRestDisjunctions();
}
private void parseRestDisjunctions() {
if (lexAn.peekToken().symbol() == Token.Symbol.OR) {
System.out.println("restdisj -> OR conjexpr restdisj");
check(Token.Symbol.OR);
parseConjunctionExpression();
parseRestDisjunctions();
}
}
private void parseConjunctionExpression() {
System.out.println("conjexpr -> cmpexpr restconj");
parseComparisonExpression();
parseRestConjunctionExpressions();
}
private void parseRestConjunctionExpressions() {
if (lexAn.peekToken().symbol() == Token.Symbol.AND) {
System.out.println("restconj -> AND cmpexpr restconj");
check(Token.Symbol.AND);
parseComparisonExpression();
parseRestConjunctionExpressions();
} else {
System.out.println("restconj -> ε");
}
}
private void parseComparisonExpression() {
System.out.println("cmpexpr -> addexpr restcmp");
parseAdditionExpression();
parseRestComparisons();
}
private void parseRestComparisons() {
Token token = lexAn.peekToken();
switch (token.symbol()) {
case EQU:
case NEQ:
case LTH:
case GTH:
case LEQ:
case GEQ:
System.out.printf("restcmp -> %s addexpr\n", token.symbol());
check(token.symbol());
parseAdditionExpression();
break;
default:
System.out.println("restcmp -> ε");
break;
}
}
private void parseAdditionExpression() {
System.out.println("addexpr -> multexpr restadd");
parseMultiplicationExpression();
parseRestAdditions();
}
private void parseRestAdditions() {
Token token = lexAn.peekToken();
switch (token.symbol()) {
case ADD:
case SUB:
System.out.printf("restadd -> %s multexpr restadd\n", token.symbol());
check(token.symbol());
parseMultiplicationExpression();
parseRestAdditions();
break;
default:
System.out.println("restadd -> ε");
break;
}
}
private void parseMultiplicationExpression() {
System.out.println("multexpr -> prefixexpr restmult");
parsePrefixExpression();
parseRestMultiplicationExpressions();
}
private void parseRestMultiplicationExpressions() {
Token token = lexAn.peekToken();
switch (token.symbol()) {
case MUL:
case DIV:
case MOD:
System.out.printf("restmult -> %s prefixexpr restmult\n", token.symbol());
check(token.symbol());
parsePrefixExpression();
parseRestMultiplicationExpressions();
break;
default:
System.out.println("restmult -> ε");
break;
}
}
private void parsePrefixExpression() {
Token token = lexAn.peekToken();
switch (token.symbol()) {
case ADD:
case SUB:
case NOT:
case PTR:
System.out.printf("prefixexpr -> %s prefixexpr\n", token.symbol());
check(token.symbol());
parsePrefixExpression();
break;
default:
System.out.println("prefixexpr -> postfixexpr");
parsePostfixExpression();
break;
}
}
private void parsePostfixExpression() {
System.out.println("postfixexpr -> primary postfixop");
parsePrimary();
parsePostfixOperator();
}
private void parsePostfixOperator() {
if (lexAn.peekToken().symbol() == Token.Symbol.PTR) {
System.out.println("postfixop -> PTR postfixop");
check(Token.Symbol.PTR);
parsePostfixOperator();
} else {
System.out.println("postfixop -> ε");
}
}
private void parsePrimary() {
Token token = lexAn.peekToken();
switch (token.symbol()) {
case IDENTIFIER:
System.out.println("primary -> IDENTIFIER exprargs");
check(Token.Symbol.IDENTIFIER);
parseExpressionArguments();
break;
case LPAREN:
System.out.println("primary -> LPAREN expression RPAREN");
check(Token.Symbol.LPAREN);
parseExpression();
check(Token.Symbol.RPAREN);
break;
case INTCONST:
case CHARCONST:
case STRINGCONST:
System.out.println("primary -> const");
parseConstant();
break;
default:
throw new Report.Error(token, "Unexpected symbol '" + token.lexeme() + "', expected identifier, constant or '('.");
}
}
private void parseExpressionArguments() {
if (lexAn.peekToken().symbol() == Token.Symbol.LPAREN) {
System.out.println("exprargs -> LPAREN arguments RPAREN");
check(Token.Symbol.LPAREN);
parseArguments();
check(Token.Symbol.RPAREN);
} else {
System.out.println("exprargs -> ε");
}
}
private void parseArguments() {
Token token = lexAn.peekToken();
if (token.symbol() == Token.Symbol.RPAREN) {
System.out.println("arguments -> ε");
} else {
System.out.println("arguments -> expression restargs");
parseExpression();
parseRestArguments();
}
}
private void parseRestArguments() {
if (lexAn.peekToken().symbol() == Token.Symbol.COMMA) {
System.out.println("restargs -> COMMA restargs");
check(Token.Symbol.COMMA);
parseExpression();
parseRestArguments();
} else {
System.out.println("restargs -> ε");
}
}
// --- ZAGON ---
/**
* Zagon sintaksnega analizatorja kot samostojnega programa.
*
* @param cmdLineArgs Argumenti v ukazni vrstici.
*/
public static void main(final String[] cmdLineArgs) {
System.out.println("This is PINS'25 compiler (syntax analysis):");
try {
if (cmdLineArgs.length == 0)
throw new Report.Error("No source file specified in the command line.");
if (cmdLineArgs.length > 1)
Report.warning("Unused arguments in the command line.");
try (SynAn synAn = new SynAn(cmdLineArgs[0])) {
synAn.parse();
}
// Upajmo, da kdaj pridemo to te tocke.
// A zavedajmo se sledecega:
// 1. Prevod je zaradi napak v programu lahko napacen :-o
// 2. Izvorni program se zdalec ni tisto, kar je programer hotel, da bi bil ;-)
Report.info("Done.");
} catch (Report.Error error) {
// Izpis opisa napake.
System.err.println(error.getMessage());
System.exit(1);
}
}
}