Как кодировать ограничение на формат значений String - PullRequest
0 голосов
/ 27 августа 2011

Как я часто наблюдаю и как часто реализую атрибут name, это просто смоделировать его как String.

Что теперь, если имя должно следовать определенному синтаксису, то есть формату?В Java я, вероятно, определил бы конструктор с проверкой его аргументов, что-то вроде:

public Name(str: String) {
    if (str == null) throw new IllegalArgumentException("Str must not be null.");
    if (!str.matches("name format expressed as regex")) throw new IllegalArgumentException("Str must match 'regex' but was " + str);
    this.str = str;
}

В Scala я нашел следующее решение:

import StdDef.Str
import StdDef.Bol
import StdDef.?

import scala.util.parsing.combinator.RegexParsers

final case class Name private (pfx: ?[Str] = None, sfx: Str) {

  override def toString = pfx.mkString + sfx

}

object Name extends RegexParsers {

  implicit def apply(str: Str): Name = parseAll(syntax, str) match {
    case Success(res, _) => Name(res._1, res._2)
    case rej: NoSuccess => error(rej.toString)
  }

  lazy val syntax = (prefix ?) ~! suffix

  lazy val prefix = (("x" | "X") ~! hyph) ^^ { case a ~ b => a + b }

  lazy val suffix = alpha ~! (alpha | digit | hyph *) ^^ { case a ~ b => a + b.mkString }

  lazy val alpha: Parser[Str] = """\p{Alpha}""".r

  lazy val digit: Parser[Str] = """\p{Digit}""".r

  lazy val hyph: Parser[Str] = "-"

  override lazy val skipWhitespace = false

}

Мои намерения здесь таковы::

  1. Составьте Name из его естественного представления, то есть String значение
  2. Проверьте, образует ли его естественное представление действительное значение Name во время построения.
  3. Запретить любую конструкцию, отличную от фабричного метода apply:(str:Str)Str.
  4. Сделать конструкцию из ее естественного представления неявной, например val a: Name = "ISBN 978-0-9815316-4-9".
  5. Разложить Name на частив соответствии с его синтаксическими элементами.
  6. Есть ошибки, выдаваемые с сообщениями, такими как:

===

--
^
[1.3] error: string matching regex `\p{Alpha}' expected but end of source found

Я хотел бызнаю, какие решения вы придумали.

После того, как я подумаю над этой темой, я в настоящее время придерживаюсь следующего подхода.

Token.scala:

abstract class Token {
  val value: Str
}
object Token {
  def apply[A <: Token](ctor: Str => A, syntax: Regex) = (value: Str) => value match {
    case syntax() => ctor(value)
    case _ => error("Value must match '" + syntax + "' but was '" + value + "'.")
  }
}

Tokens.scala:

final case class Group private (val value: Str) extends Token
final case class Name private (val value: Str) extends Token
trait Tokens {
  import foo.{ bar => outer }
  val Group = Token(outer.Group, """(?i)[a-z0-9-]++""".r)
  val Name = Token(outer.Name, """(?i)(?:x-)?+[a-z0-9-]++""".r)
}

1 Ответ

1 голос
/ 28 августа 2011

Учитывая, что вам удобно использовать регулярные выражения в Java, кажется излишним пытаться решить ту же проблему с парсером в Scala.

Придерживайтесь того, что вы знаете здесь, но добавьте поворот Scala, чтобы немного очистить решение. Регулярные выражения в Scala также определяют экстракторы, что позволяет использовать их в сопоставлении с шаблоном:

//triple-quote to make escaping easier, the .r makes it a regex
//Note how the value breaks normal naming conventions and starts in uppercase
//This is to avoid backticks when pattern matching

val TestRegex = """xxyyzz""".r

class Name(str: String) {
  str match {
    case Null => throw new IllegalArgumentException("Str must not be null")
    case TestRegex => //do nothing
    case _ => throw new IllegalArgumentException(
      "Str must match 'regex' but was " + str)
  }
}

отказ от ответственности: я не проверял этот код, он может содержать опечатки

...