3
Rg6                 @   sn   d Z ddlZddlZddlZdddddgZejdZd	d
 Zdd Zdd Z	dd Z
dd Zedkrje  dS )a   
Read ISC config grammar description produced by "cfg_test --grammar",
transform it into JSON, and print it to stdout.

Beware: This parser is pretty dumb and heavily depends on cfg_test output
format. See parse_mapbody() for more details.

Maps are recursively parsed into sub-dicts, all other elements (lists etc.)
are left intact and returned as one string.

Output example from named.conf grammar showing three variants follow.
Keys "_flags" and "_id" are present only if non-empty. Key "_grammar" denotes
end node, key "_mapbody" denotes a nested map.

{
    "acl": {
        "_flags": [
            "may occur multiple times"
        ],
        "_grammar": "<string> { <address_match_element>; ... }"
    },
    "http": {
        "_flags": [
            "may occur multiple times"
        ],
        "_id": "<string>",
        "_mapbody": {
            "endpoints": {
                "_grammar": "{ <quoted_string>; ... }"
            },
            "streams-per-connection": {
                "_grammar": "<integer>"
            }
        }
    },
    "options": {
        "_mapbody": {
            "rate-limit": {
                "_mapbody": {
                    "all-per-second": {
                        "_grammar": "<integer>"
                    }
                }
            }
        }
    }
}
    Nzmay occur multiple timesobsolete
deprecatedexperimentalz	test onlyz[a-zA-Z0-9-]+c             C   s   d| kst dt| }xvdD ]n}yt| j||}W n tk
rL   wY nX |dkr| j jdsntd| d| |d d	 krtd
| qW | d	| }| |d	 }||fS )a  Split line on comment boundary and strip right-side whitespace.
    Supports only #, //, and /* comments which end at the end of line.
    It does NOT handle:
    - quoted strings
    - /* comments which do not end at line boundary
    - multiple /* comments on a single line
    "zlines with " are not supported#///*z*/z7unsupported /* comment, does not end at the end of line   Nz*unsupported line with multiple /* comments)r   r   r   )AssertionErrorlenminindex
ValueErrorrstripendswithNotImplementedError)lineZdata_end_idx	delimiterZ
noncommentcomment r   A/home/abuild/rpmbuild/BUILD/bind-9.18.33/doc/misc/parsegrammar.pysplit_commentsM   s"    
r   c             c   s>   x8| D ]0}t |\}}|j }|j }|s,q||fV  qW dS )z?Consume single line from input, return non-comment and comment.N)r   strip)fileinr   r   r   r   r   
parse_linel   s    
r   c             C   s(   g }xt D ]}|| kr
|j| q
W |S )zMExtract known flags from comments. Must match exact strings used by cfg_test.)FLAGSappend)commentsoutflagr   r   r   parse_flagsw   s
    
r    c             C   s.  i }xt | D ]\}}t|}|dkr2||fS |j d jd}tj|sXtd||t|d j }|dd j }i ||< |j	dryt
| \}}W n tk
r   tddY nX |r||| d	< |r||| d
< ||| d< q|j	dst|r||| d	< ||| d< qW t|s*t|S )a3  Parse body of a "map" in ISC config format.

    Input lines can be only:
    - whitespace & comments only -> ignore
    - <keyword> <anything>; -> store <anything> as "_grammar" for this keyword
    - <keyword> <anything> { -> parse sub-map and store (optional) <anything> as "_id",
                                producing nested dict under "_mapbody"
    Also store known strings found at the end of line in "_flags".

    Returns:
    - tuple (map dict, map comment) when }; line is reached
    - map dict when we run out of lines without the closing };
    z};r   ;zsuspicious keyword detectedNr	   {z*unfinished nested map, missing }; detected_flags_id_mapbody_grammar)r   r    splitr   	KEY_REGEX	fullmatchr   r   r   r   parse_mapbodyr   r
   )r   Zthismapr   r   flagskeygrammarZsubkeysr   r   r   r+      s6    


r+   c              C   s2   t j } t| }W dQ R X ttj|dd dS )z/Read stdin or filename provided on command lineN   )indent)	fileinputinputr+   printjsondumps)r   r.   r   r   r   main   s    
r6   __main__)__doc__r1   r4   rer   compiler)   r   r   r    r+   r6   __name__r   r   r   r   <module>=   s    
	: