added grammar class ; added first set
This commit is contained in:
8
lab/testGrNul.grm
Normal file
8
lab/testGrNul.grm
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
$S -> $A$B
|
||||||
|
$A -> $C/a
|
||||||
|
$A -> /~
|
||||||
|
$B -> /c$Bpr
|
||||||
|
$Bpr -> /a$A$C$Bpr
|
||||||
|
$Bpr -> /~
|
||||||
|
$C -> /b
|
||||||
|
$C -> /~
|
80
src/grammar.py
Normal file
80
src/grammar.py
Normal file
@@ -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();
|
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
from grammarLexer import GrammarLexer
|
from grammarLexer import GrammarLexer
|
||||||
from grammarLexer import T_TOKEN
|
from grammarLexer import T_TOKEN
|
||||||
|
from grammar import Grammar
|
||||||
|
from terminal import Terminal
|
||||||
|
from nonTerminal import NonTerminal
|
||||||
|
|
||||||
class GrammarParser:
|
class GrammarParser:
|
||||||
__lxr = GrammarLexer()
|
__lxr = GrammarLexer()
|
||||||
@@ -10,13 +13,14 @@ class GrammarParser:
|
|||||||
|
|
||||||
def parse(self, grammarFile):
|
def parse(self, grammarFile):
|
||||||
lines = grammarFile.readlines()
|
lines = grammarFile.readlines()
|
||||||
|
|
||||||
for line in lines:
|
for line in lines:
|
||||||
|
|
||||||
self.__lxr.processLine(line)
|
self.__lxr.processLine(line)
|
||||||
|
|
||||||
tokenLine = self.__lxr.getParsedLine()
|
tokenLine = self.__lxr.getParsedLine()
|
||||||
|
|
||||||
|
|
||||||
if self.__lineIsValid(tokenLine):
|
if self.__lineIsValid(tokenLine):
|
||||||
leftSide = tokenLine.popleft().getLexeme()
|
leftSide = tokenLine.popleft().getLexeme()
|
||||||
tokenLine.popleft()
|
tokenLine.popleft()
|
||||||
@@ -30,8 +34,38 @@ class GrammarParser:
|
|||||||
|
|
||||||
self.__grammar[leftSide].append(rightSide)
|
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):
|
def __lineIsValid(self, tokenLine):
|
||||||
if len(tokenLine) < 3:
|
if len(tokenLine) < 3:
|
||||||
@@ -42,7 +76,7 @@ class GrammarParser:
|
|||||||
|
|
||||||
if tokenLine[1].getClass() != T_TOKEN.ARROW:
|
if tokenLine[1].getClass() != T_TOKEN.ARROW:
|
||||||
return False;
|
return False;
|
||||||
|
|
||||||
return True;
|
return True;
|
||||||
|
|
||||||
def __isNewNonTerm(self, token):
|
def __isNewNonTerm(self, token):
|
||||||
@@ -54,7 +88,8 @@ class GrammarParser:
|
|||||||
def printGrammar(self):
|
def printGrammar(self):
|
||||||
for left in self.__grammar:
|
for left in self.__grammar:
|
||||||
self.__printRow(left, self.__grammar[left]);
|
self.__printRow(left, self.__grammar[left]);
|
||||||
|
|
||||||
|
|
||||||
def __printRow(self, left, right):
|
def __printRow(self, left, right):
|
||||||
line = left + " -> "
|
line = left + " -> "
|
||||||
|
|
||||||
@@ -67,4 +102,3 @@ class GrammarParser:
|
|||||||
line += str(token.getClass()) + "(" + token.getLexeme() + ")"
|
line += str(token.getClass()) + "(" + token.getLexeme() + ")"
|
||||||
|
|
||||||
print(line)
|
print(line)
|
||||||
|
|
||||||
|
117
src/nonTerminal.py
Normal file
117
src/nonTerminal.py
Normal file
@@ -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)
|
24
src/terminal.py
Normal file
24
src/terminal.py
Normal file
@@ -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() + " }")
|
@@ -7,7 +7,16 @@ prs = GrammarParser()
|
|||||||
|
|
||||||
inp = input("grammar file: ");
|
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();
|
||||||
|
Reference in New Issue
Block a user