import sys
import os
class String:
def __init__(self, string, indent = 0, bullet = False):
self.string = string
self.indent = indent
self.bullet = bullet
self.prefixes = ""
self.italics = False
self.underline = False
self.bold = False
self.colour = False
self.highlight = False
self.sub_element = None
def render(self, *args):
if self.string == "\n":
print()
elif len(self.string.strip()) == 0 and self.sub_element != None:
return
else:
string = self.string
indent = self.indent
print("\033[0m", end = '')
if len(args) == 1:
if self.italics:
print("\033[3m", end = '')
if self.underline:
print("\033[4m", end = '')
if self.bold:
print("\033[1m", end= '')
if self.colour:
print(f"\033[38;2;{self.colour[0]};{self.colour[1]};
{self.colour[2]};m", end = '')
if self.highlight:
print(f"\033[48;2;{self.colour[0]};{self.colour[1]};
{self.colour[2]};m", end = '')
print(string, end='')
elif self.bullet:
print(' ' * (indent - 2) + '• ', end='')
if self.italics:
print("\033[3m", end = '')
if self.underline:
print("\033[4m", end = '')
if self.bold:
print("\033[1m", end= '')
if self.colour:
print(f"\033[38;2;{self.colour[0]};{self.colour[1]};
{self.colour[2]};m", end = '')
if self.highlight:
print(f"\033[48;2;{self.colour[0]};{self.colour[1]};
{self.colour[2]};m", end = '')
print(string[0: os.get_terminal_size()[0]- 2-(indent-2)], end='')
for _ in range(os.get_terminal_size()[0] - 2+indent, len(string),
os.get_terminal_size()[0] - (indent-2)):
print(' ' * indent + string[_ - 2: _ + os.get_terminal_size()
[0] - (indent-2)], end='')
else:
if self.italics:
print("\033[3m", end = '')
if self.underline:
print("\033[4m", end = '')
if self.bold:
print("\033[1m", end= '')
if self.colour:
print(f"\033[38;2;{self.colour[0]};{self.colour[1]};
{self.colour[2]};m", end = '')
if self.highlight:
print(f"\033[48;2;{self.colour[0]};{self.colour[1]};
{self.colour[2]};m", end = '')
for _ in range(0, len(string), os.get_terminal_size()[0] -
indent+2):
print(' ' * indent + string[_: _ + os.get_terminal_size()[0] -
indent+2], end='')
if self.sub_element != None:
self.sub_element.render(True)
def __repr__(self) -> str:
if self.sub_element == None:
return self.string
return self.string + self.sub_element.__repr__().strip()
class Tag:
def construct(self, data):
pass
def execute(self, *args):
lines = []
parser = Parser(self.data)
while parser.last_index < len(self.data):
lines += parser.parse()
return lines
def __init__(self, data) -> None:
#self.contains_multiple_tags = False
self.construct(data)
class Data(Tag):
def construct(self, data) -> None:
self.data = data # line of data
def execute(self, *args):
if "</data>" in self.data.lower():
print("\033[38;2;255;0;0;m" + "Warning: Data tags do not get expanded
when already nested in another data tag")
print("\033[38;2;255;255;255;m")
if len(args) > 0:
if args[0] == False:
return [String(self.data)]
return [String(self.data)]
class Element(Tag):
def construct(self, data):
self.data = data
def execute(self, *args):
self.data = Tag.execute(self)
for index in range(len(self.data)):
if isinstance(self.data[index], str):
self.data[index] = String(self.data[index])
prev = self.data[0]
for index in range(1, len(self.data)):
prev.sub_element = self.data[index]
prev = self.data[index]
return [self.data[0]]
class Bold(Tag):
def construct(self, data) -> None:
self.data = data
def execute(self, *args):
self.data = Tag.execute(self)
for index in range(len(self.data)):
if isinstance(self.data[index], String):
self.data[index].bold = True
return self.data
class Main(Tag):
def construct(self, data) -> None:
self.data = data
#self.contains_multiple_tags = True
def execute(self, *args):
lines = []
parser = Parser(self.data)
while parser.last_index < len(self.data):
lines += parser.parse()
#print(lines)
#os.system("clear")
for line in lines:
if isinstance(line, String):
line.render()
#print(list(line.string))
else:
print()
#print(list(line))
class Colour(Tag):
def construct(self, data) -> None:
self.data = data
def execute(self, *args):
try:
self.colour = args[0]
except:
raise Exception("No colour provided for colour tag")
try:
self.colour = list(map(int, self.colour.split(",")))
except:
raise Exception(f"Invalid RGB colour {self.colour}")
if len(self.colour) != 3:
raise Exception("Invalid RGB colour (not enough numbers)")
self.data = Tag.execute(self)
for index in range(len(self.data)):
if isinstance(self.data[index], String):
self.data[index].colour = self.colour
return self.data
class Highlight(Tag):
def construct(self, data) -> None:
self.data = data
def execute(self, *args):
try:
self.colour = args[0]
except:
raise Exception("No colour provided for colour tag")
try:
self.colour = list(map(int, self.colour.split(",")))
except:
raise Exception(f"Invalid RGB colour {self.colour}")
if len(self.colour) != 3:
raise Exception("Invalid RGB colour (not enough numbers)")
self.data = Tag.execute(self)
for index in range(len(self.data)):
if isinstance(self.data[index], String):
self.data[index].highlight = self.colour
return self.data
class Bullet(Tag):
def construct(self, data):
self.data = data
def execute(self, *args):
self.data = Tag.execute(self)
#print(self.data)
for index, data in enumerate(self.data):
if isinstance(data, String):
if not data.bullet:
self.data[index].bullet = True
self.data[index].indent += 2
return self.data
class Underline(Tag):
def construct(self, data):
self.data = data
def execute(self, *args):
self.data = Tag.execute(self)
for index in range(len(self.data)):
if isinstance(self.data[index], String):
self.data[index].underline = True
return self.data
class Italic(Tag):
def construct(self, data):
self.data =data
def execute(self, *args):
self.data = Tag.execute(self)
for index in range(len(self.data)):
if isinstance(self.data[index], String):
self.data[index].italics = True
return self.data
class Parser:
# parser function <- block of markdown
# parses recursively until just a data block
# data blocks contain one line
def __init__(self, original) -> None:
self.tags = {"element": Data, "bold": Bold, "colour": Colour, "bullet":
Bullet, "highlight": Highlight, "italic": Italic, "underline": Underline, "data":
Element}
self.string = original
self.last_index = 0
self.consumed_end_tags = []
def parse(self):
index, tag, data = self.get_tag(self.string, self.last_index)
self.last_index = max(self.last_index, index)
return tag.execute(data)
def scan(self, string, character, index = 0):
for index in range(index + 1, len(string)):
if string[index] == character:
return index
return None
def identify_tag(self, tag):
data = None
if ":" in tag:
tag_type, data = tag.split(":")
else:
tag_type = tag
if tag_type.lower() in self.tags:
return (self.tags[tag_type.lower()], tag_type.lower(), data)
else:
return (None, None, None)
def search_for_end_tag(self, string, tag, index, tag_type):
c = 0
string = string.lower()
#print(f"Call to search {string[index + 1:]}")
if tag in string[index + 1:]:
f = None
for i in range(index + 1, len(string)):
if string[i] == "<":
end = self.scan(string, ">", i)
if string[i+1:end][0] == "/":
_, _tag_type, tag_data = self.identify_tag(string[i +
2:end])
#print("end", tag_type, _tag_type, end, string[i+1: end])
if _tag_type == tag_type:
c -= 1
if c == -1:
f = i
break
else:
_, _tag_type, tag_data = self.identify_tag(string[i +
1:end])
#print(tag_type, _tag_type, end, string[i+1: end])
if _tag_type == tag_type:
c += 1
else:
continue
return f
#g = []
#for i in range(index + 1, len(string)):
#if string[i: i + len(tag)] == tag:
#g.append(i)
# print(g, c)
#if c < len(g):
#return g[c]
#else:
#print("was here")
#return None
else:
return None
def get_tag(self, string, previous_index = 0):
for index in range(previous_index, len(string)):
if string[index] == "<":
if "\n" in string[previous_index: index]:
#print("!!!!!!!!!!!!!!!!!!!!!", string[:index])
return (index, Data('\n'), [False])
elif len(string[previous_index: index]) > 0:
return (index, Data(string[previous_index: index]), [False])
if (end_of_tag := self.scan(string, ">", index)) != None:
tag, tag_type, tag_data = self.identify_tag(string[index + 1 :
end_of_tag])
if tag != None:
end_tag = self.search_for_end_tag(string, f"</{tag_type}>",
end_of_tag, tag_type)
if end_tag == None:
raise Exception(f"No closing tag for tag type:
{tag_type}")
data = string[end_of_tag + 1: end_tag]
else:
raise Exception(f"Cannot identify tag
{string[end_of_tag:]}")
return (end_tag + len(f"<{tag_type}>") + 1, tag(data),
tag_data)
return (len(string), Data(string[previous_index:]), [False])
file = None
skip = True
macro_ = False
lines = []
macros = []
for index, arg in enumerate(sys.argv):
if skip:
skip = False
continue
if arg == "-f":
file = open(sys.argv[index + 1], "r")
lines = file.readlines()
file.close()
if arg == "-m" or arg == "-macro_file":
macro_ = True
macro_file = open(sys.argv[index + 1], "r")
macros = macro_file.readlines()
macro_file.close()
if file == None:
raise Exception("Need a file")
if macro_:
for line in macros:
macro, m = line.strip().split(":=")
macro = macro.strip()
m = m.strip()
if macro[0] == '\'' and macro[-1] == '\'':
macro = macro[1:-1]
for index, line in enumerate(lines):
if macro in list(map(str.lower, line.replace(',', ' ').replace('.',
' ').replace('<', ' ').replace('>', ' ').split())):
#print(macro)
lines[index] = lines[index].replace(macro, m)
lines[index] = lines[index].replace(macro[0].upper() +
macro[1:], m.replace(macro, macro[0].upper() + macro[1:]))
end_tag = ""
for index in range(0, len(m)):
if m[index] == "<":
end = Parser('').scan(m, ">", index)
_, tag_type, _ = Parser('').identify_tag(m[index + 1: end])
end_tag = f"</{tag_type}>" + end_tag
end_tag = end_tag.strip()
for index, line in enumerate(lines):
lines[index] = line.replace(f"<{macro}>", m)
lines[index] = lines[index].replace(f"</{macro}>", end_tag)
def i(x):
if x == "\n":
return "\n"
else:
return x
#print(lines)
lines = "".join(list(map(i, lines)))
#print(lines)
Main(lines).execute()