1###################################################################### 2# The remainder of this file is from parsedesc.{g,py} 3 4def append(lst, x): 5 "Imperative append" 6 lst.append(x) 7 return lst 8 9def add_inline_token(tokens, str): 10 tokens.insert( 0, (str, eval(str, {}, {})) ) 11 return Terminal(str) 12 13def cleanup_choice(lst): 14 if len(lst) == 0: return Sequence([]) 15 if len(lst) == 1: return lst[0] 16 return apply(Choice, tuple(lst)) 17 18def cleanup_sequence(lst): 19 if len(lst) == 1: return lst[0] 20 return apply(Sequence, tuple(lst)) 21 22def cleanup_rep(node, rep): 23 if rep == 'star': return Star(node) 24 elif rep == 'plus': return Plus(node) 25 else: return node 26 27def resolve_name(tokens, id, args): 28 if id in map(lambda x: x[0], tokens): 29 # It's a token 30 if args: 31 print 'Warning: ignoring parameters on TOKEN %s<<%s>>' % (id, args) 32 return Terminal(id) 33 else: 34 # It's a name, so assume it's a nonterminal 35 return NonTerminal(id, args) 36 37%% 38parser ParserDescription: 39 option: "context-insensitive-scanner" 40 41 ignore: "[ \t\r\n]+" 42 ignore: "#.*?\r?\n" 43 token END: "$" 44 token ATTR: "<<.+?>>" 45 token STMT: "{{.+?}}" 46 token ID: '[a-zA-Z_][a-zA-Z_0-9]*' 47 token STR: '[rR]?\'([^\\n\'\\\\]|\\\\.)*\'|[rR]?"([^\\n"\\\\]|\\\\.)*"' 48 token LP: '\\(' 49 token RP: '\\)' 50 token LB: '\\[' 51 token RB: '\\]' 52 token OR: '[|]' 53 token STAR: '[*]' 54 token PLUS: '[+]' 55 token QUEST: '[?]' 56 token COLON: ':' 57 58 rule Parser: "parser" ID ":" 59 Options 60 Tokens 61 Rules<<Tokens>> 62 END 63 {{ return Generator(ID,Options,Tokens,Rules) }} 64 65 rule Options: {{ opt = {} }} 66 ( "option" ":" Str {{ opt[Str] = 1 }} )* 67 {{ return opt }} 68 69 rule Tokens: {{ tok = [] }} 70 ( 71 "token" ID ":" Str {{ tok.append( (ID,Str) ) }} 72 | "ignore" ":" Str {{ tok.append( ('#ignore',Str) ) }} 73 )* 74 {{ return tok }} 75 76 rule Rules<<tokens>>: 77 {{ rul = [] }} 78 ( 79 "rule" ID OptParam ":" ClauseA<<tokens>> 80 {{ rul.append( (ID,OptParam,ClauseA) ) }} 81 )* 82 {{ return rul }} 83 84 rule ClauseA<<tokens>>: 85 ClauseB<<tokens>> 86 {{ v = [ClauseB] }} 87 ( OR ClauseB<<tokens>> {{ v.append(ClauseB) }} )* 88 {{ return cleanup_choice(v) }} 89 90 rule ClauseB<<tokens>>: 91 {{ v = [] }} 92 ( ClauseC<<tokens>> {{ v.append(ClauseC) }} )* 93 {{ return cleanup_sequence(v) }} 94 95 rule ClauseC<<tokens>>: 96 ClauseD<<tokens>> 97 ( PLUS {{ return Plus(ClauseD) }} 98 | STAR {{ return Star(ClauseD) }} 99 | {{ return ClauseD }} ) 100 101 rule ClauseD<<tokens>>: 102 STR {{ t = (STR, eval(STR,{},{})) }} 103 {{ if t not in tokens: tokens.insert( 0, t ) }} 104 {{ return Terminal(STR) }} 105 | ID OptParam {{ return resolve_name(tokens, ID, OptParam) }} 106 | LP ClauseA<<tokens>> RP {{ return ClauseA }} 107 | LB ClauseA<<tokens>> RB {{ return Option(ClauseA) }} 108 | STMT {{ return Eval(STMT[2:-2]) }} 109 110 rule OptParam: [ ATTR {{ return ATTR[2:-2] }} ] {{ return '' }} 111 rule Str: STR {{ return eval(STR,{},{}) }} 112%% 113 114# This replaces the default main routine 115 116yapps_options = [ 117 ('context-insensitive-scanner', 'context-insensitive-scanner', 118 'Scan all tokens (see docs)') 119 ] 120 121def generate(inputfilename, outputfilename='', dump=0, **flags): 122 """Generate a grammar, given an input filename (X.g) 123 and an output filename (defaulting to X.py).""" 124 125 if not outputfilename: 126 if inputfilename[-2:]=='.g': outputfilename = inputfilename[:-2]+'.py' 127 else: raise "Invalid Filename", outputfilename 128 129 print ' SCONFIG ', join(outputfilename.split('/')[-4:], '/') 130 131 DIVIDER = '\n%%\n' # This pattern separates the pre/post parsers 132 preparser, postparser = None, None # Code before and after the parser desc 133 134 # Read the entire file 135 s = open(inputfilename,'r').read() 136 137 # See if there's a separation between the pre-parser and parser 138 f = find(s, DIVIDER) 139 if f >= 0: preparser, s = s[:f]+'\n\n', s[f+len(DIVIDER):] 140 141 # See if there's a separation between the parser and post-parser 142 f = find(s, DIVIDER) 143 if f >= 0: s, postparser = s[:f], '\n\n'+s[f+len(DIVIDER):] 144 145 # Create the parser and scanner 146 p = ParserDescription(ParserDescriptionScanner(s)) 147 if not p: return 148 149 # Now parse the file 150 t = wrap_error_reporter(p, 'Parser') 151 if not t: return # Error 152 if preparser is not None: t.preparser = preparser 153 if postparser is not None: t.postparser = postparser 154 155 # Check the options 156 for f in t.options.keys(): 157 for opt,_,_ in yapps_options: 158 if f == opt: break 159 else: 160 print 'Warning: unrecognized option', f 161 # Add command line options to the set 162 for f in flags.keys(): t.options[f] = flags[f] 163 164 # Generate the output 165 if dump: 166 t.dump_information() 167 else: 168 t.output = open(outputfilename, 'w') 169 t.generate_output() 170 171if __name__=='__main__': 172 import sys, getopt 173 optlist, args = getopt.getopt(sys.argv[1:], 'f:', ['dump']) 174 if not args or len(args) > 2: 175 print 'Usage:' 176 print ' python', sys.argv[0], '[flags] input.g [output.py]' 177 print 'Flags:' 178 print (' --dump' + ' '*40)[:35] + 'Dump out grammar information' 179 for flag, _, doc in yapps_options: 180 print (' -f' + flag + ' '*40)[:35] + doc 181 else: 182 # Read in the options and create a list of flags 183 flags = {} 184 for opt in optlist: 185 for flag, name, _ in yapps_options: 186 if opt == ('-f', flag): 187 flags[name] = 1 188 break 189 else: 190 if opt == ('--dump', ''): 191 flags['dump'] = 1 192 else: 193 print 'Warning - unrecognized option: ', opt[0], opt[1] 194 195 apply(generate, tuple(args), flags) 196

