#!/usr/bin/env python import os import re import empty import nkf import dbg def buf_new(): dic = {} ks = [] e = empty.new() e.last = '' def anon_k(): id = 0 get = lambda : 'tMp_{}'.format( id ) while get() in dic: id += 1 return get() def set(s): def get_k(): k = s[ 2 : ] return k if k else anon_k() if s.startswith( '@>' ): ks.append( get_k() ) return True if s.startswith( '@<' ): if ks: e.last = ks.pop() return True if s.startswith( '@!' ): k = get_k() if k in dic: dic.pop( k ) if k == e.last: e.last = '' return True if ks: k = ks[ -1 ] if k not in dic: dic[ k ] = '' dic[ k ] += s + '\n' return True return False get = dic.get to_last_key = lambda s, k='$$': s.replace( k, e.last ) return empty.add( e, locals() ) def read_str(path): r = False with open( path, 'rb' ) as f: try: b = f.read() ( r, opt ) = nkf.to_str( b ) except: pass return r cut_tail_nl = lambda s: s[ : -1 ] if s and s[ -1 ] == '\n' else s def cut_quote(s): if s and len( s ) >= 2: for q in ( "''", '""', '<>' ): if s[ 0 ] == q[ 0 ] and s[ -1 ] == q[ -1 ]: return s[ 1 : -1 ] return s def indent(s, opts): def get_i(): k = '-s' for opt in opts: if opt.startswith( k ): r = opt[ len( k ) : ] if not r: return 1 elif r.isdigit(): return int( r ) return 0 i = get_i() tc = ' \\' if '-tc' in opts else '' if i > 0 or tc: s = '\n'.join( map( lambda s: ' ' * i + s + tc, s.split( '\n' ) ) ) return s def new( lst=[] ): lst += [ r'^@([^@\s]+)', ] pat_lst = list( map( re.compile, lst ) ) buf = buf_new() def match_path(s): for p in pat_lst: m = p.match( s ) if m and m.groups(): path = cut_quote( m.group( 1 ) ) opts = p.sub( '', s ).split() return ( path, opts ) return ( None, '' ) paths = [ '-' ] def cvt_opts(r, opts): for opt in opts: delim = '=' if delim in opt: i = opt.index( delim ) (k, v) = ( opt[ : i ], opt[ i + len( delim ) : ] ) r = r.replace( k, v ) return r search_dirs = [ os.path.dirname( __file__ ) ] def inc_text(s): if buf.set( s ): return True (path, opts) = match_path( s ) if path == None: return None path = buf.to_last_key( path ) opts = list( map( buf.to_last_key, opts ) ) r = buf.get( path ) if r != None: paths.append( path ) return empty.new( s=cut_tail_nl( r ), opts=opts ) path_lst = [ path ] if not path.startswith( '/' ): path_lst += list( map( lambda dir_: os.path.join( dir_, path ), search_dirs ) ) for path in path_lst: if os.path.exists( path ): if path in paths and not '-ad' in opts: return True r = read_str( path ) if r == False: break paths.append( path ) return empty.new( s=cut_tail_nl( r ), opts=opts ) if '-q' in opts: return True return False def exp_line(i, s): r = inc_text( s ) if r == False: dbg.err_exit( 'err {}, {} L{}\n'.format( s, paths[ -1 ], i+1 ) ) if r == None: return s if r == True: return True s = cvt_opts( r.s, r.opts ) if '-nr' not in r.opts: s = exp( s ) s = indent( s, r.opts ) paths.pop() return s def exp(s): f_map = lambda i_s: exp_line( *i_s ) f_filter = lambda s: s != True return '\n'.join( filter( f_filter, map( f_map, enumerate( s.split( '\n' ) ) ) ) ) return empty.new( locals() ) if __name__ == "__main__": inc = new() b = nkf.get_stdin() (s, opt) = nkf.to_str( b ) s = inc.exp( s ) b = nkf.str_to( s, opt ) nkf.put_stdout( b ) # EOF