/
09.p6
67 lines (59 loc) · 1.67 KB
/
09.p6
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
use v6;
class IniFile::Actions {
method key($/) { make $/.Str }
method value($/) { make $/.Str }
method pair($/) { make $<key>.made => $<value>.made }
method header($/) { make $/[0].Str }
method block($/) { make $<pair>.map({ .made }).hash }
method section($/) { make $<header>.made => $<block>.made }
method TOP($/) {
make {
_ => $<block>.made,
$<section>.map: { .made },
}
}
}
grammar IniFile {
token ws { \h* }
token key { \w+ }
token value { <!before \s> <-[\n;]>+ <!after \s> }
rule pair {
<key>
[ '=' || <expect('=')> ]
[ <value> || <expect('value')> ]
\n+
}
token header {
'['
[ ( <-[ \[ \] \n ]>+ ) ']'
|| <error("malformed section header")> ]
}
token comment { ';' \N*\n+ }
token block { [<pair> | <comment>]* }
token section { <header> <block> }
token TOP { <block> <section>* }
method parse-ini(Str $input, :$rule = 'TOP') {
my $m = self.parse($input,
:actions(IniFile::Actions),
:$rule,
);
say $m.perl;
unless $m {
die "The input is not a valid INI file.";
}
return $m.made
}
method expect($what) {
self.error("expected $what");
}
method error($msg) {
my $parsed-so-far = self.target.substr(0, self.pos);
my @lines = $parsed-so-far.lines;
die "Cannot parse input as INI file: $msg at line @lines.elems(), after '@lines[*-1]'";
}
}
my $malformed-ini = q:to/EOI/;
key1=value2
[section1
EOI
say IniFile.parse-ini($malformed-ini).perl;