import java_cup.runtime.*; import java.io.*; import java.util.*; import symtab.*; import type.*; /* MiniJava parser for CUP. * Copyright (C) 2006 Silvano Rivoira * This program is released under the terms of the GPL; see the file * COPYING for more details. There is NO WARRANTY on this code. */ parser code {: public static Scanner s; public static int errors = 0; public static int warnings = 0; public static boolean first = true, second = false; public static void main(String argv[]) { for (int i = 0; i < argv.length; i++) { try { System.out.println("\n...Creating Symbol-Table for ["+argv[i]+"]...\n"); s = new Scanner(new FileReader(argv[i])); mjavac p = new mjavac(s); p.parse(); System.out.println("Number of errors = " + errors + "."); System.out.println("Number of warnings = " + warnings + "."); Type.printTypes(); first = false; second = true; System.out.println("\n...Generating intermediate code for ["+argv[i]+"]...\n"); s = new Scanner(new FileReader(argv[i])); p = new mjavac(s); p.parse(); System.out.println("Number of errors = " + errors + "."); System.out.println("Number of warnings = " + warnings + "."); Type.printTypes(); } catch (Exception e) { e.printStackTrace(System.out); System.exit(1); } } } public void report_error(String message, Object info) { if (info instanceof String){ errors++; System.err.println(" "+ errors + "==> " + info + " "+ message + "\n Parsing resumed from 2nd token before" + s.current_lexeme()+"\n"); } else { StringBuffer m = new StringBuffer("Error "); if (info instanceof java_cup.runtime.Symbol) m.append( "("+info.toString()+")" ); m.append(" : "+message); System.err.println(m); } } public void sem_error(String lexeme, String message) { errors++; System.err.println("Error "+ s.current_lexeme() + " : Semantic error"); System.err.println(" "+ errors + "==> " + message + ": "+ lexeme + "\n"); } public void warning(String lexeme, String message) { warnings++; System.err.println("Warning "+ s.current_lexeme()); System.err.println(" "+ warnings + "==> " + message + ": "+ lexeme + "\n"); } public void report_fatal_error(String message, Object info) { report_error(message, info); throw new RuntimeException("Fatal Syntax Error"); } :}; action code {: class Sname { public String name; public Symb cs; public Symb rs; Sname(String n, Symb c, Symb r) { name = n; cs = c; rs = r; } } :}; init with {: if(first) { Type.initTypes(); Env.initFirst(); } if(second) Env.initSecond(); :}; terminal BOOLEAN; // primitive_type terminal INT, CHAR, FLOAT; // numeric_type terminal LBRACK, RBRACK; // array_type terminal DOT; // qualified_name terminal SEMICOLON, MULT, COMMA, LBRACE, RBRACE, EQ, LPAREN, RPAREN, COLON; terminal PUBLIC; // modifier terminal CLASS; // class_declaration terminal EXTENDS; // super terminal VOID; // method_header terminal THIS, SUPER; // explicit_constructor_invocation terminal AT; // reference operator terminal IF, ELSE; // if_then_statement, if_then_else_statement terminal WHILE; // while_statement, do_statement terminal RETURN; // return_statement terminal NEW; // class_instance_creation_expression terminal PLUS, MINUS, NOT, DIV, MOD; terminal LT, GT, LTEQ, GTEQ; // relational_expression terminal EQEQ, NOTEQ; // equality_expression terminal ANDAND; // conditional_and_expression terminal OROR; // conditional_or_expression terminal QUESTION; // conditional_expression terminal ILLEGAL_CHARACTER; // illegal_character terminal java.lang.Number INTEGER_LITERAL; terminal java.lang.Number FLOATING_POINT_LITERAL; terminal java.lang.Boolean BOOLEAN_LITERAL; terminal java.lang.Character CHARACTER_LITERAL; terminal java.lang.String STRING_LITERAL; terminal java.lang.String IDENTIFIER; // name terminal NULL_LITERAL; // 1) The Syntactic Grammar non terminal goal; // 2) Lexical Structure non terminal Type literal; // 3) Types, Values, and Variables non terminal Type type, primitive_type, numeric_type; non terminal Type reference_type; non terminal Type array_type; // 4) Names non terminal Sname name; non terminal Type M1; // 5) Classes non terminal class_declarations; // 5.1) Class Declaration non terminal class_declaration; non terminal class_body, class_body_opt; non terminal Boolean modifiers_opt; non terminal class_body_declarations, class_body_declarations_opt; non terminal class_body_declaration, class_member_declaration; // 5.2) Field Declarations non terminal field_declaration, variable_declarators; non terminal variable_declarator_id; // 5.3) Method Declarations non terminal method_declaration, method_header; non terminal Type formal_parameter_list_opt, formal_parameter_list; non terminal Type formal_parameter; non terminal method_body; // 5.4) Constructor Declarations non terminal constructor_declaration, constructor_declarator; non terminal constructor_body; non terminal explicit_constructor_invocation; // 6) Blocks and Statements non terminal block; non terminal block_statements_opt, block_statements, block_statement; non terminal local_variable_declaration_statement; non terminal statement, statement_no_short_if; non terminal statement_without_trailing_substatement; non terminal empty_statement; non terminal expression_statement, statement_expression; non terminal if_then_statement; non terminal if_then_else_statement, if_then_else_statement_no_short_if; non terminal while_statement, while_statement_no_short_if; non terminal return_statement; // 7) Expressions non terminal Type primary, primary_no_new_array; non terminal Type class_instance_creation_expression; non terminal Type argument_list_opt, argument_list; non terminal Type array_creation_expression; non terminal Type dim_exprs, dim_expr, dims_opt; non terminal dims; non terminal Type field_access, method_invocation, array_access; non terminal Type postfix_expression; non terminal Type unary_expression; non terminal Type multiplicative_expression, additive_expression; non terminal Type relational_expression, equality_expression; non terminal Type conditional_and_expression, conditional_or_expression; non terminal Type conditional_expression, assignment_expression; non terminal Type assignment; non terminal Type left_hand_side; non terminal Type expression_opt, expression; start with goal; // 1) The Syntactic Grammar goal ::= class_declarations {: Hashtable forwards; if(parser.first) { forwards = Name.ForwardHashtable(); int err = forwards.size(); if(err > 0) { parser.errors = parser.errors + err; System.err.println("Error : Semantic error"); System.err.println(" ==> CANNOT FIND CLASSES ("+err+"): "+forwards+"\n"); } } :} ; // 2) Lexical Structure. literal ::= INTEGER_LITERAL {: RESULT = Type.integer(); :} | FLOATING_POINT_LITERAL {: RESULT = Type.floating(); :} | BOOLEAN_LITERAL {: RESULT = Type.bool(); :} | CHARACTER_LITERAL {: RESULT = Type.character(); :} | STRING_LITERAL {: Symb s = Env.get("String", Env.getRoot()); if(s == null) RESULT = Type.reference(Type.forwardName("String", parser.s.current_lexeme())); else RESULT = Type.reference(s.getType()); :} | NULL_LITERAL {: RESULT = Type.reference(Type.voidtype()); :} ; // 3) Types, Values, and Variables type ::= primitive_type:t {: RESULT = t; :} | reference_type:t {: RESULT = t; :} ; primitive_type ::= numeric_type:t {: RESULT = t; :} | BOOLEAN {: RESULT = Type.bool(); :} ; numeric_type ::= INT {: RESULT = Type.integer(); :} | CHAR {: RESULT = Type.character(); :} | FLOAT {: RESULT = Type.floating(); :} ; reference_type ::= name:sn {: Symb s = sn.rs; if(s == null) RESULT = Type.reference(Type.forwardName(sn.name, parser.s.current_lexeme())); else RESULT = Type.reference(s.getType()); :} | array_type:t {: RESULT = Type.reference(t); :} ; array_type ::= primitive_type:t dims {: RESULT = Type.array(0, t); :} | name:sn dims {: Symb s = sn.cs; if(s == null) { parser.sem_error(sn.name,"UNKNOWN NAME"); RESULT = Type.errortype(); } else RESULT = Type.array(0, s.getType()); :} ; // 4) Names name ::= IDENTIFIER:n {: if(parser.first) RESULT = new Sname(n, Env.get(n), Env.get(n, Env.getRoot())); if(parser.second) RESULT = new Sname(n, Env.get(n), Env.get(n, Env.getRoot())); :} | name:sn DOT IDENTIFIER:n {: Symb s1 = sn.cs; if(s1 == null) { parser.sem_error(sn.name, "UNKNOWN NAME"); RESULT = new Sname(n, null, null); } else { Type t1 = s1.getType(); if(!t1.isReference()) { parser.sem_error(""+t1, "NOT A REFERENCE"); RESULT = new Sname(n, null, null); } else { Type r = ((Reference)t1).getReferred(); if(!r.isName()) { parser.sem_error(""+r, "NOT A CLASS"); RESULT = new Sname(n, null, null); } else { Symb s2 = Env.get(n, ((Name)r).getEnv()); if(s2 == null) { parser.sem_error(n,"UNKNOWN NAME"); RESULT = new Sname(n, null, null); } else if (!s2.isPublic() && !Name.getCurrentClass().isSubclass(s2.getOwner())) { parser.sem_error(n, "ILLEGAL ACCESS TO PRIVATE ATTRIBUTE"); RESULT = new Sname(n, null, null); } else RESULT = new Sname(n, s2, Env.get(n, Env.getRoot())); } } } :} ; modifiers_opt::= {: RESULT = Boolean.valueOf(false); :} | PUBLIC {: RESULT = Boolean.valueOf(true); :} ; // 5) Classes class_declarations ::= | class_declarations class_declaration | error {: parser.report_error("class_declaration","WRONG"); :} class_declaration ; // 5.1) Class Declaration class_declaration ::= modifiers_opt:m CLASS IDENTIFIER:n1 {: System.out.println("CLASS ENTRY: "+n1); if(parser.first) if (Env.putClass(n1, m.booleanValue()) != 0) parser.sem_error(n1,"DUPLICATE CLASS NAME"); if(parser.second) { Env.next(); Name c = (Env.get(n1)).getOwner(); Name.putCurrentClass(c); } :} class_body {: System.out.println("CLASS EXIT: "+n1); if(parser.first) Env.pop(); if(parser.second) Env.next(); :} | modifiers_opt:m CLASS IDENTIFIER:n1 EXTENDS IDENTIFIER:n2 {: if(parser.first) switch (Env.putClass(n1, m.booleanValue(),n2)){ case 1: parser.sem_error(n1,"DUPLICATE CLASS NAME"); break; case 2: parser.sem_error(n2,"UNKNOWN CLASS"); } if(parser.second) { Env.next(); Env.next(); Name c = (Env.get(n1)).getOwner(); Name.putCurrentClass(c); } :} class_body:w {: if(parser.first) { Env.pop(); Env.pop(); } if(parser.second) { Env.next(); Env.next(); } :} ; class_body ::= LBRACE class_body_declarations_opt RBRACE ; class_body_declarations_opt ::= | class_body_declarations ; class_body_declarations ::= class_body_declaration | class_body_declarations class_body_declaration ; class_body_declaration ::= class_member_declaration | constructor_declaration | block ; class_member_declaration ::= field_declaration | method_declaration | SEMICOLON ; // 5.2) Field Declarations field_declaration ::= modifiers_opt type variable_declarators SEMICOLON | modifiers_opt type error {: parser.report_error("variable_declarators","WRONG"); :} SEMICOLON | modifiers_opt type error {: parser.report_error("field_declaration","WRONG"); :} method_declaration ; variable_declarators ::= variable_declarator_id | variable_declarators COMMA M1 variable_declarator_id | error {: parser.report_error("variable_declarator_id","WRONG"); :} COMMA variable_declarator_id ; M1 ::= {: RESULT = (Type)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-2)).value; :} ; variable_declarator_id ::= IDENTIFIER:n {: if(parser.first) { Boolean m = (Boolean)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-2)).value; boolean mod = m == null ? false: m.booleanValue(); Type t = (Type)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-1)).value; Name c = Name.getCurrentClass(); String cname = c.getName(); Symb csymb = Env.get(cname); Symb s; if(csymb.isPublic()) s = new Symb(t, c, mod); else { if(mod) parser.warning(n, "PUBLIC FIELD DECLARED IN PRIVATE CLASS"); s = new Symb(t, c, false); } if (!Env.putVar(n, s)) parser.sem_error(n,"DUPLICATE NAME"); } :} | variable_declarator_id LBRACK RBRACK ; // 5.3) Method Declarations method_declaration ::= method_header method_body ; method_header ::= modifiers_opt:m type:t1 IDENTIFIER:n {: if(parser.first) { if (!Env.put(n, null)) parser.sem_error(n,"DUPLICATE NAME"); System.out.println(" METHOD ENTRY: "+n); Env.push(); } if(parser.second) { System.out.println(" METHOD ENTRY: "+n); Env.next(); } :} LPAREN formal_parameter_list_opt:t2 RPAREN {: if(parser.first) { Name c = Name.getCurrentClass(); String cname = c.getName(); Symb csymb = Env.get(cname); Symb s; if(csymb.isPublic()) s = new Symb(Type.method(t2, t1), c, m.booleanValue()); else { if(m.booleanValue()) parser.warning(n, "PUBLIC METHOD DECLARED IN PRIVATE CLASS"); s = new Symb(Type.method(t2, t1), c, false); } Env.putSymb(n, s); } if(parser.second) { Symb msymb = Env.get(n); Method.putCurrent(n, (Method)msymb.getType()); } :} | modifiers_opt:m VOID IDENTIFIER:n {: if(parser.first) { if (!Env.put(n, null)) parser.sem_error(n,"DUPLICATE NAME"); System.out.println(" METHOD ENTRY: "+n); Env.push(); } if(parser.second) { System.out.println(" METHOD ENTRY: "+n); Env.next(); } :} LPAREN formal_parameter_list_opt:t RPAREN {: if(parser.first) { Name c = Name.getCurrentClass(); String cname = c.getName(); Symb csymb = Env.get(cname); Symb s; if(csymb.isPublic()) s = new Symb(Type.method(t, Type.voidtype()), c, m.booleanValue()); else { if(m.booleanValue()) parser.warning(n, "PUBLIC METHOD DECLARED IN PRIVATE CLASS"); s = new Symb(Type.method(t, Type.voidtype()), c, false); } Env.putSymb(n, s); } if(parser.second) { Symb msymb = Env.get(n); Method.putCurrent(n, (Method)msymb.getType()); } :} | error {: parser.report_error("method_header","WRONG"); Env.push(); :} LPAREN formal_parameter_list_opt RPAREN ; formal_parameter_list_opt ::= {: RESULT = Type.voidtype(); :} | formal_parameter_list:t {: RESULT = t; :} ; formal_parameter_list ::= formal_parameter:t {: RESULT = t; :} | formal_parameter_list:t1 COMMA formal_parameter:t2 {: RESULT = Type.product(t1, t2); :} | error {: parser.report_error("formal_parameter_list","WRONG"); :} formal_parameter ; formal_parameter ::= type:t variable_declarator_id {: RESULT = t; :} ; method_body ::= LBRACE block_statements_opt RBRACE {: System.out.println(" METHOD EXIT"); if(parser.first) Env.pop(); if(parser.second) Env.next(); :} | SEMICOLON {: System.out.println(" METHOD EXIT"); if(parser.first) Env.pop(); if(parser.second) Env.next(); :} ; // 5.4) Constructor Declarations constructor_declaration ::= constructor_declarator constructor_body ; constructor_declarator ::= modifiers_opt:m IDENTIFIER:n {: if(parser.first) { if (!Env.put(n, null)) parser.sem_error(n,"DUPLICATE NAME"); else { Name c = Name.getCurrentClass(); String cname = c.getName(); if (!n.equals(cname)) parser.sem_error(n+", "+cname,"CONSTRUCTOR NAME DIFFERENT FROM CLASS NAME"); } System.out.println(" CONSTRUCTOR ENTRY: "+n); Env.push(); } if(parser.second) { System.out.println(" CONSTRUCTOR ENTRY: "+n); Env.next(); } :} LPAREN formal_parameter_list_opt:t RPAREN {: if(parser.first) { Name c = Name.getCurrentClass(); String cname = c.getName(); Symb csymb = Env.get(cname); Symb s; if(csymb.isPublic()) s = new Symb(Type.constructor(t, Type.reference(Type.getName(n))), c, m.booleanValue()); else { if(m.booleanValue()) parser.warning(n, "PUBLIC CONSTRUCTOR DECLARED IN PRIVATE CLASS"); s = new Symb(Type.constructor(t, Type.reference(Type.getName(n))), c, false); } Env.putSymb(n, s); } :} ; constructor_body ::= LBRACE explicit_constructor_invocation block_statements_opt RBRACE {: System.out.println(" CONSTRUCTOR EXIT"); if(parser.first) Env.pop(); if(parser.second) Env.next(); :} | LBRACE block_statements_opt RBRACE {: System.out.println(" CONSTRUCTOR EXIT"); if(parser.first) Env.pop(); if(parser.second) Env.next(); :} ; explicit_constructor_invocation ::= THIS LPAREN argument_list_opt RPAREN SEMICOLON | SUPER LPAREN argument_list_opt RPAREN SEMICOLON | primary DOT THIS LPAREN argument_list_opt RPAREN SEMICOLON | primary DOT SUPER LPAREN argument_list_opt RPAREN SEMICOLON ; // 6) Blocks and Statements block ::= LBRACE {: System.out.println(" BLOCK ENTRY"); if(parser.first) Env.push(); if(parser.second) Env.next(); :} block_statements_opt {: System.out.println(" BLOCK EXIT"); if(parser.first) Env.pop(); if(parser.second) Env.next(); :} RBRACE // | LBRACE error // {: parser.report_error("block","WRONG"); :} // RBRACE ; block_statements_opt ::= | block_statements ; block_statements ::= block_statement | block_statements block_statement | error {: parser.report_error("block_statements","WRONG"); :} block_statement ; block_statement ::= local_variable_declaration_statement | statement ; local_variable_declaration_statement ::= type variable_declarators SEMICOLON | type error {: parser.report_error("local_variable_declaration_statement","WRONG"); :} SEMICOLON ; statement ::= statement_without_trailing_substatement | if_then_statement | if_then_else_statement | while_statement ; statement_no_short_if ::= statement_without_trailing_substatement | if_then_else_statement_no_short_if | while_statement_no_short_if ; statement_without_trailing_substatement ::= block | empty_statement | expression_statement | return_statement ; empty_statement ::= SEMICOLON ; expression_statement ::= statement_expression SEMICOLON ; statement_expression ::= assignment | method_invocation | class_instance_creation_expression ; if_then_statement ::= IF LPAREN expression:t RPAREN statement {: if(parser.second) if(!t.isBool()) parser.sem_error(""+t, "NOT A BOOLEAN"); :} | IF error {: parser.report_error("if_then_statement","WRONG"); :} RPAREN statement ; if_then_else_statement ::= IF LPAREN expression:t RPAREN statement_no_short_if ELSE statement {: if(parser.second) if(!t.isBool()) parser.sem_error(""+t, "NOT A BOOLEAN"); :} | IF LPAREN error {: parser.report_error("expression","WRONG"); :} RPAREN statement_no_short_if ELSE statement ; if_then_else_statement_no_short_if ::= IF LPAREN expression:t RPAREN statement_no_short_if ELSE statement_no_short_if {: if(parser.second) if(!t.isBool()) parser.sem_error(""+t, "NOT A BOOLEAN"); :} ; while_statement ::= WHILE LPAREN expression:t RPAREN statement {: if(parser.second) if(!t.isBool()) parser.sem_error(""+t, "NOT A BOOLEAN"); :} | WHILE error {: parser.report_error("expression","WRONG"); :} RPAREN statement ; while_statement_no_short_if ::= WHILE LPAREN expression:t RPAREN statement_no_short_if {: if(parser.second) if(!t.isBool()) parser.sem_error(""+t, "NOT A BOOLEAN"); :} ; return_statement ::= RETURN expression_opt:t SEMICOLON {: if(parser.second) { Type r = Method.getCurrent().getRange(); if(t != r) parser.sem_error(""+t+", "+r, "INCOMPATIBLE TYPES"); } :} ; // 7) Expressions primary ::= primary_no_new_array:t {: RESULT = t; :} | array_creation_expression:t {: RESULT = t; :} ; primary_no_new_array ::= literal:t {: RESULT = t; :} | THIS {: RESULT = Type.reference(Name.getCurrentClass()); :} | LPAREN expression:t RPAREN {: RESULT = t; :} | class_instance_creation_expression:t {: RESULT = t; :} | field_access:t {: RESULT = t; :} | method_invocation:t {: RESULT = t; :} | array_access:t {: RESULT = t; :} | primitive_type DOT CLASS | VOID DOT CLASS | array_type DOT CLASS | name DOT CLASS | name DOT THIS | LPAREN error {: parser.report_error("primary_no_new_array","WRONG"); :} RPAREN | error {: parser.report_error("primary_no_new_array","WRONG"); :} DOT THIS ; class_instance_creation_expression ::= NEW name:sn LPAREN argument_list_opt:t2 RPAREN class_body_opt {: if(parser.second) { if(sn.rs == null) { parser.sem_error(sn.name,"UNKNOWN NAME"); RESULT = Type.errortype(); } else { Type t = sn.rs.getType(); if (!t.isName()) { parser.sem_error(""+t, "NOT A CLASS"); RESULT = Type.errortype(); } else { Env e = ((Name)t).getEnv(); Symb s1 = Env.get(sn.name, e); Type t1 = s1.getType(); if(!t1.isConstructor()) { parser.sem_error(""+t1, "NOT A CONSTRUCTOR"); RESULT = Type.errortype(); } else if(((Constructor)t1).getDomain() != t2) { parser.sem_error(""+t2, "INCOMPATIBLE ARGUMENT WITH CONSTRUCTOR "+sn.name); RESULT = Type.errortype(); } else if (!s1.isPublic() && !Name.getCurrentClass().isSubclass(sn.rs.getOwner())) { parser.sem_error(sn.name, "ILLEGAL ACCESS TO PRIVATE CONSTRUCTOR"); RESULT = Type.errortype(); } else RESULT = ((Constructor)t1).getRange(); } } } else RESULT =Type.voidtype(); :} // | primary DOT NEW IDENTIFIER LPAREN argument_list_opt RPAREN class_body_opt ; class_body_opt ::= | class_body ; argument_list_opt ::= {: RESULT = Type.voidtype(); :} | argument_list:t {: RESULT = t; :} ; argument_list ::= expression:t {: RESULT = t; :} | argument_list:t1 COMMA expression:t2 {: RESULT = Type.product(t1, t2); :} | error {: parser.report_error("argument_list","WRONG"); :} expression ; array_creation_expression ::= NEW primitive_type:t1 dim_exprs:t2 dims_opt {: if(parser.second) if(!t2.isInteger()) { parser.sem_error(""+t2, "NOT AN INTEGER"); RESULT = Type.errortype(); } else RESULT = Type.reference(Type.array(0, t1)); else RESULT = Type.voidtype(); :} | NEW name:sn dim_exprs:t2 dims_opt {: if(parser.second) if(!t2.isInteger()) { parser.sem_error(""+t2, "NOT AN INTEGER"); RESULT = Type.errortype(); } else { if(sn.cs == null) { parser.sem_error(sn.name,"UNKNOWN NAME"); RESULT = Type.errortype(); } else RESULT = Type.reference(Type.array(0, sn.cs.getType())); } else RESULT = Type.voidtype(); :} ; dim_exprs ::= dim_expr:t {: RESULT = t; :} | dim_exprs:t1 dim_expr:t2 {: RESULT = t1; :} | error {: parser.report_error("dim_expr","WRONG"); :} dim_expr ; dim_expr ::= LBRACK expression:t RBRACK {: RESULT = t; :} ; dims_opt ::= | dims ; dims ::= LBRACK RBRACK | dims LBRACK RBRACK ; field_access ::= primary:t DOT IDENTIFIER:n {: if(parser.second) { if(!t.isReference()) { parser.sem_error(""+t, "NOT A REFERENCE"); RESULT = Type.errortype(); } else { Type r = ((Reference)t).getReferred(); if(!r.isName()) { parser.sem_error(""+r, "NOT A CLASS"); RESULT = Type.errortype(); } else { Symb s = Env.get(n, ((Name)r).getEnv()); if(s == null){ parser.sem_error(n,"UNKNOWN NAME"); RESULT = Type.errortype(); } else if(!s.isPublic() && !Name.getCurrentClass().isSubclass(s.getOwner())) { parser.sem_error(n, "ILLEGAL ACCESS TO PRIVATE FIELD"); RESULT = Type.errortype(); } else RESULT = s.getType(); } } } else RESULT = Type.voidtype(); :} | SUPER DOT IDENTIFIER:n {: if(parser.second) { Name c = Name.getCurrentClass(); String sn = c.getSuper(); if(sn == null){ parser.sem_error(""+c,"NOT A SUBCLASS"); RESULT = Type.errortype(); } else { Symb ss = Env.get(sn); Name sc = (Name)ss.getType(); Symb s = Env.get(n, sc.getEnv()); if(s == null){ parser.sem_error(n,"UNKNOWN NAME"); RESULT = Type.errortype(); } else RESULT = s.getType(); } } else RESULT = Type.voidtype(); :} // | name DOT SUPER DOT IDENTIFIER ; method_invocation ::= name:sn LPAREN argument_list_opt:t2 RPAREN {: if(parser.second) { if(sn.cs == null) { parser.sem_error(sn.name,"UNKNOWN NAME"); RESULT = Type.errortype(); } else { Type t1 = sn.cs.getType(); if(!t1.isMethod()) { parser.sem_error(""+t1, "NOT A METHOD"); RESULT = Type.errortype(); } else if(sn.name.equals("printf")||sn.name.equals("scanf")) RESULT = Type.integer(); else if(((Method)t1).getDomain() != t2) { parser.sem_error(""+t2, "INCOMPATIBLE ARGUMENT WITH METHOD "+sn.name); RESULT = Type.errortype(); } else RESULT = ((Method)t1).getRange(); } } else RESULT = Type.voidtype(); :} | primary:t1 DOT IDENTIFIER:n LPAREN argument_list_opt:t2 RPAREN {: if(parser.second) if(!t1.isReference()) { parser.sem_error(""+t1, "NOT A REFERENCE"); RESULT = Type.errortype(); } else{ Type name = ((Reference)t1).getReferred(); if (!name.isName()) { parser.sem_error(""+t1, "NOT A CLASS"); RESULT = Type.errortype(); } else { Env e = ((Name)name).getEnv(); Symb s = Env.get(n, e); if(s == null) { parser.sem_error(n, "UNKNOWN NAME"); RESULT = Type.errortype(); } else { Type t = s.getType(); if(!t.isMethod()) { parser.sem_error(""+t, "NOT A METHOD"); RESULT = Type.errortype(); } else if(((Method)t).getDomain() != t2) { parser.sem_error(""+t2, "INCOMPATIBLE ARGUMENT WITH METHOD "+n); RESULT = Type.errortype(); } else if (!s.isPublic() && !Name.getCurrentClass().isSubclass(s.getOwner())) { parser.sem_error(n, "ILLEGAL ACCESS TO PRIVATE METHOD"); RESULT = Type.errortype(); } else RESULT = ((Method)t).getRange(); } } } else RESULT = Type.voidtype(); :} | SUPER DOT IDENTIFIER:n LPAREN argument_list_opt:t RPAREN {: RESULT = t; :} // | name DOT SUPER DOT IDENTIFIER LPAREN argument_list_opt RPAREN ; array_access ::= name:sn LBRACK expression:t2 RBRACK {: if(parser.second) { if(sn.cs == null) { parser.sem_error(sn.name,"UNKNOWN NAME"); RESULT = Type.errortype(); } else { Type t1 = sn.cs.getType(); if(!t1.isReference()) { parser.sem_error(""+t1, "NOT A REFERENCE"); RESULT = Type.errortype(); } else{ Type array = ((Reference)t1).getReferred(); if (!array.isArray()) { parser.sem_error(""+array, "NOT AN ARRAY"); RESULT = Type.errortype(); } else if(!t2.isInteger()) { parser.sem_error(""+t2, "NOT AN INTEGER"); RESULT = Type.errortype(); } else RESULT = ((Array)array).getBase(); } } } else RESULT = Type.voidtype(); :} | primary_no_new_array:t1 LBRACK expression:t2 RBRACK {: if(!t2.isInteger()) { parser.sem_error(""+t2, "NOT AN INTEGER"); RESULT = Type.errortype(); } else RESULT = t1; :} ; postfix_expression ::= primary:t {: RESULT = t; :} | name:sn {: if(sn.cs == null) { parser.sem_error(sn.name,"UNKNOWN NAME"); RESULT = Type.errortype(); } else RESULT = sn.cs.getType(); :} | AT name:sn {: if(sn.cs == null) { parser.sem_error(sn.name,"UNKNOWN NAME"); RESULT = Type.errortype(); } else RESULT = sn.cs.getType(); :} ; unary_expression ::= postfix_expression:t {: RESULT = t; :} | NOT unary_expression:t {: if(parser.second) if(!t.isBool()) { parser.sem_error(""+t, "NOT A BOOLEAN"); RESULT = Type.errortype(); } else RESULT = t; :} | PLUS unary_expression:t {: if(parser.second) if(!t.isNumber()) { parser.sem_error(""+t, "NOT A NUMBER"); RESULT = Type.errortype(); } else RESULT = t; :} | MINUS unary_expression:t {: if(parser.second) if(!t.isNumber()) { parser.sem_error(""+t, "NOT A NUMBER"); RESULT = Type.errortype(); } else RESULT = t; :} ; multiplicative_expression ::= unary_expression:t {: RESULT = t; :} | multiplicative_expression:t1 MULT unary_expression:t2 {: if(parser.second) { boolean b1 = t1.isNumber(); if(!b1) parser.sem_error(""+t1, "NOT A NUMBER"); boolean b2 = t2.isNumber(); if(!b2) parser.sem_error(""+t2, "NOT A NUMBER"); if(b1 && b2) RESULT = Type.max(t1, t2); else RESULT = Type.errortype(); } :} | multiplicative_expression:t1 DIV unary_expression:t2 {: if(parser.second) { boolean b1 = t1.isNumber(); if(!b1) parser.sem_error(""+t1, "NOT A NUMBER"); boolean b2 = t2.isNumber(); if(!b2) parser.sem_error(""+t2, "NOT A NUMBER"); if(b1 && b2) RESULT = Type.max(t1, t2); else RESULT = Type.errortype(); } :} | multiplicative_expression:t1 MOD unary_expression:t2 {: if(parser.second) { boolean b1 = t1.isInteger(); if(!b1) parser.sem_error(""+t1, "NOT AN INTEGER"); boolean b2 = t2.isInteger(); if(!b2) parser.sem_error(""+t2, "NOT AN INTEGER"); if(b1 && b2) RESULT = t1; else RESULT = Type.errortype(); } :} | error {: parser.report_error("multiplicative_expression","WRONG"); :} MULT unary_expression | error {: parser.report_error("multiplicative_expression","WRONG"); :} DIV unary_expression | error {: parser.report_error("multiplicative_expression","WRONG"); :} MOD unary_expression ; additive_expression ::= multiplicative_expression:t {: RESULT = t; :} | additive_expression:t1 PLUS multiplicative_expression:t2 {: if(parser.second) { boolean b1 = t1.isNumber(); if(!b1) parser.sem_error(""+t1, "NOT A NUMBER"); boolean b2 = t2.isNumber(); if(!b2) parser.sem_error(""+t2, "NOT A NUMBER"); if(b1 && b2) RESULT = Type.max(t1, t2); else RESULT = Type.errortype(); } :} | additive_expression:t1 MINUS multiplicative_expression:t2 {: if(parser.second) { boolean b1 = t1.isNumber(); if(!b1) parser.sem_error(""+t1, "NOT A NUMBER"); boolean b2 = t2.isNumber(); if(!b2) parser.sem_error(""+t2, "NOT A NUMBER"); if(b1 && b2) RESULT = Type.max(t1, t2); else RESULT = Type.errortype(); } :} ; relational_expression ::= additive_expression:t {: RESULT = t; :} | relational_expression:t1 LT additive_expression:t2 {: if(parser.second) { boolean b1 = t1.isNumber(); if(!b1) parser.sem_error(""+t1, "NOT A NUMBER"); boolean b2 = t2.isNumber(); if(!b2) parser.sem_error(""+t2, "NOT A NUMBER"); if(b1 && b2) RESULT = Type.bool(); else RESULT = Type.errortype(); } :} | relational_expression:t1 GT additive_expression:t2 {: if(parser.second) { boolean b1 = t1.isNumber(); if(!b1) parser.sem_error(""+t1, "NOT A NUMBER"); boolean b2 = t2.isNumber(); if(!b2) parser.sem_error(""+t2, "NOT A NUMBER"); if(b1 && b2) RESULT = Type.bool(); else RESULT = Type.errortype(); } :} | relational_expression:t1 LTEQ additive_expression:t2 {: if(parser.second) { boolean b1 = t1.isNumber(); if(!b1) parser.sem_error(""+t1, "NOT A NUMBER"); boolean b2 = t2.isNumber(); if(!b2) parser.sem_error(""+t2, "NOT A NUMBER"); if(b1 && b2) RESULT = Type.bool(); else RESULT = Type.errortype(); } :} | relational_expression:t1 GTEQ additive_expression:t2 {: if(parser.second) { boolean b1 = t1.isNumber(); if(!b1) parser.sem_error(""+t1, "NOT A NUMBER"); boolean b2 = t2.isNumber(); if(!b2) parser.sem_error(""+t2, "NOT A NUMBER"); if(b1 && b2) RESULT = Type.bool(); else RESULT = Type.errortype(); } :} | error {: parser.report_error("relational_expression","WRONG"); :} LT additive_expression | error {: parser.report_error("relational_expression","WRONG"); :} GT additive_expression | error {: parser.report_error("relational_expression","WRONG"); :} LTEQ additive_expression | error {: parser.report_error("relational_expression","WRONG"); :} GTEQ additive_expression ; equality_expression ::= relational_expression:t {: RESULT = t; :} | equality_expression:t1 EQEQ relational_expression:t2 {: if(parser.second) if(t1 != t2) { parser.sem_error(""+t1+", "+t2, "INCOMPATIBLE TYPES"); RESULT = Type.errortype(); } else RESULT = t1; :} | equality_expression:t1 NOTEQ relational_expression:t2 {: if(parser.second) if(t1 != t2) { parser.sem_error(""+t1+", "+t2, "INCOMPATIBLE TYPES"); RESULT = Type.errortype(); } else RESULT = t1; :} | error {: parser.report_error("equality_expression","WRONG"); :} EQEQ relational_expression | error {: parser.report_error("equality_expression","WRONG"); :} NOTEQ relational_expression ; conditional_and_expression ::= equality_expression:t {: RESULT = t; :} | conditional_and_expression:t1 ANDAND equality_expression:t2 {: if(parser.second) { boolean b1 = t1.isBool(); if(!b1) parser.sem_error(""+t1, "NOT A BOOLEAN"); boolean b2 = t2.isBool(); if(!b2) parser.sem_error(""+t2, "NOT A BOOLEAN"); if(b1 && b2) RESULT = t1; else RESULT = Type.errortype(); } :} | error {: parser.report_error("conditional_and_expression","WRONG"); :} ANDAND equality_expression ; conditional_or_expression ::= conditional_and_expression:t {: RESULT = t; :} | conditional_or_expression:t1 OROR conditional_and_expression:t2 {: if(parser.second) { boolean b1 = t1.isBool(); if(!b1) parser.sem_error(""+t1, "NOT A BOOLEAN"); boolean b2 = t2.isBool(); if(!b2) parser.sem_error(""+t2, "NOT A BOOLEAN"); if(b1 && b2) RESULT = t1; else RESULT = Type.errortype(); } :} | error {: parser.report_error("conditional_or_expression","WRONG"); :} OROR conditional_and_expression ; conditional_expression ::= conditional_or_expression:t {: RESULT = t; :} | conditional_or_expression:t1 QUESTION expression:t2 COLON conditional_expression:t3 {: if(parser.second) { boolean b1 = t1.isBool(); if(!b1) parser.sem_error(""+t1, "NOT A BOOLEAN"); if(t2 != t3) if(t2.isNumber() && t3.isNumber()) RESULT = Type.max(t2, t3); else { parser.sem_error(""+t2+", "+t3, "INCOMPATIBLE TYPES"); RESULT = Type.errortype(); } else RESULT = t2; } :} | error {: parser.report_error("conditional_expression","WRONG"); :} QUESTION expression COLON conditional_or_expression ; assignment_expression ::= conditional_expression:t {: RESULT = t; :} | assignment:t {: RESULT = t; :} ; assignment ::= left_hand_side:t1 EQ assignment_expression:t2 {: if(parser.second) if(t1 != t2) if(t1.isNumber() && t2.isNumber()) { Type t = Type.max(t1, t2); if(t2 != t) RESULT = t; else { parser.sem_error(""+t1+", "+t2, "LEFT-HAND TYPE SHORTER THAN RIGHT-END TYPE"); RESULT = Type.errortype(); } } else { parser.sem_error(""+t1+", "+t2, "INCOMPATIBLE TYPES"); RESULT = Type.errortype(); } else RESULT = t1; else RESULT = Type.voidtype(); :} | error {: parser.report_error("left_hand_side","WRONG"); :} EQ assignment_expression ; left_hand_side ::= name:sn {: if(parser.second) { if(sn.cs == null) { parser.sem_error(sn.name,"UNKNOWN NAME"); RESULT = Type.errortype(); } else RESULT = sn.cs.getType(); } else RESULT = Type.voidtype(); :} | field_access:t {: RESULT = t; :} | array_access:t {: RESULT = t; :} ; expression_opt ::= | expression:t {: RESULT = t; :} ; expression ::= assignment_expression:t {: RESULT = t; :} ;