BibTeXCompiler.py 7.4 KB
Newer Older
1
#!/usr/bin/python3
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

#######################################################################
#
# SYMBOLS SECTION - Can be edited. Changes will be preserved.
#
#######################################################################


from functools import partial
import os
import sys
try:
    import regex as re
except ImportError:
    import re

sys.path.extend(['../../', '../', './'])

20
from DHParser import is_filename, load_if_file, \
21
    Grammar, Compiler, nil_preprocessor, \
di68kap's avatar
di68kap committed
22 23
    Lookbehind, Lookahead, Alternative, Pop, Required, Token, Synonym, \
    Option, NegativeLookbehind, OneOrMore, RegExp, Retrieve, Series, Capture, \
24 25
    ZeroOrMore, Forward, NegativeLookahead, mixin_comment, compile_source, \
    last_value, counterpart, accumulate, PreprocessorFunc, \
eckhart's avatar
eckhart committed
26
    Node, TransformationDict, Whitespace, \
eckhart's avatar
eckhart committed
27
    traverse, remove_children_if, is_anonymous, \
Eckhart Arnold's avatar
Eckhart Arnold committed
28
    reduce_single_child, replace_by_single_child, replace_or_reduce, remove_whitespace, \
29
    remove_expendables, remove_empty, remove_tokens, flatten, is_insignificant_whitespace, \
eckhart's avatar
eckhart committed
30
    is_empty, is_expendable, collapse, replace_content, remove_nodes, remove_content, remove_brackets, replace_parser, \
31
    keep_children, is_one_of, has_content, apply_if, remove_first, remove_last, \
eckhart's avatar
eckhart committed
32
    WHITESPACE_PTYPE, TOKEN_PTYPE, GLOBALS
33
from DHParser.transform import TransformationFunc
34
from DHParser.log import logging
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56


#######################################################################
#
# PREPROCESSOR SECTION - Can be edited. Changes will be preserved.
#
#######################################################################

def BibTeXPreprocessor(text):
    return text

def get_preprocessor() -> PreprocessorFunc:
    return BibTeXPreprocessor


#######################################################################
#
# PARSER SECTION - Don't edit! CHANGES WILL BE OVERWRITTEN!
#
#######################################################################

class BibTeXGrammar(Grammar):
eckhart's avatar
eckhart committed
57
    r"""Parser for a BibTeX source file.
58 59
    """
    text = Forward()
60 61
    source_hash__ = "5a291c267f7f53949384137254282b62"
    static_analysis_pending__ = [True]
eckhart's avatar
eckhart committed
62
    parser_initialization__ = ["upon instantiation"]
eckhart's avatar
eckhart committed
63
    resume_rules__ = {}
64
    COMMENT__ = r'//'
65
    WHITESPACE__ = r'\s*'
66
    WSP_RE__ = mixin_comment(whitespace=WHITESPACE__, comment=COMMENT__)
di68kap's avatar
di68kap committed
67
    wsp__ = Whitespace(WSP_RE__)
68
    EOF = NegativeLookahead(RegExp('(?i).'))
69 70
    CONTENT_STRING = OneOrMore(Alternative(RegExp('(?i)[^{}%]+'), Series(Lookahead(RegExp('(?i)%')), wsp__)))
    COMMA_TERMINATED_STRING = ZeroOrMore(Alternative(RegExp('(?i)[^,%]+'), Series(Lookahead(RegExp('(?i)%')), wsp__)))
di68kap's avatar
di68kap committed
71
    NO_BLANK_STRING = Series(RegExp('(?i)[^ \\t\\n,%]+'), wsp__)
72
    WORD = Series(RegExp('(?i)\\w+'), wsp__)
di68kap's avatar
di68kap committed
73
    text.set(ZeroOrMore(Alternative(CONTENT_STRING, Series(Series(Token("{"), wsp__), text, Series(Token("}"), wsp__)))))
74
    plain_content = Synonym(COMMA_TERMINATED_STRING)
di68kap's avatar
di68kap committed
75
    content = Alternative(Series(Series(Token("{"), wsp__), text, Series(Token("}"), wsp__)), plain_content)
76
    field = Synonym(WORD)
77 78
    key = Synonym(NO_BLANK_STRING)
    type = Synonym(WORD)
79
    entry = Series(RegExp('(?i)@'), type, Series(Token("{"), wsp__), key, ZeroOrMore(Series(Series(Token(","), wsp__), field, Series(Token("="), wsp__), content, mandatory=2)), Option(Series(Token(","), wsp__)), Series(Token("}"), wsp__), mandatory=6)
di68kap's avatar
di68kap committed
80
    comment = Series(Series(Token("@Comment{"), wsp__), text, Series(Token("}"), wsp__), mandatory=2)
81
    pre_code = ZeroOrMore(Alternative(RegExp('(?i)[^"%]+'), RegExp('(?i)%.*\\n')))
di68kap's avatar
di68kap committed
82
    preamble = Series(Series(Token("@Preamble{"), wsp__), RegExp('(?i)"'), pre_code, RegExp('(?i)"'), wsp__, Series(Token("}"), wsp__), mandatory=5)
83 84 85 86
    bibliography = ZeroOrMore(Alternative(preamble, comment, entry))
    root__ = bibliography
    
def get_grammar() -> BibTeXGrammar:
87
    global GLOBALS
88
    try:
89
        grammar = GLOBALS.BibTeX_00000001_grammar_singleton
eckhart's avatar
eckhart committed
90
    except AttributeError:
91
        GLOBALS.BibTeX_00000001_grammar_singleton = BibTeXGrammar()
92
        if hasattr(get_grammar, 'python_src__'):
93 94
            GLOBALS.BibTeX_00000001_grammar_singleton.python_src__ = get_grammar.python_src__
        grammar = GLOBALS.BibTeX_00000001_grammar_singleton
95 96 97 98 99 100 101 102 103 104 105
    return grammar


#######################################################################
#
# AST SECTION - Can be edited. Changes will be preserved.
#
#######################################################################

BibTeX_AST_transformation_table = {
    # AST Transformations for the BibTeX-grammar
106
    "<": remove_empty,
107 108 109 110 111 112 113 114 115 116 117
    "bibliography": [],
    "preamble": [],
    "pre_code": [],
    "comment": [],
    "entry": [],
    "type": [],
    "key": [],
    "field": [],
    "content": [replace_or_reduce],
    "plain_content": [],
    "text": [],
118
    ":_Token, :_RE": reduce_single_child,
119
    "*": replace_by_single_child
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
}


def BibTeXTransform() -> TransformationDict:
    return partial(traverse, processing_table=BibTeX_AST_transformation_table.copy())

def get_transformer() -> TransformationFunc:
    global thread_local_BibTeX_transformer_singleton
    try:
        transformer = thread_local_BibTeX_transformer_singleton
    except NameError:
        thread_local_BibTeX_transformer_singleton = BibTeXTransform()
        transformer = thread_local_BibTeX_transformer_singleton
    return transformer


#######################################################################
#
# COMPILER SECTION - Can be edited. Changes will be preserved.
#
#######################################################################

class BibTeXCompiler(Compiler):
    """Compiler for the abstract-syntax-tree of a BibTeX source file.
    """
    def on_bibliography(self, node):
        return node

    def on_preamble(self, node):
        pass

    def on_pre_code(self, node):
        pass

    def on_comment(self, node):
        pass

    def on_entry(self, node):
        pass

    def on_type(self, node):
        pass

    def on_key(self, node):
        pass

    def on_field(self, node):
        pass

    def on_content(self, node):
        pass

    def on_plain_content(self, node):
        pass

    def on_text(self, node):
        pass


eckhart's avatar
eckhart committed
179
def get_compiler() -> BibTeXCompiler:
180 181 182 183
    global thread_local_BibTeX_compiler_singleton
    try:
        compiler = thread_local_BibTeX_compiler_singleton
    except NameError:
eckhart's avatar
eckhart committed
184
        thread_local_BibTeX_compiler_singleton = BibTeXCompiler()
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
        compiler = thread_local_BibTeX_compiler_singleton
    return compiler


#######################################################################
#
# END OF DHPARSER-SECTIONS
#
#######################################################################


def compile_src(source):
    """Compiles ``source`` and returns (result, errors, ast).
    """
    with logging("LOGS"):
        compiler = get_compiler()
        cname = compiler.__class__.__name__
        log_file_name = os.path.basename(os.path.splitext(source)[0]) \
            if is_filename(source) < 0 else cname[:cname.find('.')] + '_out'    
        result = compile_source(source, get_preprocessor(), 
                                get_grammar(),
                                get_transformer(), compiler)
    return result


if __name__ == "__main__":
    if len(sys.argv) > 1:
        result, errors, ast = compile_src(sys.argv[1])
        if errors:
            for error in errors:
                print(error)
            sys.exit(1)
        else:
            print(result.as_xml() if isinstance(result, Node) else result)
    else:
        print("Usage: BibTeXCompiler.py [FILENAME]")