diff options
Diffstat (limited to 'parser/grammar.peg')
| -rw-r--r-- | parser/grammar.peg | 111 |
1 files changed, 66 insertions, 45 deletions
diff --git a/parser/grammar.peg b/parser/grammar.peg index 38b6dc8..760257f 100644 --- a/parser/grammar.peg +++ b/parser/grammar.peg @@ -1,5 +1,21 @@ { // Package parser parses conf language. + // + // This file is part of conf library. + // Copyright (C) 2026 Alexander NeonXP Kiryukhin <i@neonxp.ru> + // + // This program is free software: you can redistribute it and/or modify + // it under the terms of the GNU General Public License as published by + // the Free Software Foundation, either version 3 of the License, or + // (at your option) any later version. + // + // This program is distributed in the hope that it will be useful, + // but WITHOUT ANY WARRANTY; without even the implied warranty of + // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + // GNU General Public License for more details. + // + // You should have received a copy of the GNU General Public License + // along with this program. If not, see <https://www.gnu.org/licenses/>. package parser import ( @@ -13,7 +29,7 @@ if v == nil { return nil } - if v := v.([]any); len(v) == 0 { + if v, ok := v.([]any); ok && len(v) == 0 { return nil } @@ -21,32 +37,31 @@ } } -Config ← _ stmts:( Command* ) EOF { - if stmts == nil { +Config ← _ directives:( Directive* ) EOF { + if directives == nil { return model.Group{}, nil } - groupAny := toAnySlice(stmts) + groupAny := toAnySlice(directives) groupSl := make(model.Group, len(groupAny)) for i, e := range groupAny { - groupSl[i] = e.(model.Command) + groupSl[i] = e.(model.Directive) } return groupSl, nil } -Group ← '{' _ stmts:( Command* ) _ '}' { - if stmts == nil { +Group ← '{' _ directives:( Directive* ) _ '}' { + if directives == nil { return model.Group{}, nil } - groupAny := toAnySlice(stmts) + groupAny := toAnySlice(directives) groupSl := make(model.Group, len(groupAny)) for i, e := range groupAny { - groupSl[i] = e.(model.Command) + groupSl[i] = e.(model.Directive) } return groupSl, nil } -Command ← name:Ident _ args:Args _ { - var group model.Group +Directive ← name:Ident _ args:Args _ { rawArgs := args.([]any) argsSlice := make([]any, 0, len(rawArgs)) if len(rawArgs) > 0 { @@ -54,73 +69,79 @@ Command ← name:Ident _ args:Args _ { 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 + return model.Directive{Name: name.(model.Ident), RawValues: argsSlice}, 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 -} - -// Сервисные литералы +Value ← val:( Ident / String / Number ) __ { return val, nil } // {{{ Строки +String ← ( '"' DoubleStringChar* '"' / "'" SingleStringChar "'" / '`' RawStringChar* '`' ) { + return strconv.Unquote(string(c.text)) +} DoubleStringChar ← !( '"' / "\\" / EOL ) SourceChar / "\\" DoubleStringEscape SingleStringChar ← !( "'" / "\\" / EOL ) SourceChar / "\\" SingleStringEscape -RawStringChar ← !'`' SourceChar +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 +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] +Number ← '-'? UInteger ('.' DecimalDigit+)? Exponent? { + if i, err := strconv.Atoi(string(c.text)); err == nil { + return i, nil + } + return strconv.ParseFloat(string(c.text), 64) +} + +UInteger ← '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] +Ident ← (Alpha / AllowedSpec) (Alpha / AllowedSpec / Number)* { + switch string(c.text) { + case "true": + return true, nil + case "false": + return false, nil + default: + 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 )* - +// {{{ Оставшиеся спец моменты +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 ← !.
\ No newline at end of file +EOL ← '\n' +EOS ← __ ';' { return nil, nil } / _ Comment? EOL { return nil, nil } / __ EOF { return nil, nil } +EOF ← !. +// }}}
\ No newline at end of file |
