xgi.ut.dsl.ObjectParsers.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of UT_DSL_PARSING Show documentation
Show all versions of UT_DSL_PARSING Show documentation
This module contains the scala parsers that parses + interpret the dsl
The newest version!
/*
* ScenarLang a domain specific language that attempts to make it even easier
* to write unit tests with mockito
*
* Copyright (C) 2012 X. Gillard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package xgi.ut.dsl
/**
* This contains the grammar of an internal DSL that looks like json
* but lets us define java object in a simple (short) way.
*
* I parses all what is related to the object definition (variable + reference + special objects)
*/
trait ObjectParsers extends CommentsSkippingParser {
import ObjectParsers._
/* parses a type definition clause (where we determine what the type of an object should be) */
def sdeclaredType:Parser[SDeclaredType] = stringLiteral ^^ { x => SDeclaredType(unquote(x))}
/* parse a cast */
def scast:Parser[SCast] = "("~sdeclaredType~")"~svalue ^^ {case "("~dclType~")"~value => SCast(dclType, value)}
/* parses a simple/basic type */
def sbasic:Parser[SLiteral] = ( stringLiteral~opt("as"~>sdeclaredType) ^^ {x => SBasicType(unquote(x._1), x._2)}
| floatingPointNumber~opt("as"~>sdeclaredType) ^^ { x => SBasicType(str2Number(x._1), x._2)}
| "true|false".r ^^ {x=>SBasicType(x.toBoolean)}
| "null".r ^^ { _ => SNullObject}
)
/* parses a array */
def sarray:Parser[SArray] = "["~repsep(svalue, ",")~"]"~opt("as"~>sdeclaredType) ^^ {
case "["~items~"]"~optionalType => SArray(items, optionalType)
}
/* parses a map entry */
def smap:Parser[SMap] = "["~>repsep(sassoc, ",")<~"]" ^^ {SMap}
/* an association (map entry) */
def sassoc:Parser[SAssociation] = svalue~"=>"~svalue ^^ {
case key~"=>"~value =>SAssociation(key, value)
}
/* parses a structure */
def sstruct:Parser[SStructure] = opt(sconstructor)~"{"~repsep(sprop, ",")~"}"~opt("as"~>sdeclaredType) ^^ {
case optionalConstr~"{"~members~"}"~optionalType => SStructure(members, optionalType, optionalConstr)
}
/* parses a property */
def sprop:Parser[SProperty] = ident~":"~svalue ^^ { case k~":"~v => SProperty(k, v)}
/* parse a constructore with potentially more than 0 args */
def sconstructor:Parser[SConstructor] = "new"~"("~>repsep(svalue, ",")<~")"~"->" ^^ {SConstructor}
/* mocks an object */
def mockObject:Parser[MockObject]= "mock"~>sdeclaredType ^^ MockObject
/* parses a resource (aka ref to a file) */
def resource:Parser[Resource] = "resource"~>stringLiteral ^^ {x => Resource(unquote(x))}
/* parses a jaxb object defined elsewhere */
def jaxbObject:Parser[JaxbObject]= "jaxb"~"from"~resource~"as"~sdeclaredType ^^ {
case "jaxb"~"from"~res~"as"~typeName => JaxbObject(res, typeName)
}
/* parses a wildcard object ==> a matcher for anything */
def wildcard:Parser[WildCardObject]="?"~opt(opt("as")~>sdeclaredType) ^^ {case "?"~typename => WildCardObject(typename)}
def eqMatcher:Parser[EqMatch] = "eq"~>svalue ^^ {EqMatch}
def deepEqMatcher:Parser[DeepEqMatch] = "deep"~"eq"~>svalue~opt("excluding"~"["~>repsep(stringLiteral, ",")<~"]") ^^ { _ match {
case value~None => DeepEqMatch(value, Seq.empty)
case value~Some(excl) => DeepEqMatch(value, excl map {unquote(_)})
}
}
def sameMatcher:Parser[SameMatch] = "same"~>svalue ^^ {SameMatch}
def containsMatcher:Parser[ContainsMatch] = "contains"~>stringLiteral ^^ {x=>ContainsMatch(unquote(x))}
def matchesMatcher:Parser[MatchesMatch] = "matches"~>stringLiteral ^^ {x=>MatchesMatch(unquote(x))}
def endsWithMatcher:Parser[EndsWithMatch]= "ends"~"with"~>stringLiteral ^^ {x=>EndsWithMatch(unquote(x))}
def startsWithMatcher:Parser[StartsWithMatch]= "starts"~"with"~>stringLiteral ^^ {x=>StartsWithMatch(unquote(x))}
def matcher:Parser[SMatcher] = ( wildcard
| eqMatcher
| deepEqMatcher
| sameMatcher
| containsMatcher
| matchesMatcher
| endsWithMatcher
| startsWithMatcher
)
/* parses a method call */
def memberRef:Parser[MemberRef] = ( sstaticRef | fieldRef | internalIdentifier)~"."~repsep(reference, ".") ^^ {
case source~"."~invocations => MemberRef(source, invocations)
}
/* parses an internal identifier (aka a variable that does not exists outside the scenario) */
def internalIdentifier:Parser[InternalIdentifier]="$"~>ident ^^ InternalIdentifier
/* parses a field */
def fieldRef:Parser[FieldRef]= ident ^^ FieldRef
/* parses the invocation bit of a method call */
def fnInvocation:Parser[FnInvocation]=ident~"("~repsep(svalue, ",")~")" ^^ {
case fname~"("~args~")" => FnInvocation(fname, args)
}
/* parses a static method invocation */
def sstaticRef:Parser[SStaticRef] = "static"~sdeclaredType~"->"~(fnInvocation | fieldRef) ^^ {
case "static"~declType~"->"~member => SStaticRef(declType, member)
}
/* parses a reference (not a direct svalue) */
def reference:Parser[Reference]= ( memberRef
| internalIdentifier
| fnInvocation
| sstaticRef
| fieldRef
)
/* parses a literal */
def svalue:Parser[SLiteral] = ( sbasic
| sarray
| smap
| sstruct
| mockObject
| resource
| jaxbObject
| matcher
| scast
| reference
)
/* translates a text into an instance of a literal */
def parseObject(text:String):SLiteral = parseAll(svalue, text) match {
case Success(x, _) => x
case x:NoSuccess => SSyntaxError(errorString(x))
}
/**
* transforms a string into a correct number
*/
private[this] def str2Number(text:String):Any = {
val WholeNumber = """(-?\d+)""".r
text match {
case WholeNumber(y) => str2WholeNumber(text)
case other => str2FloatNumber(text)
}
}
/**
* transforms a string into a correct whole number
*/
private[this] def str2WholeNumber(text:String):Any = {
val l = text.toLong
// match does not suit here
if(l>Byte.MinValue && lShort.MinValue && lInt.MinValue && lFloat.MinValue && d """"%s"""".format(x)
case _ => value
}
val dcl = declaredType match {
case Some(declType) => """ as "%s"""".format(declType.name)
case _ => ""
}
theVal+dcl
}
}
// represents a null value
case object SNullObject extends SLiteral {
override def toString = "null"
}
// a resource (file)
sealed case class Resource(location:String) extends SLiteral {
override def toString = """resource "%s"""".format(location)
}
// an array
sealed case class SArray(values:Seq[SLiteral], declaredType:Option[SDeclaredType] = None) extends SLiteral {
override def toString = {
"""[ %s ]""".format(values mkString ", ") + (declaredType match {
case Some(declType) => """ as "%s"""".format(declType.name)
case _ => ""
})
}
}
// to construct a map[object, object]
sealed case class SMap(associations:Seq[SAssociation]) extends SLiteral{
override def toString = "[ %s ]".format(associations.mkString(", "))
}
sealed case class SAssociation(key:SLiteral, value:SLiteral) {
override def toString = "%s => %s".format(key.toString, value.toString)
}
// a structure
sealed case class SStructure(members:Seq[SProperty], declaredType:Option[SDeclaredType] = None, constructor:Option[SConstructor]=None) extends SLiteral {
override def toString ={
val constr = constructor match {
case Some(const) => const.toString
case None => ""
}
val declType = (declaredType match {
case Some(dcl) => """ as "%s"""".format(dcl.name)
case _ => ""
})
"""%s{ %s }%s""".format(constr, members.mkString(", "), declType)
}
}
// a structure property
sealed case class SProperty(key:String, value:SLiteral) extends SLiteral {
override def toString = """%s : %s""".format(key, value.toString)
}
// a particular constructor
sealed case class SConstructor(params:Seq[SLiteral]){
override def toString = """new ( %s ) -> """.format(params.mkString(", "));
}
// a mock
sealed case class MockObject(declaredType:SDeclaredType) extends SLiteral {
override def toString = """mock "%s"""".format(declaredType.name)
}
// a jaxb object
sealed case class JaxbObject(resource:Resource, declaredType:SDeclaredType) extends SLiteral {
override def toString = """jaxb from %s as "%s"""".format(resource.toString, declaredType.name)
}
// a matcher for anything
sealed abstract case class SMatcher extends SLiteral
sealed case class WildCardObject(declaredType:Option[SDeclaredType]) extends SMatcher {
override def toString = prefix+typePart
def prefix = "?"
def typePart:String = declaredType match {
case Some(dcl) => """as "%s"""".format(dcl.name)
case _ => ""
}
}
sealed case class EqMatch(value:SLiteral) extends SMatcher {
override def toString = "eq %s".format(value.toString)
}
sealed case class DeepEqMatch(value:SLiteral, exclusions:Seq[String]) extends SMatcher {
override def toString = "deep eq %s %s".format(value.toString, excl)
def excl = if(!exclusions.isEmpty) "excluding [ %s ]".format(exclusions.mkString(", ")) else ""
}
sealed case class SameMatch(value:SLiteral) extends SMatcher {
override def toString = "same %s".format(value.toString)
}
sealed case class ContainsMatch(value:String) extends SMatcher {
override def toString = """contains "%s"""".format(value)
}
sealed case class MatchesMatch(value:String) extends SMatcher {
override def toString = """matches "%s"""".format(value)
}
sealed case class StartsWithMatch(value:String) extends SMatcher {
override def toString = """starts with "%s"""".format(value)
}
sealed case class EndsWithMatch(value:String) extends SMatcher {
override def toString = """ends with "%s"""".format(value)
}
// the root type for all kind of references
abstract sealed case class Reference extends SLiteral
// a script defined variable
sealed case class InternalIdentifier(name:String) extends Reference {
override def toString = """$%s""".format(name)
}
// a static ref
sealed case class SStaticRef(declaredType:SDeclaredType, member:Reference) extends Reference {
override def toString = """static "%s -> %s"""".format(declaredType.name, member.toString)
}
// a reference to a field (if no parent then parent is the context)
sealed case class FieldRef(name:String) extends Reference {
override def toString = """%s""".format(name)
}
// the fn invocation bit
sealed case class FnInvocation(fname:String, params:Seq[SLiteral]) extends Reference {
override def toString = """%s( %s )""".format(fname, params mkString ", ")
}
// a reference to a member (chain of fields & fncalls)
sealed case class MemberRef(source:Reference, invocations:Seq[Reference]) extends Reference {
override def toString = """%s.%s""".format(source.toString(), invocations mkString ".")
}
// Not an element from the grammar : nothing but a place holder for the error message
sealed case class SSyntaxError(message:String) extends SLiteral {
override def toString = message
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy