In Perl, you can use Marpa :: R2 , the Perl interface for Marpa, a common BNF parser .
Here is a quick example:
use 5.010; use strict; use warnings; use Data::Dumper; $Data::Dumper::Indent = 1; $Data::Dumper::Terse = 1; $Data::Dumper::Deepcopy = 1; use Marpa::R2; my $g = Marpa::R2::Scanless::G->new( { source => \(<<'END_OF_SOURCE'), :default ::= action => [ name, value] lexeme default = action => [ name, value] latm => 1 scutil ::= 'scutil' '> open' '> show' path '<dictionary>' '{' pairs '}' path ~ [\w/:\-]+ pairs ::= pair+ pair ::= name ':' value name ~ [\w]+ value ::= ip | mac | interface | signature | array | dict ip ~ octet '.' octet '.' octet '.' octet octet ~ [\d]+ mac ~ [a-z0-9][a-z0-9]':'[a-z0-9][a-z0-9]':'[a-z0-9][a-z0-9]':'[a-z0-9][a-z0-9]':'[a-z0-9][a-z0-9]':'[a-z0-9][a-z0-9] interface ~ [\w]+ signature ::= signature_item+ separator => [;] signature_item ::= signature_item_name '=' signature_item_value signature_item_name ~ [\w\.]+ signature_item_value ::= ip | mac dict ::= '<dictionary>' '{' pairs '}' array ::= '<array>' '{' items '}' items ::= item+ item ::= index ':' value index ~ [\d]+ :discard ~ whitespace whitespace ~ [\s]+ END_OF_SOURCE } ); my $input = <<EOI; scutil > open > show State:/Network/Service/0F70B221-EEF7-4ACC-96D8-ECBA3A15F132/IPv4 <dictionary> { ARPResolvedHardwareAddress : 00:1b:c0:4a:82:f9 ARPResolvedIPAddress : 10.10.0.254 AdditionalRoutes : <array> { 0 : <dictionary> { DestinationAddress : 10.10.0.146 SubnetMask : 255.255.255.255 } 1 : <dictionary> { DestinationAddress : 169.254.0.0 SubnetMask : 255.255.0.0 } } Addresses : <array> { 0 : 10.10.0.146 } ConfirmedInterfaceName : en0 InterfaceName : en0 NetworkSignature : IPv4.Router=10.10.0.254;IPv4.RouterHardwareAddress=00:1b:c0:4a:82:f9 Router : 10.10.0.254 SubnetMasks : <array> { 0 : 255.255.255.0 } } EOI say Dumper $g->parse( \$input, { trace_terminals => 0 } );
Output:
\[ 'scutil', 'scutil', '> open', '> show', [ 'path', 'State:/Network/Service/0F70B221-EEF7-4ACC-96D8-ECBA3A15F132/IPv4' ], '<dictionary>', '{', [ 'pairs', [ 'pair', [ 'name', 'ARPResolvedHardwareAddress' ], ':', [ 'value', [ 'mac', '00:1b:c0:4a:82:f9' ] ] ], [ 'pair', [ 'name', 'ARPResolvedIPAddress' ], ':', [ 'value', [ 'ip', '10.10.0.254' ] ] ], [ 'pair', [ 'name', 'AdditionalRoutes' ], ':', [ 'value', [ 'array', '<array>', '{', [ 'items', [ 'item', [ 'index', '0' ], ':', [ 'value', [ 'dict', '<dictionary>', '{', [ 'pairs', [ 'pair', [ 'name', 'DestinationAddress' ], ':', [ 'value', [ 'ip', '10.10.0.146' ] ] ], [ 'pair', [ 'name', 'SubnetMask' ], ':', [ 'value', [ 'ip', '255.255.255.255' ] ] ] ], '}' ] ] ], [ 'item', [ 'index', '1' ], ':', [ 'value', [ 'dict', '<dictionary>', '{', [ 'pairs', [ 'pair', [ 'name', 'DestinationAddress' ], ':', [ 'value', [ 'ip', '169.254.0.0' ] ] ], [ 'pair', [ 'name', 'SubnetMask' ], ':', [ 'value', [ 'ip', '255.255.0.0' ] ] ] ], '}' ] ] ] ], '}' ] ] ], [ 'pair', [ 'name', 'Addresses' ], ':', [ 'value', [ 'array', '<array>', '{', [ 'items', [ 'item', [ 'index', '0' ], ':', [ 'value', [ 'ip', '10.10.0.146' ] ] ] ], '}' ] ] ], [ 'pair', [ 'name', 'ConfirmedInterfaceName' ], ':', [ 'value', [ 'interface', 'en0' ] ] ], [ 'pair', [ 'name', 'InterfaceName' ], ':', [ 'value', [ 'interface', 'en0' ] ] ], [ 'pair', [ 'name', 'NetworkSignature' ], ':', [ 'value', [ 'signature', [ 'signature_item', [ 'signature_item_name', 'IPv4.Router' ], '=', [ 'signature_item_value', [ 'ip', '10.10.0.254' ] ] ], [ 'signature_item', [ 'signature_item_name', 'IPv4.RouterHardwareAddress' ], '=', [ 'signature_item_value', [ 'mac', '00:1b:c0:4a:82:f9' ] ] ] ] ] ], [ 'pair', [ 'name', 'Router' ], ':', [ 'value', [ 'ip', '10.10.0.254' ] ] ], [ 'pair', [ 'name', 'SubnetMasks' ], ':', [ 'value', [ 'array', '<array>', '{', [ 'items', [ 'item', [ 'index', '0' ], ':', [ 'value', [ 'ip', '255.255.255.0' ] ] ] ], '}' ] ] ] ], '}' ]