Search code examples
javacompiler-construction

java compiler, finding the follow producing duplicates


ive been working on a java compiler assignment that is asking to find the First of a grammar. I have it all ready and done. all the work has been done , but i have one problem. my first is producing duplicates. for example part of my output is this

NonTerminal      First
     P          int void                                
     L          int void                                
     D          int void                                
     Vd         int void                                
     Ts         int void                                
     Fn         int void                                
     Ps         int void void   

Ps int void void , the 2nd void is a duplicate. how would i go about removing these duplicates? ill paste my main compiler code were everything happens below. i suspect i would have to make a change somewere in the findFirst method, since thats were all the action happens , but im not sure what to do.

package compilerproject;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.security.KeyStore.Entry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Compiler {

    public static void main(String[] args) {

        List<Grammar> gList = getGrammar(); 
        Map<String, List<String>> fList = firstList(gList);
        //firstlist returns a hash map LHS and RHS
        //save it into fList which is a map of Strings and List so u can use it in findFirst method
       printFirstList(fList, gList);
       ParserLibrary idList = new ParserLibrary();

    }

    public static List<String> findFirst(String v, List<Grammar> l)
    {
        List<String> First = new ArrayList<String>();

        for(int i = 0; i < l.size(); i++)
        {

            if(v.equals(l.get(i).term))
            {


                String [] s = l.get(i).prod.split(" ");

                if(!isNonTerm(s[0]))// is a terminal
                {
                    First.add(s[0]);
                }

    // if the rhs is a terminal 

Solution

  • It would save some troubles using a Set instead of a List. I kept List as return type, but changed the rest.

    public static List<String> findFirst(String v, List<Grammar> l) {
        Set<String> first = new TreeSet<>();
    
        Set<String> done = new HashSet<>();
        done.add(v);
    
        Grammer previous = null;
        for (Grammar gr : l) {
            if (v.equals(gr.term)) {
                String s = gr.prod.split(" ")[0];
                if (!isNonTerm(s)) { // is a terminal
                    first.add(s);
                }
    
                // if the rhs is a terminal 
                if (s.equalsIgnoreCase("empty") && previous != null) {
                    String[] stemp = previous.prod.split(" ");
    
                    if (v.equalsIgnoreCase(stemp[0]) && stemp.length > 1
                            && done.add(stemp[1])) {
                        first.addAll(findFirst(stemp[1], l)); // <--------- Here it happened
                    }
                    //if the rhs is empty , then get the previous grammar 
                    //split it.
                    //find the first of it and ad it to the first list
                }
                if (!v.equals(s) && isNonTerm(s) && done.add(s)) {
                    first.addAll(findFirst(s, l));
                }
            }
            previous = gr;       
        }
        return new ArrayList<String>(first);
    }
    

    I still do not find the code entirely clear. So maybe with Set at your disposal, you may find a simpler formulation. To remove the scroll bar I place the open brace on the same line.

    To prevent endless recursion I added the set done which "evidently" is not needed.