hakeの日記

Windows環境でプログラミングの勉強をしています。

Go言語 - PEGで構文解析 - 字句解析

Go言語でPEGによる構文解析方法があることを知ったので、簡単な字句解析部分のみのプログラムを書いてみました。ruleの書き方はgoyaccよりも融通が利いて便利そうです。

入手先および構文の書き方

scan.peg

package main

type Parser Peg {
    s Scan         // parserが自動生成するフィールド変数と区別するために
                   //   敢えて埋め込みを行っていない。
}

root <- expression EOT /
        expression <.+> {p.s.Err(begin)} EOT /
        <.+> {p.s.Err(begin)} EOT

EOT <- !.

expression <- literal literal*


literal <- <&'0' [0-9]+> {
                             // p,begin,end,text を使用する場合はruleを<…>で囲む。

                             // '0'で始まる数字列
                      fmt.Printf("line %d(%d) KIND:ZeroNUMBER \"%s\"\n", p.s.line, begin - p.s.lineHead, text)
                      }      /
           <[0-9]+>   {      // 数字列
                      fmt.Printf("line %d(%d) KIND:NUMBER \"%s\"\n", p.s.line, begin - p.s.lineHead, text)
                      }      /
           <[[A-Z]]+> {      // 大小英字列
                        fmt.Printf("line %d(%d) KIND:IDENT \"%s\"\n", p.s.line, begin - p.s.lineHead, text)
                      }      /
           ' '+       {  }   /
           <'\n'>       {    // 改行時処理
                         p.s.line++;
                         p.s.lineHead = begin + 1
                      }      /
           <[^0-9a-zA-Z \n]+>  { // その他文字
                        fmt.Printf("line %d(%d) KIND:OTHER \"%s\"\n", p.s.line, begin - p.s.lineHead, text)
                      }

scan.go

package main

import (
    "fmt"
)

type Scan struct {
    line       int
    lineHead   int
}

func (self *Scan) Init() {
    self.line     = 1      // 現在の行
    self.lineHead = 0      // 行の先頭文字位置
}

func (self *Scan) Err(s int) {
    fmt.Printf("\n!!error!!%d\n", s)
}


func main() {
    const s string = "line001\nint a = 10\n"  // 解析対象文字列
    
    parser := &Parser{Buffer: s}  // 解析対象文字の設定
    parser.Init()                 // parser初期化
    parser.s.Init()               // SCAN構造体初期化
    err := parser.Parse()         // 解析
    if err != nil {
        fmt.Println(err)
    } else {
        parser.Execute()          // アクション処理
    }
}

コンパイルおよび実行

peg scan.peg
go run scan.go scan.peg.go

実行結果

line 1(0) KIND:IDENT "line"
line 1(4) KIND:ZeroNUMBER "001"
line 2(0) KIND:IDENT "int"
line 2(4) KIND:IDENT "a"
line 2(6) KIND:OTHER "="
line 2(8) KIND:NUMBER "10"