view meta_connector/meta_connector.py @ 26:b9f88346cecd support auto stub generation

generate stub
author Kaito Tokumori <e105711@ie.u-ryukyu.ac.jp>
date Mon, 02 Nov 2015 04:02:13 +0900
parents 9a1cce532f29
children 894a300f199f
line wrap: on
line source

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
import argparse
import re

# parse arguments and return arguments list.
def get_args():
    parser = argparse.ArgumentParser(
    formatter_class=argparse.RawDescriptionHelpFormatter,
    description="""\
Parse meta connect syntax and create stub. Default output is stdout.

sample)
__code code0(struct Data data1, ...){
    goto code1(data1, data2, ...);
    \t|
    \tV
__code code0(struct Context* context, struct Data data1, ...){
    goto meta_code1(context, data1, data2, ..., Code1);
    """)
    parser.add_argument('input_file',\
                        nargs=None,\
                        type=str,\
                        help='input file path.')
    parser.add_argument('-o', dest='output',\
                        nargs=1,\
                        type=str,\
                        metavar='<file>',\
                        help='write output to <file>')
                        
    return parser.parse_args()

# parse input file and create meta connection list
def parse_meta_syntax(lines,file):
    comment_out = False
    caller_name = ''
    isMetaOrStub = False
    defaultMetaIsNotExist = True
    for i,l in enumerate(lines):

        regexed_l = re.search(r"[a-zA-Z0-9_]+ *\(",l)
        # get caller code segment name
        if re.search(r"^ *__code",l) is not None: # code segment declarations.
            isMetaOrStub = False
            caller_name = regexed_l.group(0).rstrip('(')
            args = get_args_from_line(l)
            if caller_name == 'meta':
                defaultMetaIsNotExist = False
            if re.search(r"^ *meta_*|stub$",caller_name) is not None: # meta code segments or stubs
                isMetaOrStub = True
            elif not cs_search(lines, caller_name+'_stub'): # normal code segments which do not have a stub.
                create_stub(file, caller_name, args.strip(','))

            if re.search(r"struct Context\* context",l) is None: # add context to arguments list if it didn't have.
                file.write("/*-- generated by script */\n")
                file.write('// '+l)
                file.write('__code {0:s}(struct Context* context{1:s}) {{\n'.format(caller_name, args))
            else:
                file.write(l)
        elif not isMetaOrStub and regexed_l is not None and re.search(r"^ *goto",l): # code segment transition.
            callee_name = regexed_l.group(0).rstrip('(')
            if callee_name == 'start_code' or re.match('meta_|meta$',callee_name):
                file.write(l)
            elif re.search(r"\(context", l) is None:
                connect_meta(file, lines, l, caller_name, callee_name)
                # file.write("/*-- connected by script */\n")
                # file.write('// '+l)
                # meta_name = 'meta_'+caller_name if cs_search(lines, 'meta_'+callee_name) else 'meta'
                # file.write("goto {0:s}(context{1:s}, {2:s});\n".format(meta_name, args,\
                #                                                         callee_name.capitalize()))
            else:
                file.write(l)
        else:
            file.write(l)
    if defaultMetaIsNotExist:
        create_default_metacs(file)

def create_default_metacs(file):
    file.write('''
__code meta(struct Context* context, enum Code next) {
    goto (context->code[next])(context);
}
    ''')

def get_args_from_line(line):
    args = line.split('(')[1].rsplit(')')[0].strip()
    args = ', ' + args if args else ''
    return args
    

def create_stub(file, name, args):
    file.write('''__code {0:s}_stub(struct Context* context) {{
    goto {0:s}(context'''.format(name))
    for arg in args_str2args_list(args):
        file.write(', &context->data['+arg.capitalize()+']->'+arg)
    file.write(');\n}\n')

def connect_meta(file, lines, l, caller_name, callee_name):
    meta_name = 'meta_'+caller_name if cs_search(lines, 'meta_'+caller_name) else 'meta'
    args = get_args_from_line(l)
    file.write("/*-- connected by script */\n")
    file.write('// '+l)
    file.write("goto {0:s}(context".format(meta_name))
    for arg in args_str2args_list(args):
        file.write(', '+arg)
    file.write(', '+callee_name.capitalize()+');\n')

def args_str2args_list(args_str):
    args_list = []
    temporary_list = args_str.split()
    for i,arg in enumerate(temporary_list):
        if re.search(r"context,*", arg) is not None:
            continue
        if re.search(r"[a-zA-Z0-9_] *,$", arg) is not None or i == len(temporary_list)-1:
            args_list.append(arg.strip(','))
    return args_list
    
# search meta code segment.
# It returns true when cs was found.
# Find it :  __code meta_'name'
def cs_search(lines, name):
    for l in lines:
        if re.search(r"^ *__code {0:s}\(".format(name),l) is not None:
            return True
    return False

def main():
    args = get_args()
    output = sys.stdout
    try:
        f = open(args.input_file,'r')
    except IOError:
        print("cannot open file %s" % input_file)
    if args.output is not None:
        try:
            output = open(args.output[0],'w')
        except IOError:
            print("cannot open file %s" % args.output)
    lines = f.readlines()
    connect_list = parse_meta_syntax(lines, output)
main()