All Downloads are FREE. Search and download functionalities are using the official Maven repository.

xgi.ut.dsl.ObjectParsers.scala Maven / Gradle / Ivy

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