http://qs321.pair.com?node_id=1232356

ameezys has asked for the wisdom of the Perl Monks concerning the following question:

Firstly this is the input file that Im reading.
module circuit_17 (N1,N2,N3,N6,N7,N22,N23); input N1,N2,N3,N6,N7; output N22,N23; wire N10,N11,N16,N19; nand nand2_1 (N10,N1,N3); nand nand2_2 (N11,N3,N6); nand nand2_3 (N16,N11,N2); nand nand2_4 (N19,N11,N7); nand nand2_5 (N22,N10,N16); nand nand2_6 (N23,N16,N19); endmodule
This is a part of the code.
my $gate_DATA = $_; if (defined($gate_DATA) && ($gate_DATA =~ /(.*) (.*) \((.*),(.*),( +.*)\);/) && ($gate_DATA !~ /module/) ) { @gate_type = $1; @gate_name = $2; @output_gate = $3; @input_A = $4; @input_B = $5; }
How do I store the gate data so I can access it later? By looping? For example,
@input_A = [ N1 N3 N11 N11 N10 N16 ]
UPDATE For gate_name, I managed to capture the whole name which is nand2_1, if I want to just capture the number behind it ( meaning from nand2_1 just capture value 1) , how do I change the way to store it?

Replies are listed 'Best First'.
Re: Store data into array by looping?
by tybalt89 (Monsignor) on Apr 10, 2019 at 00:52 UTC

    Looks like you are taking the same course as Output not correct

    Anyway, here's a small simulator I wrote from that input (slightly modified for case) that processes your input and simulates the circuit, providing all inputs and producing a table with all outputs.

    It stores gates in a hash keyed by output name where the value is perl code to evaluate the output from the inputs. This allows easy simulation later when all gates are combined to a generated sub call 'async'.

    This form of gate storage may or may not be what you are looking for.

    #!/usr/bin/perl # https://perlmonks.org/?node_id=1226380 # https://perlmonks.org/?node_id=1232356 use strict; use warnings; my (%gates, @inputs, @wires, @outputs, $depends, %bits, $topologicalor +der); while( <DATA> ) # inpu +t section { print; if( /^nand /gi ) { my ( undef, $out, $ina, $inb) = /\w+/g; $gates{$out} and die "\nDUPLICATE: $out\n"; $gates{$out} = "1 ^ (\$bits{'$ina'} & \$bits{'$inb'})"; $depends .= "$out $ina $inb\n"; } elsif( /^output /gi ) { push @outputs, /\w+/g; } elsif( /^input /gi ) { push @inputs, /\w+/g; } elsif( /^wire /gi ) { push @wires, /\w+/g; } } my @all = (@inputs, @wires, @outputs); #use Data::Dump 'dd'; dd \%gates; %gates or die "no gates found"; $depends =~ s/ $_\b//g for @inputs; # topological orde +r section while( $depends =~ s/^(\w+)\n//m ) { $topologicalorder .= "\$bits{$1} = $gates{$1};\n"; $depends =~ s/ $1\b//g; } length $depends and die "CIRCULAR or UNDEFINED:\n<$depends>"; print $topologicalorder; eval "sub async { $topologicalorder }"; my $fmt = "@{[ map '%' . y///c . 's', @all ]}\n"; # outpu +t section printf $fmt, @all; @bits{@all} = ('u') x @all; for ( glob '{0,1}' x @inputs ) { @bits{@inputs} = map 0 + $_, split //; async(); printf $fmt, @bits{@all}; } __DATA__ module circuit_17 (N1,N2,N3,N6,N7,N22,N23); input N1,N2,N3,N6,N7; output N22,N23; wire N10,N11,N16,N19; nand nand2_1 (N10,N1,N3); nand nand2_2 (N11,N3,N6); nand nand2_3 (N16,N11,N2); nand nand2_4 (N19,N11,N7); nand nand2_5 (N22,N10,N16); nand nand2_6 (N23,N16,N19); endmodule

    Seems to work. May have issues if you start using tri-state output logic.

Re: Store data into array by looping?
by LanX (Saint) on Apr 09, 2019 at 16:56 UTC
    your code has many issues ...

    ... a quick fix is to use push because it must already be in a loop

    $gate_type = $1; $gate_name = $2; $output_gate = $3; $input_A = $4; $input_B = $5; push @input_A, $input_A; # column 2

    be warned that this will collect over all modules in your file.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

Re: Store data into array by looping?
by hdb (Monsignor) on Apr 10, 2019 at 09:51 UTC

    Assuming eventually your input will be a file with multiple modules, you could parse it into a more complex data structure (array of module data, where each module is represented as a hash):

    use strict; use warnings; use Data::Dumper; my @modules; while(<DATA>){ push @modules, { name => $1 } if /^module (\w+) \(.*\)/; $modules[ -1 ]->{ input } = [ split /,/, $1 ] if /^input (.*);/ +; $modules[ -1 ]->{ output } = [ split /,/, $1 ] if /^output (.*); +/; $modules[ -1 ]->{ wire } = [ split /,/, $1 ] if /^wire (.*);/; next if /^endmodule/; if( /^(nand|nor|other)/ ) { push @{ $modules[ -1 ]->{ gates } }, {}; @{ $modules[ -1 ]->{ gates }[-1] }{ qw( type name output input +_A input_B) } = split /[\s\(\),;]+/; } } print Dumper \@modules; __DATA__ module circuit_17 (N1,N2,N3,N6,N7,N22,N23); input N1,N2,N3,N6,N7; output N22,N23; wire N10,N11,N16,N19; nand nand2_1 (N10,N1,N3); nand nand2_2 (N11,N3,N6); nand nand2_3 (N16,N11,N2); nand nand2_4 (N19,N11,N7); nand nand2_5 (N22,N10,N16); nand nand2_6 (N23,N16,N19); endmodule

    resulting in

    $VAR1 = [ { 'input' => [ 'N1', 'N2', 'N3', 'N6', 'N7' ], 'gates' => [ { 'input_A' => 'N1', 'name' => 'nand2_1', 'type' => 'nand', 'input_B' => 'N3', 'output' => 'N10' }, { 'input_A' => 'N3', 'name' => 'nand2_2', 'type' => 'nand', 'input_B' => 'N6', 'output' => 'N11' }, { 'input_A' => 'N11', 'name' => 'nand2_3', 'type' => 'nand', 'input_B' => 'N2', 'output' => 'N16' }, { 'input_A' => 'N11', 'name' => 'nand2_4', 'type' => 'nand', 'input_B' => 'N7', 'output' => 'N19' }, { 'input_A' => 'N10', 'name' => 'nand2_5', 'type' => 'nand', 'input_B' => 'N16', 'output' => 'N22' }, { 'input_A' => 'N16', 'name' => 'nand2_6', 'type' => 'nand', 'input_B' => 'N19', 'output' => 'N23' } ], 'name' => 'circuit_17', 'wire' => [ 'N10', 'N11', 'N16', 'N19' ], 'output' => [ 'N22', 'N23' ] } ];
Re: Store data into array by looping?
by FreeBeerReekingMonk (Deacon) on Apr 09, 2019 at 20:20 UTC
    Hello ameezys. You do not give much information on how you want the gate information stored. It depends on what you want to do with it.

    I only see nand in your example, but there are not's and's etc. If you want to easily loop over only nand or only not then I suggest:

    $GATE{$gate_type}{$gate_name} = { ... hash object with fields ... }

    And then build arrays of these objects to loop over them. (the %LINK variable). As pointers/references, so hopefully RAM efficient.

    But you can also store hash objects in an array (but I did not do that in the code) which makes it less cluttered, but harder to loop through.

    First I modified the regexp a bit so that it is tolerant to a space here and there. But a separate regexp needs to be made to handle the not as it has less parameters

    To have the array you want, just loop over the keys:

    # Obtain the @input_A array from %LINK my @input_A_from_link = keys %{$LINK{'input_A'}}; die Dumper(\@input_A_from_link);

    or, if you do not want the helper hash, directly from the data (using a oneliner):

    # get all input_A for nand, not, and, etc.. my @all_input_A = map { my $g = $_; @_=keys $GATE{$g}; map {$GATE{$g}{ +$_}->{'A'}} @_ } keys %GATE; die Dumper(\@all_input_A);

    The code (sorry, a bit messy):

    #!/usr/bin/perl use warnings; use Data::Dumper; use [METAMOD://List::MoreUtils] qw/ uniq /; my %GATE; my %LINK; while(<DATA>){ chomp; my $gate_DATA = $_; if (defined($gate_DATA) && ($gate_DATA =~ /(.*)\s+(.*)\s+\(\s*(.*)\s +*,\s*(.*)\s*,\s*(.*)\s*\);/) && ($gate_DATA !~ /module/) ){ # $gate_type = $1; # $gate_name = $2; $output_gate = $3; $input_A = $4; $input_B = $5; die "Line $.: FATAL Redefinition of gate $gate_DATA\n" if(defined +$GATE{$gate_type}{$gate_name}); $GATE{$gate_type}{$gate_name} = { 'name' => $gate_name, 'type' => $gate_type, 'out' => $output_gate, 'A' => $input_A, 'B' => $input_B, }; push @{$LINK{'input_A'}{$input_A}}, $GATE{$gate_type}{$gate_name}; push @{$LINK{'input_B'}{$input_B}}, $GATE{$gate_type}{$gate_name}; push @{$LINK{'output'}{$output_gate}}, $GATE{$gate_type}{$gate_nam +e}; } } # uncomment to Show the structure of %GATE # die Dumper(\%GATE); # uncomment to Show the structure of %LINK # die Dumper(\%LINK); # Yeah, %LINK is a hash of a hash of an array of a referenced hash # die Dumper($LINK{'input_A'}{'N11'}[0]->{'A'}); # die Dumper($LINK{'output'}{'N19'}[0]->{'A'}); # loop over input_A and show where it outputs to for my $gate (keys %{$LINK{'input_A'}}){ my @outputs = map { $_->{'out'} } grep { defined $_->{'out'} } @{$LI +NK{'input_A'}{$gate}}; print "input gate $gate has output @outputs\n"; } # This is how you access the data of one gate: # print $GATE{'nor'}{'NOR2_1'}->{'out'}; # get nand input_A, note the use of uniq to have a unique list (still +not sorted, though) my @nand_input_A = uniq map { $GATE{'nand'}{$_}->{'A'} } keys %{$GATE{ +'nand'}}; # get all input_A for nand, not, and, etc.. my @all_input_A = map { my $g = $_; @_=keys $GATE{$_}; map {$GATE{$g}{ +$_}->{'A'}} @_ } keys %GATE; # get all input_A for nand, not, and, etc... but this time, use readab +le perl my @all_input_A_normal; for my $gate_type (keys %GATE){ for my $gate_name (keys %{$GATE{$gate_type}}){ if(defined $GATE{$gate_type}{$gate_name}->{'A'}){ push @all_input_A_normal, $GATE{$gate_type}{$gate_name}->{'A'}; } } } print Dumper(\@all_input_A_normal); __DATA__ nand nand2_1 (N10,N1,N3); nand nand2_2 (N11,N3,N6); nand nand2_3 (N16,N11,N2); nand nand2_4 (N19,N11,N7); nand nand2_5 (N22,N10,N16); nand nand2_6 (N23,N16,N19); nor NOR2_1 (N6875, N6722, N6476);
Re: Store data into array by looping?
by Marshall (Canon) on Apr 09, 2019 at 20:22 UTC
    A few tips:
    1. Your regex is not the best. .* is "dangerous" and fraught with unintended consequences. You can go far with just \w and \s.
    2. Forget this $1,$2 stuff. Assign directly as shown below
    3. The if() is true if the regex matches, else it is false. as shown below
    4. I have many question, but this will help you get started...
    #!/usr/bin/perl use strict; use warnings; use Data::Dumper; my @gate_data; while (my $line = <DATA>) { if (my($gate_type,$gate_name,$ouput_gate,$input_A, $input_B) = $line =~ /^(\w+)\s+(\w+)\s+\((\w+),(\w+),(\w+)\)/) { print "$gate_type, $gate_name,$ ouput_gate, $input_A, $input_B\n +"; push @gate_data,[$gate_type, $gate_name,$ ouput_gate, $input_A, +$input_B]; } } print "@$_\n" for @gate_data; =prints nand, nand2_1,N10, N1, N3 nand, nand2_2,N11, N3, N6 nand, nand2_3,N16, N11, N2 nand, nand2_4,N19, N11, N7 nand, nand2_5,N22, N10, N16 nand, nand2_6,N23, N16, N19 nand nand2_1 N10 N1 N3 nand nand2_2 N11 N3 N6 nand nand2_3 N16 N11 N2 nand nand2_4 N19 N11 N7 nand nand2_5 N22 N10 N16 nand nand2_6 N23 N16 N19 =cut __DATA__ module circuit_17 (N1,N2,N3,N6,N7,N22,N23); input N1,N2,N3,N6,N7; output N22,N23; wire N10,N11,N16,N19; nand nand2_1 (N10,N1,N3); nand nand2_2 (N11,N3,N6); nand nand2_3 (N16,N11,N2); nand nand2_4 (N19,N11,N7); nand nand2_5 (N22,N10,N16); nand nand2_6 (N23,N16,N19); endmodule
    Update: It appears that this output is coming from sort of wiring program. What you show here is the human readable output. There will another form of output designed as input for whatever machine is going to further fiddle with this data. I suggest you forget about parsing this data format and get whatever that other data output is. It will already be structured into records designed to handle all of the various gate types and other parts like flip-flops, registers, etc. Reverse engineering that data structure seems pointless to me.
Re: Store data into array by looping?
by holli (Abbot) on Apr 12, 2019 at 16:09 UTC
    Regular expressions like it's still 1998. If I may:
    use warnings; use strict; use Regexp::Grammars; use Data::Dumper; my $data = join "", <DATA>; my $matcher = qr{ <debug: off> <nocontext:> ^ <Module> <[Commands]>+ <EndModule> $ <token: Module> module \s <Name> \s <BracketedNodes>; \n <token: EndModule> endmodule \n? <token: Commands> ( <Input>|<Output>|<Wire>|<Nand> );\n <token: Nand> nand \s <Name> \s <BracketedNodes> <token: Input> input \s <Nodes> <token: Output> output \s <Nodes> <token: Wire> wire \s <Nodes> <token: Name> [\p{L}\d_]+ <token: Node> N\d+ <token: Nodes> <[Node]>+ % , <token: BracketedNodes> \(<Nodes>\) }; if ( $data =~ $matcher ) { print Dumper \%/; #/ } else { print "NO MATCH"; } __DATA__ module circuit_17 (N1,N2,N3,N6,N7,N22,N23); input N1,N2,N3,N6,N7; output N22,N23; wire N10,N11,N16,N19; nand nand2_1 (N10,N1,N3); nand nand2_2 (N11,N3,N6); nand nand2_3 (N16,N11,N2); nand nand2_4 (N19,N11,N7); nand nand2_5 (N22,N10,N16); nand nand2_6 (N23,N16,N19); endmodule
    Yields Now for sanity checking you can inspect the result hash, but malformed data will simply not match the regex. If you want useful error messages you can add callbacks to the grammar.


    holli

    You can lead your users to water, but alas, you cannot drown them.
Re: Store data into array by looping?
by FreeBeerReekingMonk (Deacon) on Apr 10, 2019 at 22:08 UTC
    Replying on the UPDATE (you really should create a new question, as older questions get ignored in favour of new questions).

    Just capture nand2_1 into a variable, like you already did, and then just break it into bits, like so:

    my $gate_name = 'nand2_1'; my ($gtype, $gfirst, $gsecond) = ($gate_name =~ /^(\w+)(\d+)_(\d+)/); print "# $gtype # $gfirst # $gsecond #\n";

    output:

    # nand # 2 # 1 #