-
Notifications
You must be signed in to change notification settings - Fork 0
/
Scalog.scala
executable file
·173 lines (138 loc) · 6.91 KB
/
Scalog.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
package scalog
import scala.util.parsing.combinator._
import scala.util.parsing.input.Reader
import java.io.{FileReader, FileWriter}
/** Scalog preparser parses prolog language blocks into valid scala source code (with use of the tuProlog API)
@author Florian Dobener
@version 0.1*/
class ScalogPreParser extends JavaTokenParsers{
/** Predefined Scala source code (this is added on top of every file) */
val scalaPredef = """import alice.tuprolog._
import scala.util.parsing.combinator._
import scalog.Predef._
import scalog._
"""
/** Predefined Prolog source code (added above prolog definitions). Here are only things that are absolutly necessary to be written
into the source code. Everything else is stored in the scalog.Predef object which should be automatically imported in every file*/
val prologPredef = "val engine = new Prolog\n\n"
val scalaExpr = """[^%]*""".r
val prologBody = """[^}]*""".r
//Testing for extension of the parser to have more than one prolog block.
//Problem: Parsers does an infinit loop when using this
/*def scalaFile:Parser[String] = (opt(scalaExpr) ^^ parseOpt(x=>x)) ~ rep(prologDef | scalaExpr) ^^ {
case x ~ list => list.foldLeft(x){(conc,a) => conc + a}
}*/
def scalaFile:Parser[String] = (opt(scalaExpr) ^^ parseOpt(x=>x)) ~ (opt(prologDef) ^^ parseOpt(x=>x)) ~ (opt(scalaExpr) ^^ parseOpt(x=>x)) ^^ {
case s1 ~ p1 ~ s2 => scalaPredef + s1 + "\n\n//Scalog Definition Part\n" + p1 + "//End of Scalog Definition Part\n" + s2
}
def prologDef:Parser[String] = "%prolog" ~> prologFunDef ~ ("{" ~> prologBody <~ "}") ^^ {
case f ~ b => prologPredef + "engine.setTheory(new Theory(\"\"\"" + b + "\"\"\"))" + "\n\n" + f
}
def prologFunDef:Parser[String] = "[" ~> repsep(func,",") <~ "]" ^^ ( _.reduceLeft{(conc,x) => conc + "\n" + x })
def func:Parser[String] = (ident ~ (opt("[" ~> "[A-Z]\\w*".r <~ "]") ^^ parseOpt(x => "[" + x + "]")) ~
( "(" ~> repsep(funArg,",") <~ ")" | "" ^^ (x => List[(String,String)]())) <~ ":") ~
((varRetTyp <~ "=>") ^^ (x => List[String](x)) | (("(" ~> repsep(varRetTyp,",") <~ ")") <~ "=>")) ~
ident ~ ("(" ~> repsep(ident,",") <~ ")") ^^ funcTrafo
/*def func:Parser[String] = (((scalaFunc ~ scalaArgs <~ ":") ~ scalaRetArgs <~ "=>") ~ prologFunc) ^^ funcTrafo
def scalaFunc:Parser[String~String] = ident ~ (opt("[" ~> "[A-Z]\\w*".r <~ "]") ^^ parseOpt(x => "[" + x + "]"))
def scalaArgs:Parser[List[(String,String)]] = "(" ~> repsep(funArg,",") <~ ")" |
"" ^^ (x => List[(String,String)]())
def scalaRetArgs:Parser[List[String]] = varRetTyp ^^ (x => List[String](x)) |
"(" ~> repsep(varRetTyp,",") <~ ")"
def prologFunc:Parser[String~List[String]] = ident ~ ("(" ~> repsep(ident,",") <~ ")")*/
def funArg:Parser[(String,String)] = (ident <~ ":") ~ varTyp ^^ { case n ~ t => (n,t) }
def varTyp:Parser[String] = "Option" ~ "[" ~> varTyp <~ "]" |
("PrologList" | "List") ~ "[" ~> varTyp <~ "]" ^^ ( "PrologList["+_+"]" ) |
litValue
def varRetTyp:Parser[String] = "Option" ~ "[" ~> varRetTyp <~ "]" |
"List" ~ "[" ~> varRetTyp <~ "]" ^^ ( "List["+_+"]" ) |
litValue
def litValue:Parser[String] = "Boolean" |
"String" |
"Int" ^^ (x => "scala.Int") |
"Double" ^^ (x => "scala.Double") |
"Float" ^^ (x => "scala.Float") |
"Long" ^^ (x => "scala.Long") |
"[A-Z]\\w*".r
/** Converts a string option into a string. It also applies the function env on the value of a some case. */
def parseOpt(env:String => String)(o:Option[String]):String = o match {
case Some(x) => env(x)
case None => ""
}
/** Converts a list of string tupels to a scala variable declaration (e.g. ("name","Int") converts to name:Int).
@param args List of arguments
@return Scala variables*/
private def argConc(args:List[(String,String)]):String = args match{
case Nil => ""
case _ => args map(x => x._1 + ":" + x._2) reduceLeft { (conc, x) =>
conc + ", " + x
}
}
/** Concatenates arguments (with seperator , ). It applies the environment function f to enclose every argument.
@param args List of arguments
@param f Encloses every argument
@return Concatenated arguments*/
private def argConc[T](args:List[T], f:T => String):String = args match{
case Nil => ""
case _ => args map(f) reduceLeft { (conc, x) =>
conc + ", " + x
}
}
/** Builts the arguments for the prolog function call
@param pArgs List of prolog arguments (which gives us an expression back)
@param sArgs List of scala arguments (which are defined in the scala function)
@return Concatenation of these parameters*/
private def concPrologArgs[T](pArgs:List[T], sArgs:List[(T,T)], sNum:Int):String = pArgs match {
case x :: Nil => if(sArgs.exists(y => y._1 == x)){
if(sNum != 0) throw new ParamException
"\" + " + x + " + \""
}else x.toString
case x :: xs => if(sArgs.exists(y => y._1 == x))
"\" + " + x + " + \", " + concPrologArgs(xs,sArgs,sNum - 1)
else
x.toString + ", " + concPrologArgs(xs,sArgs,sNum - 1)
case Nil => ""
}
/** Builts the scala function which sends the tuProlog call
@param in Parser output
@return The scala function */
def funcTrafo(in: String~String~List[(String,String)]~List[String]~String~List[String]):String = in match{
case name ~ gen ~ sArgs ~ sRetArgs ~ pName ~ pArgs =>
println("Parsed " + name + " => " + pName)
val pRetArgs = pArgs.filter(x => !sArgs.exists(y => y._1 == x))
val pArg = pArgs.filter(x => sArgs.exists(y => y._1 == x))
var res = "def " + name + gen + "(" + argConc(sArgs) + "):("+ argConc(sRetArgs, "Option["+ (_:String) +"]") + ") = { \n"
res += "\t" + """val result = engine.solve("""" + pName + """(""" + concPrologArgs(pArgs,sArgs,sArgs.length) + """).")""" + "\n\n"
if(sRetArgs.length != pRetArgs.length) throw new ParamException
var rest = sRetArgs
var j = 0
pRetArgs foreach {i =>
res += "\t var r" + j + ":Option[" + rest.head + "] = None \n"
res += "\t if(result.isSuccess) r" + j + " = Some("
res += "result.getVarValue(\"" + i + "\").toString)\n"
rest = rest.tail
j += 1
}
res += "\n\n\t(" + argConc(0 until sRetArgs.length toList, "r" + (_:Int) ) + ") \n"
res += "}"
res
}
}
/** This object does the external file handling */
object Scalog extends ScalogPreParser{
/** The main method takes two arguments which are representing the files to read from and to write in. */
def main(args:Array[String]){
if(args.length == 2){
val file = new FileReader(args(0))
parseAll(scalaFile,file) match {
case Success(parsedOutput, s) => if(s.atEnd){
val newFile = new FileWriter(args(1), false)
newFile.write(parsedOutput)
newFile.close
}else throw new ParseException("expected end of input", s)
case NoSuccess(msg,x) => throw new ParseException(msg, x)
}
file.close
}else println("Not enough arguments. Try: scalog <SourceFile> <DestFile>")
}
}