LightningIsMyName

[Workshop] Parser.Scala

Apr 23rd, 2011
197
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Scala 10.11 KB | None | 0 0
  1. import scala.util.parsing.combinator._;
  2. import TextStyle._, HAlign._, VAlign._
  3.  
  4. object ConstMatcher {
  5.   def CtoInt (s:String) = s.toInt
  6.   def CtoBoolean (s:String) = s.toBoolean
  7.   def CtoString (s:String) = s.substring(1,s.length-1)
  8.   def CtoColor (s:String) = new Color(s)
  9.   def CtoId (s:String) = s
  10.   def CtoTextStyle (s:String) = TextStyle.withName(s)
  11.   def CtoHAlign (s:String) = HAlign.withName(s)
  12.   def CtoVAlign (s:String) = VAlign.withName(s)
  13. }
  14.  
  15. import ConstMatcher._
  16.  
  17. object LayoutParser extends JavaTokenParsers {
  18.  
  19.   val reservedwords = List("true","false","otherwise")
  20.   val reservedwordsregex = reservedwords.mkString("|")
  21.  
  22.   // Helper Function
  23.   def customFilter[T](pre:String, elems:List[~[String,T]]):List[T] = {
  24.     var a:List[T] = Nil
  25.     for (elem <- elems)
  26.       if (elem._1.equals(pre)) a = elem._2 :: a
  27.     return a
  28.   }
  29.  
  30.   // Identifier for variable and properties of variables
  31.   def Var: Parser[DirectExpr] = {
  32.     // In order to avoid identifying reserved words as variables, use the
  33.     // guard parser which does not consume input
  34.     not(guard(reservedwordsregex.r))~>ident~(("." ~> ident)?) ^^ {
  35.       case loc~Some(prop) => new PropVar(loc,prop)
  36.       case id~None => new Var(id)
  37.     }
  38.   }
  39.  
  40.   // ***************************************************************************
  41.   // Numerical Expressions
  42.   // ***************************************************************************
  43.   def NumExpr: Parser[DirectExpr] = Sum
  44.  
  45.   def Sum: Parser[DirectExpr] = {
  46.     (Prod ~ rep("+" ~ Prod | "-" ~ Prod)) ^^ {
  47.       case a ~ Nil => a
  48.       case a ~ b => new Sum(a::customFilter("+", b),customFilter("-", b))
  49.     }
  50.   }
  51.  
  52.   def Prod: Parser[DirectExpr] = {
  53.     (NumElem ~ rep("*" ~ NumElem | "/" ~ NumElem)) ^^ {
  54.       case a ~ Nil => a
  55.       case a ~ b => new Prod(a::customFilter("*", b), customFilter("/", b))
  56.     }
  57.   }
  58.  
  59.   def NumConst: Parser[Literal[Int]] = {
  60.     floatingPointNumber ^^ { str => new Literal(CtoInt(str)) }
  61.   }
  62.  
  63.   def NumElem: Parser[DirectExpr] = {
  64.     NumConst | ("("~>NumExpr<~")") | Var
  65.   }
  66.  
  67.   // ***************************************************************************
  68.   // Boolean Expressions
  69.   // ***************************************************************************
  70.   // TODO: In our implementation we allow only to do comparision between numbers
  71.   //       and booleans, so to allow more comparisions change NonBoolComparable
  72.   //       to include other elements
  73.   // WARNING: Using things which may have '=' in them or be mapped back to a
  74.   //          boolean, or a list of other case will cause infinite recursion
  75.   //          when parsing (-> Stack Overflow) so generally don't touch unless
  76.   //          you know what you are doing
  77.   def NonBoolComparable = NumExpr
  78.  
  79.   def BoolExpr: Parser[DirectExpr] = Disnj
  80.  
  81.   def Disnj: Parser[DirectExpr] = {
  82.     (Conj ~ rep("||" ~ Conj)) ^^ {
  83.       case a ~ Nil => a
  84.       case a ~ b => new Disnj(a::customFilter("||", b))
  85.     }
  86.   }
  87.  
  88.   def Conj: Parser[DirectExpr] = {
  89.     (BoolComp ~ rep("&&" ~ BoolComp)) ^^ {
  90.       case a ~ Nil => a
  91.       case a ~ b => new Conj(a::customFilter("&&", b))
  92.     }
  93.   }
  94.  
  95.   // Remember: comparision has the highest precedence from all boolean operators
  96.   def BoolComp: Parser[DirectExpr] = {
  97.     (BoolElem ~ (("="~>BoolElem)?)) ^^ {
  98.       case a ~ None => a
  99.       case a ~ Some(b) => new Comp(a,b)
  100.     }
  101.   }
  102.  
  103.   def NonBoolComp: Parser[DirectExpr] = {
  104.     (NonBoolComparable~"="~NonBoolComparable) ^^ { case e1~"="~e2 => new Comp(e1,e2) }
  105.   }
  106.  
  107.   def BoolConst: Parser[Literal[Boolean]] = {
  108.     ("true" | "false") ^^ { str => new Literal(CtoBoolean(str))}
  109.   }
  110.  
  111.   def BoolElem: Parser[DirectExpr] = {
  112.     BoolConst | ("("~>BoolExpr<~")") | Var ||| NonBoolComp
  113.   }
  114.  
  115.  
  116.   // ***************************************************************************
  117.   // String Expressions
  118.   // ***************************************************************************
  119.   def StringConst: Parser[Literal[String]] = {
  120.     stringLiteral ^^ { str => new Literal(CtoString(str)) }
  121.   }
  122.  
  123.   def StringElem: Parser[DirectExpr] = {
  124.     StringConst | "("~>StringExpr<~")" | Var
  125.   }
  126.  
  127.   def StringExpr: Parser[DirectExpr] = {
  128.     StringElem
  129.   }
  130.  
  131.   // ***************************************************************************
  132.   // Color Expressions
  133.   // ***************************************************************************
  134.   def ColorConst: Parser[Literal[Color]] = {
  135.     "0x[0-9A-Fa-f]{6}".r ^^ { str => new Literal(CtoColor(str)) }
  136.   }
  137.  
  138.   def ColorElem: Parser[DirectExpr] = {
  139.     ColorConst | "("~>ColorExpr<~")" | Var
  140.   }
  141.  
  142.   def ColorExpr: Parser[DirectExpr] = {
  143.     ColorElem
  144.   }
  145.  
  146.  
  147.   // ***************************************************************************
  148.   // Font Expressions
  149.   // ***************************************************************************
  150.   def FontConst: Parser[Literal[Font]] = {
  151.     ("("~StringConst~","~NumConst~","~("bold" | "italic" | "regular")~")") ^^ {
  152.       case "("~face~","~num~","~style~")" => new Literal(new Font(face.value,num.value,CtoTextStyle(style)))
  153.     }
  154.   }
  155.  
  156.   def FontElem: Parser[DirectExpr] = {
  157.     FontConst | "("~>FontExpr<~")" | Var
  158.   }
  159.  
  160.   def FontExpr: Parser[DirectExpr] = {
  161.     FontElem
  162.   }
  163.  
  164.  
  165.   // ***************************************************************************
  166.   // HAlign Expressions
  167.   // ***************************************************************************
  168.   def HAlignConst: Parser[Literal[HAlign]] = {
  169.     ("left" | "center" | "right") ^^ { str => new Literal(CtoHAlign(str))}
  170.   }
  171.  
  172.   def HAlignElem: Parser[DirectExpr] = {
  173.     HAlignConst | "("~>HAlignExpr<~")" | Var
  174.   }
  175.  
  176.   def HAlignExpr: Parser[DirectExpr] = {
  177.     HAlignElem
  178.   }
  179.  
  180.   // ***************************************************************************
  181.   // VAlign Expressions
  182.   // ***************************************************************************
  183.   def VAlignConst: Parser[Literal[VAlign]] = {
  184.     ("top" | "middle" | "bottom") ^^ { str => new Literal(CtoVAlign(str))}
  185.   }
  186.  
  187.   def VAlignElem: Parser[DirectExpr] = {
  188.     VAlignConst | "("~>VAlignExpr<~")" | Var
  189.   }
  190.  
  191.   def VAlignExpr: Parser[DirectExpr] = {
  192.     VAlignElem
  193.   }
  194.  
  195.   // ***************************************************************************
  196.   // Conditional Expressions
  197.   // ***************************************************************************
  198.  
  199.   // TODO: If you want to allow nested conditions (yuck!)
  200.   //       replace DirectExpr by Expr here
  201.   def CondBody = DirectExpr
  202.  
  203.   def SimpleCond: Parser[(Expr,Expr)] = {
  204.     (BoolExpr~"=>"~CondBody) ^^ {case bool~"=>"~expr => (bool,expr)}
  205.   }
  206.  
  207.   def OtherwiseCond: Parser[Expr] = {
  208.     "otherwise"~>":"~>CondBody
  209.   }
  210.  
  211.   def CondExpr: Parser[Expr] = {
  212.     ("{"~>rep1(SimpleCond<~",")~OtherwiseCond<~"}") ^^ { case conds~o => new Cond(conds,o) }
  213.   }
  214.  
  215.   // ***************************************************************************
  216.   // All available Expressions
  217.   // ***************************************************************************
  218.   def DirectExpr = (NumExpr ||| BoolExpr) | StringExpr | ColorExpr | FontExpr | HAlignExpr | VAlignExpr
  219.   def Expr = DirectExpr | CondExpr
  220.  
  221.  
  222.   // ***************************************************************************
  223.   // Widget Layout Expressions
  224.   // Note that in order to wrap a widget in a "panel" for specifying size limits
  225.   // or properties, the existing widget must again be wrapped in braces!
  226.   // ***************************************************************************
  227.   def Layout = Hgroup
  228.  
  229.   def Vgroup: Parser[Widget] = {
  230.     (Hgroup ~ rep("---" ~> Hgroup)) ^^ {
  231.       case a ~ Nil => a
  232.       case a ~ b => new HVContainer(a::b, true)
  233.     }
  234.   }
  235.  
  236.   def Hgroup: Parser[Widget] = {
  237.     (Widget ~ rep("|" ~> Widget)) ^^ {
  238.       case a ~ Nil => a
  239.       case a ~ b => new HVContainer(a::b, false)
  240.     }
  241.   }
  242.  
  243.   def Widget: Parser[Widget] = {
  244.     (AtomicWidget
  245.      | (("("~Layout~")"~SizeOpt~PropListOpt) ^^ {
  246.           case "("~w~")"~size~props =>
  247.             // If we had an empty wrapping, or if was just the braces, in both
  248.             // cases the if will be true and we can ommit the wrapping as junk
  249.             if (props.isEmpty && size == (None,None)) w
  250.             else new OneContainer(w,size._1,size._2,props)
  251.         })
  252.     )
  253.   }
  254.  
  255.   def Dim: Parser[Option[DirectExpr]] = {
  256.     ((floatingPointNumber ^^ { str => Some(new Literal(CtoInt(str)))})
  257.      | (("("~>NumExpr<~")") ^^ { ne => Some(ne) })
  258.      | ("?" ^^ { s => None})
  259.     )
  260.   }
  261.  
  262.   def Size: Parser[(Option[DirectExpr],Option[DirectExpr])] = {
  263.     (":" ~ Dim ~ "x" ~ Dim) ^^ { case ":" ~ w ~ "x" ~ h => (w,h) }
  264.   }
  265.   def SizeOpt: Parser[(Option[DirectExpr],Option[DirectExpr])] = {
  266.     ((Size?) ^^ { case None => (None,None); case Some(size) => size})
  267.   }
  268.  
  269.  
  270.   def Prop: Parser[(String,Expr)] = {
  271.     (ident~"="~Expr) ^^ { case id~"="~expr => (id,expr) }
  272.   }
  273.  
  274.   def PropList: Parser[Map[String,Expr]] = {
  275.     ("["~>rep1sep(Prop,",")<~"]") ^^ { props => Map(props :_*)}
  276.   }
  277.   def PropListOpt: Parser[Map[String,Expr]] = {
  278.     ((PropList?) ^^ { case None => Map[String,Expr](); case Some(props) => props })
  279.   }
  280.  
  281.   def AtomicWidget: Parser[Widget] = {
  282.     ("("~((ident?) ^^ { case None => ""; case Some(str) => str})
  283.         ~SizeOpt~")"~PropListOpt) ^^ {
  284.          case "("~id~size~")"~props => new AtomicWidget(id,size._1,size._2,props)
  285.        }
  286.   }
  287.  
  288.   // ***************************************************************************
  289.   // Layout Definitions - Program Expressions
  290.   // ***************************************************************************
  291.   def Layoutdef: Parser[(String,Widget)] = {
  292.     (ident~"<-"~Layout) ^^ { case id~"<-"~w => (id,w) }
  293.   }
  294.   def Program = Layoutdef*
  295.  
  296.   def iParse(input:String) = parseAll(Program,input)
  297.  
  298.   def main(args:Array[String]) {
  299.     var s = new java.util.Scanner(System.in)
  300.     while (true) {
  301.       print ("Enter Input: ")
  302.       val line = s.nextLine();
  303.       if (line.equals("quit")) return
  304.       iParse(line) match {
  305.         case Success(result, _) => println ("Result: "); result map println;
  306.         case _ => println("Could not parse the input string.")
  307.  
  308.       }
  309.     }
  310.   }
  311. }
Advertisement
Add Comment
Please, Sign In to add comment