15
\$\begingroup\$

Gemtext is a very simple markup format used by the alternative web protocol Gemini. Write a Gemtext to HTML converter.

From the Wiki:

A line of text is a paragraph, to be wrapped by the client. It is is independent from the lines coming before or after it.

A list item starts with an asterisk and a space. Again, the rest of the line is the line item, to be wrapped by the client.

A heading starts with one, two, or three number signs and a space. The rest of the line is the heading.

A link is never an inline link like it is for HTML: it’s simply a line starting with an equal-sign and a greater-than sign: “=>”, a space, an URI, and some text. It could be formatted like a list item, or like a paragraph. Relative URIs are explicitly allowed.

Example:

# This is a heading 
This is the first paragraph.
* a list item
* another list item 
This is the second paragraph.
=> http://example.org/ Absolute URI
=> //example.org/ No scheme URI
=> /robots.txt Just a path URI
=> GemText a page link

This should produce this HTML tree (just the equivalent tree, not the exact formatting):

EDIT you don't need the <ul> tags to produce valid HTML

<h1>This is a heading</h1>
<p>This is the first paragraph.</p>
<li>a list item</li>
<li>another list item</li>
<p>This is the second paragraph.</p>
<a href="http://example.org/">Absolute URI</a>
<a href="//example.org/">No scheme URI</a>
<a href="/robots.txt">Just a path URI</a>
<a href="GemText">a page link</a>

All text must pass through. All of <>"& should be converted to HTML entities, even if they don't confuse browsers, to be safe

EDIT: For the sake of this question, you don't have to do 2nd and 3rd level headings

This is so the shortest code in bytes wins

\$\endgroup\$
20
  • 2
    \$\begingroup\$ Welcome to PPCG! It is a reasonably well written challenge, but from next time, try to use Sandbox to get feedbaclk before posting, Thanks! \$\endgroup\$
    – Wasif
    Mar 1, 2021 at 17:36
  • 1
    \$\begingroup\$ Welcome to the site! For future reference, we recommend using the Sandbox to get feedback on challenge ideas before posting them to main. We do also require challenges to have an objective winning criteria, so I've edited in "Shortest code wins" to be in line with the [code-golf] tag \$\endgroup\$ Mar 1, 2021 at 17:36
  • 1
    \$\begingroup\$ Also, unrelated to the challenge but: were you at all aware of the Sandbox before posting this, or were mine and Wasif's comments the first you'd heard of it? \$\endgroup\$ Mar 1, 2021 at 17:38
  • 1
    \$\begingroup\$ @cairdcoinheringaahing Hadn't heard of the sandbox. I'll remember that! \$\endgroup\$ Mar 1, 2021 at 17:39
  • 1
    \$\begingroup\$ OK, but note that everything about the transformation should be in question body - so if there are any other escape characters or edge cases you want handled, then you need to fully explain them. What you have there will only force people to escape <>, so you'd need to list each HTML escape, for example. If the question you want to ask is more like that, consider deleting for now and moving to the sandbox, as there are likely a lot of changes required to make it clear. But if you just want it to be a "lazy" conversion then feel free to leave it here. \$\endgroup\$ Mar 1, 2021 at 17:50

6 Answers 6

3
\$\begingroup\$

Perl 5 (-p), 132 bytes

s,^((#)|(\*)|=> (\S+)|) *(.*?) *$,$t=$2?h1:$3?li:$4?a:p;"<$t".($4&&qq( href="$4")).">".($5=~s~[<>"&]~"&#".(ord$&).";"~ger)."</$t>",e

Try it online!

Another 132 bytes

s-^([#*]|=> (\S+)|) *(.*?) *$-"<".($t=(p,a,li,h1)[ord($1)%4]).($2&&qq( href="$2")).">".($3=~s~[<>"&]~"&#".(ord$&).";"~ger)."</$t>"-e
\$\endgroup\$
1
  • \$\begingroup\$ I was going to say you needed ul but it seems it'll be accepted without it! I'll change the question \$\endgroup\$ Mar 1, 2021 at 20:59
3
\$\begingroup\$

Java + Android, 227 206 bytes

l->l.map(s->android.text.Html.escapeHtml(s).replaceAll("^# (.*)","<h1>$1</h1>").replaceAll("^\\* (.*)","<li>$1</li>").replaceAll("^=&gt; (.*?) (.*)","<a href='$1'>$2</a>").replaceAll("^[^<].*","<p>$0</p>"))

Accepts a Stream of lines as input and returns a Stream of lines.

Try it online!

Saved 21 bytes thanks to Olivier Grégoire

Explanation

l->l.map(
   s->android.text.Html.escapeHtml(s) //Escape line using Android utility class
   .replaceAll("^# (.*)","<h1>$1</h1>")
   //Match headers 
   .replaceAll("^\\* (.*)","<li>$1</li>")
   //Match list items
   .replaceAll("^=&gt; (.*?) (.*)","<a href='$1'>$2</a>")
   //Match links (> is &gt; after being escaped)
   .replaceAll("^[^<].*","<p>$0</p>")
   //If string does not start with <, then nothing has been replaced,
   //so enclose entire line in paragraph element
)
\$\endgroup\$
5
  • \$\begingroup\$ Clever, but fails for foo # bar. You should probably add ^ at the start of each regex. Also, you can probably gain a few bytes by calling like this: l.map(s->{var x=android.text.Html.escapeHtml(s).replaceAll...; ... }) \$\endgroup\$ Mar 2, 2021 at 16:14
  • \$\begingroup\$ @OlivierGrégoire Thanks for the help. \$\endgroup\$ Mar 2, 2021 at 16:19
  • 1
    \$\begingroup\$ All in all, I could golf it to 211 bytes. \$\endgroup\$ Mar 2, 2021 at 16:26
  • \$\begingroup\$ @OlivierGrégoire Thanks! That's a nice way to avoid the conditional. \$\endgroup\$ Mar 2, 2021 at 16:28
  • 1
    \$\begingroup\$ A few more bytes can be gained if you remove the {1,3} from the first regex as it is not required to support multi-level headers, reducing to 206 bytes. \$\endgroup\$ Mar 2, 2021 at 16:30
3
\$\begingroup\$

Retina 0.8.2, 104 103 bytes

&
&amp;
"
&#34;
>
&gt;
<
&lt;
m`^
<p>
p>(#+)
h$.1>
p>\*
li>
p>=&gt; (\S+)
a href="$1">
<(\w+).*
$&</$1>

Try it online! Works with multiple headers. Edit: Saved 1 byte thanks to @OlivierGrégoire. Explanation:

&
&amp;
"
&quot;
>
&gt;
<
&lt;

HTML encode everything.

m`^
<p>

Assume each line is a paragraph.

p>(#+)
h$.1>
p>\*
li>
p>=&gt; (\S+)
a href="$1">

Fix up headings, lists and links.

<(\w+).*
$&</$1>

Add closing tags.

\$\endgroup\$
2
  • \$\begingroup\$ Could &quot; be replaced with &#34;? \$\endgroup\$ Mar 2, 2021 at 17:12
  • \$\begingroup\$ @OlivierGrégoire Nice, thanks! \$\endgroup\$
    – Neil
    Mar 2, 2021 at 18:49
1
\$\begingroup\$

sed 4.2.2, 160 bytes

s!&!&amp;!g
s!<!\&lt;!g
s!>!\&gt;!g
s!"!\&#34;!g
ta
:a
s!^*\(.*\)!<li>\1</li>!
s!^#\(.*\)!<h1>\1</h1>!
s!=&gt; \([^ ]*\)\(.*\)!<a href="\1">\2</a>!
t
i<p>
a</p>

Try it online!

\$\endgroup\$
1
\$\begingroup\$

JavaScript (Node.js), 243 215 210 194 193 191 bytes

n=>n.split`
`.map(e=>`<${t={'#':'h1','*':'li','=>':'a'}[(s=e.split` `)[0]]||'p'}${a=t<'b'?` href="${s[1]}"`:''}>${s.slice(2-!a).join` `.replace(/./g,c=>`&#${Buffer(c)[0]};`)}</${t}>`).join`
`

Try it online!

As with most of my longer answers, I will golf this gradually.

Produces unreadable HTML, but still containing the same text. If this is disallowed, let me know and I will roll it back.

-16 bytes thanks to @A Username

\$\endgroup\$
3
  • \$\begingroup\$ 229 with the readable HTML, this can be used to golf waht you have. \$\endgroup\$
    – emanresu A
    May 30, 2021 at 9:11
  • \$\begingroup\$ @Ausername Thanks! Your suggestions are helpful. Would it be possible to keep the unreadable HTML while still using your tips? This would golf down even further \$\endgroup\$
    – user100690
    May 30, 2021 at 9:12
  • \$\begingroup\$ yep, 194 \$\endgroup\$
    – emanresu A
    May 30, 2021 at 9:13
0
\$\begingroup\$

AWK, 171 bytes

awk '{if($1=="#"){$1="<h1>";$0=$0"</h1>"}else if($1=="*"){$1="<li>";$0=$0"</li>"}else if($1=="=>"){$1="<a href=\""$2"\">";$0=$0"</a>"}else{$1="<p>";$0=$0"</p>"}{print$0}}'

I'm learning AWK so probably can be done better.
Bugs:

  • There are spaces after the opening tags
  • Doesn't remove the URL (I've tried doing $2="", but it breaks the lines)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.