aboutsummaryrefslogtreecommitdiff
path: root/challenge-084/alexander-pankoff/perl/lib/MatrixParser.pm
blob: f2a8e91717c304071f9c6f23cbf69df2d5148a89 (plain)
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
package MatrixParser;
use strict;
use warnings;
use feature qw(say signatures);
no warnings 'experimental::signatures';

use List::Util qw(none);

use MatrixParser::Lexer;

sub new ( $class, $input ) {
    my $self = {
        lexer         => MatrixParser::Lexer->new($input),
        corners       => [],
        row           => 0,
        col           => 0,
        element_count => undef,
    };

    $self->{lookahead} = $self->{lexer}->next_token;

    return bless $self, $class;
}

# returns an array of hashes with the corner positions (1s) from the input
# matrix. each hash contains the corner positions for the corresponding row in
# the input matrix
sub corners($self) {
    while ( $self->{lookahead} ) {
        $self->_parse_row;
    }
    return $self->{corners};
}

sub _parse_row($self) {
    my $start = $self->_expect('[');

    my $count = 0;
    while ( !$self->_match(']') ) {
        $self->_parse_element;
        $count++;
    }

    $self->_expect(']');
    if ( defined $self->{element_count} && $self->{element_count} != $count ) {
        $self->_unexpected_element_count( $start, $count );
    }
    $self->{element_count} = $count;
    $self->{col}           = 0;
    $self->{row}++;

    return;
}

sub _parse_element($self) {
    if ( $self->_match('1') ) {
        $self->{corners}[ $self->{row} ]{ $self->{col} } = 1;
    }
    elsif ( !$self->_match('0') ) {
        $self->_unexpected_token( $self->{lookahead}, '1|0' );
    }

    $self->{col}++;
    return $self->_next_token()->lexeme;
}

sub _match ( $self, $expect ) {
    $self->_unexpected_eof()
      if !$self->{lookahead};
    return $self->{lookahead}->lexeme eq $expect;
}

sub _expect ( $self, $expect ) {
    my $token = $self->_next_token();
    return $token if $token->lexeme eq $expect;

    $self->_unexpected_token( $token, $expect );
}

sub _next_token($self) {
    my $token = $self->{lookahead};
    $self->{lookahead} = $self->{lexer}->next_token;
    return $token;
}

sub _unexpected_token ( $self, $got, $expect ) {
    die sprintf(
"unexpected character in line %d at position %d. Got '%s', expect: '%s'\n",
        $got->line, $got->pos, $got->lexeme, $expect );
}

sub _unexpected_element_count ( $self, $start, $count ) {
    die sprintf(
"unexpected element count in row %d starting at line %d at position %d. Got %d, expect: %d \n",
        $self->{row} + 1,
        $start->line, $start->pos, $count, $self->{element_count}
    );

}

sub _unexpected_eof ( $self ) {
    die sprintf(
        "unexpected end of input at line %d at position %d.\n",
        $self->{lexer}{line},
        $self->{lexer}{pos}
    );
}

1;