2 पॉइंट द्वारा GN⁺ 2024-11-03 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • llamafile के लिए नया syntax highlighter बनाने के लिए 42 भाषाओं को संभालने पर पता चला कि साधारण lexing में भी भाषा-विशेष अपवाद और पुरानी syntax बारीकी से छिपी रहती हैं
  • implementation में C++ और GNU gperf से keyword lookup को तेज बनाया गया, और string, comment, keyword-केंद्रित processing को for loop और switch आधारित finite state machine से हल किया गया
  • C के trigraph, JavaScript के u2028·u2029 line terminator, Shell·Perl·Ruby के heredoc, Kotlin·Scala·TypeScript·Swift के string interpolation बार-बार ऐसे मामले के रूप में सामने आए जिन्हें केवल lexing से संभालना मुश्किल है
  • code lines के आधार पर FORTH 125 lines से Ruby 1042 lines तक फैलता है, और Ruby में << operator, heredoc और backquote टकराते हैं, इसलिए parsing के बिना highlighting खास तौर पर कठिन है
  • llamafile का नया highlighter Windows 10 पर Meta LLaMA 3.2 3B Instruct के साथ demo किया गया और macOS·Linux·FreeBSD·NetBSD पर भी चल सकता है, जिससे यह syntax highlighting न रखने वाले ollama से अलग दिखता है

llamafile highlighter बनाने की वजह

  • llamafile के लिए नया syntax highlighter बनाने के लिए एक महीने तक 42 programming languages सीखी गईं
  • support की जाने वाली languages हैं Ada, Assembly, BASIC, C, C#, C++, COBOL, CSS, D, FORTH, FORTRAN, Go, Haskell, HTML, Java, JavaScript, Julia, JSON, Kotlin, ld, LISP, Lua, m4, Make, Markdown, MATLAB, Pascal, Perl, PHP, Python, R, Ruby, Rust, Scala, Shell, SQL, Swift, Tcl, TeX, TXT, TypeScript, Zig
  • यह list TIOBE Index के अधिकांश हिस्से को cover करती है, लेकिन Scratch text के बजाय blocks इस्तेमाल करता है, इसलिए उसे highlighting target से बाहर रखा गया

implementation का तरीका: gperf और finite state machine

  • basic syntax highlighter में सबसे बड़ा bottleneck वह जगह है जहां token के keyword होने की पहचान करते समय repeated string comparisons बढ़ जाते हैं
  • C++ और GNU gperf का उपयोग करके perfect hash table generate किया गया
    • उदाहरण gperf input true, false, null जैसे Java constants को keywords के रूप में define करता है
    • gperf से generated C file collision-free lookup के लिए ऐसा hash function बना सकती है जो केवल एक character पर विचार करता है
  • C highlighting में लगभग 4,000 keywords define करने पर भी gperf की वजह से 35 MB/s speed से processing संभव है
  • बाकी processing के लिए ज्यादातर finite state machine काफी है
    • flex, bison, ragel के बिना सिर्फ for loop और switch से basic highlighter बनाया जा सकता है
    • strings, comments, keywords पर focus करने पर आम तौर पर lexing level पर processing हो जाती है
    • C function names जैसे elements को भी highlight करना हो तो actual parsing की जरूरत पड़ सकती है
  • highlight_ada.cpp को example implementation के रूप में इस्तेमाल किया गया

llamafile demo और runtime environment

  • नए highlighter और chatbot interface ने llamafile की usability बढ़ाई, और ollama में न होने वाली syntax highlighting differentiator बनी
  • demo Windows 10 पर Meta LLaMA 3.2 3B Instruct model के साथ चलाया गया
  • यह llamafile macOS, Linux, FreeBSD, NetBSD पर भी चल सकता है
  • gemma 27b it जैसे open weight models की quality बेहतर होने से Claude इस्तेमाल करने की प्रेरणा कम होती है

C: सरल भाषा वाली छवि से अलग lexing exceptions

  • C को सरल माना जाता है, लेकिन उसके lexical elements काफी unusual हैं
  • trigraph #, [, \, ^, {, |, }, ~ जैसे characters को ??=, ??(, ??/, ??), ??', ??<, ??!, ??>, ??- से replace कर सकता है
    • C23 standard से हटाया गया है, लेकिन legacy software की वजह से compilers इसे support करते रहेंगे, ऐसा लगता है
    • अच्छे syntax highlighter को इस syntax को भी handle करना चाहिए
  • C का universal character int \uFEB2 = 1; की तरह ASCII source code में Unicode identifiers को व्यक्त कर सकता है
    • GCC error देता है अगर वह standards committee द्वारा allow किए गए specific Unicode plane में न हो
  • C का single-line comment line-end backslash का उपयोग करने पर कई lines तक फैल सकता है
    • Perl, Ruby, Shell जैसी languages जो source code में backslash escape allow करती हैं, वे भी यह C-style behavior support नहीं करतीं
    • Tcl और GNU Make इस behavior को support करते हैं, ऐसा समझा गया
    • Emacs और Pygments कभी-कभी इसे गलत handle करते हैं, और Vim backslash handling हमेशा सही करता दिखता है
  • C में null preprocessor directive भी है
    • शुरुआती v6 source code की .c files अक्सर # की एक line से शुरू होती थीं
    • यह आज भी valid code है, और cc -C -E से comments preserve करने का request करने पर भी preprocessor से कुछ comments हटवाने के लिए इस्तेमाल किया जा सकता है

comment syntax: Haskell और D

  • C में multi-line comment के अंदर फिर से multi-line comment nest नहीं किया जा सकता
  • Haskell {- ... {- ... -} ... -} form में nested comments support करता है
  • D ने C के //, /* ... */ comments वैसे ही अपनाए हैं, और अलग से /+ ... +/ form की recursive comment syntax introduce की है
  • D की lexical syntax documentation formal और detailed है, इसलिए implementation के लिए जरूरी जानकारी अच्छी तरह देती है
    • D lexical syntax documentation hex strings और heredoc strings जैसी details cover करती है
    • D में "...", backtick strings, r"...", q"...", x"..." आदि कई string forms हैं

Tcl और JavaScript: quotes और अदृश्य line terminators

  • Tcl में identifiers के अंदर quotes आ सकते हैं
    • puts a"b a"b print करता है
    • variable names में भी quotes रखे जा सकते हैं, लेकिन reference करते समय $a"b नहीं बल्कि ${a"b} notation इस्तेमाल करना होता है
  • JavaScript में regular expressions के लिए built-in lexical syntax है
    • /[/]/g की तरह character set brackets के अंदर / को escape करने की जरूरत नहीं होती
    • सिर्फ closing slash को scan करने वाला simple तरीका minified code में गलत हो सकता है
  • ECMAScript u2028 LINE SEPARATOR और u2029 PARAGRAPH SEPARATOR को line terminators के रूप में define करता है
    • ये characters असल में \n की तरह behave करते हैं
    • ये Trojan Source characters हैं, इसलिए Emacs में इन्हें क्रमशः , के रूप में दिखाने की setting की जाती है
    • कई software इन्हें पहचान नहीं पाते और question mark की तरह render कर देते हैं
    • D को छोड़कर ऐसे processing वाली किसी अन्य language की जानकारी नहीं है, ऐसा कहा गया
  • इस characteristic का उपयोग करके SectorLISP में C और JavaScript polyglot बनाया जा सका
    • lisp.js browser में चलता है और GCC से compile करके local पर भी चलाया जा सकता है
    • llamafile ऐसे code को सही highlight करता है, लेकिन अन्य highlighters में वही processing नहीं मिली, ऐसा कहा गया

Shell और heredoc के edge cases

  • Shell का heredoc cat <<EOF ... EOF form में multi-line string लिखता है
  • quoted heredoc syntax cat <<'END' variable substitution को disable करती है
  • heredoc marker को empty string रखने पर अगली empty line पर heredoc खत्म हो जाता है
    • example program hello और world को दो lines में print करता है
  • Shell, Ruby, Perl जैसी heredoc support करने वाली languages में एक ही line पर कई heredoc रखे जा सकते हैं
  • Shell में Tcl की तरह # हमेशा comment शुरू नहीं करता
    • ${x#hi-} में # prefix हटाने के लिए इस्तेमाल होता है, और example there print करता है

string interpolation: जिन languages में state stack चाहिए

  • Kotlin string " से शुरू हुई हो तब भी string interpolation की वजह से { पर किसी दूसरे state में जा सकती है
  • TypeScript, Swift, Kotlin, Scala strings के अंदर actual code डालने की दिशा में string interpolation को मजबूती से support करते हैं
  • Kotlin, Scala, TypeScript को highlight करने के लिए braces गिनने और parser state stack maintain करने की जरूरत होती है
    • TypeScript अपेक्षाकृत simple है, finite state machine में कुछ states जोड़ने से काम चल जाता है
    • Kotlin और Scala double quote और triple quote syntax दोनों में interpolation support करते हैं, इसलिए सिर्फ string lexing के लिए लगभग 13 independent states चाहिए थे
    • Swift "\(var)" interpolation syntax और triple quote support करता है, लेकिन implementation के लिए 10 states चाहिए थे

Swift, C#, FORTH: strings को wrap करने के तरीकों का फर्क

  • Swift "...", """...""", /regex/ strings को मनचाही संख्या के # से wrap करने की syntax support करता है
    • दोनों तरफ # की संख्या match करनी होती है
    • यह तरीका string के अंदर quotes या regex boundaries डालने की समस्या हल करता है
  • C# Python की triple quote multi-line strings जैसा है, लेकिन start और end पर match करते हुए अधिक संख्या में quotes इस्तेमाल किए जा सकते हैं
    • left side पर रखी quote count right-side termination condition बनती है
    • arbitrary number of quotes allow करने पर classic Python triple quote की तुलना में validity rules कम होते हैं, इसलिए finite state machine से decode करना simpler हो सकता है, ऐसा माना गया
  • FORTH whitespace boundaries से हर चीज tokenize करता है
    • string start syntax c" भी एक token है
    • c" hello world" का अर्थ अन्य languages के "hello world" जैसा है

FORTRAN और COBOL: fixed column rules

  • llamafile को FORTRAN और COBOL programmers के retire होने के बाद भी banking systems की maintenance में मददगार use case के रूप में पेश किया गया
  • air-gapped environment में controllable AI Gemma 27b से COBOL और FORTRAN code request किया जा सकता है, ऐसा कहा गया
  • FORTRAN के fixed column rules ये हैं
    • column 1 में *, c, C रखने पर वह line comment बन जाती है
    • column 6 में non-space character रखने पर 80 characters से लंबी line continue की जा सकती है
    • columns 1~5 में numbers रखने पर वे label बनते हैं
  • COBOL के rules ये हैं
    • column 7 में * रखने पर comment बनता है
    • column 7 में - रखने पर 80 characters से लंबी line continue की जा सकती है
    • columns 1~6 में line number आता है

Zig और Lua: multi-line strings के अलग समाधान

  • Zig में दो backslashes से शुरू होने वाली multi-line string syntax है
    • यह syntax Python triple quote strings में textwrap.dedent() call करने की जरूरत खत्म करती है
    • downside के रूप में semicolon देखने में अच्छा नहीं लगता, ऐसा माना गया
    • Go, Scala, Python जैसी semicolon की जरूरत न रखने वाली languages के लिए विचार करने योग्य string syntax के रूप में propose किया गया
  • Lua की multi-line string [[...]] पर आधारित है, और brackets के बीच मनचाही संख्या में = डाले जा सकते हैं
    • [==[ ... ]==] की तरह start और end में = की count match की जाती है
    • comment में भी वही तरीका इस्तेमाल किया जा सकता है
    • --[[ ... ]] और --[==[ ... ]==] दोनों forms संभव हैं

Assembly: dialects और preprocessors का combination

  • Assembly कई dialects में बंटी हुई है, इसलिए highlight करना कठिन languages में से एक है
  • llamafile का लक्ष्य AT&T, nasm आदि कई syntaxes को reasonable तरीके से handle करना है
  • keywords को line में पहले identifier और colon के बाद न आने वाले item के रूप में handle करें तो अधिकांश assembly code ठीक-ठाक दिख सकता है
  • comment syntax भी simple नहीं है
    • original UNIX comment के लिए केवल single / चाहिए था
    • GNU as अभी भी line start position पर ही इस comment को support करता है
    • Clang fixed comment support नहीं करता, इसलिए open-source code में practically इस्तेमाल करना कठिन है, ऐसा कहा गया
  • original UNIX assembler character literals में closing quote नहीं लगाता था
    • 'x character x के value 0x78 को दर्शाता है
    • GNU as इसे अब भी support करता है लेकिन LLVM support नहीं करता
    • existing code में यह syntax है, इसलिए अच्छे highlighter को support करना चाहिए
  • GNU assembler quoted identifiers allow करता है, जिससे symbols में लगभग कोई भी character रखा जा सकता है
  • Assembly अक्सर C preprocessor या m4 के साथ इस्तेमाल होती है
    • dnl, m4_dnl, C से शुरू होने वाली lines को भी comments के रूप में handle करना चाहिए

Ada और BASIC: छोटी syntax से lexing हिलने के उदाहरण

  • Ada को lex करना काफी simple है, लेकिन single quote का उपयोग unusual है
    • C की तरह 'x' character literal हो सकता है
    • Foo'Size की तरह attributes reference करते समय भी single quote इस्तेमाल होता है
    • Character'(')')'Image character declare करता है और Image function के जरिए string representation में convert करता है
  • Commodore BASIC example syntax highlighting की कई assumptions तोड़ता है
    • strings line end पर closing quote छोड़ सकती हैं
    • variable names में $ जैसे sigil लगते हैं
    • goto जैसे keywords identifiers के अंदर भी aggressively lex किए जाते हैं
  • Visual BASIC में #1/1/2024# जैसी date literal syntax है
  • Visual BASIC में #If DEBUG Then, #Else, #End If जैसे preprocessor directives भी हैं, इसलिए lexing tricky है

Perl: Shell और programming language के बीच की complexity

  • Perl Shell और programming language के बीच है, इसलिए दोनों तरफ की complexities विरासत में मिलती हैं
  • इसने regular expressions को language का first-class element बनाया, और उसका असर Python जैसी अन्य languages तक पहुंचा
  • Perl की substitution syntax s/hello/Perl/i की तरह sed जैसी है
    • / की जगह कोई भी punctuation character delimiter के रूप में इस्तेमाल किया जा सकता है
    • s!hello!Perl!i तब सुविधाजनक है जब slash regex में हो
    • s{hello}{Perl}i की तरह paired characters इस्तेमाल करने पर additional character की जरूरत होती है
  • Perl में strings की तरह highlight किए जाने वाले कई magic prefixes हैं
    • m, s, y, qr, qw, qq, qx आदि अलग-अलग delimiters के साथ इस्तेमाल होते हैं
  • y/x/y/ को division समझ लेने से बचने के लिए context देखना पड़ता है
    • Perl variables में scalar $, array @, hash % जैसे sigils होते हैं, इसलिए पूरी syntax parse किए बिना भी distinguish करने में मदद मिलती है
  • Perl source code में man page के लिए POD documentation रखने की convention है
    • line start का =word POD documentation शुरू करता है और =cut उसे खत्म करता है

Ruby: lexing का सबसे कठिन target

  • Ruby पिछले languages के union जैसी दिखती है, और syntax formal रूप से पर्याप्त document नहीं है, ऐसा आकलन है
  • Ruby manual का syntax document details में हल्का माना गया
  • Ruby backquote syntax support करती है और साथ ही backquote को method name की तरह इस्तेमाल कर सकती है, इसलिए highlighter के लिए यह तय करना कठिन है कि यह string है या नहीं
  • Ruby में << operator और heredoc दोनों हैं
    • real code में options[:includes] <<arg; true जैसी form मौजूद है
    • यह code heredoc जैसा दिख सकता है, और Emacs भी इसे गलत handle करता है
  • puts "This is #{<<HERE.strip} evil" जैसी string interpolation के अंदर heredoc वाला code भी valid है
  • 42 languages में Ruby को सबसे बड़ा shock माना गया, और Ruby को parsing के बिना lex करना संभव न हो सकता है; parsing के बाद भी interpretability का भरोसा करना मुश्किल है, ऐसा माना गया

implementation code line count से देखी गई complexity

  • हर language highlighter implementation की code line count के आधार पर FORTH सबसे simple और Ruby सबसे complex है
  • सबसे छोटा implementation highlight_forth.cpp 125 lines का है
  • अपेक्षाकृत छोटे implementations में m4 132 lines, Ada 149 lines, LISP 160 lines, MATLAB 166 lines, COBOL 186 lines, BASIC 199 lines, FORTRAN 200 lines आदि हैं
  • medium-size implementations में JavaScript 337 lines, TypeScript 371 lines, Kotlin 387 lines, Scala 387 lines, Assembly 447 lines, C 449 lines, Swift 455 lines, D 521 lines शामिल हैं
  • सबसे लंबे implementations Shell 570 lines, Perl 583 lines, highlight_ruby.cpp 1042 lines हैं

1 टिप्पणियां

 
GN⁺ 2024-11-03
Hacker News की राय
  • C trigraphs में मेरा पसंदीदा रूप do_action() ??!??! handle_error() जैसा है
    यह किसी खास error handling syntax जैसा दिखता है, लेकिन असल में ??!??! बदलकर || logical OR बन जाता है, और short-circuit evaluation के नियमों से जब do_action() non-zero value लौटाता है तो handle_error() चलाने वाली संरचना बनती है—यह बात संतोषजनक लगती है

    • https://en.wikipedia.org/wiki/Digraphs_and_trigraphs_(progra...
      ??! को | में बदला जाता है, इसलिए ??!??! ||, यानी “or” बन जाता है
    • क्या सौंदर्यगत कारणों से || की जगह पुराना C trigraph चुना गया था?
  • पढ़ने में मज़ा आया, लेकिन उलटे Lisp वाले नज़रिए से ज्यादा सहमति महसूस हुई
    मेरी समझ में इसका रुख यह है कि syntax भाषा का बहुत अहम हिस्सा नहीं है, और मदद से ज्यादा बाधा बनने की संभावना रखता है, इसलिए उसे जितना हो सके सरल और समानरूप होना चाहिए ताकि बाकी चीज़ों पर ध्यान दिया जा सके
    हालांकि Lisp में structural editing सीखना अब तक मदद से ज्यादा बाधा जैसा रहा है, और लगता है कि शायद कभी आगे इसका फल मिलेगा

    • सरल syntax कंप्यूटरों के लिए ठीक हो सकता है, लेकिन syntax मुख्यतः इंसानों के पढ़ने और लिखने के लिए डिजाइन किया जाता है
      Lisp की तरह सरल बनाने पर syntax की चर्चा बस semantics की समस्या में खिसक जाती है, यानी परत ही बदलती है
      मेरे हिसाब से जटिल semantics वाले सरल syntax की तुलना में जटिल syntax पढ़ना और लिखना कहीं आसान है। syntax errors पर जल्दी feedback मिल जाता है, लेकिन semantic errors रनटाइम तक छिपे रह सकते हैं
    • Lisp में lexer को reprogram करने वाले reader macros होते हैं, और दिखने वाली संरचना को syntax tree में बदलने की प्रक्रिया भी macros से संभाली जा सकती है
      उदाहरण के लिए https://pyret.org/ जैसी चीज़ भी है। असल में Lisp को सरल या अनिवार्य रूप से uniform मानना मुश्किल है
    • यह सुनकर थोड़ा आश्चर्य हुआ कि structural editing बाधा रही; एक सलाह दूँ तो parinfer इस्तेमाल करें और बस slurp, barf, raise इन तीन commands की key bindings जान लें
      इन चार चीज़ों से ही paredit के फायदे बिना जटिलता के लगभग 95% मिल जाते हैं, और बाकी तरकीबें आदत पड़ने के बाद सीखी जा सकती हैं
  • एक हिस्सा था, “पता नहीं कौन C को 35MB प्रति सेकंड पर syntax highlight करना चाहता है, लेकिन अब यह संभव है”; यह तेज़ तो है, लेकिन tcc बहुत पुराने कंप्यूटरों पर भी C को binary code में 29MB प्रति सेकंड compile करता है: https://bellard.org/tcc/#speed
    इससे तेज़ भी संभव होगा, लेकिन शायद जरूरत नहीं पड़ेगी

  • लेखक ने कुछ बातें छोड़ दी हैं
    TypeScript, Swift, Kotlin, Scala ही वे भाषाएँ नहीं हैं जो string interpolation में असली code डालने की हद तक गई हैं; C#, Python, JavaScript, Ruby, Shell, Make भी कुछ ऐसा ही सपोर्ट करते हैं
    Tcl में code और data को अलग करना मुश्किल है, इसलिए { } असल में एक अजीब string delimiter जैसा है, और xyzzy {#hello world} में #hello world comment है या string, यह evaluation के समय से पहले पता नहीं चल सकता
    PostgreSQL में सुविधाजनक dollar-quoted strings हैं, इसलिए https://www.postgresql.org/docs/current/sql-syntax-lexical.h... की तरह 'Dianne''s horse', $$Dianne's horse$$, $SomeTag$Dianne's horse$SomeTag$ का अर्थ समान होता है

    • Perl में भी यह संभव है
      "I have $foo $bar's: @{[$bar x $foo]}" जैसी string I have 5 x's: xxxxx output कर सकती है
      @{[...]} syntax Perl की उस विशेषता का उपयोग करता है जिसमें scalar के साथ-साथ array interpolation भी संभव है; अंदर वाला [...] array reference बनाता है और बाहर वाला @{...} उसे dereference करता है। Perl interpreter उस अंदरूनी expression में arbitrary code की अनुमति देता है
    • मुझे लगता है कि इसे “string के अंदर code जाता है” कहने के बजाय विशेष string concatenation syntax के रूप में देखना ज्यादा सही है
      तब "foo { toUpper("bar { x + y } bar") } foo" की तरह nesting की जा सकती है, और अगर + string concatenation है तो यह असल में "foo " + toUpper("bar " + (x + y) + " bar") + " foo" के बराबर हो जाता है
      पता नहीं कोई भाषा सच में ऐसे काम करती है या नहीं
    • Ruby इसे चरम तक ले जाता है
      मुझे Ruby पसंद है, लेकिन string interpolation और heredoc मिलाकर बना puts "This is #{< जैसा valid Ruby बचाव करना कठिन है
      साथ ही Ruby में whitespace भी quote character हो सकता है। ऐसे context में जहाँ left operand नहीं है, % quoted string शुरू करता है और अगला character quote की किस्म बताता है, इसलिए % hello hello रखने वाली string बन जाता है। %(this is a string) या %{this is a string} अच्छे हैं, लेकिन whitespace का इस्तेमाल मैंने असल दुनिया में कभी नहीं देखा, और irb भी उसे ठीक से handle नहीं करता, इसलिए इसे हटाया जाए तो अच्छा होगा
    • Scala की string interpolation को pattern match target के रूप में भी इस्तेमाल किया जा सकता है
      val s"${a} + ${b}" = "1 + 2" लिखने पर a 1 और b 2 हो जाते हैं
    • C# interpolated strings की शानदार बात यह है कि वे lazy evaluated होती हैं
      पहले hot loops में log.trace($"Entering iteration {i} for customer {c.ID} [{c.ShortName}]"); जैसा code logger के बाहर निकलने से पहले भी हर बार string.Concat call करता था, इसलिए कई loggers ने अपना interpolation लागू किया
      C# में DefaultInterpolatedStringHandler या custom handler pattern लेने वाले overloads declare किए जा सकते हैं, और यह overload प्राथमिकता पाकर पहले जांच सकता है कि वास्तविक log की जरूरत है या नहीं, फिर string creation को टाल सकता है
  • यहां ज़िक्र न की गई एक और grammar की विचित्रता है, जो ज़्यादातर syntax highlighter तोड़ देती है
    Java में Unicode escape सिर्फ़ string के अंदर नहीं, बल्कि कहीं भी आ सकता है
    उदाहरण के लिए class Foo\u007b} एक valid class है, और // comment के अंदर \u000A को असली newline की तरह process किया जा सकता है, इसलिए assert वाला उदाहरण भी उम्मीद से अलग व्यवहार करता है

    • इसे सही तरह से syntax highlight न कर पाना security problem भी माना जा सकता है
      Unicode escape से block comment को ख़त्म किया जा सकता है, इसलिए Java source file में comment के अंदर malicious code छिपाना हो तो बस comment में Unicode escape block होने का कोई बहाना बनाना काफ़ी है
      जो developer इस feature को नहीं जानता, वह इसे commented-out समझकर शायद आगे बढ़ जाएगा
    • Java में ऐसा मैंने कभी नहीं देखा, क्या इसका कोई सचमुच उपयोगी use case है?
  • “Ruby ऐसी भाषा है जो syntax समझने की हर कोशिश से बच निकलती है” वाला हिस्सा है, लेकिन मनमाने ढंग से reprogrammable lexer रखने वाले TeX के नज़रिए से यह प्यारा-सा स्तर है

    • Lisp reader macro भी lexer को program कर सकते हैं
  • “हर C programmer जानता है कि multi-line comment के अंदर multi-line comment नहीं डाला जा सकता”, लेकिन Standard ML programmer के लिए यह एक अप्रत्याशित सीमा हो सकती है
    (* (* Nested (**) *) comment *) जैसी nested comment valid है, और उसके बाद val _ = print "hello, world\n" भी सामान्य रूप से execute होता है
    यह सोचें कि C के आने के समय उसे expressive language माना जाता था, तो nested comment का language में शामिल न होना दिलचस्प है

    • उस बात में तीन मज़ेदार पहलू हैं
      ML में single-line comment नहीं था, इसलिए वह भी इसी तरह हैरान करने वाली सीमा है, और C को “expressive” कहना पहली बार सुन रहा हूं, लेकिन 1972 में assembly से तुलना करें तो शायद ऐसा रहा हो
      और comment syntax का language की expressiveness से क्या संबंध है, यह मुझे समझ नहीं आता; परिभाषा के हिसाब से तो बिल्कुल नहीं है
    • nested comment को lex करने के लिए stack या कम से कम nesting level counter चाहिए
      परंपरागत रूप से lexical analysis को regular expression जैसी finite state automaton तक सीमित क्षेत्र माना जाता था, इसलिए ऐसी capability lexing के दायरे से बाहर लगती थी
    • C में भी #if 0 इस्तेमाल करें तो comment nest करने का एक तरीका है
      #if 0 ... #if 0 ... #endif ... #endif की तरह wrap कर सकते हैं
    • Pascal भी उदाहरण जैसी nested comment syntax हमेशा support करता था
    • यह सिर्फ़ Standard ML ही नहीं, सामान्य ML पर भी लागू होता है
  • joe की syntax highlighting implementation को reusable रूप में देखना चाहूंगा
    https://joe-editor.sf.net/ का format Python f-string को ठीक से highlight कर पाने जितना शक्तिशाली है
    संबंधित documentation और examples https://github.com/cmur2/joe-syntax/blob/joe-4.4/misc/HowItW..., https://gist.github.com/irdc/6188f11b1e699d615ce2520f03f1d0d... पर हैं

    • joe की DFA-style parsing के आधार पर मैंने कई lexer और parser बनाए हैं
      state और transition grammar standard tools की तुलना में कहीं ज़्यादा समझने में आसान थे
      कमी यह है कि rule set लंबा-चौड़ा हो जाता है और ideal structuring थोड़ी मुश्किल हो जाती है, लेकिन code से सभी production rules infer करना आसान हो जाता है, इस मायने में मैं इसे उल्टा एक फायदा मानता हूं
    • दिलचस्प बात यह है कि Python f-string की 3.12 में grammar बदल गई, इसलिए highlighting भी version के हिसाब से बदलनी चाहिए
  • लगता है लेखक ने TeX syntax highlighting की कोशिश नहीं की है
    mental health के लिए यह अच्छा हो सकता है, क्योंकि TeX में आम तौर पर interpret किए बिना complete syntax highlighting संभव नहीं होती
    सिर्फ़ parsing भी काफ़ी नहीं है, क्योंकि हर character का काम redefine किया जा सकता है, यानी “अब K { है और C } है” जैसी हरकत भी संभव है
    असल में arXiv papers में भी कुछ ऐसे हैं जो इस cursed feature का इस्तेमाल करते हैं

    • https://github.com/Mozilla-Ocho/llamafile/blob/main/llamafil... बनाया है, और hard drive में मिले .tex files पर यह टूटे बिना काफ़ी अच्छी highlighting करता है
      लक्ष्य real-world usage का 99.9% cover करना है। ऐसा होने पर LLM से निकलने वाली चीज़ें भी आम तौर पर cover हो जाने की संभावना है
      obscure syntax भी आम तौर पर बड़ी समस्या नहीं बनता, जब तक कि कोई string या comment file के अंत तक फैलकर बाकी source code को ढक न दे
    • यह जानकर विश्वास करना मुश्किल था कि \makeatletter का मतलब “किसी चीज़ को at character बनाना” नहीं, बल्कि parsing करते समय @ character को letter की तरह treat करना है
    • Common Lisp में भी इसी तरह read table redefine किया जा सकता है। हालांकि arXiv में इसका दुरुपयोग कम होने की संभावना है
  • मेरे हिसाब से Vim जैसा अच्छा syntax highlighting engine लिखना आसान नहीं है
    syntax highlighting में context processing की ज़रूरत होती है, जहाँ किसी खास तरीके से nested content पर अलग-अलग rules लागू होते हैं
    Vim syntax highlighter आपको match और region नाम के दो तरह के items declare करने देता है; match एक simple lexical rule है, जबकि region के पास start, end और middle को अलग-अलग match करने वाली expressions होती हैं
    items को इस तरह declare किया जा सकता है कि वे केवल किसी खास region के अंदर होने पर ही active हों, और inclusion relationships भी आपस में declare किए जा सकते हैं
    इस basic semantic structure के ऊपर कई special cases के लिए features जोड़े गए हैं। Justine भी शायद interview में तुरंत इसे नहीं बना पाएंगी; यह कम से कम एक overnight assignment जैसा काम होगा

    • संभालने में मुश्किल example के तौर पर TXR language के अंदर embedded TXR Lisp है
      manpage HTML conversion result लेकर उसे TXR manual HTML में refine करने वाली genman script यहाँ है: https://www.kylheku.com/cgit/txr/tree/genman.txr
      जो हिस्सा white दिखता है वह literal template है, और Lisp code @(do ...) जैसे directives के अंदर आता है। TXR keywords purple रंग में, TXR Lisp keywords green रंग में दिखते हैं, और वही शब्द context के हिसाब से अलग हो सकता है
      quasi-strings भी nested grammar रख सकती हैं, और उनके अंदर फिर embedded code या quasi-strings आ सकती हैं। txr.vim और tl.vim syntax definition files दोनों https://www.kylheku.com/cgit/txr/tree/genvim.txr से generate होती हैं
    • भोलेपन में मैंने सोचा था कि syntax highlighter ठीक से बनाने के लिए AST में parse करके tokens पर iterate करते हुए node type के हिसाब से रंग update कर देना चाहिए
      अगर ऐसा नहीं किया जाता, तो शायद efficiency की वजह से होगा। उदाहरण के लिए, सिर्फ screen पर दिख रहे हिस्से को highlight करने के बजाय पूरी file parse करनी पड़े, तो cost काफी बढ़ सकती है
    • मैं इस पर पैसा नहीं लगाऊँगा कि Justine interview में वह नहीं बना पाएंगी ;)