solution_122b.py

#!/usr/bin/python3
# ===============================================================
# convert MD file to HTML file
# ===============================================================

import os
import re
import sys
import datetime as dt

from markdown_my_lifo_queue import my_lifo_queue

import markdown_start_end_web_page as wp

REGX01 = re.compile(r"(__"
                    r"|//"
                    r"|\*\*"
                    r"|\\\\$"
                    r"|\\\\\s"
                    r"|##"
                    r"|#)")

# ---- global flags ---------------------------------------------
# ---- NODELETE     true = output file is not deleted
# ----                     if an error occures
# ---- HTMLFILEOUT  true = output HTML file
# ---- VERBOSE      true = print "what am i doing now"
# ----                     messages

NODELETE    = True
HTMLFILEOUT = True
VERBOSE     = False

# ---------------------------------------------------------------
# ---- terminate output file
# ---------------------------------------------------------------

def terminate_output(fout,outfile):
    
    if fout:
        fout.close()
    if not NODELETE:
        delete_file(outfile)

# ---------------------------------------------------------------
# ---- delete file
# ---------------------------------------------------------------

def delete_file(filename):

    if os.path.exists(filename):
        os.remove(filename)
        if VERBOSE: print(f'File {filename} deleted')        
    else:
        if VERBOSE: print(f'File {filename} does not exist')
  
# ---------------------------------------------------------------
# ---- print list of tuples
# ---------------------------------------------------------------

def print_list(lst,title=None):
    if title is not None:
        print(title)
    print(f'list length is {len(lst)}')
    for tup in lst:
        print(tup)

# ---------------------------------------------------------------
# ---- print queue/stack
# ---- This function is here because you may not be using
# ---- 'my_lifo_queue'. Modify it to translate 'print_cue'
# ---- to which ever queue/stack you are using.
# ---------------------------------------------------------------

def print_que(que,title=None):
    que.dump(title)

# ---------------------------------------------------------------
# ---- process line looking for md tokens
# ---------------------------------------------------------------

def process_line(line,lst):

    ##print(f'process_line({line})')

    while True:

        if VERBOSE:
            print()
            print(f'processing line "{line}"')

        ## --- end of line (no md tag found)

        if not line:
            lst.append((1,''))
            return True
        
        # ---- search for md tag
        
        res = re.search(REGX01,line)

        if not res:
            lst.append((0,line))
            return True

        # ---- extract info from search results

        ln    = len(line)        
        end   = res.end()
        start = res.start()
        tag   = res.groups()[0]        

        if VERBOSE:
            print(f'ln={ln},start={start},end={end},tag="{tag}"')

        ## --- bold

        if tag == r'**':
            if ln > 2:
                lst.append((0,line[0:end-2]))
            lst.append((4,'bold'))

        ## --- italic
         
        elif tag == r'//':
            if ln > 2:
                lst.append((0,line[0:end-2]))
            lst.append((5,'italic'))
            
        ## --- underline
         
        elif tag == r'__':
            if ln > 2:
                lst.append((0,line[0:end-2]))
            lst.append((6,'underline'))

        ## --- h2


        elif tag == r'##':
            lst.append((3,'H2'))
            lst.append((0,line[end:].strip()))
            lst.append((3,'H2'))
            return True

        ## --- h1
         
        elif tag == r'#':
            lst.append((2,'H1'))
            lst.append((0,line[end:].strip()))
            lst.append((2,'H1'))
            return True

        ## --- line break

        elif tag == r'\\ ':
            lst.append((0,line[0:end-3]))
            lst.append((7,'br'))

        elif tag == r'\\':
                lst.append((0,line[0:end-2]))
                lst.append((7,'br'))
                break

        ## --- unknown tag

        else:
            print(f'internal error - unknown tag "{tag}"')
            print_list(lst)
            return False

        #---- make the line shorter skipping
        # ---- the stuff we have already seen
                
        line = line[end:]

    return True

# ---------------------------------------------------------------
# ---- process md file
# ---------------------------------------------------------------

def process_md_file(infile):

    ##print(f'process_file({infile})')

    lst = []

    line_number = 1

    with open(infile,'r') as fin:

        for line in fin:

            ## ---- remove leading/trailing spaces and \n
            ## ---- convert to raw string
            
            line = line.strip().strip('\n')

            # ---- process the text and md tags

            tf = process_line(line,lst)

            # ---- validate line processing
            
            if not tf:
                print(f'processing line {line_number} failed')
                print(f'line: "{line}"')
                print()
                sys.exit()

            # ---- increment line number

            line_number += 1

    return lst


# ---------------------------------------------------------------
# ---- generate HTML code from the list of tokens
# ---------------------------------------------------------------

def generate_html_code(lst,outfile):

    ##print(f'generate_html_code({outfile})')
    ##print_list(lst,'---- tuple list ----')

    que = my_lifo_queue()

    if VERBOSE:
        print('======== generate HTML code ========')
        if HTMLFILEOUT: print(f'output file name is {outfile}')

    # ---- open output file?

    fout = None

    if HTMLFILEOUT:
        fout = open(outfile,'w')
        if VERBOSE: print(f'output file {outfile} opened')
    
    # ---- write start web page?

    if HTMLFILEOUT:
        mystyle='''\
        <style>
        body { font-size: 1.2em; }
        </style>'''
        
        sw = wp.fstr(wp.start_of_web_page,
        {'pagetitle'  :'MD Conversion',
         'author'     :'George of the Jungle',
         'css1'       :'style1.css',
         'css2'       :'style2.css',
         'style'      :mystyle,
         'headertitle':'Converted from MD file'})
        
        # ---- output start-of-web-page to HTML file
        if HTMLFILEOUT:
            fout.write(sw + '\n')

        # ---- print start web page
        if VERBOSE: print(sw)

    # ---- fill in the web page

    for tok in lst:

        state = tok[0]
        
        # ---- plain text

        if state == 0:
            if fout   : fout.write(tok[1] + '\n')
            if VERBOSE: print(tok[1])
            continue

        # ---- line break

        elif state == 7:
            if fout   : fout.write('<br>' + '\n')
            if VERBOSE: print('<br>')
            continue
        
        elif state == 1:
            if que.state() == 1:
                if fout:
                    fout.write('</p>' + '\n')
                    fout.write('<p>' + '\n')
                if VERBOSE:
                    print('</p>')
                    print('<p>')
            else:
                if fout   : fout.write('<p>' + '\n')
                if VERBOSE: print('<p>')
                que.push(tok)
            continue

        # ---- header 1
        
        elif state == 2:
            if que.state() == 2:
                if fout   : fout.write('</h1>' + '\n')
                if VERBOSE: print('</h1>')
                que.pop()
            else:
                if fout   : fout.write('<h1>' + '\n')
                if VERBOSE: print('<h1>')
                que.push(tok)
            continue 

        # ---- header 2

        elif state == 3:
            if que.state() == 3:
                if fout   : fout.write('</h2>' + '\n')
                if VERBOSE: print('</h2>')
                que.pop()
            else:
                if fout   : fout.write('<h2>' + '\n')
                if VERBOSE: print('<h2>')
                que.push(tok)
            continue                 

        # ---- bold

        elif state == 4:
            if que.state() == 4:
                if fout   : fout.write('</b>' + '\n')
                if VERBOSE: print('</b>')
                que.pop()
            else:
                if fout   : fout.write('<b>' + '\n')
                if VERBOSE: print('<b>')
                que.push(tok)
            continue 

        # ----- italic

        elif state == 5:
            if que.state() == 5:
                if fout   : fout.write('</i>' + '\n')
                if VERBOSE: print('</i>')
                que.pop()
            else:
                if fout   : fout.write('<i>' + '\n')
                if VERBOSE: print('<i>')
                que.push(tok)
            continue 

        # ---- underline

        elif state == 6:
            if que.state() == 6:
                if fout   : fout.write('</u>' + '\n')
                if VERBOSE: print('</u>')
                que.pop()
            else:
                if fout   : fout.write('<u>' + '\n')
                if VERBOSE: print('<u>')
                que.push(tok)
            continue

        # ---- unknown
        
        else:
            print()
            print(f'unknown token/state {tok}')
            print(f'delete output file - exit program')
            terminate_output(fout,outfile)
            sys.exit()
            
    # ---- a final </p> required?

    if que.state() == 1:
        if fout   : fout.write('</p>' + '\n')
        if VERBOSE: print('</p>')
        que.pop()

    # ---- queue/stack should have zero entries?

    if que.length() > 0:

        # ---- close and delete output file
        
        print()
        print(f'Error: state queue is not empty')
        print('       MD tag error - exit program')
        print_que(que,'-------------- queue/stack -------------')

        terminate_output(fout,outfile)
        sys.exit()

    # ---- end web page?

    if HTMLFILEOUT:

        dtstr = dt.datetime.now().strftime("%B %d,%Y %I:%M:%S%p")
        ew = wp.fstr(wp.end_of_web_page,
                    {'today': dtstr})
        
        # ---- output end-of-web-page to HTML file
        fout.write(ew)

        # ---- print end web page
        if VERBOSE: print(ew)

    # ---- sucess - return True

    return True


# ---------------------------------------------------------------
# ---- configure runtime flags
# ---------------------------------------------------------------

def config_runtime(nodelete=True,htmlfileout=True,verbose=False):

    global NODELETE
    global HTMLFILEOUT
    global VERBOSE
    
    NODELETE    = nodelete
    HTMLFILEOUT = htmlfileout
    VERBOSE     = verbose
       
    print(f'Runtime: nodelete={NODELETE}, htmlfileout={HTMLFILEOUT}, verbose={VERBOSE}')