diff --git a/.gitignore b/.gitignore
index 86daf73..9f0492f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,8 +5,11 @@
.settings/
console.devmode.log
develop.db.mv.db
+develop.db.trace.db
project/project/
project/target/
target/
src/main/resources/props/default.props
src/main/resources/props/production.default.props
+.idea
+/.gitignore
diff --git a/src/main/scala/bootstrap/liftweb/Boot.scala b/src/main/scala/bootstrap/liftweb/Boot.scala
index d1132b2..ab524de 100644
--- a/src/main/scala/bootstrap/liftweb/Boot.scala
+++ b/src/main/scala/bootstrap/liftweb/Boot.scala
@@ -40,10 +40,16 @@ class Boot {
Schemifier.schemify(true, Schemifier.infoF _,
User, Attendance, Course, PosedProblem, PosedProblemSet, Problem, ProblemType,
DFAConstructionProblem, DFAConstructionSolutionAttempt,
+ ProductConstructionProblem, ProductConstructionSolutionAttempt,
+ MinimizationProblem, MinimizationSolutionAttempt,
NFAConstructionProblem, NFAConstructionSolutionAttempt,
NFAToDFAProblem, NFAToDFASolutionAttempt,
RegExConstructionProblem, RegexConstructionSolutionAttempt,
PumpingLemmaProblem, PumpingLemmaSolutionAttempt,
+ WordsInGrammarProblem, WordsInGrammarSolutionAttempt,
+ GrammarToCNFProblem, GrammarToCNFSolutionAttempt,
+ DescriptionToGrammarProblem, DescriptionToGrammarSolutionAttempt,
+ CYKProblem, CYKSolutionAttempt,
ProblemSet, Role, SolutionAttempt, Supervision)
StartupHooks.hooks map (hook => hook())
@@ -62,7 +68,7 @@ class Boot {
Menu.i("Practice Problem Sets") / "practicesets" / "index" >> loggedInPredicate submenus(
Menu.i("Solve Practice Set Problem") /"practicesets" / "solve" >> Hidden),
-
+
Menu.i("Problems") / "problems" / "index" >> isInstructorPredicate submenus(
Menu.i("Create Problem") / "problems" / "create" >> Hidden,
Menu.i("Edit Problem") / "problems" / "edit" >> Hidden),
diff --git a/src/main/scala/com/automatatutor/lib/SOAPConnection.scala b/src/main/scala/com/automatatutor/lib/SOAPConnection.scala
index 519c33f..78786de 100644
--- a/src/main/scala/com/automatatutor/lib/SOAPConnection.scala
+++ b/src/main/scala/com/automatatutor/lib/SOAPConnection.scala
@@ -118,6 +118,47 @@ object GraderConnection {
return ((responseXml \ "grade").text.toInt, (responseXml \ "feedString" \ "ul" \ "li"))
}
+
+ //Product Construction
+
+ def getProductConstructionFeedback(correctDfaDescriptionList : List[String], attemptDfaDescription : String, booleanOperation : String, maxGrade : Int) : (Int, NodeSeq) = {
+
+ def stringListToNodeList(xs: List[String]): List[Node] = xs match{
+ case Nil => List()
+ case y :: ys => Elem(null, "dfaDesc", Null, TopScope, true, XML.loadString(y)) :: stringListToNodeList(ys)
+ }
+
+ val arguments = Map[String, Node](
+ "dfaDescList" -> Elem(null, "dfaDescList", Null, TopScope, true, stringListToNodeList(correctDfaDescriptionList):_*),
+ "dfaAttemptDesc" -> XML.loadString(attemptDfaDescription),
+ "booleanOperation" -> Elem(null, "booleanOperation", Null, TopScope, true, Text(booleanOperation)),
+ "maxGrade" -> Elem(null, "maxGrade", Null, TopScope, true, Text(maxGrade.toString)),
+ "feedbackLevel" -> Elem(null, "feedbackLevel", Null, TopScope, true, Text("Hint")),
+ "enabledFeedbacks" -> Elem(null, "enabledFeedbacks", Null, TopScope, true, Text("ignored")))
+
+ val responseXml = soapConnection.callMethod(namespace, "ComputeFeedbackProductConstruction", arguments)
+
+ ((responseXml \ "grade").text.toInt, (responseXml \ "feedString" \ "ul" \ "li"))
+ }
+
+ // Minimization
+
+ //TODO: Pass Minimization Table to variable 'minimizationTableAttempt'
+ def getMinimizationFeedback(dfaDescription : String, minimizationTableDescription : String, attemptDfaDescription : String, maxGrade : Int) : (Int, NodeSeq) = {
+
+ val arguments = Map[String, Node](
+ "dfaDesc" -> XML.loadString(dfaDescription),
+ "minimizationTableAttempt" -> XML.loadString(minimizationTableDescription),
+ "dfaAttemptDesc" -> XML.loadString(attemptDfaDescription),
+ "maxGrade" -> Elem(null, "maxGrade", Null, TopScope, true, Text(maxGrade.toString)),
+ "feedbackLevel" -> Elem(null, "feedbackLevel", Null, TopScope, true, Text("Hint")),
+ "enabledFeedbacks" -> Elem(null, "enabledFeedbacks", Null, TopScope, true, Text("ignored")));
+
+ //TODO: Implement 'ComputeFeedbackMinimization' in Backend
+ val responseXml = soapConnection.callMethod(namespace, "ComputeFeedbackMinimization", arguments)
+
+ ((responseXml \ "grade").text.toInt, (responseXml \ "feedString" \ "ul" \ "li"))
+ }
// NFA
@@ -175,6 +216,84 @@ object GraderConnection {
if(responseXml.text.equals("CorrectRegex")) return List() else return List(responseXml.text)
}
+ //Grammar
+ def getGrammarParsingErrors(potentialGrammar : String) : Seq[String] = {
+ val arguments = Map[String, Node](
+ "grammar" -> Elem(null, "grammar", Null, TopScope, true, Text(potentialGrammar))
+ )
+
+ val responseXml = soapConnection.callMethod(namespace, "CheckGrammar", arguments)
+
+ if(responseXml.text.equals("CorrectGrammar")) return List() else return List(responseXml.text)
+ }
+
+ def getCNFParsingErrors(potentialGrammar : String) : Seq[String] = {
+ val arguments = Map[String, Node](
+ "grammar" -> Elem(null, "grammar", Null, TopScope, true, Text(potentialGrammar))
+ )
+
+ val responseXml = soapConnection.callMethod(namespace, "isCNF", arguments)
+
+ if((responseXml \ "res").head.text.equals("y")) return List() else return List((responseXml \ "feedback").head.text)
+ }
+
+ def getWordsInGrammarFeedback(grammar: String, wordsIn : Seq[String], wordsOut : Seq[String], maxGrade : Int) : (Int, NodeSeq) = {
+ val arguments = Map[String, Node](
+ "grammar" -> Elem(null, "grammar", Null, TopScope, true, Text(grammar)),
+ "wordsIn" ->
{ wordsIn.map((symbol : String) => Elem(null, "word", Null, TopScope, true, Text(symbol))) }
,
+ "wordsOut" -> { wordsOut.map((symbol : String) => Elem(null, "word", Null, TopScope, true, Text(symbol))) }
,
+ "maxGrade" -> Elem(null, "maxGrade", Null, TopScope, true, Text(maxGrade.toString))
+ );
+
+ val responseXml = soapConnection.callMethod(namespace, "ComputeWordsInGrammarFeedback", arguments)
+
+ return ((responseXml \ "grade").head.text.toInt, (responseXml \ "feedback"))
+ }
+
+ def getDescriptionToGrammarFeedback(solution: String, attempt: String, maxGrade : Int) : (Int, NodeSeq) = {
+ val arguments = Map[String, Node](
+ "solution" -> Elem(null, "solution", Null, TopScope, true, Text(solution)),
+ "attempt" -> Elem(null, "attempt", Null, TopScope, true, Text(attempt)),
+ "maxGrade" -> Elem(null, "maxGrade", Null, TopScope, true, Text(maxGrade.toString)),
+ "checkEmptyWord" -> Elem(null, "checkEmptyWord", Null, TopScope, true, Text(true.toString))
+ );
+
+ val responseXml = soapConnection.callMethod(namespace, "ComputeGrammarEqualityFeedback", arguments)
+
+ return ((responseXml \ "grade").head.text.toInt, (responseXml \ "feedback"))
+ }
+
+ def getGrammarToCNFFeedback(solution: String, attempt: String, maxGrade : Int) : (Int, NodeSeq) = {
+ //check that attempt is in CNF
+ val arguments1 = Map[String, Node](
+ "grammar" -> Elem(null, "grammar", Null, TopScope, true, Text(attempt))
+ );
+ val responseXml1 = soapConnection.callMethod(namespace, "isCNF", arguments1)
+ if ((responseXml1 \ "res").head.text == "n") return (-1, (responseXml1 \ "feedback"))
+
+ val arguments2 = Map[String, Node](
+ "solution" -> Elem(null, "solution", Null, TopScope, true, Text(solution)),
+ "attempt" -> Elem(null, "attempt", Null, TopScope, true, Text(attempt)),
+ "maxGrade" -> Elem(null, "maxGrade", Null, TopScope, true, Text(maxGrade.toString)),
+ "checkEmptyWord" -> Elem(null, "checkEmptyWord", Null, TopScope, true, Text(false.toString))
+ );
+ val responseXml2 = soapConnection.callMethod(namespace, "ComputeGrammarEqualityFeedback", arguments2)
+ return ((responseXml2 \ "grade").head.text.toInt, (responseXml2 \ "feedback"))
+ }
+
+ def getCYKFeedback(grammar: String, word: String, cyk_attempt: String, maxGrade : Int) : (Int, NodeSeq) = {
+ val arguments = Map[String, Node](
+ "grammar" -> Elem(null, "grammar", Null, TopScope, true, Text(grammar)),
+ "word" -> Elem(null, "word", Null, TopScope, true, Text(word)),
+ "attempt" -> XML.loadString(cyk_attempt),
+ "maxGrade" -> Elem(null, "maxGrade", Null, TopScope, true, Text(maxGrade.toString))
+ );
+
+ val responseXml = soapConnection.callMethod(namespace, "ComputeCYKFeedback", arguments)
+
+ return ((responseXml \ "grade").head.text.toInt, (responseXml \ "feedback"))
+ }
+
// Pumping lemma
def getPLParsingErrors(languageDesc : String, constraintDesc : String,
diff --git a/src/main/scala/com/automatatutor/model/CYKProblem.scala b/src/main/scala/com/automatatutor/model/CYKProblem.scala
new file mode 100644
index 0000000..a554fa5
--- /dev/null
+++ b/src/main/scala/com/automatatutor/model/CYKProblem.scala
@@ -0,0 +1,45 @@
+package com.automatatutor.model
+
+import scala.xml.NodeSeq
+import scala.xml.XML
+import net.liftweb.mapper.By
+import net.liftweb.mapper.IdPK
+import net.liftweb.mapper.LongKeyedMapper
+import net.liftweb.mapper.LongKeyedMetaMapper
+import net.liftweb.mapper.MappedString
+import net.liftweb.mapper.MappedText
+import net.liftweb.mapper.MappedInt
+import net.liftweb.mapper.MappedLongForeignKey
+import bootstrap.liftweb.StartupHook
+
+class CYKProblem extends LongKeyedMapper[CYKProblem] with IdPK with SpecificProblem[CYKProblem] {
+ def getSingleton = CYKProblem
+
+ object problemId extends MappedLongForeignKey(this, Problem)
+ object grammar extends MappedText(this)
+ object word extends MappedText(this)
+
+ def getGrammar = this.grammar.is
+ def setGrammar(g : String) = this.grammar(g)
+ def getWord = this.word.is
+ def setWord(w : String) = this.word(w)
+
+ override def copy(): CYKProblem = {
+ val retVal = new CYKProblem
+ retVal.problemId(this.problemId.get)
+ retVal.grammar(this.grammar.get)
+ retVal.word(this.word.get)
+ return retVal
+ }
+
+ override def setGeneralProblem(newProblem: Problem) = this.problemId(newProblem)
+
+}
+
+object CYKProblem extends CYKProblem with LongKeyedMetaMapper[CYKProblem] {
+ def findByGeneralProblem(generalProblem : Problem) : CYKProblem =
+ find(By(CYKProblem.problemId, generalProblem)) openOrThrowException("Must only be called if we are sure that generalProblem is a CYKProblem")
+
+ def deleteByGeneralProblem(generalProblem : Problem) : Boolean =
+ this.bulkDelete_!!(By(CYKProblem.problemId, generalProblem))
+}
\ No newline at end of file
diff --git a/src/main/scala/com/automatatutor/model/DescriptionToGrammarProblem.scala b/src/main/scala/com/automatatutor/model/DescriptionToGrammarProblem.scala
new file mode 100644
index 0000000..72cd20b
--- /dev/null
+++ b/src/main/scala/com/automatatutor/model/DescriptionToGrammarProblem.scala
@@ -0,0 +1,41 @@
+package com.automatatutor.model
+
+import scala.xml.NodeSeq
+import scala.xml.XML
+import net.liftweb.mapper.By
+import net.liftweb.mapper.IdPK
+import net.liftweb.mapper.LongKeyedMapper
+import net.liftweb.mapper.LongKeyedMetaMapper
+import net.liftweb.mapper.MappedString
+import net.liftweb.mapper.MappedText
+import net.liftweb.mapper.MappedInt
+import net.liftweb.mapper.MappedLongForeignKey
+import bootstrap.liftweb.StartupHook
+
+class DescriptionToGrammarProblem extends LongKeyedMapper[DescriptionToGrammarProblem] with IdPK with SpecificProblem[DescriptionToGrammarProblem] {
+ def getSingleton = DescriptionToGrammarProblem
+
+ object problemId extends MappedLongForeignKey(this, Problem)
+ object grammar extends MappedText(this)
+
+ def getGrammar = this.grammar.is
+ def setGrammar(g : String) = this.grammar(g)
+
+ override def copy(): DescriptionToGrammarProblem = {
+ val retVal = new DescriptionToGrammarProblem
+ retVal.problemId(this.problemId.get)
+ retVal.grammar(this.grammar.get)
+ return retVal
+ }
+
+ override def setGeneralProblem(newProblem: Problem) = this.problemId(newProblem)
+
+}
+
+object DescriptionToGrammarProblem extends DescriptionToGrammarProblem with LongKeyedMetaMapper[DescriptionToGrammarProblem] {
+ def findByGeneralProblem(generalProblem : Problem) : DescriptionToGrammarProblem =
+ find(By(DescriptionToGrammarProblem.problemId, generalProblem)) openOrThrowException("Must only be called if we are sure that generalProblem is a DescriptionToGrammarProblem")
+
+ def deleteByGeneralProblem(generalProblem : Problem) : Boolean =
+ this.bulkDelete_!!(By(DescriptionToGrammarProblem.problemId, generalProblem))
+}
\ No newline at end of file
diff --git a/src/main/scala/com/automatatutor/model/GrammarToCNFProblem.scala b/src/main/scala/com/automatatutor/model/GrammarToCNFProblem.scala
new file mode 100644
index 0000000..6e31c74
--- /dev/null
+++ b/src/main/scala/com/automatatutor/model/GrammarToCNFProblem.scala
@@ -0,0 +1,41 @@
+package com.automatatutor.model
+
+import scala.xml.NodeSeq
+import scala.xml.XML
+import net.liftweb.mapper.By
+import net.liftweb.mapper.IdPK
+import net.liftweb.mapper.LongKeyedMapper
+import net.liftweb.mapper.LongKeyedMetaMapper
+import net.liftweb.mapper.MappedString
+import net.liftweb.mapper.MappedText
+import net.liftweb.mapper.MappedInt
+import net.liftweb.mapper.MappedLongForeignKey
+import bootstrap.liftweb.StartupHook
+
+class GrammarToCNFProblem extends LongKeyedMapper[GrammarToCNFProblem] with IdPK with SpecificProblem[GrammarToCNFProblem] {
+ def getSingleton = GrammarToCNFProblem
+
+ object problemId extends MappedLongForeignKey(this, Problem)
+ object grammar extends MappedText(this)
+
+ def getGrammar = this.grammar.is
+ def setGrammar(g : String) = this.grammar(g)
+
+ override def copy(): GrammarToCNFProblem = {
+ val retVal = new GrammarToCNFProblem
+ retVal.problemId(this.problemId.get)
+ retVal.grammar(this.grammar.get)
+ return retVal
+ }
+
+ override def setGeneralProblem(newProblem: Problem) = this.problemId(newProblem)
+
+}
+
+object GrammarToCNFProblem extends GrammarToCNFProblem with LongKeyedMetaMapper[GrammarToCNFProblem] {
+ def findByGeneralProblem(generalProblem : Problem) : GrammarToCNFProblem =
+ find(By(GrammarToCNFProblem.problemId, generalProblem)) openOrThrowException("Must only be called if we are sure that generalProblem is a GrammarToCNFProblem")
+
+ def deleteByGeneralProblem(generalProblem : Problem) : Boolean =
+ this.bulkDelete_!!(By(GrammarToCNFProblem.problemId, generalProblem))
+}
\ No newline at end of file
diff --git a/src/main/scala/com/automatatutor/model/MinimizationProblem.scala b/src/main/scala/com/automatatutor/model/MinimizationProblem.scala
new file mode 100644
index 0000000..47556bc
--- /dev/null
+++ b/src/main/scala/com/automatatutor/model/MinimizationProblem.scala
@@ -0,0 +1,49 @@
+package com.automatatutor.model
+
+import net.liftweb.mapper.MappedString
+import net.liftweb.mapper.LongKeyedMapper
+import net.liftweb.mapper.LongKeyedMetaMapper
+import net.liftweb.mapper.MappedLongForeignKey
+import net.liftweb.mapper.IdPK
+import net.liftweb.mapper.By
+import net.liftweb.mapper.MappedText
+
+import scala.xml.XML
+import scala.xml.NodeSeq
+import bootstrap.liftweb.StartupHook
+
+class MinimizationProblem extends LongKeyedMapper[MinimizationProblem] with IdPK with SpecificProblem[MinimizationProblem] {
+ def getSingleton = MinimizationProblem
+
+ protected object problemId extends MappedLongForeignKey(this, Problem)
+ protected class automatonClass extends MappedText(this) {
+
+ }
+ protected val automaton: automatonClass = new automatonClass
+
+ def getGeneralProblem = this.problemId.obj openOrThrowException "Every MinimizationProblem must have a ProblemId"
+ override def setGeneralProblem(problem : Problem) : MinimizationProblem = this.problemId(problem)
+
+ def getAutomaton = this.automaton.is
+ def setAutomaton(automaton : String) = this.automaton(automaton)
+ def setAutomaton(automaton : NodeSeq) = this.automaton(automaton.mkString)
+
+ def getXmlDescription : NodeSeq = XML.loadString(this.automaton.is)
+
+ def getAlphabet : Seq[String] = (getXmlDescription \ "alphabet" \ "symbol").map(_.text)
+
+ override def copy(): MinimizationProblem = {
+ val retVal = new MinimizationProblem
+ retVal.problemId(this.problemId.get)
+ retVal.automaton(this.automaton.get)
+ return retVal
+ }
+}
+
+object MinimizationProblem extends MinimizationProblem with LongKeyedMetaMapper[MinimizationProblem] {
+ def findByGeneralProblem(generalProblem : Problem) : MinimizationProblem =
+ find(By(MinimizationProblem.problemId, generalProblem)) openOrThrowException("Must only be called if we are sure that generalProblem is a MinimizationProblem")
+
+ def deleteByGeneralProblem(generalProblem : Problem) : Boolean =
+ bulkDelete_!!(By(MinimizationProblem.problemId, generalProblem))
+}
\ No newline at end of file
diff --git a/src/main/scala/com/automatatutor/model/Problem.scala b/src/main/scala/com/automatatutor/model/Problem.scala
index 0571712..ee714b0 100644
--- a/src/main/scala/com/automatatutor/model/Problem.scala
+++ b/src/main/scala/com/automatatutor/model/Problem.scala
@@ -5,8 +5,12 @@ import com.automatatutor.snippet.NFAProblemSnippet
import com.automatatutor.snippet.NFAToDFAProblemSnippet
import com.automatatutor.snippet.ProblemSnippet
import com.automatatutor.snippet.RegExConstructionSnippet
-import com.automatatutor.snippet.PumpingLemmaProblemSnippet
-import net.liftweb.common.Empty
+import com.automatatutor.snippet.WordsInGrammarSnippet
+import com.automatatutor.snippet.DescriptionToGrammarSnippet
+import com.automatatutor.snippet.CYKProblemSnippet
+import com.automatatutor.snippet.GrammarToCNFSnippet
+import com.automatatutor.snippet.MinimizationSnippet
+import com.automatatutor.snippet.ProductConstructionSnippet
import net.liftweb.common.Full
import net.liftweb.mapper.By
import net.liftweb.mapper.IdPK
@@ -27,14 +31,26 @@ class ProblemType extends LongKeyedMapper[ProblemType] with IdPK {
val NFAConstructionTypeName = "English to NFA"
val NFAToDFATypeName = "NFA to DFA"
val EnglishToRegExTypeName = "English to Regular Expression"
- //val PLTypeName = "Pumping Lemma Proof"
+ val PLTypeName = "Pumping Lemma Proof"
val BuchiSolvingTypeName = "Buchi Game Solving"
+ val WordsInGrammarTypeName = "Words in Grammar"
+ val DescriptionToGrammarTypeName = "English to Grammar"
+ val GrammarToCNFTypeName = "Grammar to CNF"
+ val CYKTypeName = "CYK Algorithm"
+ val ProductConstructionTypeName = "Product Construction"
+ val MinimizationTypeName = "Minimization"
val knownProblemTypes : Map[String, ProblemSnippet] = Map(
DFAConstructionTypeName -> DFAConstructionSnippet,
NFAConstructionTypeName -> NFAProblemSnippet,
NFAToDFATypeName -> NFAToDFAProblemSnippet,
- EnglishToRegExTypeName -> RegExConstructionSnippet
+ EnglishToRegExTypeName -> RegExConstructionSnippet,
+ WordsInGrammarTypeName -> WordsInGrammarSnippet,
+ DescriptionToGrammarTypeName -> DescriptionToGrammarSnippet,
+ GrammarToCNFTypeName -> GrammarToCNFSnippet,
+ CYKTypeName -> CYKProblemSnippet,
+ ProductConstructionTypeName -> ProductConstructionSnippet,
+ MinimizationTypeName -> MinimizationSnippet
) ++
(if(Config.buchiGameSolving.enabled.get) { Map(BuchiSolvingTypeName -> BuchiGameSolving.SnippetAdapter) } else { Map[String, ProblemSnippet]() })
@@ -47,9 +63,15 @@ class ProblemType extends LongKeyedMapper[ProblemType] with IdPK {
def getSpecificProblem(generalProblem: Problem): SpecificProblem[_] = this.problemTypeName.get match {
case DFAConstructionTypeName => DFAConstructionProblem.findByGeneralProblem(generalProblem)
+ case ProductConstructionTypeName => ProductConstructionProblem.findByGeneralProblem(generalProblem)
+ case MinimizationTypeName => MinimizationProblem.findByGeneralProblem(generalProblem)
case NFAConstructionTypeName => NFAConstructionProblem.findByGeneralProblem(generalProblem)
case NFAToDFATypeName => NFAToDFAProblem.findByGeneralProblem(generalProblem)
case EnglishToRegExTypeName => RegExConstructionProblem.findByGeneralProblem(generalProblem)
+ case WordsInGrammarTypeName => WordsInGrammarProblem.findByGeneralProblem(generalProblem)
+ case DescriptionToGrammarTypeName => DescriptionToGrammarProblem.findByGeneralProblem(generalProblem)
+ case GrammarToCNFTypeName => GrammarToCNFProblem.findByGeneralProblem(generalProblem)
+ case CYKTypeName => CYKProblem.findByGeneralProblem(generalProblem)
}
}
diff --git a/src/main/scala/com/automatatutor/model/ProductConstructionProblem.scala b/src/main/scala/com/automatatutor/model/ProductConstructionProblem.scala
new file mode 100644
index 0000000..a7bc9ac
--- /dev/null
+++ b/src/main/scala/com/automatatutor/model/ProductConstructionProblem.scala
@@ -0,0 +1,59 @@
+package com.automatatutor.model
+
+import net.liftweb.mapper.MappedString
+import net.liftweb.mapper.LongKeyedMapper
+import net.liftweb.mapper.LongKeyedMetaMapper
+import net.liftweb.mapper.MappedLongForeignKey
+import net.liftweb.mapper.IdPK
+import net.liftweb.mapper.By
+import net.liftweb.mapper.MappedText
+import scala.xml.XML
+import scala.xml.NodeSeq
+import bootstrap.liftweb.StartupHook
+
+class ProductConstructionProblem extends LongKeyedMapper[ProductConstructionProblem] with IdPK with SpecificProblem[ProductConstructionProblem] {
+ def getSingleton = ProductConstructionProblem
+
+ protected object problemId extends MappedLongForeignKey(this, Problem)
+ protected class automatonClass extends MappedText(this) {
+
+ }
+ protected class booleanOperationClass extends MappedText(this) {
+
+ }
+ protected val automaton1 : automatonClass = new automatonClass
+ protected val automaton2 : automatonClass = new automatonClass
+ protected val automataList : List[automatonClass] = List(automaton1, automaton2)
+ protected val booleanOperation : booleanOperationClass = new booleanOperationClass
+
+ def getGeneralProblem = this.problemId.obj openOrThrowException "Every ProductConstructionProblem must have a ProblemId"
+ override def setGeneralProblem(problem : Problem) : ProductConstructionProblem = this.problemId(problem)
+
+ def getAutomataList = this.automataList
+ def getBooleanOperation = this.booleanOperation
+ def setAutomaton1(automaton : String) = this.automataList(0)(automaton)
+ def setAutomaton2(automaton : String) = this.automataList(1)(automaton)
+ def setBooleanOperation(boolOp : String) = this.booleanOperation(boolOp)
+ def setAutomaton1(automaton : NodeSeq) = this.automataList(0)(automaton.mkString)
+ def setAutomaton2(automaton : NodeSeq) = this.automataList(1)(automaton.mkString)
+
+ def getXmlDescription1 : NodeSeq = XML.loadString(this.automataList(0).is)
+ def getXmlDescription2 : NodeSeq = XML.loadString(this.automataList(1).is)
+
+ def getAlphabet : Seq[String] = (getXmlDescription1 \ "alphabet" \ "symbol").map(_.text)
+
+ override def copy(): ProductConstructionProblem = {
+ val retVal = new ProductConstructionProblem
+ retVal.problemId(this.problemId.get)
+ retVal.automataList(0)(this.automataList(0).get)
+ return retVal
+ }
+}
+
+object ProductConstructionProblem extends ProductConstructionProblem with LongKeyedMetaMapper[ProductConstructionProblem] {
+ def findByGeneralProblem(generalProblem : Problem) : ProductConstructionProblem =
+ find(By(ProductConstructionProblem.problemId, generalProblem)) openOrThrowException("Must only be called if we are sure that generalProblem is a ProductConstructionProblem")
+
+ def deleteByGeneralProblem(generalProblem : Problem) : Boolean =
+ bulkDelete_!!(By(ProductConstructionProblem.problemId, generalProblem))
+}
\ No newline at end of file
diff --git a/src/main/scala/com/automatatutor/model/SolutionAttempt.scala b/src/main/scala/com/automatatutor/model/SolutionAttempt.scala
index 0dc4f2d..5175950 100644
--- a/src/main/scala/com/automatatutor/model/SolutionAttempt.scala
+++ b/src/main/scala/com/automatatutor/model/SolutionAttempt.scala
@@ -22,6 +22,7 @@ object SolutionAttempt extends SolutionAttempt with LongKeyedMetaMapper[Solution
}
}
+//DFA Construction
class DFAConstructionSolutionAttempt extends LongKeyedMapper[DFAConstructionSolutionAttempt] with IdPK {
def getSingleton = DFAConstructionSolutionAttempt
@@ -31,10 +32,39 @@ class DFAConstructionSolutionAttempt extends LongKeyedMapper[DFAConstructionSolu
object DFAConstructionSolutionAttempt extends DFAConstructionSolutionAttempt with LongKeyedMetaMapper[DFAConstructionSolutionAttempt] {
def getByGeneralAttempt ( generalAttempt : SolutionAttempt ) : DFAConstructionSolutionAttempt = {
- return this.find(By(DFAConstructionSolutionAttempt.solutionAttemptId, generalAttempt)) openOrThrowException "Must only be called if we are sure that the general attempt also has a DFA construction attempt"
+ return this.find(By(DFAConstructionSolutionAttempt.solutionAttemptId, generalAttempt)) openOrThrowException "Must only be called if we are sure that the general attempt also has a DFA construction attempt"
}
}
+//Product Construction
+class ProductConstructionSolutionAttempt extends LongKeyedMapper[ProductConstructionSolutionAttempt] with IdPK {
+ def getSingleton = ProductConstructionSolutionAttempt
+
+ object solutionAttemptId extends MappedLongForeignKey(this, SolutionAttempt)
+ object attemptAutomaton extends MappedText(this)
+}
+
+object ProductConstructionSolutionAttempt extends ProductConstructionSolutionAttempt with LongKeyedMetaMapper[ProductConstructionSolutionAttempt] {
+ def getByGeneralAttempt ( generalAttempt : SolutionAttempt ) : ProductConstructionSolutionAttempt = {
+ return this.find(By(ProductConstructionSolutionAttempt.solutionAttemptId, generalAttempt)) openOrThrowException "Must only be called if we are sure that the general attempt also has a product construction attempt"
+ }
+}
+
+//Minimization
+class MinimizationSolutionAttempt extends LongKeyedMapper[MinimizationSolutionAttempt] with IdPK {
+ def getSingleton = MinimizationSolutionAttempt
+
+ object solutionAttemptId extends MappedLongForeignKey(this, SolutionAttempt)
+ object attemptAutomaton extends MappedText(this)
+}
+
+object MinimizationSolutionAttempt extends MinimizationSolutionAttempt with LongKeyedMetaMapper[MinimizationSolutionAttempt] {
+ def getByGeneralAttempt ( generalAttempt : SolutionAttempt ) : MinimizationSolutionAttempt = {
+ return this.find(By(MinimizationSolutionAttempt.solutionAttemptId, generalAttempt)) openOrThrowException "Must only be called if we are sure that the general attempt also has a minimization attempt"
+ }
+}
+
+//NFA Construction
class NFAConstructionSolutionAttempt extends LongKeyedMapper[NFAConstructionSolutionAttempt] with IdPK {
def getSingleton = NFAConstructionSolutionAttempt
@@ -46,6 +76,7 @@ object NFAConstructionSolutionAttempt extends NFAConstructionSolutionAttempt wit
}
+//NFA to DFA
class NFAToDFASolutionAttempt extends LongKeyedMapper[NFAToDFASolutionAttempt] with IdPK {
def getSingleton = NFAToDFASolutionAttempt
@@ -57,6 +88,7 @@ object NFAToDFASolutionAttempt extends NFAToDFASolutionAttempt with LongKeyedMet
}
+//Regex Construction
class RegexConstructionSolutionAttempt extends LongKeyedMapper[RegexConstructionSolutionAttempt] with IdPK {
def getSingleton = RegexConstructionSolutionAttempt
@@ -68,6 +100,52 @@ object RegexConstructionSolutionAttempt extends RegexConstructionSolutionAttempt
}
+//Pumping Lemma
+class WordsInGrammarSolutionAttempt extends LongKeyedMapper[WordsInGrammarSolutionAttempt] with IdPK {
+ def getSingleton = WordsInGrammarSolutionAttempt
+
+ object solutionAttemptId extends MappedLongForeignKey(this, SolutionAttempt)
+ object attemptWordsIn extends MappedText(this)
+ object attemptWordsOut extends MappedText(this)
+}
+
+object WordsInGrammarSolutionAttempt extends WordsInGrammarSolutionAttempt with LongKeyedMetaMapper[WordsInGrammarSolutionAttempt] {
+
+}
+
+class DescriptionToGrammarSolutionAttempt extends LongKeyedMapper[DescriptionToGrammarSolutionAttempt] with IdPK {
+ def getSingleton = DescriptionToGrammarSolutionAttempt
+
+ object solutionAttemptId extends MappedLongForeignKey(this, SolutionAttempt)
+ object attemptGrammar extends MappedText(this)
+}
+
+object DescriptionToGrammarSolutionAttempt extends DescriptionToGrammarSolutionAttempt with LongKeyedMetaMapper[DescriptionToGrammarSolutionAttempt] {
+
+}
+
+class GrammarToCNFSolutionAttempt extends LongKeyedMapper[GrammarToCNFSolutionAttempt] with IdPK {
+ def getSingleton = GrammarToCNFSolutionAttempt
+
+ object solutionAttemptId extends MappedLongForeignKey(this, SolutionAttempt)
+ object attemptGrammar extends MappedText(this)
+}
+
+object GrammarToCNFSolutionAttempt extends GrammarToCNFSolutionAttempt with LongKeyedMetaMapper[GrammarToCNFSolutionAttempt] {
+
+}
+
+class CYKSolutionAttempt extends LongKeyedMapper[CYKSolutionAttempt] with IdPK {
+ def getSingleton = CYKSolutionAttempt
+
+ object solutionAttemptId extends MappedLongForeignKey(this, SolutionAttempt)
+ object attempt extends MappedText(this)
+}
+
+object CYKSolutionAttempt extends CYKSolutionAttempt with LongKeyedMetaMapper[CYKSolutionAttempt] {
+
+}
+
class PumpingLemmaSolutionAttempt extends LongKeyedMapper[PumpingLemmaSolutionAttempt] with IdPK {
def getSingleton = PumpingLemmaSolutionAttempt
diff --git a/src/main/scala/com/automatatutor/model/WordsInGrammarProblem.scala b/src/main/scala/com/automatatutor/model/WordsInGrammarProblem.scala
new file mode 100644
index 0000000..c1cf78e
--- /dev/null
+++ b/src/main/scala/com/automatatutor/model/WordsInGrammarProblem.scala
@@ -0,0 +1,49 @@
+package com.automatatutor.model
+
+import scala.xml.NodeSeq
+import scala.xml.XML
+import net.liftweb.mapper.By
+import net.liftweb.mapper.IdPK
+import net.liftweb.mapper.LongKeyedMapper
+import net.liftweb.mapper.LongKeyedMetaMapper
+import net.liftweb.mapper.MappedString
+import net.liftweb.mapper.MappedText
+import net.liftweb.mapper.MappedInt
+import net.liftweb.mapper.MappedLongForeignKey
+import bootstrap.liftweb.StartupHook
+
+class WordsInGrammarProblem extends LongKeyedMapper[WordsInGrammarProblem] with IdPK with SpecificProblem[WordsInGrammarProblem] {
+ def getSingleton = WordsInGrammarProblem
+
+ object problemId extends MappedLongForeignKey(this, Problem)
+ object grammar extends MappedText(this)
+ object inNeeded extends MappedInt(this)
+ object outNeeded extends MappedInt(this)
+
+ def getGrammar = this.grammar.is
+ def setGrammar(g : String) = this.grammar(g)
+ def getInNeeded = this.inNeeded.is
+ def setInNeeded(i : Int) = this.inNeeded(i)
+ def getOutNeeded = this.outNeeded.is
+ def setOutNeeded(i : Int) = this.outNeeded(i)
+
+ override def copy(): WordsInGrammarProblem = {
+ val retVal = new WordsInGrammarProblem
+ retVal.problemId(this.problemId.get)
+ retVal.grammar(this.grammar.get)
+ retVal.inNeeded(this.inNeeded.get)
+ retVal.outNeeded(this.outNeeded.get)
+ return retVal
+ }
+
+ override def setGeneralProblem(newProblem: Problem) = this.problemId(newProblem)
+
+}
+
+object WordsInGrammarProblem extends WordsInGrammarProblem with LongKeyedMetaMapper[WordsInGrammarProblem] {
+ def findByGeneralProblem(generalProblem : Problem) : WordsInGrammarProblem =
+ find(By(WordsInGrammarProblem.problemId, generalProblem)) openOrThrowException("Must only be called if we are sure that generalProblem is a WordsInGrammarProblem")
+
+ def deleteByGeneralProblem(generalProblem : Problem) : Boolean =
+ this.bulkDelete_!!(By(WordsInGrammarProblem.problemId, generalProblem))
+}
\ No newline at end of file
diff --git a/src/main/scala/com/automatatutor/snippet/CYKProblemSnippet.scala b/src/main/scala/com/automatatutor/snippet/CYKProblemSnippet.scala
new file mode 100644
index 0000000..c08bfe1
--- /dev/null
+++ b/src/main/scala/com/automatatutor/snippet/CYKProblemSnippet.scala
@@ -0,0 +1,196 @@
+package com.automatatutor.snippet
+
+import java.util.Calendar
+import java.util.Date
+import scala.Array.canBuildFrom
+import scala.Array.fallbackCanBuildFrom
+import scala.xml.NodeSeq
+import scala.xml.NodeSeq.seqToNodeSeq
+import scala.xml.Text
+import scala.xml.XML
+import com.automatatutor.lib.GraderConnection
+import com.automatatutor.model.Problem
+import com.automatatutor.model.CYKProblem
+import com.automatatutor.model.CYKSolutionAttempt
+import com.automatatutor.model.SolutionAttempt
+import net.liftweb.common.Box
+import net.liftweb.common.Full
+import net.liftweb.http.SHtml
+import net.liftweb.http.SHtml.ElemAttr.pairToBasic
+import net.liftweb.http.Templates
+import net.liftweb.http.js.JE.JsRaw
+import net.liftweb.http.js.JsCmd
+import net.liftweb.http.js.JsCmds
+import net.liftweb.http.js.JsCmds._
+import net.liftweb.http.js.JsCmds.JsHideId
+import net.liftweb.http.js.JsCmds.JsShowId
+import net.liftweb.http.js.JsCmds.SetHtml
+import net.liftweb.http.js.JsCmds.cmdToString
+import net.liftweb.http.js.JsCmds.jsExpToJsCmd
+import net.liftweb.util.Helpers
+import net.liftweb.util.Helpers._
+import net.liftweb.util.Helpers.strToSuperArrowAssoc
+import net.liftweb.http.js.JE.Call
+import net.liftweb.common.Empty
+
+object CYKProblemSnippet extends ProblemSnippet {
+
+ override def renderCreate( createUnspecificProb : (String, String) => Problem,
+ returnFunc : () => Nothing ) : NodeSeq = {
+
+ def create(formValues : String) : JsCmd = {
+ val formValuesXml = XML.loadString(formValues)
+ val grammar = (formValuesXml \ "grammarfield").head.text
+ val word = (formValuesXml \ "wordfield").head.text
+ val shortDescription = (formValuesXml \ "shortdescfield").head.text
+
+ val parsingErrors = GraderConnection.getCNFParsingErrors(grammar)
+
+ if(parsingErrors.isEmpty) {
+ val unspecificProblem = createUnspecificProb(shortDescription, shortDescription)
+
+ val specificProblem : CYKProblem = CYKProblem.create
+ specificProblem.problemId(unspecificProblem).grammar(grammar).word(word)
+ specificProblem.save
+
+ return JsCmds.RedirectTo("/problems/index")
+ } else {
+ return JsCmds.JsShowId("submitbutton") & JsCmds.JsShowId("feedbackdisplay") & JsCmds.SetHtml("parsingerror", Text(parsingErrors.mkString(" ")))
+ }
+
+ }
+ val grammarField = SHtml.textarea("", value => {}, "cols" -> "80", "rows" -> "5", "id" -> "grammarfield")
+ val wordField = SHtml.text("", value => {}, "id" -> "wordfield")
+ val shortDescriptionField = SHtml.text("", value => {}, "id" -> "shortdescfield")
+
+ val hideSubmitButton : JsCmd = JsHideId("submitbutton")
+ val grammarFieldValXmlJs : String = "' + document.getElementById('grammarfield').value + ' "
+ val wordFieldValXmlJs : String = "' + document.getElementById('wordfield').value + ' "
+ val shortdescFieldValXmlJs : String = "' + document.getElementById('shortdescfield').value + ' "
+ val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + wordFieldValXmlJs + shortdescFieldValXmlJs + " '"), create(_))
+
+ val submit : JsCmd = hideSubmitButton & ajaxCall
+
+ val submitButton : NodeSeq = Submit
+
+ val template : NodeSeq = Templates(List("cyk-problem", "create")) openOr Text("Could not find template /cyk-problem/create")
+ Helpers.bind("createform", template,
+ "grammarfield" -> grammarField,
+ "wordfield" -> wordField,
+ "shortdescription" -> shortDescriptionField,
+ "submit" -> submitButton)
+ }
+
+ override def renderEdit : Box[(Problem, () => Nothing) => NodeSeq] = Full(renderEditFunc)
+
+ private def renderEditFunc(problem : Problem, returnFunc : () => Nothing) : NodeSeq = {
+
+ val cykProblem = CYKProblem.findByGeneralProblem(problem)
+
+ var shortDescription : String = problem.getShortDescription
+ var grammar : String = cykProblem.getGrammar
+ var word : String = cykProblem.getWord
+
+ def edit(formValues : String) : JsCmd = {
+ val formValuesXml = XML.loadString(formValues)
+ val grammar = (formValuesXml \ "grammarfield").head.text
+ val word = (formValuesXml \ "wordfield").head.text
+ val shortDescription = (formValuesXml \ "shortdescfield").head.text
+
+ val parsingErrors = GraderConnection.getCNFParsingErrors(grammar)
+
+ if(parsingErrors.isEmpty) {
+ val specificProblem : CYKProblem = CYKProblem.create
+
+ problem.setShortDescription(shortDescription).setLongDescription(shortDescription).save()
+ cykProblem.grammar(grammar).word(word).save()
+ returnFunc()
+ } else {
+ return JsCmds.JsShowId("submitbutton") & JsCmds.JsShowId("feedbackdisplay") & JsCmds.SetHtml("parsingerror", Text(parsingErrors.mkString(" ")))
+ }
+ }
+
+ val grammarField = SHtml.textarea(grammar, grammar=_, "cols" -> "80", "rows" -> "5", "id" -> "grammarfield")
+ val wordField = SHtml.text(word, word=_, "id" -> "wordfield")
+ val shortDescriptionField = SHtml.text(shortDescription, shortDescription=_, "id" -> "shortdescfield")
+
+ val hideSubmitButton : JsCmd = JsHideId("submitbutton")
+ val grammarFieldValXmlJs : String = "' + document.getElementById('grammarfield').value + ' "
+ val wordFieldValXmlJs : String = "' + document.getElementById('wordfield').value + ' "
+ val shortdescFieldValXmlJs : String = "' + document.getElementById('shortdescfield').value + ' "
+ val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + wordFieldValXmlJs + shortdescFieldValXmlJs + " '"), edit(_))
+
+ val submit : JsCmd = hideSubmitButton & ajaxCall
+
+ val submitButton : NodeSeq = Submit
+
+ val template : NodeSeq = Templates(List("cyk-problem", "edit")) openOr Text("Could not find template /cyk-problem/edit")
+ Helpers.bind("editform", template,
+ "grammarfield" -> grammarField,
+ "wordfield" -> wordField,
+ "shortdescription" -> shortDescriptionField,
+ "submit" -> submitButton)
+ }
+
+ override def renderSolve(generalProblem : Problem, maxGrade : Long, lastAttempt : Box[SolutionAttempt],
+ recordSolutionAttempt : (Int, Date) => SolutionAttempt, returnFunc : () => Unit, remainingAttempts: () => Int,
+ bestGrade: () => Int) : NodeSeq = {
+ val specificProblem = CYKProblem.findByGeneralProblem(generalProblem)
+
+ def grade(cyk_table : String) : JsCmd = {
+ if(remainingAttempts() <= 0) {
+ return JsShowId("feedbackdisplay") & SetHtml("feedbackdisplay", Text("You do not have any attempts left for this problem. Your final grade is " + bestGrade().toString + "/" + maxGrade.toString + "."))
+ }
+ val attemptTime = Calendar.getInstance.getTime()
+
+ val gradeAndFeedback = GraderConnection.getCYKFeedback(specificProblem.grammar.is, specificProblem.word.is, cyk_table, maxGrade.toInt)
+
+ val numericalGrade = gradeAndFeedback._1
+ val generalAttempt = recordSolutionAttempt(numericalGrade, attemptTime)
+
+ // Only save the specific attempt if we saved the general attempt
+ if(generalAttempt != null) {
+ CYKSolutionAttempt.create.solutionAttemptId(generalAttempt).attempt(cyk_table).save
+ }
+
+ val setNumericalGrade : JsCmd = SetHtml("grade", Text(gradeAndFeedback._1.toString + "/" + maxGrade.toString))
+ val setFeedback : JsCmd = SetHtml("feedback", gradeAndFeedback._2)
+ val showFeedback : JsCmd = JsShowId("feedbackdisplay")
+
+ return setNumericalGrade & setFeedback & showFeedback & JsCmds.JsShowId("submitbutton")
+ }
+
+ val problemDescription = generalProblem.getLongDescription
+ val grammarText = { specificProblem.getGrammar.replaceAll("->", " -> ").replaceAll("=>", " -> ").replaceAll("\\|", " \\| ").replaceAll("\\s{2,}", " ").split("\\s(?=\\S+\\s*->)").map {Text(_) ++ } reduceLeft (_ ++ _) }
+ val wordText = Text("" + specificProblem.word)
+
+ val word = specificProblem.getWord
+ val n = word.length()
+ val cyk = new Array[ Array[(Int, Int)] ] (n);
+ for(i <- 0 to n - 1) {
+ cyk(i) = new Array[(Int, Int)] (i+1)
+ for(j <- 0 to i) {
+ cyk(i)(j) = (j+1, n + j - i)
+ }
+ }
+ val cykTable = {cyk.map(row => {row.map(col => { SHtml.text("", value => {}, "class" -> "cyk", "start" -> col._1.toString(), "end" -> col._2.toString(), "size" -> "12") }{ Text("(" + col._1.toString() + "," + col._2.toString() + ")") } )} )} { word.map(c => {"'" + c.toString + "'"} ) }
+
+ val hideSubmitButton : JsCmd = JsHideId("submitbutton")
+ val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("buildCYKTableXML()"), grade(_))
+ val submitButton : NodeSeq = Submit
+ val returnLink : NodeSeq = SHtml.link("/courses/show", returnFunc, Text("Return to Course"))
+
+ val template : NodeSeq = Templates(List("cyk-problem", "solve")) openOr Text("Could not find template /cyk-problem/solve")
+ Helpers.bind("solveform", template,
+ "problemdescription" -> problemDescription,
+ "grammartext" -> grammarText,
+ "wordtext" -> wordText,
+ "cyktable" -> cykTable,
+ "submitbutton" -> submitButton,
+ "returnlink" -> returnLink)
+ }
+
+ override def onDelete( generalProblem : Problem ) : Unit = {
+
+ }
+}
\ No newline at end of file
diff --git a/src/main/scala/com/automatatutor/snippet/Courses.scala b/src/main/scala/com/automatatutor/snippet/Courses.scala
index 43659ab..a91ee30 100644
--- a/src/main/scala/com/automatatutor/snippet/Courses.scala
+++ b/src/main/scala/com/automatatutor/snippet/Courses.scala
@@ -87,6 +87,7 @@ class Courses {
def displaySupervisedCourses(courses : Seq[Course]) : NodeSeq = {
return TableHelper.renderTableWithHeader(courses,
("Course Name", (course : Course) => Text(course.getName)),
+ ("Password", (course : Course) => Text(course.getPassword)),
("Contact", (course : Course) => new CourseRenderer(course).renderContactLink),
("", (course : Course) => new CourseRenderer(course).renderManageLink),
("", (course : Course) => new CourseRenderer(course).renderDeleteLink))
@@ -315,7 +316,7 @@ class Courses {
}
}
if (startDate == null) {
- S.redirectTo("/courses/poseproblemset", () => { CourseReqVar(course); ProblemSetReqVar(problemSet) } )
+ S.redirectTo("/courses/poseproblemset", () => { CourseReqVar(course); ProblemSetReqVar(problemSet) } )
} else {
val endDate : Date = try {
@@ -328,7 +329,7 @@ class Courses {
}
if (endDate == null) {
- S.redirectTo("/courses/poseproblemset", () => { CourseReqVar(course); ProblemSetReqVar(problemSet) } )
+ S.redirectTo("/courses/poseproblemset", () => { CourseReqVar(course); ProblemSetReqVar(problemSet) } )
} else {
val posedProblemSet =
PosedProblemSet.create.setStartDate(startDate).setEndDate(endDate).setProblemSet(problemSet).setUseRandomOrder(inRandomOrder)
@@ -404,8 +405,8 @@ class Courses {
def renderSolveLink ( problem : PosedProblem ) : NodeSeq = {
if(problem.isOpen(user, posedProblemSet)) {
return SHtml.link("/courses/solveproblem",
- () => { CourseReqVar(course);
- PosedProblemSetReqVar(posedProblemSet);
+ () => { CourseReqVar(course);
+ PosedProblemSetReqVar(posedProblemSet);
PosedProblemReqVar(problem) },
Text("solve"))
} else {
@@ -461,7 +462,7 @@ class Courses {
val problem : Problem = posedProblem.getProblem
val snippet = problem.getProblemType.getProblemSnippet
return snippet.renderSolve(posedProblem.getProblem, posedProblem.getMaxGrade, Empty,
- (grade, date) => SolutionAttempt, () => S.redirectTo("/courses/index"), () => 1, () => 0)
+ (grade, date) => SolutionAttempt, () => S.redirectTo("/practicesets/index"), () => 1, () => 0)
}
def renderenrollmentform ( xhtml : NodeSeq ) : NodeSeq = {
diff --git a/src/main/scala/com/automatatutor/snippet/DescriptionToGrammarSnippet.scala b/src/main/scala/com/automatatutor/snippet/DescriptionToGrammarSnippet.scala
new file mode 100644
index 0000000..e608a91
--- /dev/null
+++ b/src/main/scala/com/automatatutor/snippet/DescriptionToGrammarSnippet.scala
@@ -0,0 +1,183 @@
+package com.automatatutor.snippet
+
+import java.util.Calendar
+import java.util.Date
+import scala.Array.canBuildFrom
+import scala.Array.fallbackCanBuildFrom
+import scala.xml.NodeSeq
+import scala.xml.NodeSeq.seqToNodeSeq
+import scala.xml.Text
+import scala.xml.XML
+import com.automatatutor.lib.GraderConnection
+import com.automatatutor.model.Problem
+import com.automatatutor.model.DescriptionToGrammarProblem
+import com.automatatutor.model.DescriptionToGrammarSolutionAttempt
+import com.automatatutor.model.SolutionAttempt
+import net.liftweb.common.Box
+import net.liftweb.common.Full
+import net.liftweb.http.SHtml
+import net.liftweb.http.SHtml.ElemAttr.pairToBasic
+import net.liftweb.http.Templates
+import net.liftweb.http.js.JE.JsRaw
+import net.liftweb.http.js.JsCmd
+import net.liftweb.http.js.JsCmds
+import net.liftweb.http.js.JsCmds._
+import net.liftweb.http.js.JsCmds.JsHideId
+import net.liftweb.http.js.JsCmds.JsShowId
+import net.liftweb.http.js.JsCmds.SetHtml
+import net.liftweb.http.js.JsCmds.cmdToString
+import net.liftweb.http.js.JsCmds.jsExpToJsCmd
+import net.liftweb.util.Helpers
+import net.liftweb.util.Helpers._
+import net.liftweb.util.Helpers.strToSuperArrowAssoc
+import net.liftweb.http.js.JE.Call
+import net.liftweb.common.Empty
+
+object DescriptionToGrammarSnippet extends ProblemSnippet {
+
+ override def renderCreate( createUnspecificProb : (String, String) => Problem,
+ returnFunc : () => Nothing ) : NodeSeq = {
+
+ def create(formValues : String) : JsCmd = {
+ val formValuesXml = XML.loadString(formValues)
+ val grammar = (formValuesXml \ "grammarfield").head.text
+ val shortDescription = (formValuesXml \ "shortdescfield").head.text
+ val longDescription = (formValuesXml \ "longdescfield").head.text
+
+ val parsingErrors = GraderConnection.getGrammarParsingErrors(grammar)
+
+ if(parsingErrors.isEmpty) {
+ val unspecificProblem = createUnspecificProb(shortDescription, longDescription)
+
+ val specificProblem : DescriptionToGrammarProblem = DescriptionToGrammarProblem.create
+ specificProblem.problemId(unspecificProblem).grammar(grammar)
+ specificProblem.save
+
+ return JsCmds.RedirectTo("/problems/index")
+ } else {
+ return JsCmds.JsShowId("submitbutton") & JsCmds.JsShowId("feedbackdisplay") & JsCmds.SetHtml("parsingerror", Text(parsingErrors.mkString(" ")))
+ }
+
+ }
+ val grammarField = SHtml.textarea("", value => {}, "cols" -> "80", "rows" -> "5", "id" -> "grammarfield")
+ val shortDescriptionField = SHtml.text("", value => {}, "id" -> "shortdescfield")
+ val longDescriptionField = SHtml.textarea("", value => {}, "cols" -> "80", "rows" -> "5", "id" -> "longdescfield")
+
+ val hideSubmitButton : JsCmd = JsHideId("submitbutton")
+ val grammarFieldValXmlJs : String = "' + document.getElementById('grammarfield').value + ' "
+ val shortdescFieldValXmlJs : String = "' + document.getElementById('shortdescfield').value + ' "
+ val longdescFieldValXmlJs : String = "' + document.getElementById('longdescfield').value + ' "
+ val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + shortdescFieldValXmlJs + longdescFieldValXmlJs + " '"), create(_))
+ val submit : JsCmd = hideSubmitButton & ajaxCall
+ val submitButton : NodeSeq = Submit
+
+ val template : NodeSeq = Templates(List("description-to-grammar-problem", "create")) openOr Text("Could not find template /description-to-grammar-problem/create")
+ Helpers.bind("createform", template,
+ "grammarfield" -> grammarField,
+ "shortdescription" -> shortDescriptionField,
+ "longdescription" -> longDescriptionField,
+ "submit" -> submitButton)
+ }
+
+ override def renderEdit : Box[(Problem, () => Nothing) => NodeSeq] = Full(renderEditFunc)
+
+ private def renderEditFunc(problem : Problem, returnFunc : () => Nothing) : NodeSeq = {
+
+ val descriptionToGrammarProblem = DescriptionToGrammarProblem.findByGeneralProblem(problem)
+
+ var shortDescription : String = problem.getShortDescription
+ var longDescription : String = problem.getLongDescription
+ var grammar : String = descriptionToGrammarProblem.getGrammar
+
+ def edit(formValues : String) : JsCmd = {
+ val formValuesXml = XML.loadString(formValues)
+ val grammar = (formValuesXml \ "grammarfield").head.text
+ val shortDescription = (formValuesXml \ "shortdescfield").head.text
+ val longDescription = (formValuesXml \ "longdescfield").head.text
+
+ val parsingErrors = GraderConnection.getGrammarParsingErrors(grammar)
+
+ if(parsingErrors.isEmpty) {
+ val specificProblem : DescriptionToGrammarProblem = DescriptionToGrammarProblem.create
+
+ problem.setShortDescription(shortDescription).setLongDescription(longDescription).save()
+ descriptionToGrammarProblem.grammar(grammar).save()
+ returnFunc()
+ } else {
+ return JsCmds.JsShowId("submitbutton") & JsCmds.JsShowId("feedbackdisplay") & JsCmds.SetHtml("parsingerror", Text(parsingErrors.mkString(" ")))
+ }
+ }
+
+ val grammarField = SHtml.textarea(grammar, grammar=_, "cols" -> "80", "rows" -> "5", "id" -> "grammarfield")
+ val shortDescriptionField = SHtml.text(shortDescription, shortDescription=_, "id" -> "shortdescfield")
+ val longDescriptionField = SHtml.textarea(longDescription, longDescription=_, "cols" -> "80", "rows" -> "1", "id" -> "longdescfield")
+
+ val hideSubmitButton : JsCmd = JsHideId("submitbutton")
+ val grammarFieldValXmlJs : String = "' + document.getElementById('grammarfield').value + ' "
+ val shortdescFieldValXmlJs : String = "' + document.getElementById('shortdescfield').value + ' "
+ val longdescFieldValXmlJs : String = "' + document.getElementById('longdescfield').value + ' "
+ val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + shortdescFieldValXmlJs + longdescFieldValXmlJs + " '"), edit(_))
+
+ val submit : JsCmd = hideSubmitButton & ajaxCall
+
+ val submitButton : NodeSeq = Submit
+
+ val template : NodeSeq = Templates(List("description-to-grammar-problem", "edit")) openOr Text("Could not find template /description-to-grammar-problem/edit")
+ Helpers.bind("editform", template,
+ "grammarfield" -> grammarField,
+ "shortdescription" -> shortDescriptionField,
+ "longdescription" -> longDescriptionField,
+ "submit" -> submitButton)
+ }
+
+ override def renderSolve(generalProblem : Problem, maxGrade : Long, lastAttempt : Box[SolutionAttempt],
+ recordSolutionAttempt : (Int, Date) => SolutionAttempt, returnFunc : () => Unit, remainingAttempts: () => Int,
+ bestGrade: () => Int) : NodeSeq = {
+ val specificProblem = DescriptionToGrammarProblem.findByGeneralProblem(generalProblem)
+
+ def grade(attemptGrammar : String) : JsCmd = {
+
+ if(remainingAttempts() <= 0) {
+ return JsShowId("feedbackdisplay") & SetHtml("feedbackdisplay", Text("You do not have any attempts left for this problem. Your final grade is " + bestGrade().toString + "/" + maxGrade.toString + "."))
+ }
+ val attemptTime = Calendar.getInstance.getTime()
+
+ val gradeAndFeedback = GraderConnection.getDescriptionToGrammarFeedback(specificProblem.grammar.is, attemptGrammar, maxGrade.toInt)
+
+ var numericalGrade = gradeAndFeedback._1
+ val generalAttempt = recordSolutionAttempt(numericalGrade, attemptTime)
+
+ // Only save the specific attempt if we saved the general attempt and grammar was parseable
+ if(generalAttempt != null && numericalGrade >= 0) {
+ DescriptionToGrammarSolutionAttempt.create.solutionAttemptId(generalAttempt).attemptGrammar(attemptGrammar).save
+ }
+
+ if (numericalGrade < 0) numericalGrade = 0; //parse error => no pints
+
+ val setNumericalGrade : JsCmd = SetHtml("grade", Text(numericalGrade.toString + "/" + maxGrade.toString))
+ val setFeedback : JsCmd = SetHtml("feedback", gradeAndFeedback._2)
+ val showFeedback : JsCmd = JsShowId("feedbackdisplay")
+
+ return setNumericalGrade & setFeedback & showFeedback & JsCmds.JsShowId("submitbutton")
+ }
+
+ val problemDescription = generalProblem.getLongDescription
+ val grammarField = SHtml.textarea("" , value => {}, "cols" -> "80", "rows" -> "5", "id" -> "grammarfield")
+
+ val hideSubmitButton : JsCmd = JsHideId("submitbutton")
+ val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("document.getElementById('grammarfield').value"), grade(_))
+ val submitButton : NodeSeq = Submit
+ val returnLink : NodeSeq = SHtml.link("/courses/show", returnFunc, Text("Return to Course"))
+
+ val template : NodeSeq = Templates(List("description-to-grammar-problem", "solve")) openOr Text("Could not find template /description-to-grammar-problem/solve")
+ Helpers.bind("solveform", template,
+ "problemdescription" -> problemDescription,
+ "grammarfield" -> grammarField,
+ "submitbutton" -> submitButton,
+ "returnlink" -> returnLink)
+ }
+
+ override def onDelete( generalProblem : Problem ) : Unit = {
+
+ }
+}
\ No newline at end of file
diff --git a/src/main/scala/com/automatatutor/snippet/GrammarToCNFSnippet.scala b/src/main/scala/com/automatatutor/snippet/GrammarToCNFSnippet.scala
new file mode 100644
index 0000000..624c123
--- /dev/null
+++ b/src/main/scala/com/automatatutor/snippet/GrammarToCNFSnippet.scala
@@ -0,0 +1,175 @@
+package com.automatatutor.snippet
+
+import java.util.Calendar
+import java.util.Date
+import scala.Array.canBuildFrom
+import scala.Array.fallbackCanBuildFrom
+import scala.xml.NodeSeq
+import scala.xml.NodeSeq.seqToNodeSeq
+import scala.xml.Text
+import scala.xml.XML
+import com.automatatutor.lib.GraderConnection
+import com.automatatutor.model.Problem
+import com.automatatutor.model.GrammarToCNFProblem
+import com.automatatutor.model.GrammarToCNFSolutionAttempt
+import com.automatatutor.model.SolutionAttempt
+import net.liftweb.common.Box
+import net.liftweb.common.Full
+import net.liftweb.http.SHtml
+import net.liftweb.http.SHtml.ElemAttr.pairToBasic
+import net.liftweb.http.Templates
+import net.liftweb.http.js.JE.JsRaw
+import net.liftweb.http.js.JsCmd
+import net.liftweb.http.js.JsCmds
+import net.liftweb.http.js.JsCmds._
+import net.liftweb.http.js.JsCmds.JsHideId
+import net.liftweb.http.js.JsCmds.JsShowId
+import net.liftweb.http.js.JsCmds.SetHtml
+import net.liftweb.http.js.JsCmds.cmdToString
+import net.liftweb.http.js.JsCmds.jsExpToJsCmd
+import net.liftweb.util.Helpers
+import net.liftweb.util.Helpers._
+import net.liftweb.util.Helpers.strToSuperArrowAssoc
+import net.liftweb.http.js.JE.Call
+import net.liftweb.common.Empty
+
+object GrammarToCNFSnippet extends ProblemSnippet {
+
+ override def renderCreate( createUnspecificProb : (String, String) => Problem,
+ returnFunc : () => Nothing ) : NodeSeq = {
+
+ def create(formValues : String) : JsCmd = {
+ val formValuesXml = XML.loadString(formValues)
+ val grammar = (formValuesXml \ "grammarfield").head.text
+ val shortDescription = (formValuesXml \ "shortdescfield").head.text
+
+ val parsingErrors = GraderConnection.getGrammarParsingErrors(grammar)
+
+ if(parsingErrors.isEmpty) {
+ val unspecificProblem = createUnspecificProb(shortDescription, shortDescription)
+
+ val specificProblem : GrammarToCNFProblem = GrammarToCNFProblem.create
+ specificProblem.problemId(unspecificProblem).grammar(grammar)
+ specificProblem.save
+
+ return JsCmds.RedirectTo("/problems/index")
+ } else {
+ return JsCmds.JsShowId("submitbutton") & JsCmds.JsShowId("feedbackdisplay") & JsCmds.SetHtml("parsingerror", Text(parsingErrors.mkString(" ")))
+ }
+
+ }
+ val grammarField = SHtml.textarea("", value => {}, "cols" -> "80", "rows" -> "5", "id" -> "grammarfield")
+ val shortDescriptionField = SHtml.text("", value => {}, "id" -> "shortdescfield")
+
+ val hideSubmitButton : JsCmd = JsHideId("submitbutton")
+ val grammarFieldValXmlJs : String = "' + document.getElementById('grammarfield').value + ' "
+ val shortdescFieldValXmlJs : String = "' + document.getElementById('shortdescfield').value + ' "
+ val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + shortdescFieldValXmlJs + " '"), create(_))
+ val submit : JsCmd = hideSubmitButton & ajaxCall
+ val submitButton : NodeSeq = Submit
+
+ val template : NodeSeq = Templates(List("grammar-to-cnf-problem", "create")) openOr Text("Could not find template /grammar-to-cnf-problem/create")
+ Helpers.bind("createform", template,
+ "grammarfield" -> grammarField,
+ "shortdescription" -> shortDescriptionField,
+ "submit" -> submitButton)
+ }
+
+ override def renderEdit : Box[(Problem, () => Nothing) => NodeSeq] = Full(renderEditFunc)
+
+ private def renderEditFunc(problem : Problem, returnFunc : () => Nothing) : NodeSeq = {
+
+ val grammarToCNFProblem = GrammarToCNFProblem.findByGeneralProblem(problem)
+
+ var shortDescription : String = problem.getShortDescription
+ var grammar : String = grammarToCNFProblem.getGrammar
+
+ def edit(formValues : String) : JsCmd = {
+ val formValuesXml = XML.loadString(formValues)
+ val grammar = (formValuesXml \ "grammarfield").head.text
+ val shortDescription = (formValuesXml \ "shortdescfield").head.text
+
+ val parsingErrors = GraderConnection.getGrammarParsingErrors(grammar)
+
+ if(parsingErrors.isEmpty) {
+ val specificProblem : GrammarToCNFProblem = GrammarToCNFProblem.create
+
+ problem.setShortDescription(shortDescription).setLongDescription(shortDescription).save()
+ grammarToCNFProblem.grammar(grammar).save()
+ returnFunc()
+ } else {
+ return JsCmds.JsShowId("submitbutton") & JsCmds.JsShowId("feedbackdisplay") & JsCmds.SetHtml("parsingerror", Text(parsingErrors.mkString(" ")))
+ }
+ }
+
+ val grammarField = SHtml.textarea(grammar, grammar=_, "cols" -> "80", "rows" -> "5", "id" -> "grammarfield")
+ val shortDescriptionField = SHtml.text(shortDescription, shortDescription=_, "id" -> "shortdescfield")
+
+ val hideSubmitButton : JsCmd = JsHideId("submitbutton")
+ val grammarFieldValXmlJs : String = "' + document.getElementById('grammarfield').value + ' "
+ val shortdescFieldValXmlJs : String = "' + document.getElementById('shortdescfield').value + ' "
+ val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + shortdescFieldValXmlJs + " '"), edit(_))
+
+ val submit : JsCmd = hideSubmitButton & ajaxCall
+
+ val submitButton : NodeSeq = Submit
+
+ val template : NodeSeq = Templates(List("grammar-to-cnf-problem", "edit")) openOr Text("Could not find template /grammar-to-cnf-problem/edit")
+ Helpers.bind("editform", template,
+ "grammarfield" -> grammarField,
+ "shortdescription" -> shortDescriptionField,
+ "submit" -> submitButton)
+ }
+
+ override def renderSolve(generalProblem : Problem, maxGrade : Long, lastAttempt : Box[SolutionAttempt],
+ recordSolutionAttempt : (Int, Date) => SolutionAttempt, returnFunc : () => Unit, remainingAttempts: () => Int,
+ bestGrade: () => Int) : NodeSeq = {
+ val specificProblem = GrammarToCNFProblem.findByGeneralProblem(generalProblem)
+
+ def grade(attemptGrammar : String) : JsCmd = {
+
+ if(remainingAttempts() <= 0) {
+ return JsShowId("feedbackdisplay") & SetHtml("feedbackdisplay", Text("You do not have any attempts left for this problem. Your final grade is " + bestGrade().toString + "/" + maxGrade.toString + "."))
+ }
+ val attemptTime = Calendar.getInstance.getTime()
+
+ //TODO
+ val gradeAndFeedback = GraderConnection.getGrammarToCNFFeedback(specificProblem.grammar.is, attemptGrammar, maxGrade.toInt)
+
+ var numericalGrade = gradeAndFeedback._1
+ val generalAttempt = recordSolutionAttempt(numericalGrade, attemptTime)
+
+ // Only save the specific attempt if we saved the general attempt and grammar was parseable
+ if(generalAttempt != null && numericalGrade >= 0) {
+ GrammarToCNFSolutionAttempt.create.solutionAttemptId(generalAttempt).attemptGrammar(attemptGrammar).save
+ }
+
+ if (numericalGrade < 0) numericalGrade = 0; //parse error => no pints
+
+ val setNumericalGrade : JsCmd = SetHtml("grade", Text(numericalGrade.toString + "/" + maxGrade.toString))
+ val setFeedback : JsCmd = SetHtml("feedback", gradeAndFeedback._2)
+ val showFeedback : JsCmd = JsShowId("feedbackdisplay")
+
+ return setNumericalGrade & setFeedback & showFeedback & JsCmds.JsShowId("submitbutton")
+ }
+
+ val grammarText = { specificProblem.getGrammar.replaceAll("->", " -> ").replaceAll("=>", " -> ").replaceAll("\\|", " \\| ").replaceAll("\\s{2,}", " ").split("\\s(?=\\S+\\s*->)").map {Text(_) ++ } reduceLeft (_ ++ _) }
+ val grammarField = SHtml.textarea("" , value => {}, "cols" -> "80", "rows" -> "5", "id" -> "grammarfield")
+
+ val hideSubmitButton : JsCmd = JsHideId("submitbutton")
+ val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("document.getElementById('grammarfield').value"), grade(_))
+ val submitButton : NodeSeq = Submit
+ val returnLink : NodeSeq = SHtml.link("/courses/show", returnFunc, Text("Return to Course"))
+
+ val template : NodeSeq = Templates(List("grammar-to-cnf-problem", "solve")) openOr Text("Could not find template /grammar-to-cnf-problem/solve")
+ Helpers.bind("solveform", template,
+ "grammartext" -> grammarText,
+ "grammarfield" -> grammarField,
+ "submitbutton" -> submitButton,
+ "returnlink" -> returnLink)
+ }
+
+ override def onDelete( generalProblem : Problem ) : Unit = {
+
+ }
+}
\ No newline at end of file
diff --git a/src/main/scala/com/automatatutor/snippet/MinimizationSnippet.scala b/src/main/scala/com/automatatutor/snippet/MinimizationSnippet.scala
new file mode 100644
index 0000000..23e8512
--- /dev/null
+++ b/src/main/scala/com/automatatutor/snippet/MinimizationSnippet.scala
@@ -0,0 +1,191 @@
+package com.automatatutor.snippet
+
+import java.util.Calendar
+import java.util.Date
+
+import scala.Array.canBuildFrom
+import scala.Array.fallbackCanBuildFrom
+import scala.xml.NodeSeq
+import scala.xml.NodeSeq.seqToNodeSeq
+import scala.xml.Text
+import scala.xml.XML
+import com.automatatutor.lib.GraderConnection
+import com.automatatutor.model._
+import net.liftweb.common.Box
+import net.liftweb.common.Full
+import net.liftweb.http.SHtml
+import net.liftweb.http.SHtml.ElemAttr.pairToBasic
+import net.liftweb.http.Templates
+import net.liftweb.http.js.JE.JsRaw
+import net.liftweb.http.js.JsCmd
+import net.liftweb.http.js.JsCmds
+import net.liftweb.http.js.JsCmds._
+import net.liftweb.http.js.JsCmds.JsHideId
+import net.liftweb.http.js.JsCmds.JsShowId
+import net.liftweb.http.js.JsCmds.SetHtml
+import net.liftweb.http.js.JsCmds.cmdToString
+import net.liftweb.http.js.JsCmds.jsExpToJsCmd
+import net.liftweb.util.Helpers
+import net.liftweb.util.Helpers._
+import net.liftweb.util.Helpers.strToSuperArrowAssoc
+import net.liftweb.http.js.JE.Call
+import net.liftweb.common.Empty
+
+object MinimizationSnippet extends ProblemSnippet {
+ def preprocessAutomatonXml ( input : String ) : String = input.filter(!List('\n', '\r').contains(_)).replace("\u0027", "\'")
+
+ override def renderCreate( createUnspecificProb : (String, String) => Problem,
+ returnFunc : () => Nothing ) : NodeSeq = {
+
+ var shortDescription : String = ""
+ var longDescription : String = ""
+ var automaton : String = ""
+
+ def create() = {
+ val unspecificProblem = createUnspecificProb(shortDescription, longDescription)
+
+ val specificProblem : MinimizationProblem = MinimizationProblem.create
+ specificProblem.setGeneralProblem(unspecificProblem).setAutomaton(automaton)
+ specificProblem.save
+
+ returnFunc()
+ }
+
+
+ // Remember to remove all newlines from the generated XML by using filter
+ val automatonField = SHtml.hidden(automatonXml => automaton = preprocessAutomatonXml(automatonXml), "", "id" -> "automatonField")
+ val shortDescriptionField = SHtml.text(shortDescription, shortDescription = _)
+ val longDescriptionField = SHtml.textarea(longDescription, longDescription = _, "cols" -> "80", "rows" -> "5")
+ val submitButton = SHtml.submit("Create", create, "onClick" -> "document.getElementById('automatonField').value = Editor.canvas.exportAutomaton()")
+
+ val template : NodeSeq = Templates(List("minimization", "create")) openOr Text("Could not find template /minimization/create")
+ Helpers.bind("createform", template,
+ "automaton" -> automatonField,
+ "shortdescription" -> shortDescriptionField,
+ "longdescription" -> longDescriptionField,
+ "submit" -> submitButton)
+ }
+
+ override def renderEdit : Box[(Problem, () => Nothing) => NodeSeq] = Full(renderEditFunc)
+
+ private def renderEditFunc(problem : Problem, returnFunc : () => Nothing) : NodeSeq = {
+ // TODO : Change DFAConstructionProblem to MinimizationProblem
+ val minimizationProblem = MinimizationProblem.findByGeneralProblem(problem)
+
+ var shortDescription : String = problem.getShortDescription
+ var longDescription : String = problem.getLongDescription
+ var automaton : String = ""
+
+ def create() = {
+ problem.setShortDescription(shortDescription).setLongDescription(longDescription).save()
+ minimizationProblem.setAutomaton(automaton).save()
+ returnFunc()
+ }
+
+ // Remember to remove all newlines from the generated XML by using filter
+ val automatonField = SHtml.hidden(automatonXml => automaton = preprocessAutomatonXml(automatonXml), "", "id" -> "automatonField")
+ val shortDescriptionField = SHtml.text(shortDescription, shortDescription = _)
+ val longDescriptionField = SHtml.textarea(longDescription, longDescription = _, "cols" -> "80", "rows" -> "5")
+ val submitButton = SHtml.submit("Edit", create, "onClick" -> "document.getElementById('automatonField').value = Editor.canvas.exportAutomaton()")
+ val setupScript =
+
+ val template : NodeSeq = Templates(List("minimization", "edit")) openOr Text("Could not find template /minimization/edit")
+ Helpers.bind("editform", template,
+ "automaton" -> automatonField,
+ "setupscript" -> setupScript,
+ "shortdescription" -> shortDescriptionField,
+ "longdescription" -> longDescriptionField,
+ "submit" -> submitButton)
+ }
+
+ override def renderSolve(generalProblem : Problem, maxGrade : Long, lastAttempt : Box[SolutionAttempt],
+ recordSolutionAttempt : (Int, Date) => SolutionAttempt, returnFunc : () => Unit, remainingAttempts: () => Int,
+ bestGrade: () => Int) : NodeSeq = {
+
+ val minimizationProblem = MinimizationProblem.findByGeneralProblem(generalProblem)
+
+ def grade(minimizationTableDescription : String /*, attemptDfaDescription : String*/ ) : JsCmd = {
+
+ //Temporary:
+ val attemptDfaDescription = minimizationProblem.getXmlDescription.toString
+
+ if(remainingAttempts() <= 0) {
+ return JsShowId("feedbackdisplay") &
+ SetHtml("feedbackdisplay",
+ Text("You do not have any attempts left for this problem. Your final grade is " +
+ bestGrade().toString + "/" +
+ maxGrade.toString + "."))
+ }
+
+ val correctDfaDescription = minimizationProblem.getXmlDescription.toString
+ val attemptTime = Calendar.getInstance.getTime()
+ //TODO: Implement getMinimizationFeedback()
+ val graderResponse = GraderConnection.getMinimizationFeedback(correctDfaDescription, minimizationTableDescription, attemptDfaDescription, maxGrade.toInt)
+
+ val numericalGrade = graderResponse._1
+ val generalAttempt = recordSolutionAttempt(numericalGrade, attemptTime)
+
+ // Only save the specific attempt if we saved the general attempt
+ if(generalAttempt != null) {
+ //TODO: Implement MinimizationSolutionAttempt
+ MinimizationSolutionAttempt.create.solutionAttemptId(generalAttempt).attemptAutomaton(attemptDfaDescription).save
+ }
+
+ val setNumericalGrade : JsCmd = SetHtml("grade", Text(graderResponse._1.toString + "/" + maxGrade.toString))
+ val setFeedback : JsCmd = SetHtml("feedback", graderResponse._2)
+ val showFeedback : JsCmd = JsShowId("feedbackdisplay")
+
+ return setNumericalGrade & setFeedback & showFeedback & JsCmds.JsShowId("submitbutton")
+ }
+
+ val problemAlphabet = minimizationProblem.getAlphabet
+ val automaton = XML.loadString(minimizationProblem.getAutomaton)
+ val n = ((automaton \ "stateSet") \ "_").length - 1
+ val cyk = new Array[ Array[(Int, Int)] ] (n);
+ for(i <- 0 to n - 1) {
+ cyk(i) = new Array[(Int, Int)] (i+1)
+ for(j <- 0 to i) {
+ cyk(i)(j) = (j, i+1)
+ }
+ }
+ val minimizationTable : NodeSeq = {cyk.map(row =>
+ {row.map(col =>
+
+ {
+ SHtml.text("", value => {}, "class" -> "cyk", "stateCount" -> (n + 1).toString(), "start" -> col._1.toString(), "end" -> col._2.toString(), "size" -> "5")
+ }{
+ Text("(" + col._1.toString() + "," + col._2.toString() + ")") }
+
+ )}
+ )}
+
+ val alphabetJavaScriptArray = "[\"" + problemAlphabet.mkString("\",\"") + "\"]"
+ val setupScript : NodeSeq =
+
+
+ val problemAlphabetNodeSeq = Text("{" + problemAlphabet.mkString(",") + "}")
+ val problemDescriptionNodeSeq = Text(generalProblem.getLongDescription)
+
+ val hideSubmitButton : JsCmd = JsHideId("submitbutton")
+ val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("buildCYKTableXML()"), grade(_))
+ val submitButton : NodeSeq = Submit
+ val returnLink : NodeSeq = SHtml.link("/courses/show", returnFunc, Text("Return to Course"))
+
+ val template : NodeSeq = Templates(List("minimization", "solve")) openOr Text("Template /minimization/solve not found")
+ return SHtml.ajaxForm(Helpers.bind("dfaeditform", template,
+ "minimizationtable" -> minimizationTable,
+ "setupscript" -> setupScript,
+ "alphabettext" -> problemAlphabetNodeSeq,
+ "problemdescription" -> problemDescriptionNodeSeq,
+ "submitbutton" -> submitButton,
+ "returnlink" -> returnLink))
+ }
+
+ override def onDelete( generalProblem : Problem ) : Unit = {
+ MinimizationProblem.deleteByGeneralProblem(generalProblem)
+ }
+}
diff --git a/src/main/scala/com/automatatutor/snippet/MinimizationTable.scala b/src/main/scala/com/automatatutor/snippet/MinimizationTable.scala
new file mode 100644
index 0000000..dd50bc5
--- /dev/null
+++ b/src/main/scala/com/automatatutor/snippet/MinimizationTable.scala
@@ -0,0 +1,69 @@
+package com.automatatutor.snippet
+
+import java.text.DateFormat
+import java.util.Calendar
+import java.util.Date
+
+import scala.Array.canBuildFrom
+import scala.xml.NodeSeq
+import scala.xml.Text
+
+import com.automatatutor.lib.DownloadHelper
+import com.automatatutor.lib.TableHelper
+import com.automatatutor.model.Attendance
+import com.automatatutor.model.Course
+import com.automatatutor.model.PosedProblem
+import com.automatatutor.model.PosedProblemSet
+import com.automatatutor.model.Problem
+import com.automatatutor.model.ProblemSet
+import com.automatatutor.model.SolutionAttempt
+import com.automatatutor.model.Supervision
+import com.automatatutor.model.User
+import com.automatatutor.renderer.CourseRenderer
+
+import net.liftweb.common.Empty
+import net.liftweb.common.Full
+import net.liftweb.http.RequestVar
+import net.liftweb.http.S
+import net.liftweb.http.SHtml
+import net.liftweb.http.SHtml.ElemAttr.pairToBasic
+import net.liftweb.http.js.JE.JsRaw
+import net.liftweb.mapper.By
+import net.liftweb.util.AnyVar.whatVarIs
+import net.liftweb.util.Helpers
+import net.liftweb.util.Helpers.bind
+import net.liftweb.util.Helpers.strToSuperArrowAssoc
+import net.liftweb.util.SecurityHelpers
+
+/**
+ * Created by Jan on 08.08.2017.
+ */
+class MinimizationTable {
+
+ def showall(ignored : NodeSeq) : NodeSeq = {
+
+ val attendedCourses = User.currentUser.map(_.getAttendedCourses) openOr List()
+ val attendedCoursesNodeSeq = if (!attendedCourses.isEmpty) {
+ Attended Courses ++ NodeSeq.Empty
+ } else {
+ Där är inte sa manga kurser! ++ NodeSeq.Empty
+ }
+
+ val supervisedCourses = User.currentUser.map(_.getSupervisedCourses) openOr List()
+ val supervisedCoursesNodeSeq = if (!supervisedCourses.isEmpty) {
+ Supervised Courses ++ NodeSeq.Empty
+ } else {
+ NodeSeq.Empty
+ }
+
+ val currentUserIsInstructor = User.currentUser.map(_.hasInstructorRole) openOr false
+ val createCourseLink = if(currentUserIsInstructor) {
+ SHtml.link("/courses/create", () => (), Text("Create new Course"))
+ } else {
+ NodeSeq.Empty
+ }
+
+ return attendedCoursesNodeSeq ++ supervisedCoursesNodeSeq ++ createCourseLink
+ }
+
+}
diff --git a/src/main/scala/com/automatatutor/snippet/NFAToDFAProblemSnippet.scala b/src/main/scala/com/automatatutor/snippet/NFAToDFAProblemSnippet.scala
index 2c24d7d..bff3dd4 100644
--- a/src/main/scala/com/automatatutor/snippet/NFAToDFAProblemSnippet.scala
+++ b/src/main/scala/com/automatatutor/snippet/NFAToDFAProblemSnippet.scala
@@ -151,9 +151,10 @@ object NFAToDFAProblemSnippet extends ProblemSnippet {
val setupScript : NodeSeq =
diff --git a/src/main/scala/com/automatatutor/snippet/ProblemSets.scala b/src/main/scala/com/automatatutor/snippet/ProblemSets.scala
index 6760c40..b3b233e 100644
--- a/src/main/scala/com/automatatutor/snippet/ProblemSets.scala
+++ b/src/main/scala/com/automatatutor/snippet/ProblemSets.scala
@@ -17,10 +17,11 @@ import com.automatatutor.lib.Binding
import com.automatatutor.lib.Renderer
import com.automatatutor.lib.Binder
import com.automatatutor.lib.DataRenderer
+import scala.collection.mutable.Set;
object ProblemSetToEdit extends RequestVar[ProblemSet](null)
-object ProblemToPose extends RequestVar[Problem](null)
+object ProblemsToPose extends RequestVar[Set[Problem]](Set())
class Problemsets {
def renderindex ( template : NodeSeq ) : NodeSeq = {
@@ -87,18 +88,29 @@ class Problemsets {
def renderaddproblem ( xhtml : NodeSeq ) : NodeSeq = {
val problemSetToEdit = ProblemSetToEdit.is
+ val problemsToPose = ProblemsToPose.is
val currentUser = User.currentUser openOrThrowException "Lift prevents non-logged in users from getting here"
val problems : Seq[Problem] = Problem.findAllByCreator(currentUser)
- return TableHelper.renderTableWithHeader(problems,
+ def checkAndSubmit() {
+ if (problemsToPose.size > 0) S.redirectTo("/problemsets/poseproblem", () => {ProblemSetToEdit(problemSetToEdit); ProblemsToPose(problemsToPose) } )
+ else S.error("No problem selected");
+ }
+
+ var addProblemTable = TableHelper.renderTableWithHeader(problems,
("Short Description", (problem : Problem) => Text(problem.getShortDescription)),
("Problem Type", (problem : Problem) => Text(problem.getTypeName)),
- ("", (problem : Problem) => SHtml.link("/problemsets/poseproblem", () => { ProblemSetToEdit(problemSetToEdit); ProblemToPose(problem) }, Text("Pose this problem"))))
+ ("Selection", (problem : Problem) => SHtml.checkbox(false, (selected) => {if (selected) problemsToPose += problem} ) )
+ )
+
+ var addSelectedProblemsSubmitButton = SHtml.submit("Pose selected problems", checkAndSubmit )
+
+ return addProblemTable ++ addSelectedProblemsSubmitButton
}
def renderposeproblem ( xhtml : NodeSeq ) : NodeSeq = {
val problemSetToEdit = ProblemSetToEdit.is
- val problemToPose = ProblemToPose.is
+ val problemsToPose = ProblemsToPose.is
var attempts = ""
var maxGrade = ""
@@ -106,11 +118,15 @@ class Problemsets {
def poseProblem = {
def redirectToSelf( exception : Exception ) = {
S.error(exception.getMessage())
- S.redirectTo("/problemsets/poseproblem", () => { ProblemSetToEdit(problemSetToEdit); ProblemToPose(problemToPose) } )
+ S.redirectTo("/problemsets/poseproblem", () => { ProblemSetToEdit(problemSetToEdit) } )
}
val numAttempts = try { attempts.toInt } catch { case e : Exception => redirectToSelf(e) }
val numMaxGrade = try { maxGrade.toInt } catch { case e : Exception => redirectToSelf(e) }
- problemSetToEdit.appendProblem( problemToPose, numAttempts, numMaxGrade )
+ problemsToPose.foreach(
+ (problem) => {
+ problemSetToEdit.appendProblem( problem, numAttempts, numMaxGrade )
+ }
+ )
S.redirectTo("/problemsets/edit", () => ProblemSetToEdit(problemSetToEdit))
}
diff --git a/src/main/scala/com/automatatutor/snippet/ProductConstructionSnippet.scala b/src/main/scala/com/automatatutor/snippet/ProductConstructionSnippet.scala
new file mode 100644
index 0000000..0a8b3c8
--- /dev/null
+++ b/src/main/scala/com/automatatutor/snippet/ProductConstructionSnippet.scala
@@ -0,0 +1,202 @@
+package com.automatatutor.snippet
+
+import java.util.Calendar
+import java.util.Date
+
+import scala.Array.canBuildFrom
+import scala.Array.fallbackCanBuildFrom
+import scala.xml.NodeSeq
+import scala.xml.NodeSeq.seqToNodeSeq
+import scala.xml.Text
+import scala.xml.XML
+import com.automatatutor.lib.GraderConnection
+import com.automatatutor.model._
+import net.liftweb.common.Box
+import net.liftweb.common.Full
+import net.liftweb.http.SHtml
+import net.liftweb.http.SHtml.ElemAttr.pairToBasic
+import net.liftweb.http.Templates
+import net.liftweb.http.js.JE.JsRaw
+import net.liftweb.http.js.JsCmd
+import net.liftweb.http.js.JsCmds
+import net.liftweb.http.js.JsCmds._
+import net.liftweb.http.js.JsCmds.JsHideId
+import net.liftweb.http.js.JsCmds.JsShowId
+import net.liftweb.http.js.JsCmds.SetHtml
+import net.liftweb.http.js.JsCmds.cmdToString
+import net.liftweb.http.js.JsCmds.jsExpToJsCmd
+import net.liftweb.util.Helpers
+import net.liftweb.util.Helpers._
+import net.liftweb.util.Helpers.strToSuperArrowAssoc
+import net.liftweb.http.js.JE.Call
+import net.liftweb.common.Empty
+
+/**
+ * Created by Jan on 21.05.2017.
+ */
+object ProductConstructionSnippet extends ProblemSnippet {
+ def preprocessAutomatonXml ( input : String ) : String = input.filter(!List('\n', '\r').contains(_)).replace("\u0027", "\'")
+
+ override def renderCreate( createUnspecificProb : (String, String) => Problem,
+ returnFunc : () => Nothing ) : NodeSeq = {
+
+ var shortDescription : String = ""
+ var longDescription : String = ""
+ var booleanOperation : String = ""
+ var automaton1 : String = ""
+ var automaton2 : String = ""
+
+ def create() = {
+ val unspecificProblem = createUnspecificProb(shortDescription, longDescription)
+
+ val specificProblem : ProductConstructionProblem = ProductConstructionProblem.create
+ specificProblem.setGeneralProblem(unspecificProblem).setAutomaton1(automaton1)
+ specificProblem.setGeneralProblem(unspecificProblem).setAutomaton2(automaton2)
+ specificProblem.setGeneralProblem(unspecificProblem).setBooleanOperation(booleanOperation)
+ specificProblem.save
+
+ returnFunc()
+ }
+
+ // Remember to remove all newlines from the generated XML by using filter
+ val automaton1Field = SHtml.hidden(automatonXml => automaton1 = preprocessAutomatonXml(automatonXml), "", "id" -> "automaton1Field")
+ val automaton2Field = SHtml.hidden(automatonXml => automaton2 = preprocessAutomatonXml(automatonXml), "", "id" -> "automaton2Field")
+ val shortDescriptionField = SHtml.text(shortDescription, shortDescription = _)
+ val longDescriptionField = SHtml.textarea(longDescription, longDescription = _, "cols" -> "80", "rows" -> "5")
+ val booleanOperationField = SHtml.text(booleanOperation, booleanOperation = _)
+ //TODO: (I) Export both automata ; (II) Export boolean Operation ; (III) Export variable amount of automata
+ val submitButton = SHtml.submit("Create", create, "onClick" ->
+ ("document.getElementById('automaton1Field').value = Editor.canvasDfa1.exportAutomaton();" +
+ "document.getElementById('automaton2Field').value = Editor.canvasDfa2.exportAutomaton()"))
+
+ val template : NodeSeq = Templates(List("product-construction", "create")) openOr Text("Could not find template /product-construction/create")
+ Helpers.bind("createform", template,
+ "automaton1" -> automaton1Field,
+ "automaton2" -> automaton2Field,
+ "boolop" -> booleanOperationField,
+ "shortdescription" -> shortDescriptionField,
+ "longdescription" -> longDescriptionField,
+ "submit" -> submitButton)
+ }
+
+ override def renderEdit : Box[(Problem, () => Nothing) => NodeSeq] = Full(renderEditFunc)
+
+ private def renderEditFunc(problem : Problem, returnFunc : () => Nothing) : NodeSeq = {
+ // TODO : Change DFAConstructionProblem to ProductConstructionProblem
+ val productConstructionProblem = ProductConstructionProblem.findByGeneralProblem(problem)
+
+ var shortDescription : String = problem.getShortDescription
+ var longDescription : String = problem.getLongDescription
+ var booleanOperation : String = productConstructionProblem.getBooleanOperation.toString()
+ var automaton1 : String = ""
+ var automaton2 : String = ""
+
+ def create() = {
+ problem.setShortDescription(shortDescription).setLongDescription(longDescription).save()
+ productConstructionProblem.setBooleanOperation(booleanOperation).save()
+ productConstructionProblem.setAutomaton1(automaton1).save()
+ productConstructionProblem.setAutomaton2(automaton2).save()
+ returnFunc()
+ }
+
+ // Remember to remove all newlines from the generated XML by using filter
+ val automaton1Field = SHtml.hidden(automatonXml => automaton1 = preprocessAutomatonXml(automatonXml), "", "id" -> "automaton1Field")
+ val automaton2Field = SHtml.hidden(automatonXml => automaton2 = preprocessAutomatonXml(automatonXml), "", "id" -> "automaton2Field")
+ val shortDescriptionField = SHtml.text(shortDescription, shortDescription = _)
+ val longDescriptionField = SHtml.textarea(longDescription, longDescription = _, "cols" -> "80", "rows" -> "5")
+ val booleanOperationField = SHtml.text(booleanOperation, booleanOperation = _)
+ val submitButton = SHtml.submit("Edit", create, "onClick" ->
+ ("document.getElementById('automaton1Field').value = Editor.canvasDfa1.exportAutomaton();" +
+ "document.getElementById('automaton2Field').value = Editor.canvasDfa2.exportAutomaton()"))
+
+ val automataList = productConstructionProblem.getAutomataList
+ val setupScript =
+
+
+ val template : NodeSeq = Templates(List("product-construction", "edit")) openOr Text("Could not find template /product-construction/edit")
+ Helpers.bind("editform", template,
+ "automaton1" -> automaton1Field,
+ "automaton2" -> automaton2Field,
+ "setupscript" -> setupScript,
+ "boolop" -> booleanOperationField,
+ "shortdescription" -> shortDescriptionField,
+ "longdescription" -> longDescriptionField,
+ "submit" -> submitButton)
+ }
+
+ override def renderSolve(generalProblem : Problem, maxGrade : Long, lastAttempt : Box[SolutionAttempt],
+ recordSolutionAttempt : (Int, Date) => SolutionAttempt, returnFunc : () => Unit, remainingAttempts: () => Int,
+ bestGrade: () => Int) : NodeSeq = {
+
+ val productConstructionProblem = ProductConstructionProblem.findByGeneralProblem(generalProblem)
+
+ def grade( attemptDfaDescription : String ) : JsCmd = {
+ if(remainingAttempts() <= 0) {
+ return JsShowId("feedbackdisplay") &
+ SetHtml("feedbackdisplay",
+ Text("You do not have any attempts left for this problem. Your final grade is " +
+ bestGrade().toString + "/" +
+ maxGrade.toString + "."))
+ }
+
+ val attemptDfaXml = XML.loadString(attemptDfaDescription)
+ //TODO - Different Automata
+ val correctDfaDescription1 = productConstructionProblem.getXmlDescription1.toString
+ val correctDfaDescription2 = productConstructionProblem.getXmlDescription2.toString
+ val booleanOperation = productConstructionProblem.getBooleanOperation.toString()
+ val attemptTime = Calendar.getInstance.getTime()
+ val x = List(correctDfaDescription1, correctDfaDescription2)
+ val graderResponse = GraderConnection.getProductConstructionFeedback(x, attemptDfaDescription, booleanOperation, maxGrade.toInt)
+
+ val numericalGrade = graderResponse._1
+ val generalAttempt = recordSolutionAttempt(numericalGrade, attemptTime)
+
+ // Only save the specific attempt if we saved the general attempt
+ if(generalAttempt != null) {
+ ProductConstructionSolutionAttempt.create.solutionAttemptId(generalAttempt).attemptAutomaton(attemptDfaDescription).save
+ }
+
+ val setNumericalGrade : JsCmd = SetHtml("grade", Text(graderResponse._1.toString + "/" + maxGrade.toString))
+ val setFeedback : JsCmd = SetHtml("feedback", graderResponse._2)
+ val showFeedback : JsCmd = JsShowId("feedbackdisplay")
+
+ return setNumericalGrade & setFeedback & showFeedback & JsCmds.JsShowId("submitbutton")
+ }
+
+ val problemAlphabet = productConstructionProblem.getAlphabet
+ val automataList = productConstructionProblem.getAutomataList
+
+ val alphabetJavaScriptArray = "[\"" + problemAlphabet.mkString("\",\"") + "\"]"
+ val setupScript : NodeSeq =
+
+
+ val problemAlphabetNodeSeq = Text("{" + problemAlphabet.mkString(",") + "}")
+ val problemDescriptionNodeSeq = Text(generalProblem.getLongDescription)
+ val booleanOperationNodeSeq = Text("{" + productConstructionProblem.getBooleanOperation.toString() + "}")
+
+ val hideSubmitButton : JsCmd = JsHideId("submitbutton")
+ val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("Editor.canvasDfaSol.exportAutomaton()"), grade(_))
+ val submitButton : NodeSeq = Submit
+ val returnLink : NodeSeq = SHtml.link("/courses/show", returnFunc, Text("Return to Course"))
+
+ val template : NodeSeq = Templates(List("product-construction", "solve")) openOr Text("Template /product-construction/solve not found")
+ return SHtml.ajaxForm(Helpers.bind("dfaeditform", template,
+ "setupscript" -> setupScript,
+ "alphabettext" -> problemAlphabetNodeSeq,
+ "boolop" -> booleanOperationNodeSeq,
+ "submitbutton" -> submitButton,
+ "returnlink" -> returnLink))
+ }
+
+ override def onDelete( generalProblem : Problem ) : Unit = {
+ ProductConstructionProblem.deleteByGeneralProblem(generalProblem)
+ }
+}
diff --git a/src/main/scala/com/automatatutor/snippet/Users.scala b/src/main/scala/com/automatatutor/snippet/Users.scala
index 193f6ac..6727603 100644
--- a/src/main/scala/com/automatatutor/snippet/Users.scala
+++ b/src/main/scala/com/automatatutor/snippet/Users.scala
@@ -7,6 +7,10 @@ import com.automatatutor.model.Attendance
import com.automatatutor.model.Course
import com.automatatutor.model.DFAConstructionProblem
import com.automatatutor.model.DFAConstructionSolutionAttempt
+import com.automatatutor.model.ProductConstructionProblem
+import com.automatatutor.model.ProductConstructionSolutionAttempt
+import com.automatatutor.model.MinimizationProblem
+import com.automatatutor.model.MinimizationSolutionAttempt
import com.automatatutor.model.NFAConstructionProblem
import com.automatatutor.model.NFAConstructionSolutionAttempt
import com.automatatutor.model.NFAToDFAProblem
@@ -115,9 +119,10 @@ class Users extends PaginatorSnippet[User] {
def resetlink(ignored : NodeSeq) : NodeSeq = {
def resetDatabase() = {
List(Attendance, Course, PosedProblem, PosedProblemSet, Problem, ProblemType,
- DFAConstructionProblem, NFAConstructionProblem,
+ DFAConstructionProblem, ProductConstructionProblem, MinimizationProblem, NFAConstructionProblem,
NFAToDFAProblem, NFAToDFASolutionAttempt,
- ProblemSet, SolutionAttempt, Supervision, DFAConstructionSolutionAttempt, NFAConstructionSolutionAttempt).map(_.bulkDelete_!!())
+ ProblemSet, SolutionAttempt, Supervision, DFAConstructionSolutionAttempt, ProductConstructionSolutionAttempt,
+ MinimizationSolutionAttempt, NFAConstructionSolutionAttempt).map(_.bulkDelete_!!())
}
val resetLink = SHtml.link("/users/index", () => resetDatabase, Text("Reset Database"))
diff --git a/src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala b/src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala
new file mode 100644
index 0000000..0f91e15
--- /dev/null
+++ b/src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala
@@ -0,0 +1,232 @@
+package com.automatatutor.snippet
+
+import java.util.Calendar
+import java.util.Date
+import scala.Array.canBuildFrom
+import scala.Array.fallbackCanBuildFrom
+import scala.xml.NodeSeq
+import scala.xml.NodeSeq.seqToNodeSeq
+import scala.xml.Text
+import scala.xml.XML
+import com.automatatutor.lib.GraderConnection
+import com.automatatutor.model.Problem
+import com.automatatutor.model.WordsInGrammarProblem
+import com.automatatutor.model.WordsInGrammarSolutionAttempt
+import com.automatatutor.model.SolutionAttempt
+import net.liftweb.common.Box
+import net.liftweb.common.Full
+import net.liftweb.http.SHtml
+import net.liftweb.http.SHtml.ElemAttr.pairToBasic
+import net.liftweb.http.Templates
+import net.liftweb.http.js.JE.JsRaw
+import net.liftweb.http.js.JsCmd
+import net.liftweb.http.js.JsCmds
+import net.liftweb.http.js.JsCmds._
+import net.liftweb.http.js.JsCmds.JsHideId
+import net.liftweb.http.js.JsCmds.JsShowId
+import net.liftweb.http.js.JsCmds.SetHtml
+import net.liftweb.http.js.JsCmds.cmdToString
+import net.liftweb.http.js.JsCmds.jsExpToJsCmd
+import net.liftweb.util.Helpers
+import net.liftweb.util.Helpers._
+import net.liftweb.util.Helpers.strToSuperArrowAssoc
+import net.liftweb.http.js.JE.Call
+import net.liftweb.common.Empty
+
+object WordsInGrammarSnippet extends ProblemSnippet {
+
+ override def renderCreate( createUnspecificProb : (String, String) => Problem,
+ returnFunc : () => Nothing ) : NodeSeq = {
+
+ def create(formValues : String) : JsCmd = {
+ val formValuesXml = XML.loadString(formValues)
+ val grammar = (formValuesXml \ "grammarfield").head.text
+ val inNeeded = (formValuesXml \ "inneededfield").head.text.toInt
+ val outNeeded = (formValuesXml \ "outneededfield").head.text.toInt
+ val shortDescription = (formValuesXml \ "shortdescfield").head.text
+
+ val parsingErrors = GraderConnection.getGrammarParsingErrors(grammar)
+
+ if(parsingErrors.isEmpty) {
+ val unspecificProblem = createUnspecificProb(shortDescription, shortDescription)
+
+ val specificProblem : WordsInGrammarProblem = WordsInGrammarProblem.create
+ specificProblem.problemId(unspecificProblem).grammar(grammar).inNeeded(inNeeded).outNeeded(outNeeded)
+ specificProblem.save
+
+ return JsCmds.RedirectTo("/problems/index")
+ } else {
+ return JsCmds.JsShowId("submitbutton") & JsCmds.JsShowId("feedbackdisplay") & JsCmds.SetHtml("parsingerror", Text(parsingErrors.mkString(" ")))
+ }
+
+ }
+ val grammarField = SHtml.textarea("", value => {}, "cols" -> "80", "rows" -> "5", "id" -> "grammarfield")
+ val inNeededField = SHtml.select( Array(("1","1"), ("2","2"), ("3","3"), ("4","4"), ("5","5")), Empty , value => {}, "id" -> "inneededfield")
+ val outNeededField = SHtml.select( Array(("1","1"), ("2","2"), ("3","3"), ("4","4"), ("5","5")), Empty , value => {}, "id" -> "outneededfield")
+ val shortDescriptionField = SHtml.text("", value => {}, "id" -> "shortdescfield")
+
+ val hideSubmitButton : JsCmd = JsHideId("submitbutton")
+ val grammarFieldValXmlJs : String = "' + document.getElementById('grammarfield').value + ' "
+ val inNeededFieldValXmlJs : String = "' + document.getElementById('inneededfield').value + ' "
+ val outNeededFieldValXmlJs : String = "' + document.getElementById('outneededfield').value + ' "
+ val shortdescFieldValXmlJs : String = "' + document.getElementById('shortdescfield').value + ' "
+ val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + inNeededFieldValXmlJs + outNeededFieldValXmlJs + shortdescFieldValXmlJs + " '"), create(_))
+
+ //val checkGrammarAndSubmit : JsCmd = JsIf(Call("multipleAlphabetChecks",Call("parseAlphabetByFieldName", "terminalsfield"),Call("parseAlphabetByFieldName", "nonterminalsfield")), hideSubmitButton & ajaxCall)
+ val submit : JsCmd = hideSubmitButton & ajaxCall
+
+ val submitButton : NodeSeq = Submit
+
+ val template : NodeSeq = Templates(List("words-in-grammar-problem", "create")) openOr Text("Could not find template /words-in-grammar-problem/create")
+ Helpers.bind("createform", template,
+ "grammarfield" -> grammarField,
+ "inneededfield" -> inNeededField,
+ "outneededfield" -> outNeededField,
+ "shortdescription" -> shortDescriptionField,
+ "submit" -> submitButton)
+ }
+
+ override def renderEdit : Box[(Problem, () => Nothing) => NodeSeq] = Full(renderEditFunc)
+
+ private def renderEditFunc(problem : Problem, returnFunc : () => Nothing) : NodeSeq = {
+
+ val wordsInGrammarProblem = WordsInGrammarProblem.findByGeneralProblem(problem)
+
+ var shortDescription : String = problem.getShortDescription
+ var grammar : String = wordsInGrammarProblem.getGrammar
+ var inNeeded : Int = wordsInGrammarProblem.getInNeeded
+ var outNeeded : Int = wordsInGrammarProblem.getOutNeeded
+
+ def edit(formValues : String) : JsCmd = {
+ val formValuesXml = XML.loadString(formValues)
+ val grammar = (formValuesXml \ "grammarfield").head.text
+ val inNeeded = (formValuesXml \ "inneededfield").head.text.toInt
+ val outNeeded = (formValuesXml \ "outneededfield").head.text.toInt
+ val shortDescription = (formValuesXml \ "shortdescfield").head.text
+
+ val parsingErrors = GraderConnection.getGrammarParsingErrors(grammar)
+
+ if(parsingErrors.isEmpty) {
+ val specificProblem : WordsInGrammarProblem = WordsInGrammarProblem.create
+
+ problem.setShortDescription(shortDescription).setLongDescription(shortDescription).save()
+ wordsInGrammarProblem.grammar(grammar).inNeeded(inNeeded).outNeeded(outNeeded).save()
+ returnFunc()
+ } else {
+ return JsCmds.JsShowId("submitbutton") & JsCmds.JsShowId("feedbackdisplay") & JsCmds.SetHtml("parsingerror", Text(parsingErrors.mkString(" ")))
+ }
+ }
+
+ val grammarField = SHtml.textarea(grammar, grammar=_, "cols" -> "80", "rows" -> "5", "id" -> "grammarfield")
+ val inNeededField = SHtml.select( Array(("1","1"), ("2","2"), ("3","3"), ("4","4"), ("5","5")), Full("" + inNeeded) , value => {}, "id" -> "inneededfield")
+ val outNeededField = SHtml.select( Array(("1","1"), ("2","2"), ("3","3"), ("4","4"), ("5","5")), Full("" + outNeeded) , value => {}, "id" -> "outneededfield")
+ val shortDescriptionField = SHtml.text(shortDescription, shortDescription=_, "id" -> "shortdescfield")
+
+ val hideSubmitButton : JsCmd = JsHideId("submitbutton")
+ val grammarFieldValXmlJs : String = "' + document.getElementById('grammarfield').value + ' "
+ val inNeededFieldValXmlJs : String = "' + document.getElementById('inneededfield').value + ' "
+ val outNeededFieldValXmlJs : String = "' + document.getElementById('outneededfield').value + ' "
+ val shortdescFieldValXmlJs : String = "' + document.getElementById('shortdescfield').value + ' "
+ val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + inNeededFieldValXmlJs + outNeededFieldValXmlJs + shortdescFieldValXmlJs + " '"), edit(_))
+
+ val submit : JsCmd = hideSubmitButton & ajaxCall
+
+ val submitButton : NodeSeq = Submit
+
+ val template : NodeSeq = Templates(List("words-in-grammar-problem", "edit")) openOr Text("Could not find template /words-in-grammar-problem/edit")
+ Helpers.bind("editform", template,
+ "grammarfield" -> grammarField,
+ "inneededfield" -> inNeededField,
+ "outneededfield" -> outNeededField,
+ "shortdescription" -> shortDescriptionField,
+ "submit" -> submitButton)
+ }
+
+ override def renderSolve(generalProblem : Problem, maxGrade : Long, lastAttempt : Box[SolutionAttempt],
+ recordSolutionAttempt : (Int, Date) => SolutionAttempt, returnFunc : () => Unit, remainingAttempts: () => Int,
+ bestGrade: () => Int) : NodeSeq = {
+ val specificProblem = WordsInGrammarProblem.findByGeneralProblem(generalProblem)
+
+ def grade(formValues : String) : JsCmd = {
+ val formValuesXml = XML.loadString(formValues)
+
+ val wordsIn = new Array[String](specificProblem.getInNeeded);
+ for(i <- 0 to specificProblem.getInNeeded - 1) {
+ wordsIn(i) = (formValuesXml \ "ins" \ ("in" + i)).head.text.replaceAll("\\s", "")
+ }
+ val wordsOut = new Array[String](specificProblem.getOutNeeded);
+ for(i <- 0 to specificProblem.getOutNeeded - 1) {
+ wordsOut(i) = (formValuesXml \ "outs" \ ("out" + i)).head.text.replaceAll("\\s", "")
+ }
+
+ if(remainingAttempts() <= 0) {
+ return JsShowId("feedbackdisplay") & SetHtml("feedbackdisplay", Text("You do not have any attempts left for this problem. Your final grade is " + bestGrade().toString + "/" + maxGrade.toString + "."))
+ }
+ val attemptTime = Calendar.getInstance.getTime()
+
+ val gradeAndFeedback = GraderConnection.getWordsInGrammarFeedback(specificProblem.grammar.is, wordsIn, wordsOut, maxGrade.toInt)
+
+ val numericalGrade = gradeAndFeedback._1
+ val generalAttempt = recordSolutionAttempt(numericalGrade, attemptTime)
+
+ // Only save the specific attempt if we saved the general attempt
+ if(generalAttempt != null) {
+ WordsInGrammarSolutionAttempt.create.solutionAttemptId(generalAttempt).attemptWordsIn((formValuesXml \ "ins").toString()).attemptWordsOut((formValuesXml \ "outs").toString()).save
+ }
+
+ val setNumericalGrade : JsCmd = SetHtml("grade", Text(gradeAndFeedback._1.toString + "/" + maxGrade.toString))
+ val setFeedback : JsCmd = SetHtml("feedback", gradeAndFeedback._2)
+ val showFeedback : JsCmd = JsShowId("feedbackdisplay")
+
+ return setNumericalGrade & setFeedback & showFeedback & JsCmds.JsShowId("submitbutton")
+ }
+
+ val problemDescription = generalProblem.getLongDescription
+ val grammarText = { specificProblem.getGrammar.replaceAll("->", " -> ").replaceAll("=>", " -> ").replaceAll("\\|", " \\| ").replaceAll("\\s{2,}", " ").split("\\s(?=\\S+\\s*->)").map {Text(_) ++ } reduceLeft (_ ++ _) }
+ var inNeededText = Text(specificProblem.inNeeded + " words")
+ if (specificProblem.inNeeded == 1) inNeededText = Text(specificProblem.inNeeded + " word")
+ var outNeededText = Text(specificProblem.outNeeded + " words")
+ if (specificProblem.outNeeded == 1) outNeededText = Text(specificProblem.outNeeded + " word")
+ val wordsInFields = new Array[NodeSeq](specificProblem.getInNeeded)
+ for(i <- 0 to specificProblem.getInNeeded - 1) {
+ wordsInFields(i) = SHtml.text("", value => {}, "id" -> ("wordinfield" + i.toString), "maxlength" -> "75")
+ }
+ val wordsInFieldNodeSeq = {wordsInFields.map(i => {i} )}
+ val wordsOutFields = new Array[NodeSeq](specificProblem.getOutNeeded)
+ for(i <- 0 to specificProblem.getOutNeeded - 1) {
+ wordsOutFields(i) = SHtml.text("", value => {}, "id" -> ("wordoutfield" + i.toString), "maxlength" -> "75")
+ }
+ val wordsOutFieldNodeSeq = {wordsOutFields.map(i => {i} )}
+
+ val insValXmlJs : StringBuilder = new StringBuilder("")
+ for(i <- 0 to specificProblem.getInNeeded - 1) {
+ insValXmlJs.append("' + sanitizeInputForXML('wordinfield" + i.toString + "') + ' ")
+ }
+ insValXmlJs.append(" ")
+ val outsValXmlJs : StringBuilder = new StringBuilder("")
+ for(i <- 0 to specificProblem.getOutNeeded - 1) {
+ outsValXmlJs.append("' + sanitizeInputForXML('wordoutfield" + i.toString + "') + ' ")
+ }
+ outsValXmlJs.append(" ")
+
+ val hideSubmitButton : JsCmd = JsHideId("submitbutton")
+ val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + insValXmlJs + outsValXmlJs + " '"), grade(_))
+ val submitButton : NodeSeq = Submit
+ val returnLink : NodeSeq = SHtml.link("/courses/show", returnFunc, Text("Return to Course"))
+
+ val template : NodeSeq = Templates(List("words-in-grammar-problem", "solve")) openOr Text("Could not find template /words-in-grammar-problem/solve")
+ Helpers.bind("solveform", template,
+ "problemdescription" -> problemDescription,
+ "grammartext" -> grammarText,
+ "wordsin" -> wordsInFieldNodeSeq,
+ "wordsout" -> wordsOutFieldNodeSeq,
+ "inneededtext" -> inNeededText,
+ "outneededtext" -> outNeededText,
+ "submitbutton" -> submitButton,
+ "returnlink" -> returnLink)
+ }
+
+ override def onDelete( generalProblem : Problem ) : Unit = {
+
+ }
+}
\ No newline at end of file
diff --git a/src/main/webapp/courses/index.html b/src/main/webapp/courses/index.html
index 414ef86..057e2f4 100644
--- a/src/main/webapp/courses/index.html
+++ b/src/main/webapp/courses/index.html
@@ -6,6 +6,7 @@
+
Enroll in course
diff --git a/src/main/webapp/cyk-problem/create.html b/src/main/webapp/cyk-problem/create.html
new file mode 100644
index 0000000..dc9a08b
--- /dev/null
+++ b/src/main/webapp/cyk-problem/create.html
@@ -0,0 +1,43 @@
+
+
Create a new CYK problem
+
+
Grammar Syntax
+
+ e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
+ 1 line per production
+ split consecutive variables with a whitespace (e.g. S -> A B C)
+ first variable is start variable
+ possible terminals: abcdefghijklmnopqrstuvwxyz()[]{}
+ possible variables: 1 or more upper case characters A-Z or numbers 0-9 (!can't start with number!)
+ empty word: a blank right side (e.g. "S -> " or "S -> a | ")
+
+
+
Problem Definition
+
+
+
+
+ Grammar:
+
+
+
+ Word:
+
+
+
+ Short Description:
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/cyk-problem/edit.html b/src/main/webapp/cyk-problem/edit.html
new file mode 100644
index 0000000..36d1394
--- /dev/null
+++ b/src/main/webapp/cyk-problem/edit.html
@@ -0,0 +1,43 @@
+
+
Edit a CYK problem
+
+
Grammar Syntax
+
+ e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
+ 1 line per production
+ split consecutive variables with a whitespace (e.g. S -> A B C)
+ first variable is start variable
+ possible terminals: abcdefghijklmnopqrstuvwxyz()[]{}
+ possible variables: 1 or more upper case characters A-Z or numbers 0-9 (!can't start with number!)
+ empty word: a blank right side (e.g. "S -> " or "S -> a | ")
+
+
+
Problem Definition
+
+
+
+
+ Grammar:
+
+
+
+ Word:
+
+
+
+ Short Description:
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/webapp/cyk-problem/solve.html b/src/main/webapp/cyk-problem/solve.html
new file mode 100644
index 0000000..6e94fb4
--- /dev/null
+++ b/src/main/webapp/cyk-problem/solve.html
@@ -0,0 +1,59 @@
+
+
+
Solve a CYK problem
+
+ Grammar Syntax
+
+ e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
+ 1 line per production
+ split consecutive variables with a whitespace (e.g. S -> A B C)
+ first variable is start variable
+ possible terminals: abcdefghijklmnopqrstuvwxyz()[]{}
+ possible variables: 1 or more upper case characters A-Z or numbers 0-9 (!can't start with number!)
+ empty word: a blank right side (e.g. "S -> " or "S -> a | ")
+
+
+ Problem
+
+ For the grammar with the productions:
+
+
+ Perform the CYK algorithm!
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/description-to-grammar-problem/create.html b/src/main/webapp/description-to-grammar-problem/create.html
new file mode 100644
index 0000000..545e71f
--- /dev/null
+++ b/src/main/webapp/description-to-grammar-problem/create.html
@@ -0,0 +1,43 @@
+
+
Create a new Description to Grammar problem
+
+
Grammar Syntax
+
+ e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
+ 1 line per production
+ split consecutive variables with a whitespace (e.g. S -> A B C)
+ first variable is start variable
+ possible terminals: abcdefghijklmnopqrstuvwxyz()[]{}
+ possible variables: 1 or more upper case characters A-Z or numbers 0-9 (!can't start with number!)
+ empty word: a blank right side (e.g. "S -> " or "S -> a | ")
+
+
+
Problem Definition
+
+
+
+
+ Grammar:
+
+
+
+ Short Description:
+
+
+
+ Long Description (Will be display after "Find a grammar that recognizes the following language: ..."):
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/description-to-grammar-problem/edit.html b/src/main/webapp/description-to-grammar-problem/edit.html
new file mode 100644
index 0000000..47ce597
--- /dev/null
+++ b/src/main/webapp/description-to-grammar-problem/edit.html
@@ -0,0 +1,46 @@
+
+
+
+
+
Edit a Description to Grammar problem
+
+
Grammar Syntax
+
+ e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
+ 1 line per production
+ split consecutive variables with a whitespace (e.g. S -> A B C)
+ first variable is start variable
+ possible terminals: abcdefghijklmnopqrstuvwxyz()[]{}
+ possible variables: 1 or more upper case characters A-Z or numbers 0-9 (!can't start with number!)
+ empty word: a blank right side (e.g. "S -> " or "S -> a | ")
+
+
+
Problem Definition
+
+
+
+
+ Grammar:
+
+
+
+ Short Description:
+
+
+
+ Long Description (Will be display after "Find a grammar that recognizes the following language: ..."):
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/webapp/description-to-grammar-problem/solve.html b/src/main/webapp/description-to-grammar-problem/solve.html
new file mode 100644
index 0000000..a73b9ed
--- /dev/null
+++ b/src/main/webapp/description-to-grammar-problem/solve.html
@@ -0,0 +1,34 @@
+
+
Solve Description to Grammar problem
+
+ Grammar Syntax
+
+ e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
+ 1 line per production
+ split consecutive variables with a whitespace (e.g. S -> A B C)
+ first variable is start variable
+ possible terminals: abcdefghijklmnopqrstuvwxyz()[]{}
+ possible variables: 1 or more upper case characters A-Z or numbers 0-9 (!can't start with number!)
+ empty word: a blank right side (e.g. "S -> " or "S -> a | ")
+
+
+ Problem
+
+ Find a grammar that recognizes the following language:
+
+
+
+
+
+
+
diff --git a/src/main/webapp/description-to-regex-problem/create.html b/src/main/webapp/description-to-regex-problem/create.html
index c6b06c9..2bcf5fa 100644
--- a/src/main/webapp/description-to-regex-problem/create.html
+++ b/src/main/webapp/description-to-regex-problem/create.html
@@ -20,7 +20,8 @@ Problem Definition
Regular Expression:
-
+
+
Short Description:
diff --git a/src/main/webapp/grammar-to-cnf-problem/create.html b/src/main/webapp/grammar-to-cnf-problem/create.html
new file mode 100644
index 0000000..4469931
--- /dev/null
+++ b/src/main/webapp/grammar-to-cnf-problem/create.html
@@ -0,0 +1,39 @@
+
+
Create a new Grammar to CNF (Comsky Normal Form) problem
+
+
Grammar Syntax
+
+ e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
+ 1 line per production
+ split consecutive variables with a whitespace (e.g. S -> A B C)
+ first variable is start variable
+ possible terminals: abcdefghijklmnopqrstuvwxyz()[]{}
+ possible variables: 1 or more upper case characters A-Z or numbers 0-9 (!can't start with number!)
+ empty word: a blank right side (e.g. "S -> " or "S -> a | ")
+
+
+
Problem Definition
+
+
+
+
+ Grammar:
+
+
+
+ Short Description:
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/grammar-to-cnf-problem/edit.html b/src/main/webapp/grammar-to-cnf-problem/edit.html
new file mode 100644
index 0000000..e3731bf
--- /dev/null
+++ b/src/main/webapp/grammar-to-cnf-problem/edit.html
@@ -0,0 +1,39 @@
+
+
Edit a Grammar to CNF (Comsky Normal Form) problem
+
+
Grammar Syntax
+
+ e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
+ 1 line per production
+ split consecutive variables with a whitespace (e.g. S -> A B C)
+ first variable is start variable
+ possible terminals: abcdefghijklmnopqrstuvwxyz()[]{}
+ possible variables: 1 or more upper case characters A-Z or numbers 0-9 (!can't start with number!)
+ empty word: a blank right side (e.g. "S -> " or "S -> a | ")
+
+
+
Problem Definition
+
+
+
+
+ Grammar:
+
+
+
+ Short Description:
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/webapp/grammar-to-cnf-problem/solve.html b/src/main/webapp/grammar-to-cnf-problem/solve.html
new file mode 100644
index 0000000..331e276
--- /dev/null
+++ b/src/main/webapp/grammar-to-cnf-problem/solve.html
@@ -0,0 +1,39 @@
+
+
Solve Grammar to CNF (Comsky Normal Form) problem
+
+ Grammar Syntax
+
+ e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
+ 1 line per production
+ split consecutive variables with a whitespace (e.g. S -> A B C)
+ first variable is start variable
+ possible terminals: abcdefghijklmnopqrstuvwxyz()[]{}
+ possible variables: 1 or more upper case characters A-Z or numbers 0-9 (!can't start with number!)
+ empty word: a blank right side (e.g. "S -> " or "S -> a | ")
+
+
+ Problem
+
+ For the grammar G with the productions:
+
+
+ Give a CNF G' such that L(G) \ "" = L(G')
+ (remember: CNF allows only productions with the form "X -> Y Z" or "X -> a")
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/javascript/alphabetUtil.js b/src/main/webapp/javascript/alphabetUtil.js
index 9604b60..3acf08f 100644
--- a/src/main/webapp/javascript/alphabetUtil.js
+++ b/src/main/webapp/javascript/alphabetUtil.js
@@ -39,4 +39,12 @@ function alphabetChecks(stringArray){
tmpArray.push(elem);
}
return true;
+}
+
+function multipleAlphabetChecks(){
+ if (arguments.length == 0) return true;
+ var concatenated = arguments[0];
+ var i;
+ for(i = 1; i < arguments.length; i++) concatenated.concat(arguments[i]);
+ return alphabetChecks(concatenated);
}
\ No newline at end of file
diff --git a/src/main/webapp/javascript/grammarUtil.js b/src/main/webapp/javascript/grammarUtil.js
new file mode 100644
index 0000000..3daa55e
--- /dev/null
+++ b/src/main/webapp/javascript/grammarUtil.js
@@ -0,0 +1,15 @@
+function formateGrammar(grammarString) {
+ var res = grammarString.replace(/->/g, " -> ");
+ res = res.replace(/=>/g, " -> ");
+ res = res.replace(/\|/g, " | ");
+ res = res.replace(/\s{2,}/g, " ")
+ res = res.replace(/\s(?=\S+\s*->)/, "\n");
+ return res;
+}
+
+function sanitizeInputForXML(id) {
+ var input = document.getElementById(id);
+ if (input === null) return "";
+ input.value = input.value.replace(/[<>&]/g, "");
+ return input.value;
+}
\ No newline at end of file
diff --git a/src/main/webapp/javascript/includeDFANFA.js b/src/main/webapp/javascript/includeDFANFA.js
index 950635c..5781bed 100644
--- a/src/main/webapp/javascript/includeDFANFA.js
+++ b/src/main/webapp/javascript/includeDFANFA.js
@@ -12,7 +12,7 @@ function initCanvas() {
Editor.canvasNfa = new $.SvgCanvas("#svgcanvasnfa", Editor.curConfigNfa, 'nondetaut');
}
if(!Editor.canvasDfa) {
- Editor.canvasDfa = new $.SvgCanvas("#svgcanvasdfa", Editor.curConfigDfa, 'detaut');
+ Editor.canvasDfa = new $.SvgCanvas("#svgcanvasdfa", Editor.curConfigDfa, 'powaut');
}
}
diff --git a/src/main/webapp/javascript/includeMinimizationCreateEdit.js b/src/main/webapp/javascript/includeMinimizationCreateEdit.js
new file mode 100644
index 0000000..8c0b1d0
--- /dev/null
+++ b/src/main/webapp/javascript/includeMinimizationCreateEdit.js
@@ -0,0 +1,17 @@
+var Editor = {
+ curConfigDfa: {
+ dimensions: [740,480]
+ }
+};
+
+function initCanvas() {
+ if(!Editor.canvas) {
+ Editor.canvas = new $.SvgCanvas("#svgcanvasdfa", Editor.curConfigDfa, 'detaut');
+ }
+}
+
+$(document).ready(function() {
+ initCanvas();
+});
+
+
diff --git a/src/main/webapp/javascript/includeMinimizationSolve.js b/src/main/webapp/javascript/includeMinimizationSolve.js
new file mode 100644
index 0000000..ac13106
--- /dev/null
+++ b/src/main/webapp/javascript/includeMinimizationSolve.js
@@ -0,0 +1,23 @@
+var Editor = {
+ curConfigDfaIn: {
+ dimensions: [740,480]
+ },
+ curConfigDfaSol: {
+ dimensions: [740,480]
+ }
+};
+
+function initCanvas() {
+ if(!Editor.canvasDfaIn) {
+ Editor.canvasDfaIn = new $.SvgCanvas("#svgcanvasdfain", Editor.curConfigDfaIn, 'detaut');
+ }
+ if(!Editor.canvasDfaSol) {
+ Editor.canvasDfaSol = new $.SvgCanvas("#svgcanvasdfasol", Editor.curConfigDfaSol, 'powaut');
+ }
+}
+
+$(document).ready(function() {
+ initCanvas();
+});
+
+
diff --git a/src/main/webapp/javascript/includeProductConstruction.js b/src/main/webapp/javascript/includeProductConstruction.js
new file mode 100644
index 0000000..4bf689a
--- /dev/null
+++ b/src/main/webapp/javascript/includeProductConstruction.js
@@ -0,0 +1,40 @@
+var Editor = {
+ curConfig: {
+ dimensions: [740,480]
+ },
+ canvasArray: new Array()
+};
+
+function initCanvas() {
+ if(Editor.canvas)
+ return;
+
+ createCanvas(1);
+}
+
+function createCanvas(no) {
+ var oldLength = Editor.canvasArray.length;
+ if(oldLength < no){
+ for(var i = oldLength; i < no; i++){
+ Editor.canvasArray.push(new $.SvgCanvas("#svgcanvasdfa", Editor.curConfig, 'detaut'));
+ }
+ } else {
+ Editor.canvasArray.length = no;
+ }
+
+ Editor.canvas = Editor.canvasArray[0];
+}
+
+function setNumberOfCanvas(no){
+ createCanvas(no)
+}
+
+//TODO
+function setActualCanvas(no) {
+ Editor.canvas = Editor.canvasArray[no];
+}
+
+$(document).ready(function() {
+ initCanvas();
+});
+
diff --git a/src/main/webapp/javascript/includeProductConstructionCreateEdit.js b/src/main/webapp/javascript/includeProductConstructionCreateEdit.js
new file mode 100644
index 0000000..4d2ad51
--- /dev/null
+++ b/src/main/webapp/javascript/includeProductConstructionCreateEdit.js
@@ -0,0 +1,23 @@
+var Editor = {
+ curConfigDfa1: {
+ dimensions: [740,480]
+ },
+ curConfigDfa2: {
+ dimensions: [740,480]
+ }
+};
+
+function initCanvas() {
+ if(!Editor.canvasDfa1) {
+ Editor.canvasDfa1 = new $.SvgCanvas("#svgcanvascreatedfa1", Editor.curConfigDfa1, 'detaut');
+ }
+ if(!Editor.canvasDfa2) {
+ Editor.canvasDfa2 = new $.SvgCanvas("#svgcanvascreatedfa2", Editor.curConfigDfa2, 'detaut');
+ }
+}
+
+$(document).ready(function() {
+ initCanvas();
+});
+
+
diff --git a/src/main/webapp/javascript/includeProductConstructionSolve.js b/src/main/webapp/javascript/includeProductConstructionSolve.js
new file mode 100644
index 0000000..b43faa9
--- /dev/null
+++ b/src/main/webapp/javascript/includeProductConstructionSolve.js
@@ -0,0 +1,29 @@
+var Editor = {
+ curConfigDfa1: {
+ dimensions: [360,240]
+ },
+ curConfigDfa2: {
+ dimensions: [360,240]
+ },
+ curConfigDfaSol: {
+ dimensions: [740,480]
+ }
+};
+
+function initCanvas() {
+ if(!Editor.canvasDfa1) {
+ Editor.canvasDfa1 = new $.SvgCanvas("#svgcanvasdfa1", Editor.curConfigDfa1, 'detaut');
+ }
+ if(!Editor.canvasDfa2) {
+ Editor.canvasDfa2 = new $.SvgCanvas("#svgcanvasdfa2", Editor.curConfigDfa2, 'detaut');
+ }
+ if(!Editor.canvasDfaSol) {
+ Editor.canvasDfaSol = new $.SvgCanvas("#svgcanvasdfasol", Editor.curConfigDfaSol, 'prodaut');
+ }
+}
+
+$(document).ready(function() {
+ initCanvas();
+});
+
+
diff --git a/src/main/webapp/javascript/interface.js b/src/main/webapp/javascript/interface.js
index 342c79b..466ab2f 100644
--- a/src/main/webapp/javascript/interface.js
+++ b/src/main/webapp/javascript/interface.js
@@ -45,6 +45,35 @@ $.SvgCanvas = function(container, config, style) {
acceptanceMarker: 'css'
},
+ //Product automaton: States are labelled as tuples of states of the original automata
+ 'prodaut': {
+ node: {
+ label: 'tuple'
+ },
+ transition: {
+ labeled: true,
+ deterministic: true
+ },
+ hasInitialNode: true,
+ twoPlayers: false,
+ acceptanceMarker: 'css'
+ },
+
+ //Powerset automaton: State are labelled as sets of states of the original automaton
+ 'powaut': {
+ node: {
+ label: 'set',
+ radius: 25
+ },
+ transition: {
+ labeled: true,
+ deterministic: true
+ },
+ hasInitialNode: true,
+ twoPlayers: false,
+ acceptanceMarker: 'css'
+ },
+
'nondetaut': {
node: {
label: 'id'
@@ -111,7 +140,7 @@ $.SvgCanvas = function(container, config, style) {
$.extend(true, config, globalConfig, styleConfig[style])
- function useHoverMenu() { return config.transition.labeled === false || config.transition.deterministic === false }
+ function useHoverMenu() { return config.transition.labeled === false || config.transition.deterministic === false || config.node.label === 'tuple' || config.node.label === 'set' }
/// Returns true iff the hover menu around node d should be displayed
function isHoverMenuVisible(d) { return (d.menu_visible && !newLink && !draggingLink && !draggingNode && showMenu)}
@@ -196,6 +225,128 @@ $.SvgCanvas = function(container, config, style) {
}
}
+ //TODO:
+ function populateHoverMenusWithNumbersBothSides(menus) {
+ var symbolsToDistributeLeft = numberOfNodesOfAutomaton1
+ var symbolsToDistributeRight = numberOfNodesOfAutomaton2
+
+ function calculateXCoordinateLeft(index) {
+ var angle = 3*Math.PI/2 - (Math.PI/2 - symbolsToDistributeLeft*config.hoverMenu.step/2) - (index + .5) * config.hoverMenu.step;
+ return polarToPlanar(config.node.radius + 10, angle).x;
+ }
+ function calculateYCoordinateLeft(index) {
+ var angle = 3*Math.PI/2 - (Math.PI/2 - symbolsToDistributeLeft*config.hoverMenu.step/2) - (index + .5) * config.hoverMenu.step;
+ return polarToPlanar(config.node.radius + 10, angle).y + 5;
+ }
+
+ function calculateXCoordinateRight(index) {
+ var angle = 3*Math.PI/2 + (Math.PI/2 - symbolsToDistributeRight*config.hoverMenu.step/2) + (index + .5) * config.hoverMenu.step;
+ return polarToPlanar(config.node.radius + 10, angle).x;
+ }
+ function calculateYCoordinateRight(index) {
+ var angle = 3*Math.PI/2 + (Math.PI/2 - symbolsToDistributeRight*config.hoverMenu.step/2) + (index + .5) * config.hoverMenu.step;
+ return polarToPlanar(config.node.radius + 10, angle).y + 5;
+ }
+
+ function onLabelMouseover(node) {
+ node.menu_visible = true;
+ showMenu = true;
+ restart();
+ }
+
+ function onLabelMousedown(node) {
+ node.menu_visible = false;
+ showMenu = false;
+ newLink = true;
+ mousedown_node = node;
+
+ if(this.getAttribute('pos') === 'left')
+ mousedown_node.left = this.textContent;
+ else
+ mousedown_node.right = this.textContent;
+
+ restart();
+ }
+
+ function onLabelMouseout(nodeData) { showMenu = false }
+
+ for(var i = 0; i < symbolsToDistributeLeft; i++){
+ menus.append('svg:text')
+ .attr('class', 'hoverMenu visible')
+ .classed('visible', isHoverMenuVisible)
+ .text(i)
+ .attr('pos', 'left')
+ .attr('x', calculateXCoordinateLeft(i))
+ .attr('y', calculateYCoordinateLeft(i))
+ .on('mouseover', onLabelMouseover)
+ .on('mousedown', onLabelMousedown)
+ .on('mouseout', onLabelMouseout);
+ }
+
+ for(var i = 0; i < symbolsToDistributeRight; i++){
+ menus.append('svg:text')
+ .attr('class', 'hoverMenu visible')
+ .classed('visible', isHoverMenuVisible)
+ .text(i)
+ .attr('pos', 'right')
+ .attr('x', calculateXCoordinateRight(i))
+ .attr('y', calculateYCoordinateRight(i))
+ .on('mouseover', onLabelMouseover)
+ .on('mousedown', onLabelMousedown)
+ .on('mouseout', onLabelMouseout);
+ }
+ }
+
+ function populateHoverMenusWithNumbers(menus) {
+
+ var symbolsToDistribute = numberOfNodesOfAutomaton1
+
+ function calculateXCoordinate(index) {
+ var angle = 3*Math.PI/2 - (symbolsToDistribute-1)/2 * config.hoverMenu.step + index * config.hoverMenu.step;
+ return polarToPlanar(config.node.radius + 10, angle).x;
+ }
+ function calculateYCoordinate(index) {
+ var angle = 3*Math.PI/2 - (symbolsToDistribute-1)/2 * config.hoverMenu.step + index * config.hoverMenu.step;
+ return polarToPlanar(config.node.radius + 10, angle).y + 5;
+ }
+
+ function onLabelMouseover(node){
+ node.menu_visible = true
+ showMenu = true
+ restart();
+ }
+ function onLabelMousedown(node){
+ node.menu_visible = false;
+ showMenu = false;
+ newLink = true;
+ mousedown_node = node;
+
+ if(mousedown_node.states.length === 0){
+ for(var i = 0; i < symbolsToDistribute; i++){
+ mousedown_node.states.push(false);
+ }
+ }
+
+ mousedown_node.states[this.getAttribute('index')] = !mousedown_node.states[this.getAttribute('index')];
+ }
+ function onLabelMouseout(nodeData){
+ showMenu = false
+ }
+
+ for(var i = 0; i < symbolsToDistribute; i++){
+ menus.append('svg:text')
+ .attr('class', 'hoverMenu visible')
+ .classed('visible', isHoverMenuVisible)
+ .text(i)
+ .attr('index', i)
+ .attr('x', calculateXCoordinate(i))
+ .attr('y', calculateYCoordinate(i))
+ .on('mouseover', onLabelMouseover)
+ .on('mousedown', onLabelMousedown)
+ .on('mouseout', onLabelMouseout);
+ }
+ }
+
function populateHoverMenusWithUnlabeled(menus) {
menus.append('svg:path')
.attr('class', 'link hoverMenu')
@@ -300,6 +451,8 @@ $.SvgCanvas = function(container, config, style) {
var width = config.dimensions[0];
var height = config.dimensions[1];
+
+
var started = false;
var locked = false;
@@ -316,6 +469,10 @@ $.SvgCanvas = function(container, config, style) {
var solveMode = false
+ //for product construction
+ var numberOfNodesOfAutomaton1 = 4,
+ numberOfNodesOfAutomaton2 = 4;
+
// init D3 force layout
var force;
@@ -785,6 +942,21 @@ $.SvgCanvas = function(container, config, style) {
case 'id':
circle.selectAll('text').text(function (d) { return d.id })
break
+ case 'tuple':
+ circle.selectAll('text').text(function (d) {return d.left + ',' + d.right })
+ break
+ case 'set':
+ circle.selectAll('text').text(
+ function (d) {
+ var s = ''
+ for(var i = 0; i < d.states.length; i++){
+ if(d.states[i]){
+ s += i + ',';
+ }
+ }
+ return s.slice(0, -1)
+ })
+ break
case 'priority':
circle.selectAll('text').text(function (d) { return d.priority })
break
@@ -952,6 +1124,7 @@ $.SvgCanvas = function(container, config, style) {
restart();
}
+ //rim of regular node
g.append('svg:circle')
.attr('class', 'node')
.attr('id', 'main')
@@ -1018,6 +1191,21 @@ $.SvgCanvas = function(container, config, style) {
case 'id':
newLabels.text(function (d) { return d.id });
break
+ case 'tuple':
+ newLabels.text(function (d) { return d.left + ',' + d.right });
+ break
+ case 'set':
+ circle.selectAll('text').text(
+ function (d) {
+ var s = ''
+ for(var i = 0; i < d.states.length; i++){
+ if(d.states[i]){
+ s += i + ',';
+ }
+ }
+ return s.slice(0, -1)
+ })
+ break
case 'priority':
newLabels.text(function (d) { return d.priority });
break
@@ -1057,8 +1245,12 @@ $.SvgCanvas = function(container, config, style) {
hoverMenu.selectAll('text').classed('visible', isHoverMenuVisible);
// Hide the prototype transition if the hover menu is not active
hoverMenu.selectAll('path').classed('visible', isHoverMenuVisible);
-
- if(config.transition.labeled) {
+
+ if(config.node.label === 'tuple'){
+ populateHoverMenusWithNumbersBothSides(menus)
+ } else if(config.node.label === 'set'){
+ populateHoverMenusWithNumbers(menus)
+ } else if(config.transition.labeled) {
populateHoverMenusWithAlphabet(menus)
} else {
populateHoverMenusWithUnlabeled(menus)
@@ -1755,6 +1947,9 @@ $.SvgCanvas = function(container, config, style) {
// Just push the info about the new node to nodes. Canvas will be updated at the next restart()
var node = {
id: idNum,
+ left: 0, //for 'prodaut'
+ right: 0, //for 'prodaut'
+ states: [], //for 'powaut'
initial: false,
accepting: false,
reflexiveNum: reflNum,
@@ -2013,8 +2208,8 @@ $.SvgCanvas = function(container, config, style) {
var stateTags = xmlDoc.getElementsByTagName("stateSet")[0].getElementsByTagName("state");
for (i = 0; i < stateTags.length; i++) {
var currState = stateTags[i];
- var posX = parseFloat(currState.getElementsByTagName("posX")[0].firstChild.nodeValue);
- var posY = parseFloat(currState.getElementsByTagName("posY")[0].firstChild.nodeValue);
+ var posX = parseFloat(currState.getElementsByTagName("posX")[0].firstChild.nodeValue) * width / 1000;
+ var posY = parseFloat(currState.getElementsByTagName("posY")[0].firstChild.nodeValue) * height / 1000;
var nodeId = parseInt(currState.getElementsByTagName("label")[0].firstChild.nodeValue);
addNode(posX, posY);
nodes[nodes.length - 1].id = nodeId;
@@ -2137,7 +2332,35 @@ $.SvgCanvas = function(container, config, style) {
if(config.node.label === 'priority') {
states += "priority='" + nodes[i].priority + "' "
}
- states += ">" + nodes[i].id + " " + Math.round(parseFloat(nodes[i].x)) + " " + Math.round(parseFloat(nodes[i].y)) + " \n";
+ states += ">" + nodes[i].id + " ";
+ if(config.node.label === 'tuple') {
+ states += "" + nodes[i].left + "," + nodes[i].right + " "
+ }
+ else if(config.node.label === 'set') {
+ states += ""
+ var last = -1
+ for(var j = 0; j < nodes[i].states.length; j++) {
+ if(nodes[i].states[j]) {
+ last = j;
+ }
+ }
+ if(last > -1) {
+ for(var j = 0; j < last; j++) {
+ if(nodes[i].states[j]) {
+ states += j + ","
+ }
+ }
+ if(nodes[i].states[last]) {
+ states += (last)
+ }
+ }
+ states += " "
+ }
+ else {
+ states += "" + nodes[i].id + " "
+ }
+
+ states += "" + Math.round(parseFloat(nodes[i].x) * 1000 / width) + " " + Math.round(parseFloat(nodes[i].y) * 1000 / height) + " \n";
}
states = states + " \n";
@@ -2220,6 +2443,25 @@ $.SvgCanvas = function(container, config, style) {
this.initialize();
}
+ /**
+ * Sets the number of states of the automata to construct product from
+ *
+ */
+ this.setNumberOfStates = function(xml1, xml2) {
+
+ var xml1Doc = Utils.text2xml(xml1);
+ var xml2Doc = Utils.text2xml(xml2);
+ var stateTags1 = xml1Doc.getElementsByTagName("stateSet")[0].getElementsByTagName("state");
+ var stateTags2 = xml2Doc.getElementsByTagName("stateSet")[0].getElementsByTagName("state");
+
+ numberOfNodesOfAutomaton1 = stateTags1.length
+ numberOfNodesOfAutomaton2 = stateTags2.length
+
+ this.initialize();
+ this.restart();
+
+ }
+
/**
* Sets whether or not to use epsilon transitions
*
diff --git a/src/main/webapp/minimization/applet-solve.html b/src/main/webapp/minimization/applet-solve.html
new file mode 100644
index 0000000..93e4fdf
--- /dev/null
+++ b/src/main/webapp/minimization/applet-solve.html
@@ -0,0 +1 @@
+
diff --git a/src/main/webapp/minimization/applet.html b/src/main/webapp/minimization/applet.html
new file mode 100644
index 0000000..f133ee1
--- /dev/null
+++ b/src/main/webapp/minimization/applet.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/minimization/create.html b/src/main/webapp/minimization/create.html
new file mode 100644
index 0000000..6181c1d
--- /dev/null
+++ b/src/main/webapp/minimization/create.html
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
Create a new Minimization problem
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Short Description:
+
+
+
+ Long Description (will appear in the problem in the form of "Construct a DFA that recognizes the following language: {long description}"):
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/minimization/edit.html b/src/main/webapp/minimization/edit.html
new file mode 100644
index 0000000..2a30a48
--- /dev/null
+++ b/src/main/webapp/minimization/edit.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
Edit minimization problem
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Short Description:
+
+
+
+ Long Description (will appear in the problem in the form of "Construct a DFA that recognizes the following language: {long description}"):
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/minimization/solve.html b/src/main/webapp/minimization/solve.html
new file mode 100644
index 0000000..04e148e
--- /dev/null
+++ b/src/main/webapp/minimization/solve.html
@@ -0,0 +1,91 @@
+
+
+
+
Solve minimization problem
+
+ Construct an automaton that recognizes the following language of strings over the alphabet
+ :
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/problemsets/addproblem.html b/src/main/webapp/problemsets/addproblem.html
index 617421e..65e7e98 100644
--- a/src/main/webapp/problemsets/addproblem.html
+++ b/src/main/webapp/problemsets/addproblem.html
@@ -2,9 +2,10 @@
Home
-
-
Problems created by you
-
-
+
+
Problems created by you
+
+
+
diff --git a/src/main/webapp/product-construction/applet-create-edit.html b/src/main/webapp/product-construction/applet-create-edit.html
new file mode 100644
index 0000000..7c6ce2a
--- /dev/null
+++ b/src/main/webapp/product-construction/applet-create-edit.html
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/product-construction/applet-solve-product.html b/src/main/webapp/product-construction/applet-solve-product.html
new file mode 100644
index 0000000..b54d880
--- /dev/null
+++ b/src/main/webapp/product-construction/applet-solve-product.html
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/product-construction/applet.html b/src/main/webapp/product-construction/applet.html
new file mode 100644
index 0000000..85f2d3d
--- /dev/null
+++ b/src/main/webapp/product-construction/applet.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/product-construction/create.html b/src/main/webapp/product-construction/create.html
new file mode 100644
index 0000000..6375754
--- /dev/null
+++ b/src/main/webapp/product-construction/create.html
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
Create a new Product-construction problem
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Boolean Operation:
+
+
+
+ Short Description:
+
+
+
+ Long Description (will appear in the problem in the form of "Construct a DFA that recognizes the following language: {long description}"):
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/product-construction/edit.html b/src/main/webapp/product-construction/edit.html
new file mode 100644
index 0000000..9c9ee61
--- /dev/null
+++ b/src/main/webapp/product-construction/edit.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
Edit product-construction problem
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Boolean Operation:
+
+
+
+ Short Description:
+
+
+
+ Long Description (will appear in the problem in the form of "Construct a DFA that recognizes the following language: {long description}"):
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/product-construction/solve.html b/src/main/webapp/product-construction/solve.html
new file mode 100644
index 0000000..fa51025
--- /dev/null
+++ b/src/main/webapp/product-construction/solve.html
@@ -0,0 +1,30 @@
+
+
Solve product-construction problem
+
+ Combine the given automata via product construction
+ :
+ using the boolean operation
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/stylesheets/proto2.css b/src/main/webapp/stylesheets/proto2.css
index 0aefa4e..9b5c434 100644
--- a/src/main/webapp/stylesheets/proto2.css
+++ b/src/main/webapp/stylesheets/proto2.css
@@ -33,6 +33,20 @@ padding: 3px 6px !important;
overflow: auto;
}
+#automata_tutor #svgcanvasdfa1.svgcanvas {
+ background-color: #FFFFFF;
+ width: 365px;
+ height: 240px;
+ margin: 5px;
+}
+
+#automata_tutor #svgcanvasdfa2.svgcanvas {
+ background-color: #FFFFFF;
+ width: 365px;
+ height: 240px;
+ margin: 5px;
+}
+
#automata_tutor #svgcanvas {
background-color: #FFFFFF;
width: 740px;
@@ -47,6 +61,48 @@ padding: 3px 6px !important;
margin: 5px;
}
+#automata_tutor #svgcanvasdfa1 {
+ background-color: #FFFFFF;
+ width: 330px;
+ height: 240px;
+ margin: 5px;
+}
+
+#automata_tutor #svgcanvasdfa2 {
+ background-color: #FFFFFF;
+ width: 330px;
+ height: 240px;
+ margin: 5px;
+}
+
+#automata_tutor #svgcanvascreatedfa1 {
+ background-color: #FFFFFF;
+ width: 740px;
+ height: 480px;
+ margin: 5px;
+}
+
+#automata_tutor #svgcanvascreatedfa2 {
+ background-color: #FFFFFF;
+ width: 740px;
+ height: 480px;
+ margin: 5px;
+}
+
+#automata_tutor #svgcanvasdfain {
+ background-color: #FFFFFF;
+ width: 740px;
+ height: 480px;
+ margin: 5px;
+}
+
+#automata_tutor #svgcanvasdfasol {
+ background-color: #FFFFFF;
+ width: 740px;
+ height: 480px;
+ margin: 5px;
+}
+
#automata_tutor #svgcanvasnfa {
background-color: #FFFFFF;
width: 740px;
diff --git a/src/main/webapp/words-in-grammar-problem/create.html b/src/main/webapp/words-in-grammar-problem/create.html
new file mode 100644
index 0000000..32991d2
--- /dev/null
+++ b/src/main/webapp/words-in-grammar-problem/create.html
@@ -0,0 +1,47 @@
+
+
Create a new Words in Grammar problem
+
+
Grammar Syntax
+
+ e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
+ 1 line per production
+ split consecutive variables with a whitespace (e.g. S -> A B C)
+ first variable is start variable
+ possible terminals: abcdefghijklmnopqrstuvwxyz()[]{}
+ possible variables: 1 or more upper case characters A-Z or numbers 0-9 (!can't start with number!)
+ empty word: a blank right side (e.g. "S -> " or "S -> a | ")
+
+
+
Problem Definition
+
+
+
+
+ Number of words IN grammar:
+
+
+
+ Number of words NOT IN grammar:
+
+
+
+ Grammar:
+
+
+
+ Short Description:
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/words-in-grammar-problem/edit.html b/src/main/webapp/words-in-grammar-problem/edit.html
new file mode 100644
index 0000000..a1d2ac0
--- /dev/null
+++ b/src/main/webapp/words-in-grammar-problem/edit.html
@@ -0,0 +1,47 @@
+
+
Edit a Words in Grammar problem
+
+
Grammar Syntax
+
+ e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
+ 1 line per production
+ split consecutive variables with a whitespace (e.g. S -> A B C)
+ first variable is start variable
+ possible terminals: abcdefghijklmnopqrstuvwxyz()[]{}
+ possible variables: 1 or more upper case characters A-Z or numbers 0-9 (!can't start with number!)
+ empty word: a blank right side (e.g. "S -> " or "S -> a | ")
+
+
+
Problem Definition
+
+
+
+
+ Number of words IN grammar:
+
+
+
+ Number of words NOT IN grammar:
+
+
+
+ Grammar:
+
+
+
+ Short Description:
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/webapp/words-in-grammar-problem/solve.html b/src/main/webapp/words-in-grammar-problem/solve.html
new file mode 100644
index 0000000..5495767
--- /dev/null
+++ b/src/main/webapp/words-in-grammar-problem/solve.html
@@ -0,0 +1,42 @@
+
+
+
+
Solve Words in Grammar problem
+
+ Grammar Syntax
+
+ e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
+ 1 line per production
+ split consecutive variables with a whitespace (e.g. S -> A B C)
+ first variable is start variable
+ possible terminals: abcdefghijklmnopqrstuvwxyz()[]{}
+ possible variables: 1 or more upper case characters A-Z or numbers 0-9 (!can't start with number!)
+ empty word: a blank right side (e.g. "S -> " or "S -> a | ")
+
+
+ Problem
+
+ For the grammar with the productions:
+
+
+ Give that the grammar recognizes and that the grammar doesn't recognize!
+
+
+
+
+
+
+
+