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 ← !.
|