示例#1
0
def typecheck(ast):
    """ Check the type of the ast recursively.
    """
    metakinds = infos.semantics.metakinds  # @UndefinedVariable
    kind_dicts = infos.semantics.kind_dicts  # @UndefinedVariable

    def on_ast_nodes(visitor, node, expected):
        if _is_typed(node):
            return #Already visited, because of force visitation
        mk = metakinds[node._kind]
        if mk == 'class':
            for field in node._children:
                visitor.visit(node[field], kind_dicts[node._kind][field])
        _type_term(node, mk, expected)

    def on_ast_leafs(visitor, leaf, expected):
        if leaf and _is_typed(leaf):
            _check_kind(leaf, expected)
            return #Already typed and visited, because of force visitation
        #### force visitation to enable recursives types...
        if isinstance(leaf, Ident) and _is_current_module(leaf._node): #TODO 5: use follow_modlocal_links
            visitor.visit(leaf._node, expected)
        if isinstance(leaf, Alias):
            target = leaf._is_alias_of
            if _is_current_module(target):
                visitor.visit(target, expected)
        #### end of forcing the recursivity
        if leaf:
            _type_term(leaf, metakinds[leaf._kind], expected)

    checker = AstVisitor(default=on_ast_nodes, onleaf=on_ast_leafs, kind='bf')
    checker.node_bf(ast, list(metakinds))
    _type_term(ast, 'ast', ['_ast'])
示例#2
0
def do_pass(plant):
    """ This pass follows the links up to the nodes which are the one defining
    the actual channels.
    """
    # Collect the needed information and the nodes to consider.
    v = AstVisitor({'node'          : node,
                    'linux'         : sys,
                    'lynxsecure'    : hypervisor,
                    'certikos'      : hypervisor,
                    'lynxsecure_vm' : machine,
                    'certikos_vm'   : machine,
                    'machine'       : machine },
                   onleaf=follow_links(AstVisitor.leaf_bf), #@UndefinedVariable
                   kind='bf')
    # Create a dict having ast nodes as keys (qnames),
    plantinfo = tools.DictfromField('_qname')()
    plantinfo.nodes = []
    plantinfo.topics = []
    plantinfo.machines = []
    plantinfo.plant = plant
    v.visit(plant, plantinfo)

    channels.do_pass(plantinfo)

    return plantinfo
示例#3
0
def reduce(astnode, filter_pred):
    def default(visitor, n, _):
        t = types.of(n)
        if isinstance(t, str):  # Basic value
            return language.interpret_value(t, n._val), _
        else:
            return (str(n._qname), visitor.visit(filter_pred(n._children),
                                                 _)[0]), _

    def array(visitor, a, _):
        return (str(a._qname), [visitor.visit(x, _)[0]
                                for x in a['VALUES']]), _

    def struct(visitor, s, _):
        return (str(s._qname),
                {x._name: visitor.visit(x, _)[0]
                 for x in s['FIELDS']}), _

    visitor = AstVisitor(
        {
            'array': array,
            'struct': struct,
            'topic': struct
        },
        default=default,
        onleaf=follow_links(AstVisitor.leaf_mapacc),  #@UndefinedVariable
        kind='mapacc')
    return visitor.visit(astnode, ())[0]
示例#4
0
def do_pass(ast):

    d = {
        'namespace': ast._name,
        'package_name': qn.package_ast(ast),
        'ast': qn.cmake_ast(ast),
        'ast_fun': qn.c_astfun(ast),
        'user_src_folder': user_src_path,
        'module_sources': '',
        'module_includes': '',
        'module_libs': '',
        'module_find_libs': ''
    }

    # Generate the package file
    build_deps = ['radl_lib']
    for p in infos.loaded_modules:
        build_deps.append(qn.package_ast(p))

    d['ast_deps'] = ' '.join(build_deps)

    astfolder = ws_path(d['package_name'])

    # Generate the ast files
    c_filename, c_file, h_filename, h_file = ASTdump.gen(ast)
    d['ast_c_filename'] = c_filename
    d['ast_h_filename'] = h_filename
    write_file(astfolder / c_filename, c_file)
    write_file(astfolder / 'include' / h_filename, h_file)

    clear(d, cmake_templates)
    clear(d, lib_static_templates_cmake_sublevel)
    clear(d, lib_cmake_templates_cmake_sublevel)

    visitor = AstVisitor(
        fun_dict_of((module_settings, static_library, cmake_library)),
        onleaf=follow_modlocal_links(AstVisitor.leaf_bf),  # @UndefinedVariable
        kind='bf')
    # We follow links to have correct dependencies, but we need unicity
    d['_seen'] = set()

    visitor.node_bf(ast, d)  #visitor follow links and unique passage

    app(d, cmake_templates)

    # Generate the cmake file
    cmake = d['cmakelists']
    cmake_file = astfolder / 'CMakeLists.txt'
    write_file(cmake_file, cmake)

    catkin_pkg.gen(astfolder, d['package_name'], build_deps, [])

    user_src = astfolder / d['user_src_folder']
    link_path(user_src, infos.module_base_path, relative=infos.relative_links)
示例#5
0
def gen(ast):
    name_dag = dict()
    decls = dict()
    defs = dict()
    d = dict({
        'assgns': '',
        'vdecls': '',
        'include_user_module_header': '',
        'post_init_hook': ''
    })
    visitor = AstVisitor({'module_settings': module_settings},
                         default=collect_node,
                         onleaf=collect_leaf,
                         kind='red')
    visitor.visit(ast, (name_dag, decls, defs, d))
    names_sccs = tarjan(name_dag)
    d['header_defsdecls'] = ''
    #First declare the types
    for scc in names_sccs:
        #write all typedefs first to allow circular refs
        if len(scc) > 1:
            #For now, since types are not recursive, we should not have scc.
            #             internal_error("We have a scc of size {}".format(len(scc)))
            pass
        for tname in scc:
            tdecl = decls.get(tname, None)
            if tdecl:
                d['header_defsdecls'] += tdecl
        #write the definitions
        for tname in scc:
            tdef = defs.get(tname, None)
            if tdef:
                str
                d['header_defsdecls'] += tdef

    d['asttype'] = qn.c_typename(ast)
    d['astvalue'] = qn.c_varname(ast)
    d['astinit'] = qn.c_modinit(ast)
    d['astfun'] = qn.c_astfun(ast)

    d['ast_includes'] = d['includes_init'] = ''
    for n in infos.loaded_modules:
        d['ast_includes'] += '\n#include "{}"'.format(qn.file_ast(n))
        d['includes_init'] += '\n  {}();'.format(qn.c_modinit(n))

    d['ast_h_filename'] = qn.file_ast(ast, '.h')
    d['ast_c_filename'] = qn.file_ast(ast, '.c')

    return (d['ast_c_filename'], cpp_template.format(**d), d['ast_h_filename'],
            header_template.format(**d))
示例#6
0
def do_pass(ast, root_namespace):
    """ Transform alias nodes into leafs of type Alias,
    behaving like the Ident it points to.
    This has to be done with a frozen ast or extra care is needed after this.
    """
    def _alias(visitor, node, _):
        internal_assert(len(node._children) == 1, "incorrect alias node")
        ident = node._children[0]
        internal_assert(isinstance(ident, Ident), "incorrect alias node")
        a = Alias(node._qname, node._location, ident)
        return a, _

    visitor = AstVisitor({'_alias': _alias}, inplace=True, kind='mapacc')
    visitor.visit(ast, ())
    update_idents(ast, root_namespace)
示例#7
0
def collect(package_folder, package_name, ast, generate_all):
    """ return a set of struct types to be generated.
    It is not to be generated if it has a field EXTERNAL_ROS_DEF,
    or if it is already in infos.ros_types.
    """
    def _st(visitor, node, s):
        """ s is a mapping between names and filepath of messages to generate.
        """
        if node._kind == 'topic':  #special treatment to add private fields
            t = struct_of_topic(types.of(node))
        else:
            t = types.of(node)
        ext_rd = node['EXTERNAL_ROS_DEF']
        if ext_rd:  #External def to be used
            name = ext_rd['FULLNAME']._val
            header = ext_rd['HEADER']._val
        else:
            reg_name = infos.ros_type_of_struct.get(t, None)
            if not reg_name:  #Msg name and file to create
                msgname = qn.rosmsg(node)
                s[t] = msg_msg_file(package_folder, msgname)
                msgpkg = package_name
                infos.ros_type_of_struct[t] = (msgpkg, msgname)
            else:  #Msg file already created
                (msgpkg, msgname) = reg_name
            header = msg_cpp_header(msgpkg, msgname)
            #keep only the actual string to identify the type and header file
            name = msg_cpp_qname(msgpkg, msgname)
        #Store our findings in the node for future retrieval
        node._ros_msgtype_name = name
        node._ros_msgtype_header = header
        return visitor.node_red(node, s)  #recursive call

    if generate_all:
        onleaf = onleaf = follow_links(
            AstVisitor.leaf_red)  # @UndefinedVariable
    else:
        onleaf = AstVisitor.leaf_red  # @UndefinedVariable

    visitor = AstVisitor({
        'topic': _st,
        'struct': _st
    },
                         kind='red',
                         onleaf=onleaf)
    s = visitor.visit(ast, dict())
    return s
示例#8
0
def gen_launchfiles(plantinfo, package_name, package_folder):
    """ This pass is in charge of generating the launch files specific to a plant.
    The corresponding package is supposed to be already generated.
    @return: the list of generated file handles
    """

    ## Do the actual plant pass to generate the needed launch files.

    d = dict()
    d['source_file'] = str(infos.source_file)
    v = AstVisitor(
        fun_dict_of((machine, node)),
        onleaf=follow_links(AstVisitor.leaf_bf),  #@UndefinedVariable
        kind='bf')
    d['package_path'] = package_folder
    d['launch_files'] = []
    v.visit(plantinfo.plant, d)

    return d['launch_files']
示例#9
0
def _check_and_map_topics_publisher(ast):
    """ When there is one and only one publisher per topic, cross ref the publisher
    in the topic as topic._publisher"""
    def publication(visitor, pub, acc):
        """ Publication are not recursives """
        node, maping = acc
        top = pub['TOPIC']
        if top._qname in maping:
            error("Topic {} has multiple publisher.".format(top), top._location)
        maping[top._qname] = Ident.of(node)
        return pub, (node, maping)
    def node(visitor, node, acc):
        """ set the current node in the accumulator """
        _, maping = acc
        acc = node, maping
        return visitor.node_mapacc(node, acc)

    visitor = AstVisitor(locals(), inplace=True)
    _, (_, maping) = visitor.visit(ast, (None, {}))
    return maping
示例#10
0
def plant2dot(plantinfo):
    qn = str(plantinfo.plant._qname)
    # Create the hierarchical graph with all the nodes (no edges for now)
    plantd = Dot(qn,
                 simplify=True,
                 comment="Generated by radler for {}".format(qn))
    v = AstVisitor({'node' : node,
                    'lynxsecure_vm' : machine,
                    'certikos_vm'   : machine,
                    'machine'       : machine },
                   onleaf=follow_links(AstVisitor.leaf_bf), #@UndefinedVariable
                   kind='bf')
    v.visit(plantinfo.plant, plantd)

    # Add all edges
    for cl in plantinfo.channels.values():
        for c in cl:
            if c.incoming:
                plantd.add_edge(Edge(str(c.pub), str(c.sub)))
    print(plantd.to_string())
示例#11
0
def _link(ast, maping):
    def topic(visitor, topic, maping):
        try:
            i = maping[topic._qname]
            topic._publisher = i
        except KeyError:
            warning(
                "The topic {} doesn't have any publisher.".format(
                    topic._qname), topic._location)
        return topic, maping

    AstVisitor({'topic': topic}, inplace=True).visit(ast, maping)
示例#12
0
def _check_and_map_topics_publisher(ast):
    """ When there is one and only one publisher per topic, cross ref the publisher
    in the topic as topic._publisher"""
    def publication(visitor, pub, acc):
        """ Publication are not recursives """
        node, maping = acc
        top = pub['TOPIC']
        if top._qname in maping:
            error("Topic {} has multiple publisher.".format(top),
                  top._location)
        maping[top._qname] = Ident.of(node)
        return pub, (node, maping)

    def node(visitor, node, acc):
        """ set the current node in the accumulator """
        _, maping = acc
        acc = node, maping
        return visitor.node_mapacc(node, acc)

    visitor = AstVisitor(locals(), inplace=True)
    _, (_, maping) = visitor.visit(ast, (None, {}))
    return maping
示例#13
0
def gen(localroot, msg_list, msg_dir, ast, extra_files=None):

    onleaf = AstVisitor.leaf_bf  # @UndefinedVariable

    visitor = AstVisitor({'node' : _from_node}, kind='bf', onleaf=onleaf)

    d = {'module'     : ast._name,
         'module_lib' : qn.cmake_ast(ast._qname.rootmodule()),
         'ast_fun'    : qn.c_astfun(ast),
         '_localroot' : localroot
         }

    clear(d, cmake_templates)
    clear(d, cmake_msgs_templates)
    clear(d, node_templates_cmake_sublevel)

    visitor.visit(ast, d)

    toload = infos.loaded_modules
    loaded_modules = ' '.join(qn.cmake_ast(n) for n in toload)
    d['find_modules'] = d['module_lib'] + ' radl_lib roscpp ' + loaded_modules
    d['run_modules'] = ' roscpp'
    d['ros_modules'] = ' '.join(n.name() for n in toload)

# Trying to be smart make dependencies hard since otherwise we can't blindly
# add any ros package as a message dependency.
#     if len(msg_list) > 0:

    msg_files = (str(relative_path(m, msg_dir)) for m in msg_list)
    d['msg_files'] = listjoin(' ', msg_files)
    d['extra_files'] = listjoin(' ', extra_files) if extra_files else ''
    d['find_modules'] += ' message_generation'
    d['run_modules'] += ' message_runtime'

    app(d, cmake_msgs_templates)

    app(d, cmake_templates)

    write_file(localroot / "CMakeLists.txt", d['cmakeliststxt'])
示例#14
0
文件: rosmsg.py 项目: xiaohe27/radlm
def collect(ast):
    """ return a set of struct types to be generated.
    It is not to be generated if it has a field EXTERNAL_ROS_DEF,
    or if it is already in infos.ros_types.
    """
    def _st(visitor, node, s):
        """ s is a mapping between names and filepath of messages to generate.
        """
        if node._kind == 'topic':  #special treatment to add private fields
            t = struct_of_topic(types.of(node))
        else:
            t = types.of(node)
        ext_rd = node['EXTERNAL_ROS_DEF']
        if ext_rd:  #External def to be used
            name = ext_rd['FULLNAME']._val
            header = ext_rd['HEADER']._val
        else:
            reg_name = infos.ros_type_of_struct.get(t, None)
            if not reg_name:  #Msg name and file to create
                name = ast._namespace.generate('radl__msg')
                #note: the .msg file is different from the header linked to it
                s[t] = filepath(qn_msgfile(name, suffix='.msg'))
                infos.ros_type_of_struct[t] = name
            else:  #Msg file already created
                name = reg_name
            header = qn_file(name, suffix='.h')
            #keep only the actual string to identify the type and header file
            name = qn_cpp(name)
        #Store our findings in the node for future retrieval
        node._ros_msgtype_name = name
        node._ros_msgtype_header = header
        return visitor.node_mapacc(node, s)  #recursive call

    visitor = AstVisitor({'topic': _st, 'struct': _st})
    _, s = visitor.visit(ast, dict())
    return s
示例#15
0
文件: rosmsg.py 项目: xiaohe27/radlm
def collect(ast):
    """ return a set of struct types to be generated.
    It is not to be generated if it has a field EXTERNAL_ROS_DEF,
    or if it is already in infos.ros_types.
    """
    def _st(visitor, node, s):
        """ s is a mapping between names and filepath of messages to generate.
        """
        if node._kind == 'topic': #special treatment to add private fields
            t = struct_of_topic(types.of(node))
        else:
            t = types.of(node)
        ext_rd = node['EXTERNAL_ROS_DEF']
        if ext_rd: #External def to be used
            name = ext_rd['FULLNAME']._val
            header = ext_rd['HEADER']._val
        else:
            reg_name = infos.ros_type_of_struct.get(t, None)
            if not reg_name: #Msg name and file to create
                name = ast._namespace.generate('radl__msg')
                #note: the .msg file is different from the header linked to it
                s[t] = filepath(qn_msgfile(name, suffix='.msg'))
                infos.ros_type_of_struct[t] = name
            else: #Msg file already created
                name = reg_name
            header = qn_file(name, suffix='.h')
            #keep only the actual string to identify the type and header file
            name = qn_cpp(name)
        #Store our findings in the node for future retrieval
        node._ros_msgtype_name = name
        node._ros_msgtype_header = header
        return visitor.node_mapacc(node, s) #recursive call

    visitor = AstVisitor({'topic': _st, 'struct' : _st})
    _, s = visitor.visit(ast, dict())
    return s
示例#16
0
def do_pass(ast):
    v = AstVisitor(default=onnode, onleaf=onleaf, kind='bf')
    v.visit(ast, [])
示例#17
0
def do_pass(ast):
    visitor = AstVisitor({'module_settings' : module_settings}, kind='red')
    visitor.visit(ast, False)
示例#18
0
from radler.radlr.rast import AstVisitor, Ident, Alias

def _un_onleaf(visitor, leaf, namespace):
    """ Make sure Alias leaf are target for their name. """
    if isinstance(leaf, Alias):
        namespace.refresh(leaf._qname, leaf)
    return leaf, namespace

def _un_onnode(visitor, node, namespace):
    """ Make sure nodes are target for their name. """
    namespace.refresh(node._qname, node)
    visitor.mapacc(node._children, node._namespace)
    return node, namespace


updater_namespace = AstVisitor(default=_un_onnode, onleaf=_un_onleaf,
                               inplace=True)
""" Make sure namespace has correct name->node associations. """

def _ui_onleaf(visitor, leaf, namespace):
    """ Correct Ident->node and Alias->node associations. """
    if isinstance(leaf, Ident):
        leaf._reattach(namespace.lookup_node(leaf._qname))
    if isinstance(leaf, Alias):
        ident = leaf._is_alias_of
        ident._reattach(namespace.lookup_node(ident._qname))
    return leaf, namespace

def _ui_onnode(visitor, node, namespace):
    """ Keep track of the namespace. """
    visitor.mapacc(node._children, node._namespace)
    return node, namespace
示例#19
0
文件: pwds.py 项目: xiaohe27/radlm
def add(ast):
    """ Add a _pwd attribute to nodes indicating current user path."""
    visitor = AstVisitor(default=_pwd, mapacc=True)
    path = Path()
    visitor.visit(ast, path)
示例#20
0
from radler.radlr.rast import AstVisitor, Ident, Alias

def _un_onleaf(visitor, leaf, namespace):
    """ Make sure Alias leaf are target for their name. """
    if isinstance(leaf, Alias):
        namespace.refresh(leaf._qname, leaf)

def _un_onnode(visitor, node, namespace):
    """ Make sure nodes are target for their name
    and Keep track of the namespace.
    """
    namespace.refresh(node._qname, node)
    visitor.bf(node._children, node._namespace)

# Make sure namespace has correct name->node associations.
updater_ns = AstVisitor(default=_un_onnode, onleaf=_un_onleaf, kind='bf')


def _ui_onleaf(visitor, leaf, namespace):
    """ Correct Ident->node and Alias->node associations. """
    if isinstance(leaf, Ident):
        leaf._reattach(namespace.lookup_node(leaf._qname))
    if isinstance(leaf, Alias):
        ident = leaf._is_alias_of
        ident._reattach(namespace.lookup_node(ident._qname))

def _ui_onnode(visitor, node, namespace):
    """ Keep track of the namespace for faster lookups. """
    visitor.bf(node._children, node._namespace)

# Make sure Idents and Alias are correctly associated.
示例#21
0
def do_pass(ast):
    visitor = AstVisitor({'struct': onstruct, 'topic': onstruct}, kind='bf')
    visitor.visit(ast)
示例#22
0
    for t in lib_templates:
        d[t] = ''
    _, d = visitor.update({'cxx_file': _from_cxx}).node_mapred(lib, d)
    d['dirs'] = '"{}"'.format('" "'.join(map(str, d['dirs'])))
    d['sources'] = ' '.join(map(str, d['sources']))
    cwd = rosutils.user_file_relativepath / lib._pwd
    d['headers'] = ' '.join(str(cwd / h._val) for h in lib['HEADER_PATHS'])

    for t in sl:
        app(d, t)
    return (), d


_visitor = AstVisitor({
    'node': _from_node,
    'cmake_library': _from_catkinlib,
    'static_library': _from_staticlib
})


def gen(msg_list, gened_cpp_files, ast):
    #The Cmakefile waits for msg files relative to the msg dir
    msg_dir = filepath(qn_msgfile(QualifiedName(ast._qname, '', True)))
    msg_files = (str(m.relative_to(msg_dir)) for m in msg_list)
    localroot = qn_dir(ast._qname)
    d = {
        'namespace': ast._qname.name(),
        'msg_files': '\n  '.join(msg_files),
        'gened_cpp_files': gened_cpp_files,
        'localroot': localroot
    }
示例#23
0
文件: rosnode.py 项目: xiaohe27/radlm
              'gathered_flags'    : '|'     , 'out_flags_struct_def'  : '\n  ',
              'report_fill'       : '\n  '  , 'report_msg_fill'   : '\n  '}

def app(d, s):
    v = templates[s].format(**d)
    if s not in d or not d[s]: d[s] = v
    else: d[s] = separators[s].join((d[s], v))


def _include_cxx_class(visitor, node, acc):
        _, acc = visitor.node_mapred(node, acc)
        #f = node._pwd / node['HEADER']._val
        f = node['HEADER']._val
        acc.append('#include "' + str(f) + '"')
        return _, acc
_include_visitor = AstVisitor({'cxx_class' : _include_cxx_class})

def getincludes(node):
    _, paths = _include_visitor.visit(node, [])
    return '\n'.join(paths)

def to_times(node):
    if node == None:
        return "-1"
    else:
        return str(1000000000//int(node._val))

def to_rate(node):
    if node == None:
        return "-1"
    else:
示例#24
0
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Radler.  If not, see <http://www.gnu.org/licenses/>.

'''
@license: GPLv3

Created on Jun, 2014

@author: Léonard Gérard [email protected]

Verify array values are coherent .

'''
from radler.radlr import types
from radler.radlr.rast import AstVisitor


def _tc_arrays(visitor, array, _):
    """Type Check arrays, simply call types.of() """
    types.of(array)
    return _

_tc_visitor = AstVisitor({'array' : _tc_arrays}, kind='red')

def typecheck(ast):
    _tc_visitor.visit(ast, ())
示例#25
0
def do_pass(ast):
    v = AstVisitor(fun_dict_of((node, )), kind='bf')
    v.visit(ast, ())
示例#26
0
def do_pass(ast):
    """ Add a _wd attribute to nodes indicating current user path."""
    visitor = AstVisitor(default=_wd, kind='bf')
    visitor.visit(ast, Path())
示例#27
0
def gen_tree_to_ast(language_tree, env):
    #We create a visitor with a visitor... so the convention here is that
    #mvisitor is the meta visitor which is using menv as accumulator, while
    #visitor is the generated one using ast as accumulator.
    #menv is a dictionnary storing the generated visitor methods.

    def typ(mvisitor, mnode, menv):
        """ A typ node has six childs
            'type' defKind 'REGEX' regex
        It generates one rule named kind from defkind
        """
        #No need to do recursive traversal (no rules are generated lower),
        #    but go fetch the kind in defKind.
        kind = mnode.children[1].children[0]

        def m_def_gen(annoted):
            def m_def(visitor, node, namespace):
                childs, _ = visitor.mapacc(node.children, namespace)
                loc = loc_of_parsimonious(node)
                ident = childs[0][0] if childs[0] else None
                if isinstance(ident, parsimonious.nodes.Node):
                    try:
                        qname = namespace.qualify(ident.text)
                    except ExistingIdent:
                        error("This name is already used.", loc)
                else:
                    qname = namespace.generate("_" + kind)
                match_dct = childs[2 if annoted else 1].match.groupdict()
                value = match_dct['value'] if 'value' in match_dct else ''
                if 'unit' in match_dct:
                    normalize = language.unit_normalize[kind][
                        match_dct['unit']]
                    value = normalize(value)
                n = AstNode(kind, qname, [value], namespace, loc)
                namespace.associate(qname, n)
                return n, namespace

            return m_def

        #basic rules are simply replaced by their lone child
        menv[kind] = ParseVisitor.left_mapacc
        menv[kind + '_def'] = ParseVisitor.left_mapacc
        menv[kind + '_value'] = ParseVisitor.left_mapacc
        menv[kind + '_annoted'] = m_def_gen(True)
        menv[kind + '_not_annoted'] = m_def_gen(False)
        return (), menv

    def field(mvisitor, mnode, menv):
        """ A field node has three childs symbol some_kind ('*' / '+' / '?')?
        Return the tuple (symbol, mod)
        """
        (name, _, mod) = mnode.children
        return (name, mod), env

    def clas(mvisitor, mnode, menv):
        """ A clas node has three childs 'class' defKind field*
        Three rules needs to be dealt with, kind, name_def, name_annoted
        with kind the defKind kind.
        """
        #         mnode, menv = mvisitor.node_mapacc(mnode, menv)
        kind = mnode.children[1].children[0]
        field_specs = []
        for field in mnode.children[2]:
            mod = field.children[2]
            fname = field.children[0].text
            field_specs.append((fname, mod))

        def m_def_gen(annoted):
            def m_def(visitor, node, namespace):
                ident = node.children[0][0] if node.children[0] else None
                loc = loc_of_parsimonious(node)
                if isinstance(ident, parsimonious.nodes.Node):
                    try:
                        qname = namespace.qualify(ident.text)
                    except ExistingIdent:
                        error("This name is already used.", loc)
                else:  #generate a name since none is given
                    qname = namespace.generate("_" + kind)
                thisnamespace = namespace.push(qname)
                childs, _ = visitor.mapacc(node.children, thisnamespace)
                fields = BucketDict(childs[2 if annoted else 1])
                for (fname, mod) in field_specs:
                    v = fields.get(fname, False)
                    err = lambda m: error(m.format(fname), loc)
                    if not mod:
                        if v is False:
                            err("field {} is mandatory.")
                        elif isinstance(v, list):
                            err("field {} requires one and only one value.")
                    elif mod == '?':
                        if v is False:
                            fields[fname] = None
                        elif len(v) != 1:
                            err("field {} may be given at most one value.")
                        else:
                            fields[fname] = v[0]
                    elif mod == '*':
                        if v is False:
                            fields[fname] = []
                    elif mod == '+':
                        if v is False:
                            err("field {} requires at least one value.")
                    else:
                        internal_error("unknown modifier")
                n = AstNode(kind, qname, fields, thisnamespace, loc)
                namespace.associate(qname, n)
                return n, namespace

            return m_def

        #basic rules are simply replaced by their lone child
        menv[kind] = ParseVisitor.left_mapacc
        menv[kind + '_def'] = ParseVisitor.left_mapacc
        menv[kind + '_value'] = partial(ParseVisitor.left_mapacc, el_num=1)
        menv[kind + '_annoted'] = m_def_gen(True)
        menv[kind + '_not_annoted'] = m_def_gen(False)
        return (), menv

    def meta(mvisitor, mnode, menv):
        _ = mvisitor.mapacc(mnode.children, menv)

        def _lang(visitor, node, namespace):
            #depth first, get a list of definitions
            defs, _ = ParseVisitor.left_mapacc(visitor, node, namespace)
            return (loc_of_parsimonious(node), defs), namespace

        def _alias_def(visitor, node, namespace):
            #depth first, return three childs, ident '=' ident
            loc = loc_of_parsimonious(node)
            ((alias, _, target), _) = visitor.mapacc(node.children, namespace)
            try:
                qname = namespace.qualify(alias.text)
            except ExistingIdent:
                error("This name is already used.", loc)
            n = AstNode('_alias', qname, [target], namespace, loc)
            namespace.associate(qname, n)
            return n, namespace

        #get the _ident from _solo_ident
        menv['_solo_ident'] = ParseVisitor.left_mapacc
        menv['_lang'] = _lang
        menv['_alias_def'] = _alias_def
        gen = ParseVisitor(menv)
        return gen, menv

    # generate the visitor and return it.
    metagen = ParseVisitor(locals())
    menv = dict()
    gen, menv = metagen.visit(language_tree, menv)

    # Resolve idents.
    # We are working on an ast with some leafs being parse nodes (_ident).
    def onleaf(visitor, leaf, namespace):
        if isinstance(leaf, parsimonious.nodes.Node):
            if leaf.expr_name != '_qname':
                internal_error("The Ast have parsing node "
                               "{} left over".format(leaf.expr_name))
            try:
                node = namespace.resolve(leaf.text)
            except NonExistingIdent:
                msg = "Undefined identifier {}".format(leaf.text)
                error(msg, loc_of_parsimonious(leaf))
            return Ident(node, loc_of_parsimonious(leaf)), namespace
        else:
            return leaf, namespace

    def onnode(visitor, node, namespace):
        node, _ = visitor.node_mapacc(node, node._namespace)
        return node, namespace

    resolver = AstVisitor(onleaf=onleaf, default=onnode)

    def tree_to_ast(program_tree, program_qname, namespace):
        thisnamespace = namespace.push(program_qname)
        (location, defs), _ = gen.visit(program_tree, thisnamespace)
        ast = AstNode('_ast', program_qname, defs, thisnamespace, location)
        namespace.associate(program_qname, ast)
        ast, _ = resolver.visit(ast, namespace)
        return ast

    return tree_to_ast
示例#28
0
'''
Created on Jun, 2014

@author: Léonard Gérard [email protected]

Verify array values are coherent .

'''
from radler.radlr import types
from radler.radlr.rast import AstVisitor


def _tc_arrays(visitor, array, _):
    """Type Check arrays, simply call types.of() """
    types.of(array)
    return (), _


_tc_visitor = AstVisitor({'array': _tc_arrays}, mapacc=True)


def typecheck(ast):
    _tc_visitor.visit(ast, ())