root/crypto/heimdal/lib/asn1/asn1parse.y
/*
 * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
 * (Royal Institute of Technology, Stockholm, Sweden).
 * All rights reserved.
 *
 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the Institute nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/* $Id$ */

%{

#include <config.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "symbol.h"
#include "lex.h"
#include "gen_locl.h"
#include "der.h"

RCSID("$Id$");

static Type *new_type (Typetype t);
static struct constraint_spec *new_constraint_spec(enum ctype);
static Type *new_tag(int tagclass, int tagvalue, int tagenv, Type *oldtype);
void yyerror (const char *);
static struct objid *new_objid(const char *label, int value);
static void add_oid_to_tail(struct objid *, struct objid *);
static void fix_labels(Symbol *s);

struct string_list {
    char *string;
    struct string_list *next;
};

/* Declarations for Bison */
#define YYMALLOC malloc
#define YYFREE   free

%}

%union {
    int64_t constant;
    struct value *value;
    struct range *range;
    char *name;
    Type *type;
    Member *member;
    struct objid *objid;
    char *defval;
    struct string_list *sl;
    struct tagtype tag;
    struct memhead *members;
    struct constraint_spec *constraint_spec;
}

%token kw_ABSENT
%token kw_ABSTRACT_SYNTAX
%token kw_ALL
%token kw_APPLICATION
%token kw_AUTOMATIC
%token kw_BEGIN
%token kw_BIT
%token kw_BMPString
%token kw_BOOLEAN
%token kw_BY
%token kw_CHARACTER
%token kw_CHOICE
%token kw_CLASS
%token kw_COMPONENT
%token kw_COMPONENTS
%token kw_CONSTRAINED
%token kw_CONTAINING
%token kw_DEFAULT
%token kw_DEFINITIONS
%token kw_EMBEDDED
%token kw_ENCODED
%token kw_END
%token kw_ENUMERATED
%token kw_EXCEPT
%token kw_EXPLICIT
%token kw_EXPORTS
%token kw_EXTENSIBILITY
%token kw_EXTERNAL
%token kw_FALSE
%token kw_FROM
%token kw_GeneralString
%token kw_GeneralizedTime
%token kw_GraphicString
%token kw_IA5String
%token kw_IDENTIFIER
%token kw_IMPLICIT
%token kw_IMPLIED
%token kw_IMPORTS
%token kw_INCLUDES
%token kw_INSTANCE
%token kw_INTEGER
%token kw_INTERSECTION
%token kw_ISO646String
%token kw_MAX
%token kw_MIN
%token kw_MINUS_INFINITY
%token kw_NULL
%token kw_NumericString
%token kw_OBJECT
%token kw_OCTET
%token kw_OF
%token kw_OPTIONAL
%token kw_ObjectDescriptor
%token kw_PATTERN
%token kw_PDV
%token kw_PLUS_INFINITY
%token kw_PRESENT
%token kw_PRIVATE
%token kw_PrintableString
%token kw_REAL
%token kw_RELATIVE_OID
%token kw_SEQUENCE
%token kw_SET
%token kw_SIZE
%token kw_STRING
%token kw_SYNTAX
%token kw_T61String
%token kw_TAGS
%token kw_TRUE
%token kw_TYPE_IDENTIFIER
%token kw_TeletexString
%token kw_UNION
%token kw_UNIQUE
%token kw_UNIVERSAL
%token kw_UTCTime
%token kw_UTF8String
%token kw_UniversalString
%token kw_VideotexString
%token kw_VisibleString
%token kw_WITH

%token RANGE
%token EEQUAL
%token ELLIPSIS

%token <name> IDENTIFIER  referencename
%token <name> STRING

%token <constant> NUMBER
%type <constant> SignedNumber
%type <constant> Class tagenv

%type <value> Value
%type <value> BuiltinValue
%type <value> IntegerValue
%type <value> BooleanValue
%type <value> ObjectIdentifierValue
%type <value> CharacterStringValue
%type <value> NullValue
%type <value> DefinedValue
%type <value> ReferencedValue
%type <value> Valuereference

%type <type> Type
%type <type> BuiltinType
%type <type> BitStringType
%type <type> BooleanType
%type <type> ChoiceType
%type <type> ConstrainedType
%type <type> EnumeratedType
%type <type> IntegerType
%type <type> NullType
%type <type> OctetStringType
%type <type> SequenceType
%type <type> SequenceOfType
%type <type> SetType
%type <type> SetOfType
%type <type> TaggedType
%type <type> ReferencedType
%type <type> DefinedType
%type <type> UsefulType
%type <type> ObjectIdentifierType
%type <type> CharacterStringType
%type <type> RestrictedCharactedStringType

%type <tag> Tag

%type <member> ComponentType
%type <member> NamedBit
%type <member> NamedNumber
%type <member> NamedType
%type <members> ComponentTypeList
%type <members> Enumerations
%type <members> NamedBitList
%type <members> NamedNumberList

%type <objid> objid objid_list objid_element objid_opt
%type <range> range size

%type <sl> referencenames

%type <constraint_spec> Constraint
%type <constraint_spec> ConstraintSpec
%type <constraint_spec> GeneralConstraint
%type <constraint_spec> ContentsConstraint
%type <constraint_spec> UserDefinedConstraint



%start ModuleDefinition

%%

ModuleDefinition: IDENTIFIER objid_opt kw_DEFINITIONS TagDefault ExtensionDefault
                        EEQUAL kw_BEGIN ModuleBody kw_END
                {
                        checkundefined();
                }
                ;

TagDefault      : kw_EXPLICIT kw_TAGS
                | kw_IMPLICIT kw_TAGS
                      { lex_error_message("implicit tagging is not supported"); }
                | kw_AUTOMATIC kw_TAGS
                      { lex_error_message("automatic tagging is not supported"); }
                | /* empty */
                ;

ExtensionDefault: kw_EXTENSIBILITY kw_IMPLIED
                      { lex_error_message("no extensibility options supported"); }
                | /* empty */
                ;

ModuleBody      : Exports Imports AssignmentList
                | /* empty */
                ;

Imports         : kw_IMPORTS SymbolsImported ';'
                | /* empty */
                ;

SymbolsImported : SymbolsFromModuleList
                | /* empty */
                ;

SymbolsFromModuleList: SymbolsFromModule
                | SymbolsFromModuleList SymbolsFromModule
                ;

SymbolsFromModule: referencenames kw_FROM IDENTIFIER objid_opt
                {
                    struct string_list *sl;
                    for(sl = $1; sl != NULL; sl = sl->next) {
                        Symbol *s = addsym(sl->string);
                        s->stype = Stype;
                        gen_template_import(s);
                    }
                    add_import($3);
                }
                ;

Exports         : kw_EXPORTS referencenames ';'
                {
                    struct string_list *sl;
                    for(sl = $2; sl != NULL; sl = sl->next)
                        add_export(sl->string);
                }
                | kw_EXPORTS kw_ALL
                | /* empty */
                ;

AssignmentList  : Assignment
                | Assignment AssignmentList
                ;

Assignment      : TypeAssignment
                | ValueAssignment
                ;

referencenames  : IDENTIFIER ',' referencenames
                {
                    $$ = emalloc(sizeof(*$$));
                    $$->string = $1;
                    $$->next = $3;
                }
                | IDENTIFIER
                {
                    $$ = emalloc(sizeof(*$$));
                    $$->string = $1;
                    $$->next = NULL;
                }
                ;

TypeAssignment  : IDENTIFIER EEQUAL Type
                {
                    Symbol *s = addsym ($1);
                    s->stype = Stype;
                    s->type = $3;
                    fix_labels(s);
                    generate_type (s);
                }
                ;

Type            : BuiltinType
                | ReferencedType
                | ConstrainedType
                ;

BuiltinType     : BitStringType
                | BooleanType
                | CharacterStringType
                | ChoiceType
                | EnumeratedType
                | IntegerType
                | NullType
                | ObjectIdentifierType
                | OctetStringType
                | SequenceType
                | SequenceOfType
                | SetType
                | SetOfType
                | TaggedType
                ;

BooleanType     : kw_BOOLEAN
                {
                        $$ = new_tag(ASN1_C_UNIV, UT_Boolean,
                                     TE_EXPLICIT, new_type(TBoolean));
                }
                ;

range           : '(' Value RANGE Value ')'
                {
                    if($2->type != integervalue)
                        lex_error_message("Non-integer used in first part of range");
                    if($2->type != integervalue)
                        lex_error_message("Non-integer in second part of range");
                    $$ = ecalloc(1, sizeof(*$$));
                    $$->min = $2->u.integervalue;
                    $$->max = $4->u.integervalue;
                }
                | '(' Value RANGE kw_MAX ')'
                {
                    if($2->type != integervalue)
                        lex_error_message("Non-integer in first part of range");
                    $$ = ecalloc(1, sizeof(*$$));
                    $$->min = $2->u.integervalue;
                    $$->max = INT_MAX;
                }
                | '(' kw_MIN RANGE Value ')'
                {
                    if($4->type != integervalue)
                        lex_error_message("Non-integer in second part of range");
                    $$ = ecalloc(1, sizeof(*$$));
                    $$->min = INT_MIN;
                    $$->max = $4->u.integervalue;
                }
                | '(' Value ')'
                {
                    if($2->type != integervalue)
                        lex_error_message("Non-integer used in limit");
                    $$ = ecalloc(1, sizeof(*$$));
                    $$->min = $2->u.integervalue;
                    $$->max = $2->u.integervalue;
                }
                ;


IntegerType     : kw_INTEGER
                {
                        $$ = new_tag(ASN1_C_UNIV, UT_Integer,
                                     TE_EXPLICIT, new_type(TInteger));
                }
                | kw_INTEGER range
                {
                        $$ = new_type(TInteger);
                        $$->range = $2;
                        $$ = new_tag(ASN1_C_UNIV, UT_Integer, TE_EXPLICIT, $$);
                }
                | kw_INTEGER '{' NamedNumberList '}'
                {
                  $$ = new_type(TInteger);
                  $$->members = $3;
                  $$ = new_tag(ASN1_C_UNIV, UT_Integer, TE_EXPLICIT, $$);
                }
                ;

NamedNumberList : NamedNumber
                {
                        $$ = emalloc(sizeof(*$$));
                        ASN1_TAILQ_INIT($$);
                        ASN1_TAILQ_INSERT_HEAD($$, $1, members);
                }
                | NamedNumberList ',' NamedNumber
                {
                        ASN1_TAILQ_INSERT_TAIL($1, $3, members);
                        $$ = $1;
                }
                | NamedNumberList ',' ELLIPSIS
                        { $$ = $1; } /* XXX used for Enumerations */
                ;

NamedNumber     : IDENTIFIER '(' SignedNumber ')'
                {
                        $$ = emalloc(sizeof(*$$));
                        $$->name = $1;
                        $$->gen_name = estrdup($1);
                        output_name ($$->gen_name);
                        $$->val = $3;
                        $$->optional = 0;
                        $$->ellipsis = 0;
                        $$->type = NULL;
                }
                ;

EnumeratedType  : kw_ENUMERATED '{' Enumerations '}'
                {
                  $$ = new_type(TInteger);
                  $$->members = $3;
                  $$ = new_tag(ASN1_C_UNIV, UT_Enumerated, TE_EXPLICIT, $$);
                }
                ;

Enumerations    : NamedNumberList /* XXX */
                ;

BitStringType   : kw_BIT kw_STRING
                {
                  $$ = new_type(TBitString);
                  $$->members = emalloc(sizeof(*$$->members));
                  ASN1_TAILQ_INIT($$->members);
                  $$ = new_tag(ASN1_C_UNIV, UT_BitString, TE_EXPLICIT, $$);
                }
                | kw_BIT kw_STRING '{' NamedBitList '}'
                {
                  $$ = new_type(TBitString);
                  $$->members = $4;
                  $$ = new_tag(ASN1_C_UNIV, UT_BitString, TE_EXPLICIT, $$);
                }
                ;

ObjectIdentifierType: kw_OBJECT kw_IDENTIFIER
                {
                        $$ = new_tag(ASN1_C_UNIV, UT_OID,
                                     TE_EXPLICIT, new_type(TOID));
                }
                ;
OctetStringType : kw_OCTET kw_STRING size
                {
                    Type *t = new_type(TOctetString);
                    t->range = $3;
                    $$ = new_tag(ASN1_C_UNIV, UT_OctetString,
                                 TE_EXPLICIT, t);
                }
                ;

NullType        : kw_NULL
                {
                        $$ = new_tag(ASN1_C_UNIV, UT_Null,
                                     TE_EXPLICIT, new_type(TNull));
                }
                ;

size            :
                { $$ = NULL; }
                | kw_SIZE range
                { $$ = $2; }
                ;


SequenceType    : kw_SEQUENCE '{' /* ComponentTypeLists */ ComponentTypeList '}'
                {
                  $$ = new_type(TSequence);
                  $$->members = $3;
                  $$ = new_tag(ASN1_C_UNIV, UT_Sequence, TE_EXPLICIT, $$);
                }
                | kw_SEQUENCE '{' '}'
                {
                  $$ = new_type(TSequence);
                  $$->members = NULL;
                  $$ = new_tag(ASN1_C_UNIV, UT_Sequence, TE_EXPLICIT, $$);
                }
                ;

SequenceOfType  : kw_SEQUENCE size kw_OF Type
                {
                  $$ = new_type(TSequenceOf);
                  $$->range = $2;
                  $$->subtype = $4;
                  $$ = new_tag(ASN1_C_UNIV, UT_Sequence, TE_EXPLICIT, $$);
                }
                ;

SetType         : kw_SET '{' /* ComponentTypeLists */ ComponentTypeList '}'
                {
                  $$ = new_type(TSet);
                  $$->members = $3;
                  $$ = new_tag(ASN1_C_UNIV, UT_Set, TE_EXPLICIT, $$);
                }
                | kw_SET '{' '}'
                {
                  $$ = new_type(TSet);
                  $$->members = NULL;
                  $$ = new_tag(ASN1_C_UNIV, UT_Set, TE_EXPLICIT, $$);
                }
                ;

SetOfType       : kw_SET kw_OF Type
                {
                  $$ = new_type(TSetOf);
                  $$->subtype = $3;
                  $$ = new_tag(ASN1_C_UNIV, UT_Set, TE_EXPLICIT, $$);
                }
                ;

ChoiceType      : kw_CHOICE '{' /* AlternativeTypeLists */ ComponentTypeList '}'
                {
                  $$ = new_type(TChoice);
                  $$->members = $3;
                }
                ;

ReferencedType  : DefinedType
                | UsefulType
                ;

DefinedType     : IDENTIFIER
                {
                  Symbol *s = addsym($1);
                  $$ = new_type(TType);
                  if(s->stype != Stype && s->stype != SUndefined)
                    lex_error_message ("%s is not a type\n", $1);
                  else
                    $$->symbol = s;
                }
                ;

UsefulType      : kw_GeneralizedTime
                {
                        $$ = new_tag(ASN1_C_UNIV, UT_GeneralizedTime,
                                     TE_EXPLICIT, new_type(TGeneralizedTime));
                }
                | kw_UTCTime
                {
                        $$ = new_tag(ASN1_C_UNIV, UT_UTCTime,
                                     TE_EXPLICIT, new_type(TUTCTime));
                }
                ;

ConstrainedType : Type Constraint
                {
                    /* if (Constraint.type == contentConstrant) {
                       assert(Constraint.u.constraint.type == octetstring|bitstring-w/o-NamedBitList); // remember to check type reference too
                       if (Constraint.u.constraint.type) {
                         assert((Constraint.u.constraint.type.length % 8) == 0);
                       }
                      }
                      if (Constraint.u.constraint.encoding) {
                        type == der-oid|ber-oid
                      }
                    */
                }
                ;


Constraint      : '(' ConstraintSpec ')'
                {
                    $$ = $2;
                }
                ;

ConstraintSpec  : GeneralConstraint
                ;

GeneralConstraint: ContentsConstraint
                | UserDefinedConstraint
                ;

ContentsConstraint: kw_CONTAINING Type
                {
                    $$ = new_constraint_spec(CT_CONTENTS);
                    $$->u.content.type = $2;
                    $$->u.content.encoding = NULL;
                }
                | kw_ENCODED kw_BY Value
                {
                    if ($3->type != objectidentifiervalue)
                        lex_error_message("Non-OID used in ENCODED BY constraint");
                    $$ = new_constraint_spec(CT_CONTENTS);
                    $$->u.content.type = NULL;
                    $$->u.content.encoding = $3;
                }
                | kw_CONTAINING Type kw_ENCODED kw_BY Value
                {
                    if ($5->type != objectidentifiervalue)
                        lex_error_message("Non-OID used in ENCODED BY constraint");
                    $$ = new_constraint_spec(CT_CONTENTS);
                    $$->u.content.type = $2;
                    $$->u.content.encoding = $5;
                }
                ;

UserDefinedConstraint: kw_CONSTRAINED kw_BY '{' '}'
                {
                    $$ = new_constraint_spec(CT_USER);
                }
                ;

TaggedType      : Tag tagenv Type
                {
                        $$ = new_type(TTag);
                        $$->tag = $1;
                        $$->tag.tagenv = $2;
                        if($3->type == TTag && $2 == TE_IMPLICIT) {
                                $$->subtype = $3->subtype;
                                free($3);
                        } else
                                $$->subtype = $3;
                }
                ;

Tag             : '[' Class NUMBER ']'
                {
                        $$.tagclass = $2;
                        $$.tagvalue = $3;
                        $$.tagenv = TE_EXPLICIT;
                }
                ;

Class           : /* */
                {
                        $$ = ASN1_C_CONTEXT;
                }
                | kw_UNIVERSAL
                {
                        $$ = ASN1_C_UNIV;
                }
                | kw_APPLICATION
                {
                        $$ = ASN1_C_APPL;
                }
                | kw_PRIVATE
                {
                        $$ = ASN1_C_PRIVATE;
                }
                ;

tagenv          : /* */
                {
                        $$ = TE_EXPLICIT;
                }
                | kw_EXPLICIT
                {
                        $$ = TE_EXPLICIT;
                }
                | kw_IMPLICIT
                {
                        $$ = TE_IMPLICIT;
                }
                ;


ValueAssignment : IDENTIFIER Type EEQUAL Value
                {
                        Symbol *s;
                        s = addsym ($1);

                        s->stype = SValue;
                        s->value = $4;
                        generate_constant (s);
                }
                ;

CharacterStringType: RestrictedCharactedStringType
                ;

RestrictedCharactedStringType: kw_GeneralString
                {
                        $$ = new_tag(ASN1_C_UNIV, UT_GeneralString,
                                     TE_EXPLICIT, new_type(TGeneralString));
                }
                | kw_TeletexString
                {
                        $$ = new_tag(ASN1_C_UNIV, UT_TeletexString,
                                     TE_EXPLICIT, new_type(TTeletexString));
                }
                | kw_UTF8String
                {
                        $$ = new_tag(ASN1_C_UNIV, UT_UTF8String,
                                     TE_EXPLICIT, new_type(TUTF8String));
                }
                | kw_PrintableString
                {
                        $$ = new_tag(ASN1_C_UNIV, UT_PrintableString,
                                     TE_EXPLICIT, new_type(TPrintableString));
                }
                | kw_VisibleString
                {
                        $$ = new_tag(ASN1_C_UNIV, UT_VisibleString,
                                     TE_EXPLICIT, new_type(TVisibleString));
                }
                | kw_IA5String
                {
                        $$ = new_tag(ASN1_C_UNIV, UT_IA5String,
                                     TE_EXPLICIT, new_type(TIA5String));
                }
                | kw_BMPString
                {
                        $$ = new_tag(ASN1_C_UNIV, UT_BMPString,
                                     TE_EXPLICIT, new_type(TBMPString));
                }
                | kw_UniversalString
                {
                        $$ = new_tag(ASN1_C_UNIV, UT_UniversalString,
                                     TE_EXPLICIT, new_type(TUniversalString));
                }

                ;

ComponentTypeList: ComponentType
                {
                        $$ = emalloc(sizeof(*$$));
                        ASN1_TAILQ_INIT($$);
                        ASN1_TAILQ_INSERT_HEAD($$, $1, members);
                }
                | ComponentTypeList ',' ComponentType
                {
                        ASN1_TAILQ_INSERT_TAIL($1, $3, members);
                        $$ = $1;
                }
                | ComponentTypeList ',' ELLIPSIS
                {
                        struct member *m = ecalloc(1, sizeof(*m));
                        m->name = estrdup("...");
                        m->gen_name = estrdup("asn1_ellipsis");
                        m->ellipsis = 1;
                        ASN1_TAILQ_INSERT_TAIL($1, m, members);
                        $$ = $1;
                }
                ;

NamedType       : IDENTIFIER Type
                {
                  $$ = emalloc(sizeof(*$$));
                  $$->name = $1;
                  $$->gen_name = estrdup($1);
                  output_name ($$->gen_name);
                  $$->type = $2;
                  $$->ellipsis = 0;
                }
                ;

ComponentType   : NamedType
                {
                        $$ = $1;
                        $$->optional = 0;
                        $$->defval = NULL;
                }
                | NamedType kw_OPTIONAL
                {
                        $$ = $1;
                        $$->optional = 1;
                        $$->defval = NULL;
                }
                | NamedType kw_DEFAULT Value
                {
                        $$ = $1;
                        $$->optional = 0;
                        $$->defval = $3;
                }
                ;

NamedBitList    : NamedBit
                {
                        $$ = emalloc(sizeof(*$$));
                        ASN1_TAILQ_INIT($$);
                        ASN1_TAILQ_INSERT_HEAD($$, $1, members);
                }
                | NamedBitList ',' NamedBit
                {
                        ASN1_TAILQ_INSERT_TAIL($1, $3, members);
                        $$ = $1;
                }
                ;

NamedBit        : IDENTIFIER '(' NUMBER ')'
                {
                  $$ = emalloc(sizeof(*$$));
                  $$->name = $1;
                  $$->gen_name = estrdup($1);
                  output_name ($$->gen_name);
                  $$->val = $3;
                  $$->optional = 0;
                  $$->ellipsis = 0;
                  $$->type = NULL;
                }
                ;

objid_opt       : objid
                | /* empty */ { $$ = NULL; }
                ;

objid           : '{' objid_list '}'
                {
                        $$ = $2;
                }
                ;

objid_list      :  /* empty */
                {
                        $$ = NULL;
                }
                | objid_element objid_list
                {
                        if ($2) {
                                $$ = $2;
                                add_oid_to_tail($2, $1);
                        } else {
                                $$ = $1;
                        }
                }
                ;

objid_element   : IDENTIFIER '(' NUMBER ')'
                {
                        $$ = new_objid($1, $3);
                }
                | IDENTIFIER
                {
                    Symbol *s = addsym($1);
                    if(s->stype != SValue ||
                       s->value->type != objectidentifiervalue) {
                        lex_error_message("%s is not an object identifier\n",
                                      s->name);
                        exit(1);
                    }
                    $$ = s->value->u.objectidentifiervalue;
                }
                | NUMBER
                {
                    $$ = new_objid(NULL, $1);
                }
                ;

Value           : BuiltinValue
                | ReferencedValue
                ;

BuiltinValue    : BooleanValue
                | CharacterStringValue
                | IntegerValue
                | ObjectIdentifierValue
                | NullValue
                ;

ReferencedValue : DefinedValue
                ;

DefinedValue    : Valuereference
                ;

Valuereference  : IDENTIFIER
                {
                        Symbol *s = addsym($1);
                        if(s->stype != SValue)
                                lex_error_message ("%s is not a value\n",
                                                s->name);
                        else
                                $$ = s->value;
                }
                ;

CharacterStringValue: STRING
                {
                        $$ = emalloc(sizeof(*$$));
                        $$->type = stringvalue;
                        $$->u.stringvalue = $1;
                }
                ;

BooleanValue    : kw_TRUE
                {
                        $$ = emalloc(sizeof(*$$));
                        $$->type = booleanvalue;
                        $$->u.booleanvalue = 0;
                }
                | kw_FALSE
                {
                        $$ = emalloc(sizeof(*$$));
                        $$->type = booleanvalue;
                        $$->u.booleanvalue = 0;
                }
                ;

IntegerValue    : SignedNumber
                {
                        $$ = emalloc(sizeof(*$$));
                        $$->type = integervalue;
                        $$->u.integervalue = $1;
                }
                ;

SignedNumber    : NUMBER
                ;

NullValue       : kw_NULL
                {
                }
                ;

ObjectIdentifierValue: objid
                {
                        $$ = emalloc(sizeof(*$$));
                        $$->type = objectidentifiervalue;
                        $$->u.objectidentifiervalue = $1;
                }
                ;

%%

void
yyerror (const char *s)
{
     lex_error_message ("%s\n", s);
}

static Type *
new_tag(int tagclass, int tagvalue, int tagenv, Type *oldtype)
{
    Type *t;
    if(oldtype->type == TTag && oldtype->tag.tagenv == TE_IMPLICIT) {
        t = oldtype;
        oldtype = oldtype->subtype; /* XXX */
    } else
        t = new_type (TTag);

    t->tag.tagclass = tagclass;
    t->tag.tagvalue = tagvalue;
    t->tag.tagenv = tagenv;
    t->subtype = oldtype;
    return t;
}

static struct objid *
new_objid(const char *label, int value)
{
    struct objid *s;
    s = emalloc(sizeof(*s));
    s->label = label;
    s->value = value;
    s->next = NULL;
    return s;
}

static void
add_oid_to_tail(struct objid *head, struct objid *tail)
{
    struct objid *o;
    o = head;
    while (o->next)
        o = o->next;
    o->next = tail;
}

static Type *
new_type (Typetype tt)
{
    Type *t = ecalloc(1, sizeof(*t));
    t->type = tt;
    return t;
}

static struct constraint_spec *
new_constraint_spec(enum ctype ct)
{
    struct constraint_spec *c = ecalloc(1, sizeof(*c));
    c->ctype = ct;
    return c;
}

static void fix_labels2(Type *t, const char *prefix);
static void fix_labels1(struct memhead *members, const char *prefix)
{
    Member *m;

    if(members == NULL)
        return;
    ASN1_TAILQ_FOREACH(m, members, members) {
        if (asprintf(&m->label, "%s_%s", prefix, m->gen_name) < 0)
            errx(1, "malloc");
        if (m->label == NULL)
            errx(1, "malloc");
        if(m->type != NULL)
            fix_labels2(m->type, m->label);
    }
}

static void fix_labels2(Type *t, const char *prefix)
{
    for(; t; t = t->subtype)
        fix_labels1(t->members, prefix);
}

static void
fix_labels(Symbol *s)
{
    char *p = NULL;
    if (asprintf(&p, "choice_%s", s->gen_name) < 0 || p == NULL)
        errx(1, "malloc");
    fix_labels2(s->type, p);
    free(p);
}