aboutsummaryrefslogtreecommitdiff
path: root/parser/grammar.peg
blob: 38b6dc84fa4961409c2b0f2513afb85be3a7c7d0 (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
{
    // Package parser parses conf language.
    package parser

    import (
        "strconv"
        "errors"

        "go.neonxp.ru/conf/model"
    )

    func toAnySlice(v any) []any {
        if v == nil {
            return nil
        }
        if v := v.([]any); len(v) == 0 {
            return nil
        }

        return v.([]any)
    }
}

Config ← _ stmts:( Command* ) EOF {
    if stmts == nil {
        return model.Group{}, nil
    }
    groupAny := toAnySlice(stmts)
    groupSl := make(model.Group, len(groupAny))
    for i, e := range groupAny {
        groupSl[i] = e.(model.Command)
    }
    return groupSl, nil
}

Group ← '{' _ stmts:( Command* ) _ '}' {
    if stmts == nil {
        return model.Group{}, nil
    }
    groupAny := toAnySlice(stmts)
    groupSl := make(model.Group, len(groupAny))
    for i, e := range groupAny {
        groupSl[i] = e.(model.Command)
    }
    return groupSl, nil
}

Command ← name:Ident _ args:Args _ {
    var group model.Group
    rawArgs := args.([]any)
    argsSlice := make([]any, 0, len(rawArgs))
    if len(rawArgs) > 0 {
        for _, s := range rawArgs {
            if s == nil {
                continue
            }
            if g, ok := s.(model.Group); ok {
                group = g
                continue
            }
            if l, ok := s.([]any); ok {
                l = slices.DeleteFunc(l, func(x any) bool { return x == nil })
                argsSlice = append(argsSlice, l...)
            }
        }
    }
    return model.Command{Name: name.(model.Ident), Args: argsSlice, Group: group}, nil
}

Args ← Values? (Group / EOS)

Values ← vals:Value* { return toAnySlice(vals), nil }

Value ← val:( Ident / String / Number / Boolean ) __ { return val, nil }
String ← ( '"' DoubleStringChar* '"' / "'" SingleStringChar "'" / '`' RawStringChar* '`' ) {
    return string(c.text), nil
}

// Сервисные литералы

// {{{ Строки
DoubleStringChar ← !( '"' / "\\" / EOL ) SourceChar / "\\" DoubleStringEscape
SingleStringChar ← !( "'" / "\\" / EOL ) SourceChar / "\\" SingleStringEscape
RawStringChar ← !'`' SourceChar

DoubleStringEscape ← '"' / CommonEscapeSequence
SingleStringEscape ← "'" / CommonEscapeSequence

CommonEscapeSequence ← SingleCharEscape / OctalEscape / HexEscape / LongUnicodeEscape / ShortUnicodeEscape
SingleCharEscape ← 'a' / 'b' / 'n' / 'f' / 'r' / 't' / 'v' / '\\'
OctalEscape ← OctalDigit OctalDigit OctalDigit
HexEscape ← 'x' HexDigit HexDigit
LongUnicodeEscape ← 'U' HexDigit HexDigit HexDigit HexDigit HexDigit HexDigit HexDigit HexDigit
ShortUnicodeEscape ← 'u' HexDigit HexDigit HexDigit HexDigit

OctalDigit ← [0-7]
DecimalDigit ← [0-9]
HexDigit ← [0-9a-f]i
SourceChar ← .
// }}}

// {{{ Числа
Number ← '-'? Integer ( '.' DecimalDigit+ )? Exponent? { return strconv.ParseFloat(string(c.text), 64) }
Integer ← '0' / NonZeroDecimalDigit DecimalDigit*
Exponent ← 'e'i [+-]? DecimalDigit+
DecimalDigit ← [0-9]
NonZeroDecimalDigit ← [1-9]
// }}}

// {{{ Идентификатор
Ident ← (Alpha / AllowedSpec) (Alpha / AllowedSpec / Number)* { return model.Ident(c.text), nil }
Alpha ← [a-zA-Z]
AllowedSpec ← '$' / '@' / '%' / '_' / '-' / '+'
// }}}

Boolean ← "true" { return true, nil } / "false" { return false, nil }

Comment ← '#' ( ![\r\n] . )*
__ ← ( Whitespace / EOL / Comment )*
_ ← ( [ \t\r\n] / Comment )*

Whitespace ← [ \t\r]
EOL ← '\n'
EOS ← __ ';' { return nil, nil } / _ Comment? EOL { return nil, nil } / __ EOF { return nil, nil }

EOF ← !.