diff --git a/texla/Parser/Blocks/Block.py b/texla/Parser/Blocks/Block.py index 206d8b1..3c45e17 100644 --- a/texla/Parser/Blocks/Block.py +++ b/texla/Parser/Blocks/Block.py @@ -1,134 +1,133 @@ from .Utilities import * """Base Block definition""" class Block: """ Block general attributes: -block_name: the new of the "type" of the block -id: unique id for the block in the tree -parent_block: parent in the tree -attributes: a dictionary for description of the block. All useful parser data go into attributes -ch_blocks: a list of children_blocks -section_level: the position of the block compared to sectioning levels defined in utility.py Derived Block could add more attributes. """ @staticmethod def parse(parser, tex, parent_block, params): """ The method must return a tuple with the created Block and the last used index of tex string.""" pass def __init__(self, block_name, content, parent_block): """ Base constructor for Block. It saves the parent_block and block name and create the new id for the new block. It creates data structures like the attributed dictionary and children nodes list. It always saves a content variable. By default, it sets the section_level of the block to that of the parend_block. """ self.block_name = block_name self.content = content if not parent_block is None: self.parent_block = parent_block self.id = parent_block.id + '-' + utility.get_random_string(3) #Section level: #by default the level is the same of parent block self.section_level = self.parent_block.section_level #depth in the tree self.tree_depth = self.parent_block.tree_depth+1 else: #if this is the root block self.parent_block = None self.id = '@' self.section_level = -1 self.tree_depth = 0 #dictionary for attributes - self.attributes = {'N_chblocks' : 0} + self.attributes = {} #list for childrend blocks self.ch_blocks = [] self.N_chblocks = 0 def add_child_block(self, block): """ IMPORTANT: this function is called by the self.parse fuction. It MUST NOT be called from outside, expecially the parser """ self.ch_blocks.append(block) self.N_chblocks +=1 - self.attributes['N_chblocks']+=1 def add_children_blocks(self, blocks): """ IMPORTANT: this function is called by the self.parse fuction. It MUST NOT be called from outside, expecially the parser """ self.ch_blocks += blocks self.N_chblocks +=len(blocks) - self.attributes['N_chblocks']+=len(blocks) def change_parent_block(self, new_parent): """s function changes the parent of the block. It changes parent object, id, and tree_depth. The section level is not changes for consistency. All children are updated. - """ + """ self.parent_block = new_parent #rebuiding id self.id = new_parent.id + '-' + utility.get_random_string(3) #the section level is not changed, #but tree_depth is updated self.tree_depth = new_parent. tree_depth + 1 #now childrens are updated for ch in self.ch_blocks: ch.change_parent_block(self) def get_children(self, bl_name): """ This function return a list of children blocks corresponding to the requested type. If there are not children blocks of that type it returns a void list.""" result = [] for bl in self.ch_blocks: if bl.block_name == bl_name: result.append(bl) return result def __str__(self): return ''.format( self.block_name, self.id) def to_json(self, level=0): """ This functions create a json ouput that represents the tree of subblocks of the called block. """ json = '' levelb = level+3 json += (' '*level + '{\n') json += (' '*levelb + '"ID":"'+ self.id+'",\n') json += (' '*levelb + '"block_name":"'+ self.block_name+'",\n') + json += (' '*levelb + '"N. ch_blocks":"'+ str(self.N_chblocks)+'",\n') json += (' '*levelb + '"tree_depth":"'+ str(self.tree_depth)+'",\n') for k,v in self.attributes.items(): json += (' '*levelb + '"'+k+ '":"'+str(v)+ '",\n' ) json += (' '*levelb + '"children_blocks":[\n') for b in self.ch_blocks: json+= b.to_json(levelb+3) json += (' '*levelb+'],\n') json += (' '*level + '}\n') return json def n_blocks(self): """s function returns the - number of all children blocks recursively.""" + number of all children blocks recursively.""" n = len(self.ch_blocks) for c in self.ch_blocks: n+= c.n_blocks() return n diff --git a/texla/Parser/Blocks/DefaultBlock.py b/texla/Parser/Blocks/DefaultBlock.py index 0bdc4c8..90d4f20 100644 --- a/texla/Parser/Blocks/DefaultBlock.py +++ b/texla/Parser/Blocks/DefaultBlock.py @@ -1,49 +1,50 @@ '''Default Block''' import logging from .Utilities import * from .Block import Block class DefaultBlock(Block): ''' This Block is used when the parser doesn't find a proper parser_hook to call for a matched env or command''' @staticmethod def parse_env(parser ,tex, parent_block, params): #getting the name of env if 'env' in params: env_name = params['env'] else: env_name = 'no_env' if 'star' in params: env_name = env_name + '*' if params['star'] else env_name #default block is created block = DefaultBlock(tex, env_name, parent_block) #We cannot look inside tex, we don't know #what to parser. #we return the block return block @staticmethod def parse_cmd(parser ,tex, parent_block, params): cmd = params['cmd'] cmd = cmd + '*' if params['star'] else cmd #the options has to be matched from the tex match = CommandParser.get_command_options(tex) #match is (options string, left tex ptex = '\\'+cmd+match[0] #default block is created block = DefaultBlock(ptex, cmd, parent_block) #we return the block and the left tex to parse return (block, match[1]) def __init__(self, tex, block_name, parent_block): super().__init__('default', tex, parent_block) #the tex is added also as attribute self.type = block_name - self.attributes['content'] = tex + self.attributes["type"] = block_name + #the content is already saved by the base block parser_hooks = { 'default_env' : DefaultBlock.parse_env, 'default_cmd' : DefaultBlock.parse_cmd, } diff --git a/texla/Parser/TreeExplorer.py b/texla/Parser/TreeExplorer.py index 2a829d2..e6ff2b7 100644 --- a/texla/Parser/TreeExplorer.py +++ b/texla/Parser/TreeExplorer.py @@ -1,120 +1,122 @@ import logging class TreeExplorer: """ The TreeExplorer class is an utility to navigate and extract information from the tree of parsed blocks. For example it is useful to extract the tree of the parents of a block for debugging reasons. It is useful also in rendering to localize blocks inside the document. """ def __init__(self, root_block): """ The constructor needs a root_block to begin the tree""" self.root_block = root_block self.blocks = {'@': root_block} self.block_names = {} #registering blocks by id self.register_blocks(root_block.ch_blocks) @staticmethod def create_tree_from_children(block): #first of all we need the root_block current = block while True: if current.parent_block is None: root_block = current break current = current.parent_block #now we can return a new TreeExplorer #constructed from the found root. return TreeExplorer(root_block) def register_blocks(self, blocks): """This methods reads all the blocks tree from the root_block and created a dictionary with id:block""" for block in blocks: self.blocks[block.id] = block if block.N_chblocks > 0: self.register_blocks(block.ch_blocks) def update_blocks_register(self): """This methods update the blocks' ids register recalling register_blocks with the root_block""" self.register_blocks(self.root_block.ch_blocks) def get_parents_list(self, block): """This method returns the list of the parent blocks of the requested block """ if isinstance(block, str): block = self.blocks[block] parents = [] current = block while True: if current == self.root_block: break parents.append(current.parent_block) current = current.parent_block parents.reverse() return parents def get_parents_list_ids(self, block): parents = self.get_parents_list(block) return [x.id for x in parents] def get_block(self, blockid): return self.blocks.get(blockid) def print_tree(self, block, filter_list=None): """This methods prints a beautified tree starting from block parameter and his children. If filter_list is present only the block with the id in the list are printed. It returns a list of output strings""" output = [] if filter_list is None or block.id in filter_list: lstr = ". "* (block.tree_depth+1) output.append(lstr+ ". "+ " "+"_"*40 ) output.append(lstr+ "#"+"---"+ ">|ID : {}".format(block.id)) output.append(lstr+ ". "+ " |block_name : {}". format(block.block_name)) output.append(lstr+ ". "+ " |attributes: ") for at,attr in block.attributes.items(): output.append(lstr+ ". " + " | - "+ "{} : {}". format(at, attr)) + output.append(lstr+ ". "+ " |content : {}". + format(block.content)) output.append(lstr+ ". ."+"\u203E"*40+"\n") output = "\n".join(output) #iterating on the block children for bl in block.ch_blocks: output += self.print_tree(bl, filter_list) return output def print_tree_to_blocks(self, blocks): """This methods print the tree of parents of the list of blocks passed as parameter. First of all it gets all the parents ids and then prints the tree using the list as filter.""" fl = [] for bl in blocks: fl+= self.get_parents_list_ids(bl) if isinstance(bl, str): fl.append(bl) else: fl.append(bl.id) return self.print_tree(self.root_block, filter_list=fl) def print_tree_to_block(self, block): return self.print_tree_to_blocks([block]) def print_all_tree(self): return self.print_tree(self.root_block) def register_block_names(self): """This function registers the block_names, creating a dictionary with blocks groups by type""" self.block_names.clear() for bl in self.blocks.values(): if not bl in self.block_names: self.block_names[bl.block_name] = [] - self.block_names[bl.block_name].append(bl) \ No newline at end of file + self.block_names[bl.block_name].append(bl) diff --git a/texla/Reporter.py b/texla/Reporter.py index 58ac3a0..1ea8609 100644 --- a/texla/Reporter.py +++ b/texla/Reporter.py @@ -1,40 +1,52 @@ from .Parser.TreeExplorer import TreeExplorer import logging class Reporter: def __init__(self, tree): self.tree_explorer = tree #registering the block_names self.tree_explorer.register_block_names() self.not_parsed_blocks = self.tree_explorer.block_names["default"] self.not_rendered_blocks = [] self.not_parsed_types = {} self.not_rendered_types = {} for bl in self.not_parsed_blocks: if bl.type not in self.not_parsed_types: self.not_parsed_types[bl.type] = [] self.not_parsed_types[bl.type].append(bl) def add_not_rendered_block(self, block): """This method saves a block that is not rendered by the Renderer.""" self.not_rendered_blocks.append(block) if not block.block_name in self.not_rendered_types: self.not_rendered_types[block.block_name] = [] self.not_rendered_types[block.block_name].append(block) def print_report(self, console=True): logging.info('\033[0;34m############### TEXLA REPORT ###############\033[0m') s = [] s.append("\n- NOT PARSED blocks:") for bl, v in self.not_parsed_types.items(): s.append("\t- {} : {}".format(bl, len(v))) s.append("\n- NOT RENDERED blocks:") for bl, v in self.not_rendered_types.items(): s.append("\t- {} : {}".format(bl, len(v))) text= "\n".join(s) if console: logging.info(text) + #saving to file also the block trees with open("sandbox/texla_report.txt",'w') as file: - file.write(text) + t = ["############### TEXLA REPORT ###############"] + t.append("\n- NOT PARSED blocks:") + for bl, v in self.not_parsed_types.items(): + t.append("\t- {} : {}".format(bl, len(v))) + t.append("\n- NOT PARSED blocks details:") + t.append(self.tree_explorer.print_tree_to_blocks(self.not_parsed_blocks)) + t.append("\n- NOT RENDERED blocks:") + for bl, v in self.not_rendered_types.items(): + t.append("\t- {} : {}".format(bl, len(v))) + t.append("\n- NOT RENDERED blocks details:") + t.append(self.tree_explorer.print_tree_to_blocks(self.not_rendered_blocks)) + file.write("\n".join(t))