-
Notifications
You must be signed in to change notification settings - Fork 2
/
RecipeParser.pm
157 lines (119 loc) · 2.87 KB
/
RecipeParser.pm
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package DU::RecipeParser;
use 5.16.1;
use warnings;
use Pegex;
use Sub::Exporter::Progressive -setup => {
exports => [qw( decode_recipe encode_recipe )],
groups => {
default => [qw( decode_recipe encode_recipe )],
},
};
use Lingua::EN::Inflect 'PL';
my $grammar = <<'PEGEX';
%grammar drinkup
%version 0.0.1
recipe:
cocktail
description
ingredients ~
footers
cocktail: /
(: ~ <EOL> )? # Leading whitespace?
(<NS> <ANY>*?) # Cocktail name. Capture it.
~ <EOL> # Trailing whitespace
/
description: /
(<ALL>*?) # Description text
<EOL>+
(= <BLANK>* <STAR> ) # Not the ingredients
/
# Ingredient parts
ingredients: ingredient+
ingredient:
/ <SPACE>* <STAR> <SPACE>* /
number
/ <SPACE>* /
unit
/ <SPACE>* /
name
/ <SPACE>* <EOL> /
note?
# probably need to attempt to parse 1 1/2 or 1 + 1/2
# cribbed from json-pgx, minus the exponent version because if you use that
# in a recipe you should change your units!
number: /(
(: 0 | [1-9] <DIGIT>* )?
(: <DOT> <DIGIT>+ )?
(: ~ <PLUS>? ~ <DIGIT>+ ~ <SLASH> ~ <DIGIT>+ )?
)/
ounce: /(?i:ounces? | oz)/
tablespoon: /(?i:tbsp|tablespoons?|(?-i:T\s))/
teaspoon: /(?i:tsp|teaspoons?|(?-i:t\s))/
dash: /(?i:dash(:es)?)/
unit: (<ounce>|<tablespoon>|<teaspoon>|<dash>)/(?i: ~ of)?/
name: / (<ANY>+?) ~ (= <EOL>) /
note: / <SPACE>* <HASH> <SPACE>* (<ANY>+?) <SPACE>* <EOL> /
footers: metadata+
metadata: / (<footer_name>) <COLON> <SPACE>* (<ANY>+?) ~ <EOL> /
footer_name: / [<WORDS>-]+ /
PEGEX
sub decode_recipe {
pegex($grammar, {receiver => 'DU::RecipeParser::Data'})->parse($_[0]);
}
sub encode_recipe {
my $ingredients;
for my $i (@{$_[0]->{ingredients}}) {
$ingredients .= " * $i->{amount} " . PL($i->{unit}, $i->{amount})
. " of $i->{name}\n";
$ingredients .= " # $i->{note}\n" if $i->{note};
}
my $source = '';
$source = "Source: $_[0]->{source}" if $_[0]->{source};
my $variant = '';
$variant = "Variant_of_Drink: $_[0]->{variant_of_drink}"
if $_[0]->{variant_of_drink};
<<"RECIPE";
$_[0]->{name}
$_[0]->{description}
$ingredients
$source
$variant
RECIPE
}
package DU::RecipeParser::Data;
use base 'Pegex::Receiver';
my $data;
sub initial {
$data = {};
}
sub got_unit { $_[1]->[0] }
sub got_cocktail {
$data->{name} = $_[1];
}
sub got_ounce { 'ounce' }
sub got_tablespoon { 'tablespoon' }
sub got_teaspoon { 'teaspoon' }
sub got_dash { 'dash' }
sub got_description {
$data->{description} = $_[1];
}
sub got_instructions {
$data->{instructions} = $_[1];
}
sub got_ingredients {
$data->{ingredients} = [
map +{
amount => 0 + $_->[0],
unit => $_->[1],
name => $_->[2],
($_->[3] ? (note => $_->[3]) : ()),
}, @{$_[1]}
];
}
sub got_metadata {
$data->{lc $_[1]->[0]} = $_[1]->[1]
}
sub final {
return $data;
}
1;