diff options
Diffstat (limited to '3-ruby-tags')
| -rw-r--r-- | 3-ruby-tags/Gemfile | 5 | ||||
| -rw-r--r-- | 3-ruby-tags/Gemfile.lock | 14 | ||||
| -rw-r--r-- | 3-ruby-tags/authors.markdown | 3 | ||||
| -rw-r--r-- | 3-ruby-tags/entry.rb | 120 | ||||
| -rw-r--r-- | 3-ruby-tags/index.html | 45 | ||||
| -rw-r--r-- | 3-ruby-tags/remarks.markdown | 63 |
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. |
