summaryrefslogtreecommitdiff
path: root/src/scalap/scala/tools/scalap/scalax/rules/Rules.scala
blob: dd17c46f79fbfa0cf1b5e71b4b8b1d4992f2f6ae (plain) (blame)
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
// -----------------------------------------------------------------------------
//
//  Scalax - The Scala Community Library
//  Copyright (c) 2005-8 The Scalax Project. All rights reserved.
//
//  The primary distribution site is http://scalax.scalaforge.org/
//
//  This software is released under the terms of the Revised BSD License.
//  There is NO WARRANTY.  See the file LICENSE for the full text.
//
// -----------------------------------------------------------------------------

package scala.tools.scalap
package scalax
package rules

import language.postfixOps

trait Name {
  def name: String
  override def toString = name
}

/** A factory for rules.
  *
  * @author Andrew Foggin
  *
  * Inspired by the Scala parser combinator.
  */
trait Rules {

  import scala.language.implicitConversions
  implicit def rule[In, Out, A, X](f: In => Result[Out, A, X]): Rule[In, Out, A, X] = new DefaultRule(f)
  implicit def inRule[In, Out, A, X](rule: Rule[In, Out, A, X]): InRule[In, Out, A, X] = new InRule(rule)
  implicit def seqRule[In, A, X](rule: Rule[In, In, A, X]): SeqRule[In, A, X] = new SeqRule(rule)

  trait FromRule[In] {
    def apply[Out, A, X](f: In => Result[Out, A, X]): Rule[In, Out, A, X]
  }

  def from[In] = new FromRule[In] {
    def apply[Out, A, X](f: In => Result[Out, A, X]) = rule(f)
  }

  def state[s] = new StateRules {
    type S = s
    val factory = Rules.this
  }

  def success[Out, A](out: Out, a: A) = rule { in: Any => Success(out, a) }

  def failure = rule { in: Any => Failure }

  def error[In] = rule { in: In => Error(in) }
  def error[X](err: X) = rule { in: Any => Error(err) }

  def oneOf[In, Out, A, X](rules: Rule[In, Out, A, X] *): Rule[In, Out, A, X] = new Choice[In, Out, A, X] {
    val factory = Rules.this
    val choices = rules.toList
  }

  def ruleWithName[In, Out, A, X](_name: String, f: In => Result[Out, A, X]): Rule[In, Out, A, X] with Name =
    new DefaultRule(f) with Name {
      val name = _name
    }

  class DefaultRule[In, Out, A, X](f: In => Result[Out, A, X]) extends Rule[In, Out, A, X] {
    val factory = Rules.this
    def apply(in: In) = f(in)
  }

 /** Converts a rule into a function that throws an Exception on failure. */
  def expect[In, Out, A, Any](rule: Rule[In, Out, A, Any]): In => A = (in) => rule(in) match {
    case Success(_, a) => a
    case Failure => throw new ScalaSigParserError("Unexpected failure")
    case Error(x) => throw new ScalaSigParserError("Unexpected error: " + x)
  }
}

/** A factory for rules that apply to a particular context.
  *
  * @requires S the context to which rules apply.
  *
  * @author Andrew Foggin
  *
  * Inspired by the Scala parser combinator.
  */
trait StateRules {
  type S
  type Rule[+A, +X] = rules.Rule[S, S, A, X]

  val factory: Rules
  import factory._

  def apply[A, X](f: S => Result[S, A, X]) = rule(f)

  def unit[A](a: => A) = apply { s => Success(s, a) }
  def read[A](f: S => A) = apply { s => Success(s, f(s)) }

  def get = apply { s => Success(s, s) }
  def set(s: => S) = apply { oldS => Success(s, oldS) }

  def update(f: S => S) = apply { s => Success(s, f(s)) }

  def nil = unit(Nil)
  def none = unit(None)

  /** Create a rule that identities if f(in) is true. */
  def cond(f: S => Boolean) = get filter f

  /** Create a rule that succeeds if all of the given rules succeed.
      @param rules the rules to apply in sequence.
  */
  def allOf[A, X](rules: Seq[Rule[A, X]]) = {
    def rep(in: S, rules: List[Rule[A, X]], results: List[A]): Result[S, List[A], X] = {
      rules match {
        case Nil => Success(in, results.reverse)
        case rule::tl => rule(in) match {
          case Failure => Failure
          case Error(x) => Error(x)
          case Success(out, v) => rep(out, tl, v::results)
        }
      }
    }
    in: S => rep(in, rules.toList, Nil)
  }


  /** Create a rule that succeeds with a list of all the provided rules that succeed.
      @param rules the rules to apply in sequence.
  */
  def anyOf[A, X](rules: Seq[Rule[A, X]]) = allOf(rules.map(_ ?)) ^^ { opts => opts.flatMap(x => x) }

  /** Repeatedly apply a rule from initial value until finished condition is met. */
  def repeatUntil[T, X](rule: Rule[T => T, X])(finished: T => Boolean)(initial: T) = apply {
    // more compact using HoF but written this way so it's tail-recursive
    def rep(in: S, t: T): Result[S, T, X] = {
      if (finished(t)) Success(in, t)
      else rule(in) match {
        case Success(out, f) => rep(out, f(t)) // SI-5189 f.asInstanceOf[T => T]
        case Failure => Failure
        case Error(x) => Error(x)
      }
    }
    in => rep(in, initial)
  }


}

trait RulesWithState extends Rules with StateRules {
  val factory = this
}