ログ解析者がScalaをかじった結果。。。

Scalaは、いわゆる関数型、命令型どっちでもいける全方位型言語である。

一方で、全容を理解するのが難しい言語ではある。（奥行きがある）
しかし、ログ解析者からすると、パーサーの部分だけ理解できればよい、という面もある。

↓のようなINIファイルのパースのみに集中する。。。

<pre>
     6[dataStorage1]
     7indexer=Elasticsearch
     8url=localhost:9200
     9user=admin
    10password=qiita
    11
    12[dataStorage2]
    13driver=Splunk
    14url=local:8089
    15user=admin
    16password=qiita
</pre>

Scalaには、ケースクラスという非常に強力なスペックがある。
ケースクラスとはなにか、とは難しいだが、ログ解析者からすればパターンマッチに派生強化したクラスだと理解する。

Scalaは、ケースクラスによるパターンマッチが非常に強力な上にパーサーコンビネータという機能があり、これを組み合わせるとAST（抽象構文木）を駆使するのが楽にできる。

ASTの何がいいかというと、ログ解析者的には、JSON,mapなどに変換できるところである。

![scala-ast.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163445/b036912d-83dd-a22c-05f9-c768a0d13d83.png)

↑に対応するコードをcase classを用いて書く。。

    60// String
    61case class String_of_AST(string: String) extends AST
    62// Property
    63case class Property_of_AST(key: String_of_AST, value: String_of_AST) extends AST
    64// Section
    65case class Section_of_AST(name: String_of_AST, properties: List[Property_of_AST]) extends AST
    66// Section(s)
    67case class Sections_of_AST(sections: List[Section_of_AST]) extends AST

![scala-syntax.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163445/413362c7-14f3-6831-b583-e9e7c835a52a.png)

↑に対応するコードをパーサーコンビネータで書く。。

    35class IniParser extends RegexParsers {
    36
    37  // String
    38  def string :Parser[String_of_AST] = """[^\[\]=\s]*""".r^^{
    39    case value => String_of_AST(value)
    40  }
    41    42  // Property
    43  def property :Parser[Property_of_AST] = string~"="~string^^{
    44    case (key~_~value) => Property_of_AST(key, value)
    45  }
    46
    47  // Section
    48  def section :Parser[Section_of_AST] = "["~>string~"]"~rep(property)^^{
    49    case (section~_~properties) => Section_of_AST(section, properties)
    50  }
    51
    52  // Section(s)
    53  def sections :Parser[Sections_of_AST] = rep(section)^^{
    54    case sections => Sections_of_AST(sections)
    55  }

パーサーの最終的な目的はなにか？

ログ解析者的には、
意味(Semantics)が同じものを異なる形式(Syntax)にいろいろ変換するためである。

コードを見てみる。。。

     1object ParserSample extends App {
     2
     3  val parser = new IniParser
     4
     5  val result = parser.parseAll(parser.sections, """
     6[db1]
     7indexer=Elasticsearch
     8url=localhost:9200
     9user=admin
    10password=qiita
    11
    12[db2]
    13driver=Splunk
    14url=local:8089
    15user=admin
    16password=qiita
    17""")
    18
    19  val sections = result.get
    20
    21  println(sections)
    22  println("")
    23
    24  val map = sections.sections.map { section =>
    25    (section.name.string -> section.properties.map { property =>
    26      (property.key.string -> property.value.string)
    27    }.toMap)
    28  }.toMap
    29
    30  println(map)
    31}
    32
    33import scala.util.parsing.combinator.RegexParsers
    34
    35class IniParser extends RegexParsers {
    36
    37  // String
    38  def string :Parser[String_of_AST] = """[^\[\]=\s]*""".r^^{
    39    case value => String_of_AST(value)
    40  }
    41    42  // Property
    43  def property :Parser[Property_of_AST] = string~"="~string^^{
    44    case (key~_~value) => Property_of_AST(key, value)
    45  }
    46
    47  // Section
    48  def section :Parser[Section_of_AST] = "["~>string~"]"~rep(property)^^{
    49    case (section~_~properties) => Section_of_AST(section, properties)
    50  }
    51
    52  // Section(s)
    53  def sections :Parser[Sections_of_AST] = rep(section)^^{
    54    case sections => Sections_of_AST(sections)
    55  }
    56}
    57
    58trait AST
    59
    60// String
    61case class String_of_AST(string: String) extends AST
    62// Property
    63case class Property_of_AST(key: String_of_AST, value: String_of_AST) extends AST
    64// Section
    65case class Section_of_AST(name: String_of_AST, properties: List[Property_of_AST]) extends AST
    66// Section(s)
    67case class Sections_of_AST(sections: List[Section_of_AST]) extends AST

実行してみる。。

<pre>
$ scala Parser.scala
Sections_of_AST(List(Section_of_AST(String_of_AST(db1),List(Property_of_AST(String_of_AST(indexer),String_of_AST(Elasticsearch)), Property_of_AST(String_of_AST(url),String_of_AST(localhost:9200)), Property_of_AST(String_of_AST(user),String_of_AST(admin)), Property_of_AST(String_of_AST(password),String_of_AST(qiita)))), Section_of_AST(String_of_AST(db2),List(Property_of_AST(String_of_AST(driver),String_of_AST(Splunk)), Property_of_AST(String_of_AST(url),String_of_AST(local:8089)), Property_of_AST(String_of_AST(user),String_of_AST(admin)), Property_of_AST(String_of_AST(password),String_of_AST(qiita))))))

Map(db1 -> Map(indexer -> Elasticsearch, url -> localhost:9200, user -> admin, password -> qiita), db2 -> Map(driver -> Splunk, url -> local:8089, user -> admin, password -> qiita))
</pre>

（｀ー´）b