Source code for pyensae.sphinxext.csharp_parser

# -*- coding: utf-8 -*-
"""
Parses :epkg:`C# `.


:githublink:`%|py|6`
"""
from antlr4.tree.Tree import TerminalNodeImpl
from antlr4 import ParseTreeWalker
from ..languages.antlr_grammar_use import get_parser_lexer, parse_code
from ..languages.CSharpParserListener import CSharpParserListener


[docs]class CSharpElement: """ Base class of a :epkg:`C#` element. :githublink:`%|py|15` """ _kinds = {'class', 'method', 'domain', 'type'} _privates = {'public', 'private', 'protected'}
[docs] def __init__(self, domain, name, doc=None, source=None, code=None, **kwargs): """ :param domain: domain :param name: name :param doc: documentation :param source: source file :param code: code :githublink:`%|py|27` """ self.name = name self.doc = doc self.domain = domain self.source = source self.code = code self.options = kwargs
[docs] def __str__(self): """ usual :githublink:`%|py|38` """ return f"{self.name}"
[docs]class CSharpDomain(CSharpElement): """ Base class of a :epkg:`C#` domaon. :githublink:`%|py|45` """
[docs] def __init__(self, domain, name, doc=None, source=None, code=None, **kwargs): """ :param domain: domain :param name: name :param doc: documentation :param source: source file :param code: code :githublink:`%|py|54` """ if "{" in domain or "{" in name: raise ValueError( "Issue with domain name\ndomain={0}\nname={1}".format(domain, name)) CSharpElement.__init__(self, domain, name, doc, source, code, **kwargs)
[docs] def __str__(self): """ usual :githublink:`%|py|63` """ return f"namespace {self.name}"
[docs]class CSharpVariable(CSharpElement): """ :epkg:`C#` variables :githublink:`%|py|70` """
[docs] def __init__(self, domain, name, typ, value=None, doc=None, source=None, code=None, **kwargs): """ :param domain: domain :param name: name :param typ: type :param value: default value if it exists :param doc: documentation :param source: source file :param code: code :githublink:`%|py|81` """ CSharpElement.__init__(self, domain, name, doc=doc, source=source, code=code, **kwargs) if not isinstance(typ, CSharpType): raise TypeError("typ must be of type CSharpType") self.type = typ self.value = value
[docs] def __str__(self): """ usual :githublink:`%|py|92` """ if isinstance(self.value, str): v = self.value.replace('"', '\\"') value = f'"{v}"' else: value = self.value return f"{self.type} {self.name} = {value}"
[docs]class CSharpType(CSharpVariable): """ :epkg:`C#` type :githublink:`%|py|104` """
[docs] def __init__(self, domain, name, typ, doc=None, source=None, code=None, **kwargs): """ :param domain: domain :param name: name :param typ: type :param source: source file :param code: code :githublink:`%|py|113` """ CSharpVariable.__init__( self, domain, name, typ=typ, doc=doc, source=source, code=code, **kwargs)
[docs] def __str__(self): """ usual :githublink:`%|py|120` """ return f"{self.name}"
[docs]class CSharpMethod(CSharpElement): """ :epkg:`C#` function. :githublink:`%|py|127` """
[docs] def __init__(self, domain, name, rtype, params=None, private='public', static=False, doc=None, source=None, code=None, **kwargs): """ :param domain: domain :param name: name :param rtype: return type :param private: private, public or protected :param static: static or not :param parameters: parameters :param doc: documentation :param code: code :githublink:`%|py|140` """ CSharpElement.__init__(self, domain, name, doc=doc, source=source, code=code, **kwargs) if private not in CSharpElement._privates: raise ValueError( f"Unable to find '{private}' in {CSharpElement._privates}.") self.params = params self.source = source self.static = static self.private = private self.rtype = rtype
[docs] def __str__(self): """ usual :githublink:`%|py|155` """ pars = ", ".join(str(p) for p in self.params) return f"{self.domain}.{self.name} ({self.kind})"
[docs]class CSharpClass(CSharpElement): """ :epkg:`C#` class. :githublink:`%|py|163` """
[docs] def __init__(self, domain, name, methods=None, constants=None, params=None, private='public', static=False, doc=None, source=None, code=None, **kwargs): """ :param domain: domain :param name: name :param methods: methods :param constants: constants :param private: private, public or protected :param static: static or not :param doc: documentation :param source: source file :param code: code :githublink:`%|py|178` """ CSharpElement.__init__(self, domain, name, doc=doc, source=source, code=code, **kwargs) if private not in CSharpElement._privates: raise ValueError( f"Unable to find '{private}' in {CSharpElement._privates}.") self.source = source self.static = static self.private = private self.methods = methods self.contansts = constants
[docs] def __str__(self): """ usual :githublink:`%|py|193` """ return f"class {self.domain}.{self.name}"
[docs]class CSharpParserListenerSignatures(CSharpParserListener): """ :epkg:`C#` Listener :githublink:`%|py|200` """
[docs] def __init__(self, parser, source): """ constructor :param parser: parser used to parse the code :githublink:`%|py|207` """ CSharpParserListener.__init__(self) self.buffer = [] self.parser = parser self._elements = [] self._context = [] self._obj = None self._source = source
[docs] def stack_element(self, el): """ Adds an element. :githublink:`%|py|219` """ self._obj = el
[docs] def enter_body(self): """ Adds an element. :githublink:`%|py|225` """ if self._obj is None: raise RuntimeError("self._obj cannot be None") self._context.append(self._obj)
[docs] def exit_body(self): """ Adds an element. :githublink:`%|py|233` """ el = self._context.pop() if el is None: raise RuntimeError("el cannot be None") self._elements.append(el)
# def enterEveryRule(self, ctx): # pass # def leaveEveryRule(self, ctx): # pass def get_code(self, ctx): line, col = ctx.start.line, ctx.start.column class_name = ctx.__class__.__name__ return class_name, line, col
[docs] def enumerate_all_children(self, ctx): """ Enumerate all children. :param ctx: context :return: iterator :githublink:`%|py|256` """ def roll(ctx): if not isinstance(ctx, TerminalNodeImpl): yield ctx for ch in ctx.getChildren(): for r in roll(ch): yield r for r in roll(ctx): yield r
############ # namespace ############ def enterNamespace_declaration(self, ctx): self._namespace_code = ctx.getText() def enterNamespace_body(self, ctx): pass def enterNamespace_or_type_name(self, ctx): code = getattr(self, '_namespace_code', None) if code: self._namespace_code = None name = ctx.getText() children = list(self.enumerate_all_children(ctx)) line1 = ctx.start.line line2 = ctx.stop.line self.stack_element(CSharpDomain( name, name, code=code, source=self._source, lines=[line1, line2])) self.enter_body() def exitNamespace_or_type_name(self, ctx): pass def exitNamespace_body(self, ctx): self.exit_body() def exitNamespace_declaration(self, ctx): pass
[docs]class CSharpParser: """ Parses :epkg:`C#`. :githublink:`%|py|302` """
[docs] def __init__(self): """ :githublink:`%|py|306` """ clparser, cllexer = get_parser_lexer("C#") self._parser = clparser self._lexer = cllexer
[docs] def parse(self, code, source=None): """ Returns all elements of codes inside a string. :param code: string :param source: source :return: list of :class:`CSharpElement <pyensae.sphinxext.csharp_parser.CSharpElement>` :githublink:`%|py|318` """ self._source = source parser = parse_code(code, self._parser, self._lexer) tree = parser.compilation_unit() walker = ParseTreeWalker() listen = CSharpParserListenerSignatures(parser, source) walker.walk(listen, tree) return listen._elements