diff --git a/lab/testGrNul.grm b/lab/testGrNul.grm new file mode 100644 index 0000000..fe7f084 --- /dev/null +++ b/lab/testGrNul.grm @@ -0,0 +1,8 @@ +$S -> $A$B +$A -> $C/a +$A -> /~ +$B -> /c$Bpr +$Bpr -> /a$A$C$Bpr +$Bpr -> /~ +$C -> /b +$C -> /~ diff --git a/src/grammar.py b/src/grammar.py new file mode 100644 index 0000000..4c47e08 --- /dev/null +++ b/src/grammar.py @@ -0,0 +1,80 @@ +from terminal import Terminal +from nonTerminal import NonTerminal + +class Grammar: + __atomKeyMapping = [] + __productions = [] + __first = {} + + __nonTerminals = {} + __terminals = {} + + def addProduction(self, nonTerminal, atoms): + + + self.__addAtom(nonTerminal); + + for atom in atoms: + self.__addAtom(atom) + + key = nonTerminal.toString() + self.__nonTerminals[key].addProduction(atoms) + + def __addAtom(self, atom): + if isinstance(atom, Terminal): + self.__addAtomToSet(self.__terminals, atom) + elif isinstance(atom, NonTerminal): + self.__addAtomToSet(self.__nonTerminals, atom) + + def __addAtomToSet(self, atomSet, atom): + key = atom.toString() + + if atomSet.get(key) == None: + atomSet[key] = atom + + + def toString(self): + retStr = "" + + for key in self.__nonTerminals: + nonTerminal = self.__nonTerminals[key] + + retStr += nonTerminal.productionsToString() + "\n"; + + return retStr + + def generateMetrics(self): + self.__generateFirstSets() + + + def __generateFirstSets(self): + for key in self.__nonTerminals: + nonTerminal = self.__nonTerminals[key] + nonTerminal.generateFirstSet(); + + + def printNullableSet(self): + output = "Nullable = { " + + for key in self.__nonTerminals: + nonTerminal = self.__nonTerminals[key] + + if nonTerminal.isNullable(): + output += nonTerminal.toString() + " " + + output += "}" + + print(output) + + def printFirstSets(self): + print("First Sets:") + + for key in self.__nonTerminals: + nonTerminal = self.__nonTerminals[key] + + nonTerminal.printFirstSet(); + + for key in self.__terminals: + terminal = self.__terminals[key] + + terminal.printFirstSet(); diff --git a/src/grammarParser.py b/src/grammarParser.py index fc52a5a..afd31c7 100644 --- a/src/grammarParser.py +++ b/src/grammarParser.py @@ -2,6 +2,9 @@ from grammarLexer import GrammarLexer from grammarLexer import T_TOKEN +from grammar import Grammar +from terminal import Terminal +from nonTerminal import NonTerminal class GrammarParser: __lxr = GrammarLexer() @@ -10,13 +13,14 @@ class GrammarParser: def parse(self, grammarFile): lines = grammarFile.readlines() - + for line in lines: self.__lxr.processLine(line) tokenLine = self.__lxr.getParsedLine() - + + if self.__lineIsValid(tokenLine): leftSide = tokenLine.popleft().getLexeme() tokenLine.popleft() @@ -30,8 +34,38 @@ class GrammarParser: self.__grammar[leftSide].append(rightSide) - print(self.__grammar) - + def parseGrm(self, grammarFile): + grm = Grammar() + lines = grammarFile.readlines() + + for line in lines: + self.__lxr.processLine(line) + tokenLine = self.__lxr.getParsedLine() + + if self.__lineIsValid(tokenLine): + leftSide = NonTerminal(tokenLine.popleft().getLexeme()) + tokenLine.popleft() + + rightSide = [] + tmpToken = None + + while len(tokenLine) > 0: + tmpToken = tokenLine.popleft() + + if tmpToken.getClass() == T_TOKEN.NON_TERMINAL: + rightSide.append(NonTerminal(tmpToken.getLexeme())) + elif tmpToken.getClass() == T_TOKEN.TERMINAL: + lexeme = tmpToken.getLexeme(); + + if lexeme == "~": + lexeme = None; + + rightSide.append(Terminal(lexeme)) + + grm.addProduction(leftSide, rightSide) + + return grm + def __lineIsValid(self, tokenLine): if len(tokenLine) < 3: @@ -42,7 +76,7 @@ class GrammarParser: if tokenLine[1].getClass() != T_TOKEN.ARROW: return False; - + return True; def __isNewNonTerm(self, token): @@ -54,7 +88,8 @@ class GrammarParser: def printGrammar(self): for left in self.__grammar: self.__printRow(left, self.__grammar[left]); - + + def __printRow(self, left, right): line = left + " -> " @@ -67,4 +102,3 @@ class GrammarParser: line += str(token.getClass()) + "(" + token.getLexeme() + ")" print(line) - diff --git a/src/nonTerminal.py b/src/nonTerminal.py new file mode 100644 index 0000000..f30e21f --- /dev/null +++ b/src/nonTerminal.py @@ -0,0 +1,117 @@ +from terminal import Terminal + +class NonTerminal: + def __init__(self, string): + self.__string = None + self.__isNullable = False + self.__productions = [] + self.__first = [] + self.__firstSetIsGenerated = False; + + self.__string = string + + def addProduction(self, rhs): + self.__firstSetIsGenrated = False; + + if (len(rhs) == 1 and + isinstance(rhs[0], Terminal) and + rhs[0].isEmptyString()): + + self.__isNullable = True + + self.__productions.append(rhs) + + def productionsToString(self): + if len(self.__productions) == 0: + return "N/A" + + output = self.toString() + " -> " + + for index in range(len(self.__productions) - 1): + production = self.__productions[index] + + for atom in production: + output += atom.toString() + + output += " | " + + for atom in self.__productions[len(self.__productions) - 1]: + output += atom.toString() + + return output + + def printProductions(self): + print(self.productionsToString()) + + def generateFirstSet(self): + for production in self.__productions: + self.__addFirsSetForProduction(production) + + self.__firstSetIsGenrated = True; + + def __addFirsSetForProduction(self, production): + stillNullable = True + lenProd = len(production) + index = 0; + + while stillNullable and index < lenProd: + atom = production[index] + + if isinstance(atom, Terminal): + self.__addAtomToFirstSet(atom) + stillNullable = False + elif atom != self: + if atom.isNullable(): + if index < lenProd - 1: + self.__copyFirstSetFrom(atom, False) + else: + self.__copyFirstSetFrom(atom, True) + else: + self.__copyFirstSetFrom(atom, False) + + + index += 1; + + def __addAtomToFirstSet(self, newAtom): + key = newAtom.toString() + + for atom in self.__first: + if atom.toString() == key: + return + + self.__first.append(newAtom) + + def __copyFirstSetFrom(self, atom, includeEmptyString): + + for innerAtom in atom.getFirstSet(): + if isinstance(innerAtom, Terminal): + if innerAtom.isEmptyString(): + if includeEmptyString: + self.__addAtomToFirstSet(innerAtom) + else: + self.__addAtomToFirstSet(innerAtom) + + def getFirstSet(self): + if self.__firstSetIsGenrated == False: + self.generateFirstSet(); + + return self.__first + + def isNullable(self): + return self.__isNullable + + def toString(self): + return "N(" + self.__string + ")" + + def getString(self): + return self.__string + + def printFirstSet(self): + output = "First("+ self.toString() +") = { " + + for atom in self.__first: + output += atom.toString() + " "; + + output += "}" + + print(output) diff --git a/src/terminal.py b/src/terminal.py new file mode 100644 index 0000000..a7619e9 --- /dev/null +++ b/src/terminal.py @@ -0,0 +1,24 @@ +class Terminal: + + def __init__(self, string): + self.__string = None + self.__isEmptyString = True + + if string == "": + self.__string = "" + else: + self.__string = string + self.__isEmptyString = False + + + def getFirstSet(self): + return [self] + + def isEmptyString(self): + return self.__isEmptyString + + def toString(self): + return "T(" + self.__string + ")" + + def printFirstSet(self): + print("First("+ self.toString() +") = { " + self.toString() + " }") diff --git a/src/testParser.py b/src/testParser.py index da13f0e..2fa071a 100755 --- a/src/testParser.py +++ b/src/testParser.py @@ -7,7 +7,16 @@ prs = GrammarParser() inp = input("grammar file: "); -prs.parse(open(inp, "r")); +grm = prs.parseGrm(open(inp, "r")); -prs.printGrammar(); +print(grm.toString()) +grm.generateMetrics() + +print("----------------") +grm.printNullableSet() + +print("----------------") +grm.printFirstSets() + +#prs.printGrammar();