diff options
Diffstat (limited to 'util/sconfig/parsedesc.g')
-rw-r--r-- | util/sconfig/parsedesc.g | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/util/sconfig/parsedesc.g b/util/sconfig/parsedesc.g new file mode 100644 index 0000000000..7113c6d6f3 --- /dev/null +++ b/util/sconfig/parsedesc.g @@ -0,0 +1,196 @@ +###################################################################### +# The remainder of this file is from parsedesc.{g,py} + +def append(lst, x): + "Imperative append" + lst.append(x) + return lst + +def add_inline_token(tokens, str): + tokens.insert( 0, (str, eval(str, {}, {})) ) + return Terminal(str) + +def cleanup_choice(lst): + if len(lst) == 0: return Sequence([]) + if len(lst) == 1: return lst[0] + return apply(Choice, tuple(lst)) + +def cleanup_sequence(lst): + if len(lst) == 1: return lst[0] + return apply(Sequence, tuple(lst)) + +def cleanup_rep(node, rep): + if rep == 'star': return Star(node) + elif rep == 'plus': return Plus(node) + else: return node + +def resolve_name(tokens, id, args): + if id in map(lambda x: x[0], tokens): + # It's a token + if args: + print 'Warning: ignoring parameters on TOKEN %s<<%s>>' % (id, args) + return Terminal(id) + else: + # It's a name, so assume it's a nonterminal + return NonTerminal(id, args) + +%% +parser ParserDescription: + option: "context-insensitive-scanner" + + ignore: "[ \t\r\n]+" + ignore: "#.*?\r?\n" + token END: "$" + token ATTR: "<<.+?>>" + token STMT: "{{.+?}}" + token ID: '[a-zA-Z_][a-zA-Z_0-9]*' + token STR: '[rR]?\'([^\\n\'\\\\]|\\\\.)*\'|[rR]?"([^\\n"\\\\]|\\\\.)*"' + token LP: '\\(' + token RP: '\\)' + token LB: '\\[' + token RB: '\\]' + token OR: '[|]' + token STAR: '[*]' + token PLUS: '[+]' + token QUEST: '[?]' + token COLON: ':' + + rule Parser: "parser" ID ":" + Options + Tokens + Rules<<Tokens>> + END + {{ return Generator(ID,Options,Tokens,Rules) }} + + rule Options: {{ opt = {} }} + ( "option" ":" Str {{ opt[Str] = 1 }} )* + {{ return opt }} + + rule Tokens: {{ tok = [] }} + ( + "token" ID ":" Str {{ tok.append( (ID,Str) ) }} + | "ignore" ":" Str {{ tok.append( ('#ignore',Str) ) }} + )* + {{ return tok }} + + rule Rules<<tokens>>: + {{ rul = [] }} + ( + "rule" ID OptParam ":" ClauseA<<tokens>> + {{ rul.append( (ID,OptParam,ClauseA) ) }} + )* + {{ return rul }} + + rule ClauseA<<tokens>>: + ClauseB<<tokens>> + {{ v = [ClauseB] }} + ( OR ClauseB<<tokens>> {{ v.append(ClauseB) }} )* + {{ return cleanup_choice(v) }} + + rule ClauseB<<tokens>>: + {{ v = [] }} + ( ClauseC<<tokens>> {{ v.append(ClauseC) }} )* + {{ return cleanup_sequence(v) }} + + rule ClauseC<<tokens>>: + ClauseD<<tokens>> + ( PLUS {{ return Plus(ClauseD) }} + | STAR {{ return Star(ClauseD) }} + | {{ return ClauseD }} ) + + rule ClauseD<<tokens>>: + STR {{ t = (STR, eval(STR,{},{})) }} + {{ if t not in tokens: tokens.insert( 0, t ) }} + {{ return Terminal(STR) }} + | ID OptParam {{ return resolve_name(tokens, ID, OptParam) }} + | LP ClauseA<<tokens>> RP {{ return ClauseA }} + | LB ClauseA<<tokens>> RB {{ return Option(ClauseA) }} + | STMT {{ return Eval(STMT[2:-2]) }} + + rule OptParam: [ ATTR {{ return ATTR[2:-2] }} ] {{ return '' }} + rule Str: STR {{ return eval(STR,{},{}) }} +%% + +# This replaces the default main routine + +yapps_options = [ + ('context-insensitive-scanner', 'context-insensitive-scanner', + 'Scan all tokens (see docs)') + ] + +def generate(inputfilename, outputfilename='', dump=0, **flags): + """Generate a grammar, given an input filename (X.g) + and an output filename (defaulting to X.py).""" + + if not outputfilename: + if inputfilename[-2:]=='.g': outputfilename = inputfilename[:-2]+'.py' + else: raise "Invalid Filename", outputfilename + + print 'Input Grammar:', inputfilename + print 'Output File:', outputfilename + + DIVIDER = '\n%%\n' # This pattern separates the pre/post parsers + preparser, postparser = None, None # Code before and after the parser desc + + # Read the entire file + s = open(inputfilename,'r').read() + + # See if there's a separation between the pre-parser and parser + f = find(s, DIVIDER) + if f >= 0: preparser, s = s[:f]+'\n\n', s[f+len(DIVIDER):] + + # See if there's a separation between the parser and post-parser + f = find(s, DIVIDER) + if f >= 0: s, postparser = s[:f], '\n\n'+s[f+len(DIVIDER):] + + # Create the parser and scanner + p = ParserDescription(ParserDescriptionScanner(s)) + if not p: return + + # Now parse the file + t = wrap_error_reporter(p, 'Parser') + if not t: return # Error + if preparser is not None: t.preparser = preparser + if postparser is not None: t.postparser = postparser + + # Check the options + for f in t.options.keys(): + for opt,_,_ in yapps_options: + if f == opt: break + else: + print 'Warning: unrecognized option', f + # Add command line options to the set + for f in flags.keys(): t.options[f] = flags[f] + + # Generate the output + if dump: + t.dump_information() + else: + t.output = open(outputfilename, 'w') + t.generate_output() + +if __name__=='__main__': + import sys, getopt + optlist, args = getopt.getopt(sys.argv[1:], 'f:', ['dump']) + if not args or len(args) > 2: + print 'Usage:' + print ' python', sys.argv[0], '[flags] input.g [output.py]' + print 'Flags:' + print (' --dump' + ' '*40)[:35] + 'Dump out grammar information' + for flag, _, doc in yapps_options: + print (' -f' + flag + ' '*40)[:35] + doc + else: + # Read in the options and create a list of flags + flags = {} + for opt in optlist: + for flag, name, _ in yapps_options: + if opt == ('-f', flag): + flags[name] = 1 + break + else: + if opt == ('--dump', ''): + flags['dump'] = 1 + else: + print 'Warning - unrecognized option: ', opt[0], opt[1] + + apply(generate, tuple(args), flags) |