Skip to content

Commit

Permalink
Merge pull request #21 from vivz/develop
Browse files Browse the repository at this point in the history
Good work guys!
  • Loading branch information
jbrundrett authored May 14, 2017
2 parents 30cacdf + 6b4625b commit 6e02f59
Show file tree
Hide file tree
Showing 21 changed files with 1,094 additions and 290 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@
.DS_Store
*pycache*
*.pyc
data/
*.DS_Store
8 changes: 8 additions & 0 deletions LL1_Academy/management/commands/cleardatabase.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.core.management.base import BaseCommand, CommandError
from LL1_Academy.models import Grammar

class Command(BaseCommand):
help = 'This will delete all grammars in the database be careful'

def handle(self, *args, **options):
Grammar.objects.all().delete()
File renamed without changes.
25 changes: 25 additions & 0 deletions LL1_Academy/management/commands/populate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from django.core.management.base import BaseCommand, CommandError
import time
from LL1_Academy.tools import MassGrammarGenerator
import os

class Command(BaseCommand):
help = 'This command will populate the database with a small set of grammars'

def handle(self, *args, **options):
#Number of randomly generated grammars
num = 100

#Number variables this run will include.
#For example [2,3] will run the script to generate
#grammars with 2 variables and 3 variables
nVariables = [2, 3]

nonTerminals = ['A','B','C','D']
terminals = ['x','y','z','w']

for n in nVariables:
start_time = time.time()
mg = MassGrammarGenerator.MassGrammarGenerator(n)
mg.run(num,nonTerminals[:n],terminals)
print("{}Variables: {} seconds---".format(n,(time.time() - start_time)))
5 changes: 3 additions & 2 deletions LL1_Academy/templates/LL1_Academy/_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
<link rel="stylesheet" href="{% static 'css/main.css' %}" />
<link rel="stylesheet" href="https://cdn.iconmonstr.com/1.2.0/css/iconmonstr-iconic-font.min.css">
<link rel="stylesheet" type="text/css" href="{% static 'sweetalert/sweetalert.css' %}">
<link href="https://fonts.googleapis.com/css?family=Muli:900" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Lato" rel="stylesheet">
</head>

{% block templates %}
{% endblock %}

<body>

<body style="font-family: 'Lato', sans-serif;">
{% block content %}
{% endblock %}

Expand Down
7 changes: 6 additions & 1 deletion LL1_Academy/templates/LL1_Academy/index.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{% extends "LL1_Academy/_base.html" %}
{% block content %}
<a href="learn"><button>Start Learning</button></a>
<div id="landingPage">
<h1 id="siteTitle">LL(1) Academy</h1>
<div id="startButton">
<a href="learn"><button class="button" id="startLearning">Start Learning</button></a>
</div>
</div>
{% endblock %}
32 changes: 23 additions & 9 deletions LL1_Academy/templates/LL1_Academy/learn.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,38 @@

<script id="question-template" type="text/x-handlebars-template">
<div class="question" id="active">
<p class="question-title">What is the {{category}} set of symbol {{symbol}}?</p>
{{#if symbol}}
<p class="question-title">What is the {{category}} set of symbol {{symbol}}?</p>
{{else}}
<p class="question-title">Is the grammar LL(1)?</p>
{{/if}}
<form id="question-input" onsubmit="return false;">
<input type="text" id="question-answer" placeholder="Answer as comma separated e.g. x,y" required>
<div class="feedback"></div>
{{#if symbol}}
<input type="text" id="question-answer" placeholder="example: x,y,z" required>
{{#if opt}}
<button class="button" type="button" id="opt-char">{{opt}}</button>
{{/if}}
{{else}}
<input type="radio" name="ll1" value="True" id="input1" style="display:inline;" required> True
<input type="radio" name="ll1" value="False" id="input2" style="display:inline;margin-left:10px;"> False
{{/if}}
<div class="feedback"></div>
<button type="submit" class="button">Submit</button>
</form>
<div class="answerbox"></div>
</div>
</script>

{% endverbatim %}
{% endblock %}

{% block content %}

<h1 id="siteBanner">LL(1) Academy</h1>
<div class="container">
<div class="row">
<!-- grammar here -->
<div class="small-12 medium-6 columns" id="grammar-container">
<h3>Consider the grammar</h3>
<h3 id="grammarHeader">Consider the grammar</h3>
<div id="grammar">
{% for grammar in grammar_object %}
<div class="productions">
Expand All @@ -45,10 +58,11 @@ <h3>Consider the grammar</h3>
{% endfor %}
</div>
<div id="explainer">
<p>
where {{ non_terminals }} is the set of nonterminal symbols, {{ start_symbol }} is the start symbol, {{ terminals }}
is the set of terminal symbols, and &epsilon; denotes the empty string.
<p>
<p>{{ non_terminals }} is the set of nonterminal symbols</p>
<p>{{ start_symbol }} is the start symbol</p>
<p>{{ terminals }} is the set of terminal symbols</p>
<p>&epsilon; denotes the empty string</p>
<p>$ denotes end of string</p>
</div>
</div>

Expand Down
42 changes: 40 additions & 2 deletions LL1_Academy/tests.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,41 @@
from django.test import TestCase
from django.test import TestCase, Client
from django.urls import reverse
from LL1_Academy.models import Grammar, Question


class UrlTest(TestCase):
def setup(self):
g = Grammar(prods="{'A': ['xA', 'Bz'],'B': ['yB']}", nonTerminals="AB", terminals="xyz", startsymbol="A")
g.save()
q = Question(gid=g, qnum=0, category="FI", symbol="A", answer="xy")
q.save()

def test_index1(self):
response = self.client.get('/')
self.assertEqual(response.status_code, 200)

def test_index2(self):
response = self.client.get('/index')
self.assertEqual(response.status_code, 200)

def test_learn(self):
response = self.client.get('/learn')
self.assertEqual(response.status_code, 200)

def test_get_question(self):
response = self.client.get('/get_question')
self.assertEqual(response.status_code, 200)

def test_check_answer(self):
response = self.client.get('/check_answer')
self.assertEqual(response.status_code, 404)

class RenderingTest(TestCase):
def test_index1(self):
response = self.client.get('/')
self.assertTemplateUsed(response, 'LL1_Academy/index.html')

def test_index1(self):
response = self.client.get('/learn')
self.assertTemplateUsed(response, 'LL1_Academy/learn.html')

# Create your tests here.
158 changes: 92 additions & 66 deletions LL1_Academy/tools/MassGrammarGenerator.py
Original file line number Diff line number Diff line change
@@ -1,75 +1,101 @@
from LL1_Academy.tools import GrammarChecker,SingleGrammarGenerator
from LL1_Academy.tools import GrammarChecker,SingleGrammarGenerator, SvmLearn
from LL1_Academy.models import Grammar, Question
import os

#
#Description: The MassGrammarGenerator class outputs a specified number of
# random grammars based on the given list of terminals and non-terminals
#
#Input: num: the number of grammars to be generated
# nonTerminals: a list of characters representing the nonterminals of length 2-4
# Terminals: a list of characters representing the terminals of length 2-4
#
#Output: a specified number of grammars written to some plaintext files under ./txt.
# You may need to create this directory first before running this script in order
# for it to work.
#
#Usage:
# mg = MassGrammarGenerator()
# mg.run(1000,['S','F'],['a','(','+',')'])
#
#Description: The MassGrammarGenerator class randomly generates grammar and filter
#out the interesting ones to store in the database.

script_dir = os.path.dirname(__file__) #<-- absolute dir the script is in


class MassGrammarGenerator:
def __init__(self):
self.statusSummary = {-1:0,
0:0,
1:0,
2:0}

def __init__(self, n):
self.statusSummary = {"LL1":0, "non-LL1":0}
self.gc = GrammarChecker.GrammarChecker()
self.g = SingleGrammarGenerator.SingleGrammarGenerator()

#builds the ML model using our trainingData
fI = os.path.join(script_dir, 'trainingData/'+str(n)+'var-interesting')
fN = os.path.join(script_dir, 'trainingData/'+str(n)+'var-not-interesting')
self.learnModel = SvmLearn.SvmLearn(n,fI,fN)

def run(self,num, nonTerminals, terminals, writeToTxt = False):
gc = GrammarChecker.GrammarChecker()
g = SingleGrammarGenerator.SingleGrammarGenerator()
n = len(nonTerminals)
if writeToTxt:
f = open(os.path.join(script_dir, 'data/'+str(n)+'Variables'), 'w')

#generate num grammars and check them, discard the left hand recursion ones
def run(self,num, nonTerminals, terminals):
self.nonTerminals = nonTerminals
self.terminals =terminals

#generate num grammars, filter them and write the good ones to DB
for i in range(0,num):
grammar = g.generate(nonTerminals, terminals)
firstSets, followSets, parsingTable, status, reachable = gc.solve(grammar,'A',False)
self.statusSummary[status] += 1
if (not status == -1) and reachable:
self.statusSummary[2] += 1
if writeToTxt:
f.write(str(grammar)+'\n \tFirst Set: '+str(firstSets)+'\n \tFollow Set: '+str(followSets)+'\n \tReachable: '+ str(reachable) +'\n\n')
else:
#write to DB if not to TXT
newG = Grammar(prods=str(grammar), nonTerminals=''.join(nonTerminals), terminals=''.join(terminals), startSymbol='A')
newG.save()

#LL1 Question
ans = 'True' if status==0 else 'False'
qLL = Question(gid=newG,qnum=0,category='LL',answer=ans)
qLL.save()

#ParseTable Question
qPT = Question(gid=newG,qnum=1,category='PT',answer=str(parsingTable))
qPT.save()

#First and Follow Set
qnum = 2
for v in nonTerminals:
qFirst = Question(gid=newG,qnum=qnum,category='FI',symbol=v,answer=''.join(firstSets[v]))
qFirst.save()
qnum +=1
qFollow = Question(gid=newG,qnum=qnum,category='FO',symbol=v,answer=''.join(followSets[v]))
qFollow.save()
qnum +=1

#print a small summary
print(str(n)+"Variables:\nleft recursion: "+str(self.statusSummary[-1])
+"; LL(1): " + str(self.statusSummary[0])
+"; not LL(1): " + str(self.statusSummary[1])
+ "; Reachable: " + str(self.statusSummary[2]))
self.createOneGrammar()

#prints a small report
print(str(len(self.nonTerminals)) + " Variables: "
+ str(self.statusSummary["LL1"] + self.statusSummary["non-LL1"])
+ " interesting grammars picked out of "+str(num)+"\n\tLL1: " + str(self.statusSummary["LL1"])
+ "\n\tnot LL(1): " + str(self.statusSummary["non-LL1"]))

def createOneGrammar(self):
#This function randomly generates a single grammar, and saves it to the DB if
#it is not left-recursion and interesting

#generate a single grammar randomly
grammar = self.g.generate(self.nonTerminals, self.terminals)

#get answers using the checker
#result = firstSets, followSets, parsingTable, status, reachable
result = self.gc.solve(grammar,'A',False)

#Abandon left recursive grammars and grammars with non-reachable variables
if (result[3] == -1) or (not result[4]):
return

#If the ML model decidese it's interesting, save to DB
prediction = self.learnModel.predictGrammar(grammar,result[0],result[1])
if prediction[0]==1:
self.saveToDB(grammar,result)
print("YES: "+str(grammar)+"\n\tFirst: "+str(result[0])+"\n\tFollow: "+str(result[1]))
else:
print("NO: "+str(grammar)+"\n\tFirst: "+str(result[0])+"\n\tFollow: "+str(result[1]))


def saveToDB(self,grammar,result):
#This function takes the grammar and the result returned by gc.solve. It
#populates the Grammar and Question table in DB with the correct fields

firstSets, followSets, parsingTable, status, reachable = result
newG = Grammar(prods=str(grammar), nonTerminals=''.join(self.nonTerminals),
terminals=''.join(self.terminals), startSymbol='A')
newG.save()

#First and Follow Set
qnum = 0
for v in self.nonTerminals:
qFirst = Question(gid=newG,qnum=qnum,category='FI',
symbol=v,answer=''.join(firstSets[v]))
qFirst.save()
qnum +=1

for v in self.nonTerminals:
qFollow = Question(gid=newG,qnum=qnum,category='FO',
symbol=v,answer=''.join(followSets[v]))
qFollow.save()
qnum +=1

#LL1 Question
if status == 0:
ans = 'True'
self.statusSummary["LL1"]+=1
else:
ans = 'False'
self.statusSummary["non-LL1"]+=1
qLL = Question(gid=newG,qnum=qnum,category='LL',answer=ans)
qLL.save()
qnum+=1

#Parse Table Question
qPT = Question(gid=newG,qnum=qnum,category='PT',answer=str(parsingTable))
qPT.save()




Loading

0 comments on commit 6e02f59

Please sign in to comment.