Blame parser.y

Packit ef9df4
%token WORD 1
Packit ef9df4
%token CLASS 2
Packit ef9df4
%token SEQUENCE 3
Packit ef9df4
%token SET 4
Packit ef9df4
%token CHOICE 5
Packit ef9df4
%token OF 6
Packit ef9df4
%token IMPLICIT 7
Packit ef9df4
%token EXPLICIT 8
Packit ef9df4
%token OPTIONAL 9
Packit ef9df4
%token LBRACE 10
Packit ef9df4
%token RBRACE 11
Packit ef9df4
%token COMMA 12
Packit ef9df4
%token ANY 13
Packit ef9df4
%token ASSIGN 14
Packit ef9df4
%token NUMBER 15
Packit ef9df4
%token ENUM 16
Packit ef9df4
%token COMPONENTS 17
Packit ef9df4
%token POSTRBRACE 18
Packit ef9df4
%token DEFINED 19
Packit ef9df4
%token BY 20
Packit ef9df4
%token EXTENSION_MARKER 21
Packit ef9df4
Packit ef9df4
%{
Packit ef9df4
# Copyright (c) 2000-2005 Graham Barr <gbarr@pobox.com>. All rights reserved.
Packit ef9df4
# This program is free software; you can redistribute it and/or
Packit ef9df4
# modify it under the same terms as Perl itself.
Packit ef9df4
Packit ef9df4
package Convert::ASN1::parser;
Packit ef9df4
Packit ef9df4
use strict;
Packit ef9df4
use Convert::ASN1 qw(:all);
Packit ef9df4
use vars qw(
Packit ef9df4
  $asn $yychar $yyerrflag $yynerrs $yyn @yyss
Packit ef9df4
  $yyssp $yystate @yyvs $yyvsp $yylval $yys $yym $yyval
Packit ef9df4
);
Packit ef9df4
Packit ef9df4
BEGIN { Convert::ASN1->_internal_syms }
Packit ef9df4
Packit ef9df4
my $yydebug=0;
Packit ef9df4
my %yystate;
Packit ef9df4
Packit ef9df4
my %base_type = (
Packit ef9df4
  BOOLEAN	    => [ asn_encode_tag(ASN_BOOLEAN),		opBOOLEAN ],
Packit ef9df4
  INTEGER	    => [ asn_encode_tag(ASN_INTEGER),		opINTEGER ],
Packit ef9df4
  BIT_STRING	    => [ asn_encode_tag(ASN_BIT_STR),		opBITSTR  ],
Packit ef9df4
  OCTET_STRING	    => [ asn_encode_tag(ASN_OCTET_STR),		opSTRING  ],
Packit ef9df4
  STRING	    => [ asn_encode_tag(ASN_OCTET_STR),		opSTRING  ],
Packit ef9df4
  NULL 		    => [ asn_encode_tag(ASN_NULL),		opNULL    ],
Packit ef9df4
  OBJECT_IDENTIFIER => [ asn_encode_tag(ASN_OBJECT_ID),		opOBJID   ],
Packit ef9df4
  REAL		    => [ asn_encode_tag(ASN_REAL),		opREAL    ],
Packit ef9df4
  ENUMERATED	    => [ asn_encode_tag(ASN_ENUMERATED),	opINTEGER ],
Packit ef9df4
  ENUM		    => [ asn_encode_tag(ASN_ENUMERATED),	opINTEGER ],
Packit ef9df4
  'RELATIVE-OID'    => [ asn_encode_tag(ASN_RELATIVE_OID),	opROID	  ],
Packit ef9df4
Packit ef9df4
  SEQUENCE	    => [ asn_encode_tag(ASN_SEQUENCE | ASN_CONSTRUCTOR), opSEQUENCE ],
Packit ef9df4
  EXPLICIT	    => [ asn_encode_tag(ASN_SEQUENCE | ASN_CONSTRUCTOR), opEXPLICIT ],
Packit ef9df4
  SET               => [ asn_encode_tag(ASN_SET      | ASN_CONSTRUCTOR), opSET ],
Packit ef9df4
Packit ef9df4
  ObjectDescriptor  => [ asn_encode_tag(ASN_UNIVERSAL |  7), opSTRING ],
Packit ef9df4
  UTF8String        => [ asn_encode_tag(ASN_UNIVERSAL | 12), opUTF8 ],
Packit ef9df4
  NumericString     => [ asn_encode_tag(ASN_UNIVERSAL | 18), opSTRING ],
Packit ef9df4
  PrintableString   => [ asn_encode_tag(ASN_UNIVERSAL | 19), opSTRING ],
Packit ef9df4
  TeletexString     => [ asn_encode_tag(ASN_UNIVERSAL | 20), opSTRING ],
Packit ef9df4
  T61String         => [ asn_encode_tag(ASN_UNIVERSAL | 20), opSTRING ],
Packit ef9df4
  VideotexString    => [ asn_encode_tag(ASN_UNIVERSAL | 21), opSTRING ],
Packit ef9df4
  IA5String         => [ asn_encode_tag(ASN_UNIVERSAL | 22), opSTRING ],
Packit ef9df4
  UTCTime           => [ asn_encode_tag(ASN_UNIVERSAL | 23), opUTIME ],
Packit ef9df4
  GeneralizedTime   => [ asn_encode_tag(ASN_UNIVERSAL | 24), opGTIME ],
Packit ef9df4
  GraphicString     => [ asn_encode_tag(ASN_UNIVERSAL | 25), opSTRING ],
Packit ef9df4
  VisibleString     => [ asn_encode_tag(ASN_UNIVERSAL | 26), opSTRING ],
Packit ef9df4
  ISO646String      => [ asn_encode_tag(ASN_UNIVERSAL | 26), opSTRING ],
Packit ef9df4
  GeneralString     => [ asn_encode_tag(ASN_UNIVERSAL | 27), opSTRING ],
Packit ef9df4
  CharacterString   => [ asn_encode_tag(ASN_UNIVERSAL | 28), opSTRING ],
Packit ef9df4
  UniversalString   => [ asn_encode_tag(ASN_UNIVERSAL | 28), opSTRING ],
Packit ef9df4
  BMPString         => [ asn_encode_tag(ASN_UNIVERSAL | 30), opSTRING ],
Packit ef9df4
  BCDString         => [ asn_encode_tag(ASN_OCTET_STR), opBCD ],
Packit ef9df4
Packit ef9df4
  CHOICE => [ '', opCHOICE ],
Packit ef9df4
  ANY    => [ '', opANY ],
Packit ef9df4
Packit ef9df4
  EXTENSION_MARKER => [ '', opEXTENSIONS ],
Packit ef9df4
);
Packit ef9df4
Packit ef9df4
my $tagdefault = 1; # 0:IMPLICIT , 1:EXPLICIT default
Packit ef9df4
Packit ef9df4
# args: class,plicit
Packit ef9df4
sub need_explicit {
Packit ef9df4
  (defined($_[0]) && (defined($_[1])?$_[1]:$tagdefault));
Packit ef9df4
}
Packit ef9df4
Packit ef9df4
# Given an OP, wrap it in a SEQUENCE
Packit ef9df4
Packit ef9df4
sub explicit {
Packit ef9df4
  my $op = shift;
Packit ef9df4
  my @seq = @$op;
Packit ef9df4
Packit ef9df4
  @seq[cTYPE,cCHILD,cVAR,cLOOP] = ('EXPLICIT',[$op],undef,undef);
Packit ef9df4
  @{$op}[cTAG,cOPT] = ();
Packit ef9df4
Packit ef9df4
  \@seq;
Packit ef9df4
}
Packit ef9df4
Packit ef9df4
%}
Packit ef9df4
Packit ef9df4
%%
Packit ef9df4
Packit ef9df4
top	: slist		{ $$ = { '' => $1 }; }
Packit ef9df4
	| module
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
module  : WORD ASSIGN aitem
Packit ef9df4
		{
Packit ef9df4
		  $$ = { $1, [$3] };
Packit ef9df4
		}
Packit ef9df4
	| module WORD ASSIGN aitem
Packit ef9df4
		{
Packit ef9df4
		  $$=$1;
Packit ef9df4
		  $$->{$2} = [$4];
Packit ef9df4
		}
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
aitem	: class plicit anyelem postrb
Packit ef9df4
		{
Packit ef9df4
		  $3->[cTAG] = $1;
Packit ef9df4
		  $$ = need_explicit($1,$2) ? explicit($3) : $3;
Packit ef9df4
		}
Packit ef9df4
	| celem
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
anyelem : onelem
Packit ef9df4
	| eelem
Packit ef9df4
	| oelem
Packit ef9df4
	| selem
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
celem	: COMPONENTS OF WORD
Packit ef9df4
		{
Packit ef9df4
		  @{$$ = []}[cTYPE,cCHILD] = ('COMPONENTS', $3);
Packit ef9df4
		}
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
seqset	: SEQUENCE
Packit ef9df4
	| SET
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
selem	: seqset OF class plicit sselem optional
Packit ef9df4
		{
Packit ef9df4
		  $5->[cTAG] = $3;
Packit ef9df4
		  @{$$ = []}[cTYPE,cCHILD,cLOOP,cOPT] = ($1, [$5], 1, $6);
Packit ef9df4
		  $$ = explicit($$) if need_explicit($3,$4);
Packit ef9df4
		}
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
sselem	: eelem
Packit ef9df4
	| oelem
Packit ef9df4
	| onelem
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
onelem	: SEQUENCE LBRACE slist RBRACE
Packit ef9df4
		{
Packit ef9df4
		  @{$$ = []}[cTYPE,cCHILD] = ('SEQUENCE', $3);
Packit ef9df4
		}
Packit ef9df4
	| SET      LBRACE slist RBRACE
Packit ef9df4
		{
Packit ef9df4
		  @{$$ = []}[cTYPE,cCHILD] = ('SET', $3);
Packit ef9df4
		}
Packit ef9df4
	| CHOICE   LBRACE nlist RBRACE
Packit ef9df4
		{
Packit ef9df4
		  @{$$ = []}[cTYPE,cCHILD] = ('CHOICE', $3);
Packit ef9df4
		}
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
eelem   : ENUM LBRACE elist RBRACE
Packit ef9df4
		{
Packit ef9df4
		  @{$$ = []}[cTYPE] = ('ENUM');
Packit ef9df4
		}
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
oielem	: WORD					{ @{$$ = []}[cTYPE] = $1; }
Packit ef9df4
	| SEQUENCE				{ @{$$ = []}[cTYPE] = $1; }
Packit ef9df4
	| SET					{ @{$$ = []}[cTYPE] = $1; }
Packit ef9df4
	| ANY defined
Packit ef9df4
		{
Packit ef9df4
		  @{$$ = []}[cTYPE,cCHILD,cDEFINE] = ('ANY',undef,$2);
Packit ef9df4
		}
Packit ef9df4
	| ENUM					{ @{$$ = []}[cTYPE] = $1; }
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
defined :			{ $$=undef; }
Packit ef9df4
	| DEFINED BY WORD	{ $$=$3; }
Packit ef9df4
	;	
Packit ef9df4
Packit ef9df4
oelem	: oielem 
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
nlist	: nlist1		{ $$ = $1; }
Packit ef9df4
	| nlist1 POSTRBRACE	{ $$ = $1; }
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
nlist1	: nitem
Packit ef9df4
		{
Packit ef9df4
		  $$ = [ $1 ];
Packit ef9df4
		}
Packit ef9df4
	| nlist1 POSTRBRACE nitem
Packit ef9df4
		{
Packit ef9df4
		  push @{$$=$1}, $3;
Packit ef9df4
		}
Packit ef9df4
	| nlist1 COMMA nitem
Packit ef9df4
		{
Packit ef9df4
		  push @{$$=$1}, $3;
Packit ef9df4
		}
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
nitem	: WORD class plicit anyelem
Packit ef9df4
		{
Packit ef9df4
		  @{$$=$4}[cVAR,cTAG] = ($1,$2);
Packit ef9df4
		  $$ = explicit($$) if need_explicit($2,$3);
Packit ef9df4
		}
Packit ef9df4
	| EXTENSION_MARKER
Packit ef9df4
		{
Packit ef9df4
		    @{$$=[]}[cTYPE] = 'EXTENSION_MARKER';
Packit ef9df4
		}
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
Packit ef9df4
slist	:                       { $$ = []; }
Packit ef9df4
        | slist1
Packit ef9df4
		{
Packit ef9df4
		  my $extension = 0;
Packit ef9df4
		  $$ = [];
Packit ef9df4
		  for my $i (@{$1}) {
Packit ef9df4
		    $extension = 1 if $i->[cTYPE] eq 'EXTENSION_MARKER';
Packit ef9df4
		    $i->[cEXT] = $i->[cOPT];
Packit ef9df4
		    $i->[cEXT] = 1 if $extension;
Packit ef9df4
		    push @{$$}, $i unless $i->[cTYPE] eq 'EXTENSION_MARKER';
Packit ef9df4
		  }
Packit ef9df4
		  my $e = []; $e->[cTYPE] = 'EXTENSION_MARKER';
Packit ef9df4
		  push @{$$}, $e if $extension;
Packit ef9df4
		}
Packit ef9df4
	| slist1 POSTRBRACE
Packit ef9df4
		{
Packit ef9df4
		  my $extension = 0;
Packit ef9df4
		  $$ = [];
Packit ef9df4
		  for my $i (@{$1}) {
Packit ef9df4
		    $extension = 1 if $i->[cTYPE] eq 'EXTENSION_MARKER';
Packit ef9df4
		    $i->[cEXT] = $i->[cOPT];
Packit ef9df4
		    $i->[cEXT] = 1 if $extension;
Packit ef9df4
		    push @{$$}, $i unless $i->[cTYPE] eq 'EXTENSION_MARKER';
Packit ef9df4
		  }
Packit ef9df4
		  my $e = []; $e->[cTYPE] = 'EXTENSION_MARKER';
Packit ef9df4
		  push @{$$}, $e if $extension;
Packit ef9df4
		}
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
slist1	: sitem
Packit ef9df4
		{
Packit ef9df4
		  $$ = [ $1 ];
Packit ef9df4
		}
Packit ef9df4
	| slist1 COMMA sitem
Packit ef9df4
		{
Packit ef9df4
		  push @{$$=$1}, $3;
Packit ef9df4
		}
Packit ef9df4
	| slist1 POSTRBRACE sitem
Packit ef9df4
		{
Packit ef9df4
		  push @{$$=$1}, $3;
Packit ef9df4
		}
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
snitem	: oelem optional
Packit ef9df4
		{
Packit ef9df4
		  @{$$=$1}[cOPT] = ($2);
Packit ef9df4
		}
Packit ef9df4
	| eelem
Packit ef9df4
	| selem
Packit ef9df4
	| onelem
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
sitem	: WORD class plicit snitem 
Packit ef9df4
		{
Packit ef9df4
		  @{$$=$4}[cVAR,cTAG] = ($1,$2);
Packit ef9df4
		  $$->[cOPT] = $1 if $$->[cOPT];
Packit ef9df4
		  $$ = explicit($$) if need_explicit($2,$3);
Packit ef9df4
		}
Packit ef9df4
	| celem
Packit ef9df4
	| class plicit onelem
Packit ef9df4
		{
Packit ef9df4
		  @{$$=$3}[cTAG] = ($1);
Packit ef9df4
		  $$ = explicit($$) if need_explicit($1,$2);
Packit ef9df4
		}
Packit ef9df4
	| EXTENSION_MARKER
Packit ef9df4
		{
Packit ef9df4
		    @{$$=[]}[cTYPE] = 'EXTENSION_MARKER';
Packit ef9df4
		}
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
optional :			{ $$ = undef; }
Packit ef9df4
	 | OPTIONAL		{ $$ = 1;     }
Packit ef9df4
	 ;
Packit ef9df4
Packit ef9df4
Packit ef9df4
class	:			{ $$ = undef; }
Packit ef9df4
	| CLASS
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
plicit	:			{ $$ = undef; }
Packit ef9df4
	| EXPLICIT		{ $$ = 1;     }
Packit ef9df4
	| IMPLICIT		{ $$ = 0;     }
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
elist	: eitem			{}
Packit ef9df4
	| elist COMMA eitem	{}
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
eitem	: WORD NUMBER		{}
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
postrb	:			{}
Packit ef9df4
	| POSTRBRACE		{}
Packit ef9df4
	;
Packit ef9df4
Packit ef9df4
%%
Packit ef9df4
Packit ef9df4
my %reserved = (
Packit ef9df4
  'OPTIONAL' 	=> $OPTIONAL,
Packit ef9df4
  'CHOICE' 	=> $CHOICE,
Packit ef9df4
  'OF' 		=> $OF,
Packit ef9df4
  'IMPLICIT' 	=> $IMPLICIT,
Packit ef9df4
  'EXPLICIT' 	=> $EXPLICIT,
Packit ef9df4
  'SEQUENCE'    => $SEQUENCE,
Packit ef9df4
  'SET'         => $SET,
Packit ef9df4
  'ANY'         => $ANY,
Packit ef9df4
  'ENUM'        => $ENUM,
Packit ef9df4
  'ENUMERATED'  => $ENUM,
Packit ef9df4
  'COMPONENTS'  => $COMPONENTS,
Packit ef9df4
  '{'		=> $LBRACE,
Packit ef9df4
  '}'		=> $RBRACE,
Packit ef9df4
  ','		=> $COMMA,
Packit ef9df4
  '::='         => $ASSIGN,
Packit ef9df4
  'DEFINED'     => $DEFINED,
Packit ef9df4
  'BY'		=> $BY
Packit ef9df4
);
Packit ef9df4
Packit ef9df4
my $reserved = join("|", reverse sort grep { /\w/ } keys %reserved);
Packit ef9df4
Packit ef9df4
my %tag_class = (
Packit ef9df4
  APPLICATION => ASN_APPLICATION,
Packit ef9df4
  UNIVERSAL   => ASN_UNIVERSAL,
Packit ef9df4
  PRIVATE     => ASN_PRIVATE,
Packit ef9df4
  CONTEXT     => ASN_CONTEXT,
Packit ef9df4
  ''	      => ASN_CONTEXT # if not specified, its CONTEXT
Packit ef9df4
);
Packit ef9df4
Packit ef9df4
;##
Packit ef9df4
;## This is NOT thread safe !!!!!!
Packit ef9df4
;##
Packit ef9df4
Packit ef9df4
my $pos;
Packit ef9df4
my $last_pos;
Packit ef9df4
my @stacked;
Packit ef9df4
Packit ef9df4
sub parse {
Packit ef9df4
  local(*asn) = \($_[0]);
Packit ef9df4
  $tagdefault = $_[1] eq 'EXPLICIT' ? 1 : 0;
Packit ef9df4
  ($pos,$last_pos,@stacked) = ();
Packit ef9df4
Packit ef9df4
  eval {
Packit ef9df4
    local $SIG{__DIE__};
Packit ef9df4
    compile(verify(yyparse()));
Packit ef9df4
  }
Packit ef9df4
}
Packit ef9df4
Packit ef9df4
sub compile_one {
Packit ef9df4
  my $tree = shift;
Packit ef9df4
  my $ops = shift;
Packit ef9df4
  my $name = shift;
Packit ef9df4
  foreach my $op (@$ops) {
Packit ef9df4
    next unless ref($op) eq 'ARRAY';
Packit ef9df4
    bless $op;
Packit ef9df4
    my $type = $op->[cTYPE];
Packit ef9df4
    if (exists $base_type{$type}) {
Packit ef9df4
      $op->[cTYPE] = $base_type{$type}->[1];
Packit ef9df4
      $op->[cTAG] = defined($op->[cTAG]) ? asn_encode_tag($op->[cTAG]): $base_type{$type}->[0];
Packit ef9df4
    }
Packit ef9df4
    else {
Packit ef9df4
      die "Unknown type '$type'\n" unless exists $tree->{$type};
Packit ef9df4
      my $ref = compile_one(
Packit ef9df4
		  $tree,
Packit ef9df4
		  $tree->{$type},
Packit ef9df4
		  defined($op->[cVAR]) ? $name . "." . $op->[cVAR] : $name
Packit ef9df4
		);
Packit ef9df4
      if (defined($op->[cTAG]) && $ref->[0][cTYPE] == opCHOICE) {
Packit ef9df4
        @{$op}[cTYPE,cCHILD] = (opSEQUENCE,$ref);
Packit ef9df4
      }
Packit ef9df4
      else {
Packit ef9df4
        @{$op}[cTYPE,cCHILD,cLOOP] = @{$ref->[0]}[cTYPE,cCHILD,cLOOP];
Packit ef9df4
      }
Packit ef9df4
      $op->[cTAG] = defined($op->[cTAG]) ? asn_encode_tag($op->[cTAG]): $ref->[0][cTAG];
Packit ef9df4
    }
Packit ef9df4
    $op->[cTAG] |= pack("C",ASN_CONSTRUCTOR)
Packit ef9df4
      if length $op->[cTAG] && ($op->[cTYPE] == opSET || $op->[cTYPE] == opEXPLICIT || $op->[cTYPE] == opSEQUENCE);
Packit ef9df4
Packit ef9df4
    if ($op->[cCHILD]) {
Packit ef9df4
      ;# If we have children we are one of
Packit ef9df4
      ;#  opSET opSEQUENCE opCHOICE opEXPLICIT
Packit ef9df4
Packit ef9df4
      compile_one($tree, $op->[cCHILD], defined($op->[cVAR]) ? $name . "." . $op->[cVAR] : $name);
Packit ef9df4
Packit ef9df4
      ;# If a CHOICE is given a tag, then it must be EXPLICIT
Packit ef9df4
      if ($op->[cTYPE] == opCHOICE && defined($op->[cTAG]) && length($op->[cTAG])) {
Packit ef9df4
	$op = bless explicit($op);
Packit ef9df4
	$op->[cTYPE] = opSEQUENCE;
Packit ef9df4
      }
Packit ef9df4
Packit ef9df4
      if ( @{$op->[cCHILD]} > 1) {
Packit ef9df4
        ;#if ($op->[cTYPE] != opSEQUENCE) {
Packit ef9df4
        ;# Here we need to flatten CHOICEs and check that SET and CHOICE
Packit ef9df4
        ;# do not contain duplicate tags
Packit ef9df4
        ;#}
Packit ef9df4
	if ($op->[cTYPE] == opSET) {
Packit ef9df4
	  ;# In case we do CER encoding we order the SET elements by thier tags
Packit ef9df4
	  my @tags = map { 
Packit ef9df4
	    length($_->[cTAG])
Packit ef9df4
		? $_->[cTAG]
Packit ef9df4
		: $_->[cTYPE] == opCHOICE
Packit ef9df4
			? (sort map { $_->[cTAG] } $_->[cCHILD])[0]
Packit ef9df4
			: ''
Packit ef9df4
	  } @{$op->[cCHILD]};
Packit ef9df4
	  @{$op->[cCHILD]} = @{$op->[cCHILD]}[sort { $tags[$a] cmp $tags[$b] } 0..$#tags];
Packit ef9df4
	}
Packit ef9df4
      }
Packit ef9df4
      else {
Packit ef9df4
	;# A SET of one element can be treated the same as a SEQUENCE
Packit ef9df4
	$op->[cTYPE] = opSEQUENCE if $op->[cTYPE] == opSET;
Packit ef9df4
      }
Packit ef9df4
    }
Packit ef9df4
  }
Packit ef9df4
  $ops;
Packit ef9df4
}
Packit ef9df4
Packit ef9df4
sub compile {
Packit ef9df4
  my $tree = shift;
Packit ef9df4
Packit ef9df4
  ;# The tree should be valid enough to be able to
Packit ef9df4
  ;#  - resolve references
Packit ef9df4
  ;#  - encode tags
Packit ef9df4
  ;#  - verify CHOICEs do not contain duplicate tags
Packit ef9df4
Packit ef9df4
  ;# once references have been resolved, and also due to
Packit ef9df4
  ;# flattening of COMPONENTS, it is possible for an op
Packit ef9df4
  ;# to appear in multiple places. So once an op is
Packit ef9df4
  ;# compiled we bless it. This ensure we dont try to
Packit ef9df4
  ;# compile it again.
Packit ef9df4
Packit ef9df4
  while(my($k,$v) = each %$tree) {
Packit ef9df4
    compile_one($tree,$v,$k);
Packit ef9df4
  }
Packit ef9df4
Packit ef9df4
  $tree;
Packit ef9df4
}
Packit ef9df4
Packit ef9df4
sub verify {
Packit ef9df4
  my $tree = shift or return;
Packit ef9df4
  my $err = "";
Packit ef9df4
Packit ef9df4
  ;# Well it parsed correctly, now we
Packit ef9df4
  ;#  - check references exist
Packit ef9df4
  ;#  - flatten COMPONENTS OF (checking for loops)
Packit ef9df4
  ;#  - check for duplicate var names
Packit ef9df4
Packit ef9df4
  while(my($name,$ops) = each %$tree) {
Packit ef9df4
    my $stash = {};
Packit ef9df4
    my @scope = ();
Packit ef9df4
    my $path = "";
Packit ef9df4
    my $idx = 0;
Packit ef9df4
Packit ef9df4
    while($ops) {
Packit ef9df4
      if ($idx < @$ops) {
Packit ef9df4
	my $op = $ops->[$idx++];
Packit ef9df4
	my $var;
Packit ef9df4
	if (defined ($var = $op->[cVAR])) {
Packit ef9df4
	  
Packit ef9df4
	  $err .= "$name: $path.$var used multiple times\n"
Packit ef9df4
	    if $stash->{$var}++;
Packit ef9df4
Packit ef9df4
	}
Packit ef9df4
	if (defined $op->[cCHILD]) {
Packit ef9df4
	  if (ref $op->[cCHILD]) {
Packit ef9df4
	    push @scope, [$stash, $path, $ops, $idx];
Packit ef9df4
	    if (defined $var) {
Packit ef9df4
	      $stash = {};
Packit ef9df4
	      $path .= "." . $var;
Packit ef9df4
	    }
Packit ef9df4
	    $idx = 0;
Packit ef9df4
	    $ops = $op->[cCHILD];
Packit ef9df4
	  }
Packit ef9df4
	  elsif ($op->[cTYPE] eq 'COMPONENTS') {
Packit ef9df4
	    splice(@$ops,--$idx,1,expand_ops($tree, $op->[cCHILD]));
Packit ef9df4
	  }
Packit ef9df4
          else {
Packit ef9df4
	    die "Internal error\n";
Packit ef9df4
          }
Packit ef9df4
	}
Packit ef9df4
      }
Packit ef9df4
      else {
Packit ef9df4
	my $s = pop @scope
Packit ef9df4
	  or last;
Packit ef9df4
	($stash,$path,$ops,$idx) = @$s;
Packit ef9df4
      }
Packit ef9df4
    }
Packit ef9df4
  }
Packit ef9df4
  die $err if length $err;
Packit ef9df4
  $tree;
Packit ef9df4
}
Packit ef9df4
Packit ef9df4
sub expand_ops {
Packit ef9df4
  my $tree = shift;
Packit ef9df4
  my $want = shift;
Packit ef9df4
  my $seen = shift || { };
Packit ef9df4
  
Packit ef9df4
  die "COMPONENTS OF loop $want\n" if $seen->{$want}++;
Packit ef9df4
  die "Undefined macro $want\n" unless exists $tree->{$want};
Packit ef9df4
  my $ops = $tree->{$want};
Packit ef9df4
  die "Bad macro for COMPUNENTS OF '$want'\n"
Packit ef9df4
    unless @$ops == 1
Packit ef9df4
        && ($ops->[0][cTYPE] eq 'SEQUENCE' || $ops->[0][cTYPE] eq 'SET')
Packit ef9df4
        && ref $ops->[0][cCHILD];
Packit ef9df4
  $ops = $ops->[0][cCHILD];
Packit ef9df4
  for(my $idx = 0 ; $idx < @$ops ; ) {
Packit ef9df4
    my $op = $ops->[$idx++];
Packit ef9df4
    if ($op->[cTYPE] eq 'COMPONENTS') {
Packit ef9df4
      splice(@$ops,--$idx,1,expand_ops($tree, $op->[cCHILD], $seen));
Packit ef9df4
    }
Packit ef9df4
  }
Packit ef9df4
Packit ef9df4
  @$ops;
Packit ef9df4
}
Packit ef9df4
Packit ef9df4
sub _yylex {
Packit ef9df4
  my $ret = &_yylex;
Packit ef9df4
  warn $ret;
Packit ef9df4
  $ret;
Packit ef9df4
}
Packit ef9df4
Packit ef9df4
sub yylex {
Packit ef9df4
  return shift @stacked if @stacked;
Packit ef9df4
Packit ef9df4
  while ($asn =~ /\G(?:
Packit ef9df4
	  (\s+|--[^\n]*)
Packit ef9df4
	|
Packit ef9df4
	  ([,{}]|::=)
Packit ef9df4
	|
Packit ef9df4
	  ($reserved)\b
Packit ef9df4
	|
Packit ef9df4
	  (
Packit ef9df4
	    (?:OCTET|BIT)\s+STRING
Packit ef9df4
	   |
Packit ef9df4
	    OBJECT\s+IDENTIFIER
Packit ef9df4
	   |
Packit ef9df4
	    RELATIVE-OID
Packit ef9df4
	  )\b
Packit ef9df4
	|
Packit ef9df4
	  (\w+(?:-\w+)*)
Packit ef9df4
	|
Packit ef9df4
	    \[\s*
Packit ef9df4
	  (
Packit ef9df4
	   (?:(?:APPLICATION|PRIVATE|UNIVERSAL|CONTEXT)\s+)?
Packit ef9df4
	   \d+
Packit ef9df4
          )
Packit ef9df4
	    \s*\]
Packit ef9df4
	|
Packit ef9df4
	  \((\d+)\)
Packit ef9df4
	|
Packit ef9df4
	  (\.\.\.)
Packit ef9df4
	)/sxgo
Packit ef9df4
  ) {
Packit ef9df4
Packit ef9df4
    ($last_pos,$pos) = ($pos,pos($asn));
Packit ef9df4
Packit ef9df4
    next if defined $1; # comment or whitespace
Packit ef9df4
Packit ef9df4
    if (defined $2 or defined $3) {
Packit ef9df4
      my $ret = $+;
Packit ef9df4
Packit ef9df4
      # A comma is not required after a '}' so to aid the
Packit ef9df4
      # parser we insert a fake token after any '}'
Packit ef9df4
      if ($ret eq '}') {
Packit ef9df4
        my $p   = pos($asn);
Packit ef9df4
        my @tmp = @stacked;
Packit ef9df4
        @stacked = ();
Packit ef9df4
        pos($asn) = $p if yylex() != $COMMA;    # swallow it
Packit ef9df4
        @stacked = (@tmp, $POSTRBRACE);
Packit ef9df4
      }
Packit ef9df4
Packit ef9df4
      return $reserved{$yylval = $ret};
Packit ef9df4
    }
Packit ef9df4
Packit ef9df4
    if (defined $4) {
Packit ef9df4
      ($yylval = $+) =~ s/\s+/_/g;
Packit ef9df4
      return $WORD;
Packit ef9df4
    }
Packit ef9df4
Packit ef9df4
    if (defined $5) {
Packit ef9df4
      $yylval = $+;
Packit ef9df4
      return $WORD;
Packit ef9df4
    }
Packit ef9df4
Packit ef9df4
    if (defined $6) {
Packit ef9df4
      my($class,$num) = ($+ =~ /^([A-Z]*)\s*(\d+)$/);
Packit ef9df4
      $yylval = asn_tag($tag_class{$class}, $num); 
Packit ef9df4
      return $CLASS;
Packit ef9df4
    }
Packit ef9df4
Packit ef9df4
    if (defined $7) {
Packit ef9df4
      $yylval = $+;
Packit ef9df4
      return $NUMBER;
Packit ef9df4
    }
Packit ef9df4
Packit ef9df4
    if (defined $8) {
Packit ef9df4
      return $EXTENSION_MARKER;
Packit ef9df4
    }
Packit ef9df4
Packit ef9df4
    die "Internal error\n";
Packit ef9df4
Packit ef9df4
  }
Packit ef9df4
Packit ef9df4
  die "Parse error before ",substr($asn,$pos,40),"\n"
Packit ef9df4
    unless $pos == length($asn);
Packit ef9df4
Packit ef9df4
  0
Packit ef9df4
}
Packit ef9df4
Packit ef9df4
sub yyerror {
Packit ef9df4
  die @_," ",substr($asn,$last_pos,40),"\n";
Packit ef9df4
}
Packit ef9df4
Packit ef9df4
1;
Packit ef9df4