aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPacky Anderson <packy@cpan.org>2024-03-06 15:54:18 -0500
committerPacky Anderson <packy@cpan.org>2024-03-06 15:54:18 -0500
commita7f7de38fee8e8aac07ff1d2b3df1e69c65e2622 (patch)
tree7e80871b11243143690cacca82d789e6bdc51b71
parent38ae28aaeacfc9dd53e892f94d5e83d4a83395d2 (diff)
downloadperlweeklychallenge-club-a7f7de38fee8e8aac07ff1d2b3df1e69c65e2622.tar.gz
perlweeklychallenge-club-a7f7de38fee8e8aac07ff1d2b3df1e69c65e2622.tar.bz2
perlweeklychallenge-club-a7f7de38fee8e8aac07ff1d2b3df1e69c65e2622.zip
Challenge 259 solutions by Packy Anderson
* Raku - Task 1 & 2 * Perl - Task 1 * Python - Task 1 1 Blog post
-rw-r--r--challenge-259/packy-anderson/README.md2
-rw-r--r--challenge-259/packy-anderson/blog.txt1
-rw-r--r--challenge-259/packy-anderson/data/parser-1.txt1
-rw-r--r--challenge-259/packy-anderson/data/parser-2.txt1
-rw-r--r--challenge-259/packy-anderson/data/parser-3.txt1
-rw-r--r--challenge-259/packy-anderson/data/parser-4.txt3
-rw-r--r--challenge-259/packy-anderson/data/parser-5.txt15
-rwxr-xr-xchallenge-259/packy-anderson/perl/ch-1.pl65
-rwxr-xr-xchallenge-259/packy-anderson/python/ch-1.py56
-rwxr-xr-xchallenge-259/packy-anderson/raku/ch-1.raku62
-rwxr-xr-xchallenge-259/packy-anderson/raku/ch-2.raku89
11 files changed, 295 insertions, 1 deletions
diff --git a/challenge-259/packy-anderson/README.md b/challenge-259/packy-anderson/README.md
index 64879ee70c..fb3836f0bb 100644
--- a/challenge-259/packy-anderson/README.md
+++ b/challenge-259/packy-anderson/README.md
@@ -16,4 +16,4 @@
## Blog Post
-[Even Digits have a Sum!](https://packy.dardan.com/b/J8)
+[Ba-a-nking Day! Ba-a-nking Day! That's all I really wanted to say...](https://packy.dardan.com/b/JC)
diff --git a/challenge-259/packy-anderson/blog.txt b/challenge-259/packy-anderson/blog.txt
new file mode 100644
index 0000000000..5b67e28b89
--- /dev/null
+++ b/challenge-259/packy-anderson/blog.txt
@@ -0,0 +1 @@
+https://packy.dardan.com/b/JC \ No newline at end of file
diff --git a/challenge-259/packy-anderson/data/parser-1.txt b/challenge-259/packy-anderson/data/parser-1.txt
new file mode 100644
index 0000000000..9fb85876cd
--- /dev/null
+++ b/challenge-259/packy-anderson/data/parser-1.txt
@@ -0,0 +1 @@
+{% id field1="value1" field2="value2" field3=42 %} \ No newline at end of file
diff --git a/challenge-259/packy-anderson/data/parser-2.txt b/challenge-259/packy-anderson/data/parser-2.txt
new file mode 100644
index 0000000000..67eaf60282
--- /dev/null
+++ b/challenge-259/packy-anderson/data/parser-2.txt
@@ -0,0 +1 @@
+{% youtube title="Title \"quoted\" done" %} \ No newline at end of file
diff --git a/challenge-259/packy-anderson/data/parser-3.txt b/challenge-259/packy-anderson/data/parser-3.txt
new file mode 100644
index 0000000000..e7448b0bc3
--- /dev/null
+++ b/challenge-259/packy-anderson/data/parser-3.txt
@@ -0,0 +1 @@
+{% youtube title="Title with escaped backslash \\" %} \ No newline at end of file
diff --git a/challenge-259/packy-anderson/data/parser-4.txt b/challenge-259/packy-anderson/data/parser-4.txt
new file mode 100644
index 0000000000..957c351f1a
--- /dev/null
+++ b/challenge-259/packy-anderson/data/parser-4.txt
@@ -0,0 +1,3 @@
+{% id filed1="value1" %}
+LINES
+{% endid %}
diff --git a/challenge-259/packy-anderson/data/parser-5.txt b/challenge-259/packy-anderson/data/parser-5.txt
new file mode 100644
index 0000000000..378ecf6099
--- /dev/null
+++ b/challenge-259/packy-anderson/data/parser-5.txt
@@ -0,0 +1,15 @@
+JUNK
+{% id filed1="value1" %}
+LINES
+LINES
+LINES
+{% endid %}
+
+{% foo foonum=3 %}
+FOO
+BAR
+BAZ
+{% endfoo %}
+JUNK
+JUNK
+JUNK
diff --git a/challenge-259/packy-anderson/perl/ch-1.pl b/challenge-259/packy-anderson/perl/ch-1.pl
new file mode 100755
index 0000000000..b1389d9d65
--- /dev/null
+++ b/challenge-259/packy-anderson/perl/ch-1.pl
@@ -0,0 +1,65 @@
+#!/usr/bin/env perl
+use v5.38;
+
+use List::Util qw( any );
+use Time::Piece;
+use Time::Seconds qw( ONE_DAY );
+
+sub bankingDayOffset($start, $offset, @holidays) {
+ # convert string to Date
+ my $date = Time::Piece->strptime($start, "%Y-%m-%d")
+ ->truncate(to => 'day');
+ my $cnt = 0;
+
+ # convert holidays to Date objects
+ @holidays = map {
+ Time::Piece->strptime($_, "%Y-%m-%d")
+ ->truncate(to => 'day')
+ } @holidays;
+
+ my @explain;
+ my $this_day = $date->fullday;
+ while ($offset) {
+ $date += ONE_DAY; # add 1 day
+ my $next_day = $date->fullday;
+ if (
+ $date->wday == 7 || # it's a Saturday
+ $date->wday == 1 # it's a Sunday
+ ) {
+ push @explain,
+ "$next_day skipped because it's a weekend";
+ }
+ elsif (any { $date == $_ } @holidays) { # it's a Holiday
+ push @explain,
+ "$next_day skipped because it's a holiday";
+ }
+ else {
+ $offset--; $cnt++;
+ push @explain,
+ "$this_day bumped to $next_day (offset $cnt)";
+ $this_day = $next_day;
+ }
+ }
+ return $date->strftime('%F'), join("\n", @explain);
+}
+
+sub solution($start, $offset, @holidays) {
+ my $holiday_display = '';
+ if (@holidays) {
+ $holiday_display = qq{,\n \$bank_holidays = ['}
+ . join(q{', '}, @holidays) . q{']};
+ }
+ say qq{Input: \$start_date = '$start', \$offset = $offset$holiday_display};
+ my ($output, $explain) = bankingDayOffset($start, $offset, @holidays);
+ say qq{Output: '$output'};
+ say qq{\n$explain};
+}
+
+say "Example 1:";
+solution('2018-06-28', 3, '2018-07-03');
+
+say "\nExample 2:";
+solution('2018-06-28', 3);
+
+say "\nExample 3:";
+solution('2023-12-29', 5, '2024-01-01'); \ No newline at end of file
diff --git a/challenge-259/packy-anderson/python/ch-1.py b/challenge-259/packy-anderson/python/ch-1.py
new file mode 100755
index 0000000000..fc608d9033
--- /dev/null
+++ b/challenge-259/packy-anderson/python/ch-1.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+
+from datetime import date, timedelta
+
+def bankingDayOffset(start, offset, holidays):
+ d = date.fromisoformat(start) # convert string to Date
+ cnt = 0
+
+ # convert holidays to Date objects
+ holidays = [ date.fromisoformat(h) for h in holidays ]
+
+ explain = []
+ this_day = d.strftime('%A')
+ while offset:
+ d += timedelta(days = 1) # add 1 day
+ next_day = d.strftime('%A')
+ if (
+ d.isoweekday() == 6 or # it's a Saturday
+ d.isoweekday() == 7 # it's a Sunday
+ ):
+ explain.append(
+ f"{next_day} skipped because it's a weekend"
+ )
+ elif any([d == h for h in holidays]): # it's a Holiday
+ explain.append(
+ f"{next_day} skipped because it's a holiday"
+ )
+ else:
+ offset -= 1
+ cnt += 1
+ explain.append(
+ f"{next_day} bumped to {next_day} (offset {cnt})"
+ )
+ this_day = next_day
+ return d.strftime('%F'), "\n".join(explain)
+
+def solution(start, offset, holidays):
+ holiday_display = ''
+ if holidays:
+ holiday_display = (
+ "\n $bank_holidays = ['" +
+ "', '".join(holidays) + "']"
+ )
+ print(f"Input: $start_date = '{start}', $offset = {offset}{holiday_display}")
+ output, explain = bankingDayOffset(start, offset, holidays)
+ print(f"Output: '{output}'")
+ print(f"\n{explain}")
+
+print('Example 1:')
+solution('2018-06-28', 3, ['2018-07-03'])
+
+print('\nExample 2:')
+solution('2018-06-28', 3, [])
+
+print('\nExample 3:')
+solution('2023-12-29', 5, ['2024-01-01'])
diff --git a/challenge-259/packy-anderson/raku/ch-1.raku b/challenge-259/packy-anderson/raku/ch-1.raku
new file mode 100755
index 0000000000..d8eb834a31
--- /dev/null
+++ b/challenge-259/packy-anderson/raku/ch-1.raku
@@ -0,0 +1,62 @@
+#!/usr/bin/env raku
+use v6;
+
+use Date::Names;
+
+sub bankingDayOffset($start, $offset, @holidays) {
+ my $date = Date.new($start); # convert string to Date
+ my $off = $offset;
+ my $cnt = 0;
+
+ # convert holidays to Date objects
+ @holidays = map { Date.new($_) }, @holidays;
+
+ # instantiate a Date::Names object
+ my $dn = Date::Names.new;
+
+ my @explain;
+ my $this_day = $dn.dow($date.day-of-week);
+ while ($off) {
+ $date++;
+ my $next_day = $dn.dow($date.day-of-week);
+ if (
+ $date.day-of-week == 6 || # it's a Saturday
+ $date.day-of-week == 7 # it's a Sunday
+ ) {
+ @explain.push:
+ "$next_day skipped because it's a weekend";
+ }
+ elsif ($date == @holidays.any) { # it's a Holiday
+ @explain.push:
+ "$next_day skipped because it's a holiday";
+ }
+ else {
+ $off--; $cnt++;
+ @explain.push:
+ "$this_day bumped to $next_day (offset $cnt)";
+ $this_day = $next_day;
+ }
+ }
+ return $date.gist, @explain.join("\n");
+}
+
+sub solution($start, $offset, @holidays) {
+ my $holiday_display = '';
+ if (@holidays) {
+ $holiday_display = qq{,\n \$bank_holidays = ['}
+ ~ @holidays.join(q{', '}) ~ q{']};
+ }
+ say qq{Input: \$start_date = '$start', \$offset = $offset$holiday_display};
+ my ($output, $explain) = bankingDayOffset($start, $offset, @holidays);
+ say qq{Output: '$output'};
+ say qq{\n$explain};
+}
+
+say "Example 1:";
+solution('2018-06-28', 3, ['2018-07-03']);
+
+say "\nExample 2:";
+solution('2018-06-28', 3, []);
+
+say "\nExample 3:";
+solution('2023-12-29', 5, ['2024-01-01']); \ No newline at end of file
diff --git a/challenge-259/packy-anderson/raku/ch-2.raku b/challenge-259/packy-anderson/raku/ch-2.raku
new file mode 100755
index 0000000000..04894b08f9
--- /dev/null
+++ b/challenge-259/packy-anderson/raku/ch-2.raku
@@ -0,0 +1,89 @@
+#!/usr/bin/env raku
+use v6;
+
+grammar Parser {
+ rule TOP { [ <line> | <text> ] }
+
+ rule line { '{%' <id> [ <field-value> ]* '%}' }
+
+ # negative lookbehind and negative lookahead
+ rule text { <!after 「{%」 > <-[ \n ]>+ <!before 「%}」 >}
+
+ token id { \w+ }
+ token field { \w+ }
+
+ token number { \d+ [ \. \d+ ]? }
+
+ token quoted-string { '"' <string> '"' }
+ token string {
+ [
+ <-[ " ]> # any character not a quote
+ |
+ 「\\」 # an escaped backslash
+ |
+ \\\" # an escaped quote
+ ]*
+ }
+
+ rule field-value { <field> '=' [<number> | <quoted-string>] }
+}
+
+sub MAIN($file) {
+ my %data;
+ my @ids;
+ my $in_id = '';
+ for $file.IO.lines -> $line {
+ # parse this line of the file
+ my $p = Parser.parse($line);
+
+ # is there a line with {% ... %} ?
+ if ($p<line>) {
+ my $id = $p<line><id>.Str;
+ # is the id the end of a block?
+ if (my $c = ($id ~~ / end(\w+) /)) { # capture after end
+ if (%data{$c[0]}:exists) { # it is!
+ $id = $c[0];
+ $in_id = ''; # clear the id we're processing
+ if (%data{$id}{'text'}) {
+ # if there's text, remove the final "newline"
+ %data{$id}{'text'} ~~ s/\\n$//;
+ }
+ next; # skip to next line of file
+ }
+ }
+ @ids.push($id); # keep list of ids in order
+ $in_id = $id; # keep track of the current id for text
+ # initialize base data for this id
+ %data{$id} = { name => $id };
+ # if we have fields...
+ if ($p<line><field-value>) {
+ # loop over them and store them in the data
+ for $p<line><field-value> -> $fv {
+ my $field = $fv<field>;
+ my $value = $fv<number> ?? $fv<number>
+ !! $fv<quoted-string><string>;
+ %data{$id}{'fields'}{$field} = $value;
+ }
+ }
+ }
+ # if we have non-{% ... %} lines and we have an ID
+ elsif ($p<text> && $in_id) {
+ # append a "newline" to the end
+ %data{$in_id}{'text'} ~= $p<text> ~ "\\n";
+ }
+ }
+
+ # dump the data
+ for @ids -> $id {
+ my %group = %data{$id};
+ say "\{";
+ say " name => %group{'name'},";
+ say " fields => \{";
+ for %group{'fields'}.keys.sort -> $k {
+ say " $k => %group{'fields'}{$k},";
+ }
+ say " }";
+ say " text => %group{'text'}" if %group{'text'};
+ say "\}";
+ }
+}