roundabout,
created on Friday, 29 March 2024, 14:19:58 (1711721998),
received on Wednesday, 31 July 2024, 06:54:43 (1722408883)
Author identity: vlad <vlad.muntoiu@gmail.com>
27d17ece01b688bf45527e34503662cb231c8cbc
markdown.py
@@ -104,6 +104,59 @@ class Paragraph(Container):
return "p"
class CodeBlock(Element):
def __init__(self, content, language="text"):
super().__init__()
self.content = content
self.language = language
def __repr__(self):
return f"Code block ({self.language}):\n\t" + repr(self.content)
@property
def tag_name(self):
return "pre"
class UnorderedList(Element):
def __init__(self, content):
super().__init__()
self.content = content
def __repr__(self):
return "Unordered list:\n\t" + repr(self.content)
@property
def tag_name(self):
return "ul"
class OrderedList(Element):
def __init__(self, content):
super().__init__()
self.content = content
def __repr__(self):
return "Ordered list:\n\t" + repr(self.content)
@property
def tag_name(self):
return "ol"
class ListItem(Paragraph):
def __init__(self, content):
super().__init__("")
self.content = tokenise(content)
def __repr__(self):
return "List item:\n\t" + repr(self.content)
@property
def tag_name(self):
return "li"
class Blockquote(Paragraph):
def __init__(self, content):
super().__init__("")
@@ -244,11 +297,12 @@ def tokenise(source):
current_block = Element()
lines = source.split("\n")
lines = [line[1:] if line.startswith(" ") else line for line in source.split("\n")] # remove leading spaces
i = 0
while i < len(lines):
line = lines[i]
print(i, line)
if not line.strip():
# Void block
@@ -256,6 +310,21 @@ def tokenise(source):
current_block = Element()
i += 1
elif (lines[i].startswith("*") or lines[i].startswith("+") or lines[i].startswith("-")) and lines[i][1:].startswith(" "):
if not isinstance(current_block, UnorderedList):
tokens.append(current_block)
content = []
while i < len(lines) and ((lines[i].startswith("*") or lines[i].startswith("+") or lines[i].startswith("-")) and lines[i][1:].startswith(" ")):
inner_content = lines[i][2:].strip() + "\n"
i += 1
while i < len(lines) and lines[i].startswith(" "):
inner_content += lines[i][2:] + "\n"
i += 1
content.append(ListItem(inner_content))
current_block = UnorderedList(content)
elif only_chars(line.strip(), "-_* ") and len(line.strip()) >= 3:
# Horizontal rule
@@ -276,20 +345,43 @@ def tokenise(source):
content = ""
while i < len(lines) and lines[i].startswith(">"):
content += lines[i].lstrip(">").strip() + "\n"
while i < len(lines) and (lines[i].startswith(">") or (not lines[i].startswith("#") and not lines[i].startswith(">") and lines[i].strip()) and not only_chars(line.strip(), "-_* ") and len(line.strip()) >= 3):
content += lines[i].lstrip(">") + "\n"
i += 1
current_block = Blockquote(content)
elif leading(line, "~") == 3 or leading(line, "`") == 3:
if not isinstance(current_block, CodeBlock):
tokens.append(current_block)
language = line.lstrip("`~").strip()
content = ""
i += 1 # skip the opening fence
while i < len(lines) and not lines[i].strip() in ("```", "~~~"):
content += lines[i] + "\n"
i += 1
if i < len(lines):
i += 1 # prevent a new block from beginning with the closing fence
current_block = CodeBlock(content, language=language)
else:
if not isinstance(current_block, Paragraph):
# Paragraph is default
# Create a paragraph, if there is no other specifier
tokens.append(current_block)
content = ""
while i < len(lines) and not lines[i].startswith("#") and not lines[i].startswith(">") and lines[i].strip():
while (i < len(lines)
and not lines[i].startswith("#")
and not lines[i].startswith(">")
and not lines[i].startswith("* ")
and not lines[i].startswith("+ ")
and not lines[i].startswith("- ")
and not lines[i].startswith("~~~")
and not lines[i].startswith("```")
and lines[i].strip()):
content += lines[i].strip() + "\n"
i += 1
@@ -331,28 +423,27 @@ if __name__ == '__main__':
# Generate an AST from a markdown file
ast = tokenise(
r"""
# Hello World!
## Title 1
### Part 1
#### Chapter _1_
##### Article 1
###### Section 1
Lorem **i`p`sum**
dolor `sit` amet
consectetur \
*adipiscing* elit
* * *
> Make it as simple as possible, [but not simpler](https://wikipedia.org).
> > If you can't explain it simply, you don't understand it well enough.
...
> This is a blockquote
> that spans multiple lines
> and contains a list:
>
> - Item 1
> - Item 2
> - Item 3
> - Subitem 1
> - Subitem 2
>
> And some more text
>
Does it also support that bullet?
Or continuation lines?
* Alternatively, this is a list
* That uses asterisks
"""
)
for i in ast:
print(repr(i))
# for i in ast:
# print(repr(i))
# Now convert the AST to HTML
print(make_html(ast).prettify(formatter=beautifulsoup.formatter.HTMLFormatter(indent=4)))
static/logo-inkscape.svg
@@ -8,14 +8,13 @@
version="1.1"
id="svg1"
xml:space="preserve"
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
sodipodi:docname="logo.svg"
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
sodipodi:docname="logo-inkscape.svg"
inkscape:export-filename="android-chrome-512x512.png"
inkscape:export-xdpi="1024"
inkscape:export-ydpi="1024"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview1"
@@ -27,9 +26,9 @@
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
inkscape:zoom="11.253144"
inkscape:cx="16.88417"
inkscape:cy="23.104654"
inkscape:zoom="7.2404119"
inkscape:cx="80.31311"
inkscape:cy="43.229585"
inkscape:current-layer="layer1"
showgrid="false" /><defs
id="defs1"><clipPath
@@ -197,7 +196,7 @@
style="fill:#fafafa;stroke-width:0.312501;stroke-miterlimit:8"
d="M 0,0.49999974 0.99999986,0 0.87499955,0.49999974 0.99999986,0.99999986 Z"
sodipodi:nodetypes="ccccc" /></marker><marker
markerWidth="4"
markerWidth="3.4641015"
markerHeight="4"
refX="6.9282032"
refY="8.0000001"
@@ -210,51 +209,87 @@
style="vector-effect:non-scaling-stroke;fill:#ffffff;stroke-width:0.388505;stroke-miterlimit:8;-inkscape-stroke:hairline"
d="M 13.856406,0 12,8.0000001 13.856406,16 0,8.0000001 Z"
sodipodi:nodetypes="ccccc"
inkscape:transform-center-x="0.61102905" /></marker></defs><g
inkscape:transform-center-x="0.61102905" /></marker><marker
markerWidth="4"
markerHeight="3.464"
refX="6.7499981"
refY="6.7499989"
orient="0.00"
id="marker12"
viewBox="0 0 13.499996 13.499998"
preserveAspectRatio="none"
markerUnits="userSpaceOnUse"><path
id="path10"
style="color:#000000;fill:#ffffff;stroke-width:3.77953;stroke-miterlimit:8;-inkscape-stroke:none"
d="M 6.7500019,0 13.499996,13.499998 6.7500019,11.812494 0,13.499998 Z"
sodipodi:nodetypes="ccccc" /></marker></defs><g
inkscape:label="Strat 1"
inkscape:groupmode="layer"
id="layer1"><circle
style="vector-effect:non-scaling-stroke;fill:#2196f3;stroke-width:0.264583;stroke-miterlimit:8;-inkscape-stroke:hairline"
id="path1"
cx="6.3500004"
cy="6.3500004"
r="6.3500004" /><g
id="layer1"><g
id="path5"
inkscape:transform-center-x="0.20362483"
inkscape:transform-center-y="-3.1617043"
transform="rotate(-120)" /><path
transform="rotate(-120)" /><circle
style="vector-effect:non-scaling-stroke;fill:#2196f3;stroke-width:0.264583;stroke-miterlimit:8;-inkscape-stroke:hairline"
id="circle1"
cx="6.3500004"
cy="6.3500004"
r="6.3500004" /><path
style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1.19062;stroke-linecap:butt;stroke-miterlimit:8;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker3)"
id="path4"
id="path3"
sodipodi:type="arc"
sodipodi:cx="-8.674262"
sodipodi:cy="2.3242614"
sodipodi:cx="-8.6742611"
sodipodi:cy="2.3242612"
sodipodi:rx="3.7041667"
sodipodi:ry="3.7041667"
sodipodi:start="1.8325957"
sodipodi:end="3.403392"
sodipodi:arc-type="arc"
d="M -9.6329709,5.9022117 A 3.7041667,3.7041667 0 0 1 -12.252212,1.3655527"
d="M -9.6329699,5.9022114 A 3.7041667,3.7041667 0 0 1 -12.252211,1.3655525"
sodipodi:open="true"
transform="rotate(-120)"
inkscape:transform-center-x="-2.9765626"
inkscape:transform-center-y="1.2049253" /><use
x="0"
y="0"
xlink:href="#path4"
inkscape:transform-center-x="0.41097738"
inkscape:transform-center-y="-3.3875401"
id="use7"
transform="rotate(-120,6.3500004,6.3500004)" /><use
x="0"
y="0"
xlink:href="#use7"
inkscape:transform-center-x="2.4979692"
inkscape:transform-center-y="1.9167591"
id="use8"
transform="rotate(-120,6.3500004,6.3500004)" /><rect
inkscape:transform-center-y="1.2049253" /><path
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1.19062;stroke-linecap:butt;stroke-miterlimit:8;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker3)"
id="use3"
sodipodi:type="arc"
sodipodi:cx="2.3242612"
sodipodi:cy="-8.674262"
sodipodi:rx="3.7041667"
sodipodi:ry="3.7041667"
sodipodi:start="1.8325957"
sodipodi:end="3.403392"
sodipodi:arc-type="arc"
d="m 1.3655524,-5.0963118 a 3.7041667,3.7041667 0 0 1 -2.6192415,-4.536659"
sodipodi:open="true"
transform="rotate(120)"
inkscape:transform-center-x="-2.9765626"
inkscape:transform-center-y="1.2049253" /><path
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1.19062;stroke-linecap:butt;stroke-miterlimit:8;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker3)"
id="use4"
sodipodi:type="arc"
sodipodi:cx="6.3500004"
sodipodi:cy="6.3499999"
sodipodi:rx="3.7041667"
sodipodi:ry="3.7041667"
sodipodi:start="1.8325957"
sodipodi:end="3.403392"
sodipodi:arc-type="arc"
d="M 5.3912916,9.9279502 A 3.7041667,3.7041667 0 0 1 2.7720501,5.3912912"
sodipodi:open="true"
inkscape:transform-center-x="-2.9765626"
inkscape:transform-center-y="1.2049253" /><rect
style="opacity:1;fill:none;fill-opacity:1;stroke:none;stroke-width:1.19062;stroke-linecap:butt;stroke-miterlimit:8;stroke-dasharray:none;stroke-opacity:1"
id="rect8"
id="rect4"
width="12.7"
height="12.7"
x="0"
y="0" /></g></svg>
x="9.5367433e-08"
y="9.5367433e-08" /><g
id="path9"><path
style="color:#000000;fill:#607d8b;-inkscape-stroke:none"
d="m -17.20504,-11.301342 0.0208,-0.008"
id="path11" /><path
style="color:#000000;fill:#673ab7;-inkscape-stroke:none"
d="m -17.398437,-11.865234 -0.01953,0.0078 0.425782,1.111328 0.02148,-0.0078 z"
id="path12" /><g
id="g10" /></g></g></svg>