a
    V$cf                     @   s  d dl mZmZmZmZmZ d dlmZmZm	Z	m
Z
mZmZmZmZmZmZmZmZ d dlZd dlZd dlZd dlZd dlZd dlZd dlZd dlZeeeZe Zdada i Z!i Z"da#dd Z$i Z%dd Z&dPd	d
Z'dQddZ(e Z)dd Z*e Z+dd Z,dRddZ-i Z.dd Z/dd Z0dd Z1dd Z2dd Z3dd Z4d d! Z5d"d# Z6d$d% Z7d&d' Z8d(d) Z9d*d+ Z:d,d- Z;d.d/ Z<d0d1 Z=d2d3 Z>d4d5 Z?d6d7 Z@d8d9 ZAd:d; ZBd<d= ZCd>d? ZDd@dA ZEdBdC ZFdDdE ZGdFdG ZHG dHdI dIeIZJdJdK ZKdLdM ZLdNdO ZMdS )S    )divisionabsolute_importwith_statementprint_functionunicode_literals)PY2
basestringbchrbordchropenpystrrangeroundstrtobytesunicodeNFc                 G   sF   t rdtjt jt jf }nd}|| | 7 }td t| dad S )Nz%s:%d  T)report_noderenpyparserunicode_filenamefilename
linenumberprinterror_reported)msgargsout r   renpy/lint.pyreportB   s    r!   c                 G   s(   | t vr$dt | < t| | } t|  d S )NT)addedr   r   )r   r   r   r   r    addV   s    r#   c                 C   sb   t | | td|}|sdS ttj|dr4dS |dtv rFdS td||  |r^t	| dS )a  
    :doc: lint

    Tries to evaluate an expression, and writes an error to lint.txt if
    it fails.

    `where`
        A string giving the location the expression is found. Used to
        generate an error message of the form "Could not evaluate `expr`
        in `where`."

    `expr`
        The expression to try evaluating.

    `additional`
        If given, an additional line of information that is addded to the
        error message.
    z\s*([a-zA-Z_]\w*)N   zCould not evaluate '%s', in %s.)
try_compilerematchhasattrr   storegroup__builtins__r!   r#   )whereexpr
additionalmr   r   r    try_eval^   s    
r0   c                 C   s@   zt j| W n* ty:   td||  |r6t| Y n0 dS )a  
    :doc: lint

    Tries to compile an expression, and writes an error to lint.txt if
    it fails.

    `where`
        A string giving the location the expression is found. Used to
        generate an error message of the form "Could not evaluate `expr`
        in `where`."

    `expr`
        The expression to try compiling.

    `additional`
        If given, an additional line of information that is addded to the
        error message.
    z6'%s' could not be compiled as a python expression, %s.N)r   pythonpy_compile_eval_bytecode	Exceptionr!   r#   )r,   r-   r.   r   r   r    r%      s    r%   c                    s   | t v rdS | d }t }t }| dd D ].}|d dkrP||dd  q,|| q,tjjj D ]\}}|d |krqjt|dd   fdd|D rqjt|dd}|durΈ t||d |B   fd	d|D rqjt |   dS d
S )z
    Returns true if the image is a plausible image that can be used in a show
    statement. This returns true if at least one image exists with the same
    tag and containing all of the attributes (and none of the removed attributes).
    Tr   r$   N-c                    s   g | ]}| v r|qS r   r   .0iattrsr   r    
<listcomp>       z*image_exists_imprecise.<locals>.<listcomp>_list_attributesc                    s   g | ]}| vr|qS r   r   r5   r8   r   r    r:      r;   F)	imprecise_cachesetr#   r   displayimageimagesitemsgetattr)namenametagrequiredbannedr7   imdlir   r8   r    image_exists_imprecise   s.    
rK   c              	      s@  | t v rdS | d }t }t }| dd D ].}|d dkrP||dd  q,|| q,tjjj D ]\}}|d |krqjt|dd   | rqj|  r,zjtjj	 }|d ft
 fdd| dd D  |_t
fdd| dd D |_d|_|| W n ty*   Y qjY n0 t |   dS d	S )
z
    Returns true if an image exists with the same tag and attributes as
    `name`. (The attributes are allowed to occur in any order.)
    Tr   r$   Nr4   c                 3   s   | ]}| v r|V  qd S Nr   r5   r8   r   r    	<genexpr>   r;   z'image_exists_precise.<locals>.<genexpr>c                 3   s   | ]}| v r|V  qd S rL   r   r5   )restr   r    rM      r;   F)precise_cacher>   r#   r   r?   r@   rA   rB   coreDisplayableArgumentstuplerD   r   lint
_duplicater3   )rD   rE   rF   rG   r7   rH   rI   dar   )r9   rN   r    image_exists_precise   s8    * 

rV   Tc                 C   s   | }t jj| d dp&t jjdd}|dur8|| } |pB| d }dt|< |rTdS |sdt| rddS t| rpdS tdd| dS )z?
    Checks a scene or show statement for image existence.
    r   NTz'%s' is not an image. )	r   configadjust_attributesgetimage_prefixesrK   rV   r!   join)rD   
expressiontagpreciseorigfr   r   r    image_exists  s    $rb   c                 C   sh   t |d }|du rd S |du r4td|  | d S tj|s\td|  | dt |< d S dt |< d S )NTFz)%s uses file '%s', which is not loadable.)check_file_cacherZ   r!   
capitalizer   loaderloadable)whatfnpresentr   r   r    
check_file/  s    rj   c                    sh    fdd}|t jj_g  z"t|t jjjr:|dd  W n tyN   Y n0  D ]}t	| | qTd S )Nc                    s     |   d S rL   )extendpredict_files)imgfilesr   r    predict_imageB  s    z(check_displayable.<locals>.predict_imagec                 S   s   |   S rL   )predict_onear   r   r    <lambda>K  r;   z#check_displayable.<locals>.<lambda>)
r   r?   predictr@   
isinstancerP   Displayable	visit_allr3   rj   )rg   rI   rp   rh   r   rn   r    check_displayable@  s    
ry   c                 C   s*   d | j}td| tjjj| j  d S )NrW   zimage %s)r\   imgnamery   r   r?   r@   rA   )noderD   r   r   r    check_imageT  s    r|   c                 C   sh   t | dkr*| d d d | d | d dd fS t | dkr`| d | d | d | d | d | d d fS | S d S )N   r   r$               )len)tr   r   r    imspec[  s
    *r   c           
      C   s   | j s
d S t | j \}}}}}}}tj||p0|}|tjjvrV|tjjvrVtd| t||||d |D ]}	t	d|	d qjd S )N/Uses layer '%s', which is not in config.layers.)r_   z'the at list of a scene or show statmentz7Perhaps you forgot to define or misspelled a transform.)
r   r   exportsdefault_layerrX   layers
top_layersr!   rb   r0   )
r{   r_   rD   r]   r^   at_listlayer_zorder_behindr7   r   r   r    
check_showe  s    
r   c                 C   s$   | j s
d S t | j d }dt|< d S )Nr~   T)r   r[   )r{   r^   r   r   r    precheck_showx  s    r   c                 C   sj   t | j \}}}}}}}|p"|d }tj||}|tjjvrT|tjjvrTtd| |tvrftd| d S )Nr   r   zyThe image tag '%s' is not the prefix of a declared image, nor was it used in a show statement before this hide statement.)	r   r   r   r   rX   r   r   r!   r[   )r{   rD   _expressionr^   _at_listr   r   r   r   r   r    
check_hide  s    
r   c                 C   s   t d| jd d S )Nza with statement or clause;Perhaps you forgot to declare, or misspelled, a transition?)r0   r-   r{   r   r   r    
check_with  s    r   c                 C   sf   dd }t j| z| d W t j  nt j  0 z|   W n ty`   td Y n0 d S )Nc                 S   s   t d|  d S )Nz%s)r!   )r   r   r   r    error  s    zcheck_user.<locals>.errorrS   z9Didn't properly report what the next statement should be.)r   r   push_error_handlercallpop_error_handlerget_nextr3   r!   )r{   r   r   r   r    
check_user  s    r   c                 C   s<   |  dd} |  dd} |  dd} |  dd} d|  d S )	N\z\\"z\"	z\t
z\n)replace)sr   r   r    
quote_text  s
    r   c                 C   s   t jj| }|r"td|t|  d| v rt jjrd}d}d}|t| k r| | }|d7 }|dkrt|dkrd}d}q>|dkr||7 }|dkrd}q|dv rd}q|d	v rd}qtd
|t|  d}q>|dkr>||7 }|dkr>d}q>|dkrtd|t|  d S )Nz
%s (in %s)%r   r   r$   (r~   z#0123456780- +hlLzdiouxXeEfFgGcrs%z'Unknown string format code '%s' (in %s))z,Unterminated string format code '%s' (in %s))	r   textextrascheck_text_tagsr!   r   rX   old_substitutionsr   )r   r   stateposfmtcr   r   r    text_checks  s<    r   c                 C   s0  | j rFztj| j }W qJ tyB   td| j  td d }Y qJ0 nd }| jr^td| jd t	| j
 | jsrd S | j d u rd S t|tjjsd S |jd u rd S | j| jfD ]~}|d u rq|jf| }|}tjj|d d ptjjd d }|d ur||}t|r
qtd| rqtdd| qd S )	Nz;Could not evaluate '%s' in the who part of a say statement.z)Perhaps you forgot to define a character?z"the with clause of a say statementr   r   )sidezGCould not find image (%s) corresponding to attributes on say statement.rW   )whor   asteval_whor3   r!   r#   with_r0   r   rg   who_fastrv   	characterADVCharacter	image_tag
attributestemporary_attributesrX   rY   rZ   rK   r\   )r{   charr   rD   r`   ra   r   r   r    	check_say  s@    


$
r   c                 C   sX   | j rtd| j d dd | jD s,td | jD ] \}}}|rJtd| t| q2d S )Nz#the with clause of a menu statementr   c                 S   s    g | ]\}}}|r|||fqS r   r   )r6   lr   br   r   r    r:     r;   zcheck_menu.<locals>.<listcomp>z1The menu does not contain any selectable choices.zin the if clause of a menuitem)r   r0   rB   r!   r%   r   )r{   r   r   r   r   r   r    
check_menu  s    
r   c                 C   s*   | j r
d S tjj| js&td| j d S )Nz&The jump is to nonexistent label '%s'.)r]   r   gamescript	has_labeltargetr!   r   r   r   r    
check_jump  s    r   c                 C   s*   | j r
d S tjj| js&td| j d S )Nz&The call is to nonexistent label '%s'.)r]   r   r   r   r   labelr!   r   r   r   r    
check_call'  s    r   c                 C   s   t d| j d S )Nz'in the condition of the while statement)r%   	conditionr   r   r   r    check_while0  s    r   c                 C   s   | j D ]\}}td| qd S )Nz"in a condition of the if statement)entriesr%   )r{   r   _blockr   r   r    check_if4  s    r   c                 C   sv   | j dkr"|dkr"td| j d S | j dkr0d S | jtjjv rBd S | jtv rZtd|| j | jtv rrtd|| j d S )Nzstore.persistentdefinez^Define should not be used with a persistent variable. Use default persistent.%s = ... instead.r)   zB'%s %s' replaces a python built-in name, which may cause problems.zB'%s %s' replaces a Ren'Py built-in name, which may cause problems.)r)   r!   varnamer   rX   lint_ignore_replacespython_builtinsrenpy_builtins)r{   kindr   r   r    check_define9  s    


r   c                 C   s   |dkrt }n*|dkr4t}| jdkr.| jdu s8dS ndS | j}|drT|dd }|rhd|| j}n| j}|tj	j
v r~dS ||}|rtd|||j|j | ||< dS )	zJ
    Check if a define or default statement has already been created.
    defaultr   =Nzstore.r   z{}.{}z{} {} already defined at {}:{})all_default_statementsall_define_statmentsoperatorindexr)   
startswithformatr   r   rX   lint_ignore_redefinerZ   r!   r   r   )r{   r   scanned
store_name	full_nameoriginal_noder   r   r    check_redefinedL  s4    

r   c           
      C   s   |j std| || d S tj  dd }|tjj}|tjj|g D ]d}|D ]Z}|t	|d  }|tjj
v rV|jj|d}||}	|	  td| || |	  qNqVqNd S )Nz{}, property {}c                 S   s   t | } | jtd | S )Nkey)listsortr   )r   r   r   r    
sort_short  s    z4check_style_property_displayable.<locals>.sort_short)prefix)_duplicatablery   r   r   styleinit_inspectprefix_altsaffectsrZ   r   all_properties_argscopyrT   _unique)
rD   propertyrI   r   altspr   rN   r   ddr   r   r     check_style_property_displayableu  s*    


r   c                 C   s   |j D ]v}| D ]h\}}|dr^t|tjjjrTt|j	
 D ]}t| | qBn
t| | t|tjjjrt| || qqd S )Nfont)
propertiesrB   endswithrv   r   r   r   	FontGroupr>   mapvaluesrj   r?   rP   rw   r   )rD   r   r   kvra   r   r   r    check_style  s    


r   c                 C   sF   dd }| j }|d urB|j D ]\}}|| q||j ||j d S )Nc                 S   s*   | d u rd S t tj| s&ttj| d  d S rL   )r(   r   r)   setattrnr   r   r    add_arg  s    zcheck_label.<locals>.add_arg)
parametersextraposextrakw)r{   r   pir7   _r   r   r    check_label  s    

r  c                 C   s4   | j jd u r0tjjr0td| j j td| j j d S )Nz2The screen %s has not been given a parameter list.z4This can be fixed by writing 'screen %s():' instead.)screenr  r   rX   lint_screens_without_parametersr!   rD   r#   r   r   r   r    check_screen  s    r	  c                  C   sT   t jj D ]B\} }d| d  }| dd  D ]}|d|7 }q,td| | qd S )Nzstyle.r   r$   z[{!r}]zStyle )r   r   stylesrB   r   r   )r   r   rD   r7   r   r   r    check_styles  s
    r  c                 C   sP   t | }g }tt|D ],\}}|r8|d s8|dd |d| qd|S )Nr}   r   ,r   )r   	enumeratereversedinsertr\   )r   r   rvr7   r   r   r   r    humanize  s    r  c               	   C   sP   t j D ]@\} }z|d W q
W n ty6   Y n0 td| td q
dS )zF
    Checks files to ensure that they are displayable in unicode.
    asciiz1%s contains non-ASCII characters in its filename.zC(ZIP file distributions can only reliably include ASCII filenames.)N)r   re   listdirfilesencoder3   r!   r#   )_dirnamer   r   r   r    check_filename_encodings  s    

r  c                   @   s    e Zd ZdZdd Zdd ZdS )Countz2
    Stores information about the word count.
    c                 C   s   d| _ d| _d| _d S )Nr   )blockswords
characters)selfr   r   r    __init__  s    zCount.__init__c                 C   s:   |  j d7  _ |  jt| 7  _|  jt|7  _d S )Nr$   )r  r  r   splitr  )r  r   r   r   r    r#     s    z	Count.addN)__name__
__module____qualname____doc__r  r#   r   r   r   r    r    s   
r  c                 C   s.   | j dd}|ds"|dr&dS dS dS )z>
    Returns true if the node is in the common directory.
    r   /zcommon/zrenpy/common/TFN)r   r   r   )r   r   r   r   r    common  s    r#  c                 C   s   dg}t t}|  D ]\}}|| | qt| ddD ]\}}|  t|dkrj|d d }nHt|dkr|d d |d  d	 }n"d
|dd d |d  d	 }|d| t	| |dkrdnd d t|dkrdnd  q@|S )z1
    Returns a list of character stat lines.
    z,Character statistics (for default language):T)reverser$   r   z has r~   z and z have z, Nz, and  * z block z blocks zof dialoguez each..)
collectionsdefaultdictr   rB   appendsortedr   r   r\   r  )
charastatsr  count_to_charr   countcharsstartr   r   r    report_character_stats  s*    
"r1  c                     sL  t jjddd} | jddddd | jd	d
dd |  }|jrVt|jddd}|t_dt j	_
tdt j d t   t jd t jd i at jjjD ]}dt|d < qtt j	jj}|jdd d tt tt}d}d}d}d}	|D ]"}
t|
t jj t jj!frt"|
 q|D ]b}
t#|
r2q|
a$t|
t jj%rZ|	d7 }	t&|
 qt|
t jj rxt'|
d qt|
t jj!rt'|
d qt|
t jj(rt)|
 qt|
t jj*rt+|
 qt|
t jj,r"t-|
  | .|
j/ |du r||
j0r|
j0nd  d7  < qt|
t jj1rFt2|
 |d7 }qt|
t jj3rbt4|
 qt|
t jj5r~t6|
 qt|
t jj7rt8|
 nt|
t jj9rt:|
 nt|
t jj;rt<|
 nt|
t jj=rt>|
 nt|
t jj?r|
j@}nt|
t jjArd}nlt|
t jjBr6|d7 }tC|
 nJt|
t jjDr\tE|
d tF|
d n$t|
t jjGrtE|
d tF|
d qda$tH  tI  t jJjKD ]}|  qg  fdd}td td td  td t }|jd!d d |D ]}|| qLd"MtN|tN|	tN| t jJjOrJt jJjPrJLtQ| D ]}t|tRtfsh|f}|D ]\}|Sd#rd#}d$}|d%d }nd}d}tTU|d&tV| D ]}t||  |}qqltd qNt jJjWD ]}|  qtd t jJjOrt jJjXd'krtd( td td) td* tYrH|jZrHt jj[dd+ dS ),z_
    The master lint function, that's responsible for staging all of the
    other checks.
    z:Checks the script for errors and prints script statistics.F)descriptionrequire_commandr   ?r)   zThe file to write to.)nargsactionhelpz--error-code
store_truezYIf given, the error code is 0 if the game has no lint errros, 1 if lint errors are found.)r6  r7  wzutf-8)encodingTu   ﻿z lint report, generated at: _start_storer   c                 S   s   | j S rL   )r   r   r   r   r    rt   P  r;   zlint.<locals>.<lambda>r   Nr$   narratorr   r   c              	      s|    |  }|j dkrd S | d u r$d}n
d| }|dt|j t|jt|jd|j |j  d|j |j  7 }| d S )Nr   zThe gamezThe {0} translationz contains {0} dialogue blocks, containing {1} words
and {2} characters, for an average of {3:.1f} words and {4:.0f}
characters per block. g      ?)r  r   r  r  r  r*  )languager.  r   countslinesr   r    report_language  s    

	zlint.<locals>.report_languager   zStatistics:c                 S   s   | sdS | S )Nr   r   rr   r   r   r    rt     r;   z9The game contains {0} menus, {1} images, and {2} screens.r&  z   r}   N   autoz;Remember to set config.developer to False before releasing.zHLint is not a substitute for thorough testing. Remember to update Ren'PyzBbefore releasing. New releases fix bugs and improve compatibility.)status)\r   	argumentsArgumentParseradd_argument
parse_argsr   r   sysstdoutr   rS   r   versiontimectimer   execute_default_statementcall_in_new_contextr[   r?   r@   rA   r   r   	all_stmtsr   r(  r)  r  intrv   r   ShowScener   r#  r   Imager|   r   Hider   Withr   Sayr   r#   rg   r   Menur   Jumpr   Callr   Whiler   Ifr   UserStatementr   Labelr  	Translater=  EndTranslateScreenr	  Definer   r   Defaultr  r  rX   
lint_hooksr*  r   r  	developerlint_character_statisticsr1  rR   r   textwrapwrapr   lint_stats_callbacksoriginal_developerr   
error_codequit)apr   ra   r   rP  r,  r=  
menu_countscreen_countimage_countr{   rA  	languagesr7   r   llr   	altprefixlllr   r>  r    rS   +  s    





"








rS   )N)N)T)N
__future__r   r   r   r   r   renpy.compatr   r   r	   r
   r   r   r   r   r   r   r   r   codecsrL  r&   rI  r(  rg  builtinsr   r>   dirr   r   r[   r   r   r   r   r!   r"   r#   r0   r%   r=   rK   rO   rV   rb   rc   rj   ry   r|   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r	  r  r  r  objectr  r#  r1  rS   r   r   r   r    <module>   sr   8
,
-4


,2		)"	 