aboutsummaryrefslogtreecommitdiffhomepage
path: root/3-ruby-tags
diff options
context:
space:
mode:
Diffstat (limited to '3-ruby-tags')
-rw-r--r--3-ruby-tags/Gemfile5
-rw-r--r--3-ruby-tags/Gemfile.lock14
-rw-r--r--3-ruby-tags/authors.markdown3
-rw-r--r--3-ruby-tags/entry.rb120
-rw-r--r--3-ruby-tags/index.html45
-rw-r--r--3-ruby-tags/remarks.markdown63
6 files changed, 250 insertions, 0 deletions
diff --git a/3-ruby-tags/Gemfile b/3-ruby-tags/Gemfile
new file mode 100644
index 0000000..72dcf70
--- /dev/null
+++ b/3-ruby-tags/Gemfile
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+source "https://rubygems.org"
+
+gem "webrick"
diff --git a/3-ruby-tags/Gemfile.lock b/3-ruby-tags/Gemfile.lock
new file mode 100644
index 0000000..b0d2268
--- /dev/null
+++ b/3-ruby-tags/Gemfile.lock
@@ -0,0 +1,14 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ webrick (1.9.1)
+
+PLATFORMS
+ ruby
+ x86_64-linux
+
+DEPENDENCIES
+ webrick
+
+BUNDLED WITH
+ 2.6.2
diff --git a/3-ruby-tags/authors.markdown b/3-ruby-tags/authors.markdown
new file mode 100644
index 0000000..f308e56
--- /dev/null
+++ b/3-ruby-tags/authors.markdown
@@ -0,0 +1,3 @@
+* Kensuke Imamura (@nsfisis)
+ * nsfisis@gmail.com
+ * cctld: jp
diff --git a/3-ruby-tags/entry.rb b/3-ruby-tags/entry.rb
new file mode 100644
index 0000000..4ad7b7f
--- /dev/null
+++ b/3-ruby-tags/entry.rb
@@ -0,0 +1,120 @@
+eval $s=<<'TRICK'
+require "cgi"
+require "js"
+require "prism"
+
+@s = String.new
+def p(s) = @s << s
+
+def tag(t, s, c) = "<#{t} class=#{c}>#{s}</#{t}>"
+def span(s, c) = tag("span", s, c)
+
+def ruby(t)
+ s = CGI.escape_html(t.value)
+ if r = rt(t)
+ tag("ruby", s + tag("rp", "(", "") + tag("rt", r, "") + tag("rp", ")", ""), t.type)
+ else
+ span(s, t.type)
+ end
+end
+
+def kana(s)
+ s
+ &.scan(/.{2}/)
+ &.map{|c| (0x30A0 + c.to_i).chr(Encoding::UTF_8)}
+ &.*("")
+end
+
+def rt(t)
+ r = {
+ :"&&" => "1136",
+ :"=" => "04199275",
+ :"||" => "623147",
+ :$s => "41750825",
+ :* => "111775",
+ :+ => "557325",
+ :- => "62044225",
+ :<< => "02588341",
+ :@s => "0235400825",
+ LESS: "2371064274",
+ USTAR: "",
+ c: "2392",
+ cgi: "239224920204",
+ chr: "336792",
+ def: "3953",
+ document: "411369658340",
+ downcase: "320683179225",
+ each: "043533",
+ else: "087525",
+ encoding: "0883199239038316",
+ end: "088341",
+ end_column: "088341117364",
+ end_line: "088341730483",
+ end_with?: "088341060326",
+ escape_html: "082517925508043338039208640875",
+ getelementbyid: "183540087665834048040204390392",
+ global: "1677924875",
+ if: "0453",
+ innerhtml: "0483429208043338039208640875",
+ js: "2407920825",
+ kana: "1142",
+ l: "0875",
+ lex: "76351525",
+ location: "771792237183",
+ map: "623555",
+ new: "436992",
+ nil: "4375",
+ p: "5292",
+ prism: "55742664",
+ r: "029275",
+ require: "7415790402",
+ rp: "0292755292",
+ rt: "029275380392",
+ ruby: "7551",
+ s: "0825",
+ scan: "25136783",
+ span: "254983",
+ star: "111775",
+ start_column: "25319240117364",
+ start_line: "25319240730483",
+ string: "2540748316",
+ t: "380392",
+ tag: "3116",
+ to_i: "4005920204",
+ type: "310455",
+ utf_8: "70923803920853080440",
+ value: "48746992",
+ x: "08351525",
+ y: "7904",
+ }
+ kana(
+ r[:"#{t.type}"] ||
+ r[s = :"#{t.value.downcase}"] ||
+ s.end_with?(":") && r[:"#{s[..-2]}"] ||
+ nil
+ )
+end
+
+y = 1
+x = 0
+Prism.lex($s).value[..-2].each {|t, *|
+ l = t.location
+ r = l.start_line
+ if y < r
+ p "\n" * (r - y)
+ x = 0
+ end
+ c = l.start_column
+ if x < c
+ p " " * (c - x)
+ end
+ p ruby(t)
+ y = l.end_line
+ x = l.end_column
+}
+
+JS.global[:document].getElementById("src")[:innerHTML] =
+ span("eval $s=<<'TRICK'\n", "COMMENT") +
+ @s +
+ span("TRICK\n", "COMMENT")
+TRICK
diff --git a/3-ruby-tags/index.html b/3-ruby-tags/index.html
new file mode 100644
index 0000000..63a6a6d
--- /dev/null
+++ b/3-ruby-tags/index.html
@@ -0,0 +1,45 @@
+<html>
+ <script src="https://cdn.jsdelivr.net/npm/@ruby/3.4-wasm-wasi@2.7.1/dist/browser.script.iife.js"></script>
+ <script type="text/ruby" src="entry.rb"></script>
+ <title>TRICK 2025 (Episode I)</title>
+ <body>
+ <style>
+ pre {
+ background-color: #eee;
+ color: #333;
+ padding: 1em;
+ border-radius: 4px;
+ overflow: auto;
+ }
+
+ .COMMENT {
+ color: #777;
+ font-style: italic;
+ }
+
+ .CONSTANT, .GLOBAL_VARIABLE, .INSTANCE_VARIABLE, .IDENTIFIER {
+ color: #088;
+ }
+
+ .KEYWORD_BREAK, .KEYWORD_CASE, .KEYWORD_DEF, .KEYWORD_ELSE, .KEYWORD_END, .KEYWORD_IF, .KEYWORD_IF_MODIFIER, .KEYWORD_WHEN {
+ color: #04c;
+ font-weight: bold;
+ }
+
+ .INTEGER, .KEYWORD_NIL {
+ color: #990;
+ }
+
+ .STRING_BEGIN, .STRING_END, .HEREDOC_START, .HEREDOC_END, .REGEXP_BEGIN, .REGEXP_END, .SYMBOL_BEGIN, .LABEL, .STRING_CONTENT {
+ color: #d50;
+ }
+
+ ruby * {
+ user-select: none;
+ }
+ </style>
+ <main>
+ <pre style="font-size: 16px;"><code id="src"></code></pre>
+ </main>
+ </body>
+</html>
diff --git a/3-ruby-tags/remarks.markdown b/3-ruby-tags/remarks.markdown
new file mode 100644
index 0000000..2621a32
--- /dev/null
+++ b/3-ruby-tags/remarks.markdown
@@ -0,0 +1,63 @@
+# Ruby Tags
+
+
+
+## Remarks
+
+This program utilizes `ruby.wasm`. To run it, first launch an HTTP server:
+
+```
+$ bundle install
+$ bundle exec ruby -run -e httpd
+```
+
+Then access <http://127.0.0.1:8080/>.
+
+NOTE: gems are used only for the server.
+
+It has been tested under the following environment:
+
+```
+$ ruby --version
+ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [x86_64-linux]
+```
+
+`ruby.wasm` version:
+
+```
+<script src="https://cdn.jsdelivr.net/npm/@ruby/3.4-wasm-wasi@2.7.1/dist/browser.script.iife.js"></script>
+```
+
+
+
+## Description
+
+This is a quine-like program that renders its own source code as HTML elements in browser.
+In addition, it highlights the source code and adds "ruby" annotations for each token.
+
+Example for `<ruby>` tag: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ruby
+
+> ```
+> <ruby> 明日 <rp>(</rp><rt>Ashita</rt><rp>)</rp> </ruby>
+> ```
+
+The pronounciation table is compressed and hard-coded to `entry.rb`.
+
+
+
+## Internals
+
+### Syntax Highlighting
+
+Syntax highlighting is powered by Prism, a recently introduced parser/lexer ecosystem. The program calls `Prism.lex()` and iterates over the resulting token stream to apply highlighting.
+
+### Ruby Annotations
+
+Initially, I tried adding ruby annotations to *every* token. However, the pronounciation data for all tokens was too large to fit within the size limit. Although I implemented a simple compression algorithm (see `kana()` function), I finally had to reduce the table size.
+
+### Compression Algorithm for the Pronounciation Table
+
+The compression algorithm is fairly simple: each Katakana letter is encoded as a two-digit integer, offset by U+30A0.
+There are certainly more sophisticated approaches.
+
+Because the `index.html` has no strict size limit according to the regulation, the table data could be embedded there instead. However, that approach was not very elegant.