[{"data":1,"prerenderedAt":4932},["ShallowReactive",2],{"page-\u002Flua\u002F06-string-manipulation":3},{"id":4,"title":5,"body":6,"description":4925,"extension":4926,"meta":4927,"navigation":147,"path":4928,"seo":4929,"stem":4930,"__hash__":4931},"content\u002Flua\u002F06-string-manipulation.md","Lua 06 — Strings, Patterns & Byte Processing",{"type":7,"value":8,"toc":4918},"minimark",[9,14,44,49,4914],[10,11,13],"h1",{"id":12},"_06-strings-patterns-byte-processing","06 — Strings, Patterns & Byte Processing",[15,16,17,18,22,23,26,27,31,32,35,36,39,40,43],"p",{},"Lua strings are ",[19,20,21],"strong",{},"immutable, interned byte sequences",". There is no separate \"character\" type — a character is a 1-byte string. Pattern matching uses Lua's own ",[19,24,25],{},"pattern engine"," (not regex): simpler syntax, no alternation (",[28,29,30],"code",{},"|",") or lookahead, but with captures, anchors, and a frontier pattern. The ",[28,33,34],{},"string"," library operates on bytes; ",[28,37,38],{},"utf8"," (5.3+) operates on code points. For complex parsing, use ",[19,41,42],{},"LPeg"," (Parsing Expression Grammars).",[45,46,48],"h2",{"id":47},"string-representation-interning","String Representation & Interning",[50,51,53,394,398,419,1298,1302,3974,3983,4014,4028,4083,4091,4292,4296,4315,4412,4435,4486,4510,4525,4781,4785],"code-wrapper",{"language":52},"lua",[54,55,59],"pre",{"className":56,"code":57,"language":52,"meta":58,"style":58},"language-lua shiki shiki-themes github-light github-dark","-- Strings are immutable: any \"modification\" creates a new string\nlocal s = \"hello\"\nlocal t = s:upper()  -- t is a NEW string \"HELLO\"; s is unchanged\n-- s and \"hello\" may share the same interned object\nprint(s == \"hello\")  -- true (pointer comparison after interning — no memcmp)\n\n-- Long strings: [[ ]] preserves everything literally (no \\n, \\t escaping)\nlocal template = [[\n{\n  \"name\": \"Alice\",\n  \"value\": \"no need to escape \\n here — it's literal\"\n}\n]]\n-- Nestable: [==[ ]==] — = count must match, allows [[ ]] inside\nlocal code = [==[\n  local s = [[inner]]  -- this [[ ]] is inside [==[ ]==] — no conflict\n]==]\n\n-- Byte length: # (not character length for multi-byte UTF-8)\n#\"héllo\"  -- 6 (é = 2 bytes: 0xC3 0xA9)\nutf8.len(\"héllo\")  -- 5 (character count)\n\n-- Indexing: NO s[i] syntax. Use string.sub(s, i, j)\nlocal s = \"hello\"\ns:sub(1, 1)    -- \"h\" (byte 1 — 1-indexed)\ns:sub(-1)      -- \"o\" (last byte — negative = from end)\ns:sub(-2)      -- \"lo\" (last 2 bytes)\ns:sub(2, -2)   -- \"ell\" (from byte 2 to byte -2)\n","",[28,60,61,70,88,115,121,142,149,155,168,174,180,186,192,198,204,217,226,237,242,248,260,277,282,288,299,326,348,369],{"__ignoreMap":58},[62,63,66],"span",{"class":64,"line":65},"line",1,[62,67,69],{"class":68},"sdCPZ","-- Strings are immutable: any \"modification\" creates a new string\n",[62,71,73,77,81,84],{"class":64,"line":72},2,[62,74,76],{"class":75},"svdQ7","local",[62,78,80],{"class":79},"ssxIu"," s ",[62,82,83],{"class":75},"=",[62,85,87],{"class":86},"sJ6F3"," \"hello\"\n",[62,89,91,93,96,98,102,105,109,112],{"class":64,"line":90},3,[62,92,76],{"class":75},[62,94,95],{"class":79}," t ",[62,97,83],{"class":75},[62,99,101],{"class":100},"sIsaT"," s",[62,103,104],{"class":79},":",[62,106,108],{"class":107},"snvgF","upper",[62,110,111],{"class":79},"()  ",[62,113,114],{"class":68},"-- t is a NEW string \"HELLO\"; s is unchanged\n",[62,116,118],{"class":64,"line":117},4,[62,119,120],{"class":68},"-- s and \"hello\" may share the same interned object\n",[62,122,124,127,130,133,136,139],{"class":64,"line":123},5,[62,125,126],{"class":107},"print",[62,128,129],{"class":79},"(s ",[62,131,132],{"class":75},"==",[62,134,135],{"class":86}," \"hello\"",[62,137,138],{"class":79},")  ",[62,140,141],{"class":68},"-- true (pointer comparison after interning — no memcmp)\n",[62,143,145],{"class":64,"line":144},6,[62,146,148],{"emptyLinePlaceholder":147},true,"\n",[62,150,152],{"class":64,"line":151},7,[62,153,154],{"class":68},"-- Long strings: [[ ]] preserves everything literally (no \\n, \\t escaping)\n",[62,156,158,160,163,165],{"class":64,"line":157},8,[62,159,76],{"class":75},[62,161,162],{"class":79}," template ",[62,164,83],{"class":75},[62,166,167],{"class":86}," [[\n",[62,169,171],{"class":64,"line":170},9,[62,172,173],{"class":86},"{\n",[62,175,177],{"class":64,"line":176},10,[62,178,179],{"class":86},"  \"name\": \"Alice\",\n",[62,181,183],{"class":64,"line":182},11,[62,184,185],{"class":86},"  \"value\": \"no need to escape \\n here — it's literal\"\n",[62,187,189],{"class":64,"line":188},12,[62,190,191],{"class":86},"}\n",[62,193,195],{"class":64,"line":194},13,[62,196,197],{"class":86},"]]\n",[62,199,201],{"class":64,"line":200},14,[62,202,203],{"class":68},"-- Nestable: [==[ ]==] — = count must match, allows [[ ]] inside\n",[62,205,207,209,212,214],{"class":64,"line":206},15,[62,208,76],{"class":75},[62,210,211],{"class":79}," code ",[62,213,83],{"class":75},[62,215,216],{"class":86}," [==[\n",[62,218,220,223],{"class":64,"line":219},16,[62,221,222],{"class":86},"  local s = [[inner]]  -- this [[ ]] is inside [==[ ]==] ",[62,224,225],{"class":79},"— no conflict\n",[62,227,229,232,234],{"class":64,"line":228},17,[62,230,231],{"class":79},"]",[62,233,132],{"class":75},[62,235,236],{"class":79},"]\n",[62,238,240],{"class":64,"line":239},18,[62,241,148],{"emptyLinePlaceholder":147},[62,243,245],{"class":64,"line":244},19,[62,246,247],{"class":68},"-- Byte length: # (not character length for multi-byte UTF-8)\n",[62,249,251,254,257],{"class":64,"line":250},20,[62,252,253],{"class":75},"#",[62,255,256],{"class":86},"\"héllo\"  ",[62,258,259],{"class":68},"-- 6 (é = 2 bytes: 0xC3 0xA9)\n",[62,261,263,266,269,272,274],{"class":64,"line":262},21,[62,264,265],{"class":107},"utf8.len",[62,267,268],{"class":79},"(",[62,270,271],{"class":86},"\"héllo\"",[62,273,138],{"class":79},[62,275,276],{"class":68},"-- 5 (character count)\n",[62,278,280],{"class":64,"line":279},22,[62,281,148],{"emptyLinePlaceholder":147},[62,283,285],{"class":64,"line":284},23,[62,286,287],{"class":68},"-- Indexing: NO s[i] syntax. Use string.sub(s, i, j)\n",[62,289,291,293,295,297],{"class":64,"line":290},24,[62,292,76],{"class":75},[62,294,80],{"class":79},[62,296,83],{"class":75},[62,298,87],{"class":86},[62,300,302,305,307,310,312,315,318,320,323],{"class":64,"line":301},25,[62,303,304],{"class":100},"s",[62,306,104],{"class":79},[62,308,309],{"class":107},"sub",[62,311,268],{"class":79},[62,313,314],{"class":107},"1",[62,316,317],{"class":79},", ",[62,319,314],{"class":107},[62,321,322],{"class":79},")    ",[62,324,325],{"class":68},"-- \"h\" (byte 1 — 1-indexed)\n",[62,327,329,331,333,335,337,340,342,345],{"class":64,"line":328},26,[62,330,304],{"class":100},[62,332,104],{"class":79},[62,334,309],{"class":107},[62,336,268],{"class":79},[62,338,339],{"class":75},"-",[62,341,314],{"class":107},[62,343,344],{"class":79},")      ",[62,346,347],{"class":68},"-- \"o\" (last byte — negative = from end)\n",[62,349,351,353,355,357,359,361,364,366],{"class":64,"line":350},27,[62,352,304],{"class":100},[62,354,104],{"class":79},[62,356,309],{"class":107},[62,358,268],{"class":79},[62,360,339],{"class":75},[62,362,363],{"class":107},"2",[62,365,344],{"class":79},[62,367,368],{"class":68},"-- \"lo\" (last 2 bytes)\n",[62,370,372,374,376,378,380,382,384,386,388,391],{"class":64,"line":371},28,[62,373,304],{"class":100},[62,375,104],{"class":79},[62,377,309],{"class":107},[62,379,268],{"class":79},[62,381,363],{"class":107},[62,383,317],{"class":79},[62,385,339],{"class":75},[62,387,363],{"class":107},[62,389,390],{"class":79},")   ",[62,392,393],{"class":68},"-- \"ell\" (from byte 2 to byte -2)\n",[45,395,397],{"id":396},"pattern-engine-not-regex","Pattern Engine — Not Regex",[15,399,400,401,403,404,407,408,411,412,414,415,418],{},"Lua patterns look like regex but are a different engine. No ",[28,402,30],{},", no ",[28,405,406],{},"(?:)",", no lookahead. But has captures, ",[28,409,410],{},"%"," classes, ",[28,413,339],{}," (lazy), and frontier ",[28,416,417],{},"%f",".",[50,420,421,574,589],{"language":52},[54,422,424],{"className":56,"code":423,"language":52,"meta":58,"style":58},"-- Character classes (use % prefix, not \\):\n-- %a  letter         %A  non-letter\n-- %d  digit          %D  non-digit\n-- %s  whitespace     %S  non-whitespace\n-- %w  alphanumeric   %W  non-alphanumeric\n-- %l  lowercase      %u  uppercase\n-- %p  punctuation    %c  control char\n-- %x  hex digit\n-- .  any character\n-- [abc]  char class     [^abc]  negation\n-- [a-z]  range          [%w_]   combined\n\n-- Quantifiers:\n-- +   1 or more (greedy)\n-- *   0 or more (greedy)\n-- -   0 or more (lazy\u002Fnon-greedy)  ← unique to Lua, not in regex\n-- ?   0 or 1\n\n-- Anchors:\n-- ^   start of string (only at pattern start)\n-- $   end of string (only at pattern end)\n\n-- Captures: ( ) groups; %1, %2 backreferences in replacement\n-- Frontier: %f[set] — boundary between non-set and set char (like lookahead for boundary)\n\n-- Key differences from regex:\n-- NO alternation: (cat|dog) doesn't work — use multiple matches or LPeg\n-- NO lookahead\u002Flookbehind\n-- NO backreferences in the MATCH pattern (only in gsub replacement)\n-- % is the escape, not \\  (use %. for literal dot, %% for literal %)\n",[28,425,426,431,436,441,446,451,456,461,466,471,476,481,485,490,495,500,505,510,514,519,524,529,533,538,543,547,552,557,562,568],{"__ignoreMap":58},[62,427,428],{"class":64,"line":65},[62,429,430],{"class":68},"-- Character classes (use % prefix, not \\):\n",[62,432,433],{"class":64,"line":72},[62,434,435],{"class":68},"-- %a  letter         %A  non-letter\n",[62,437,438],{"class":64,"line":90},[62,439,440],{"class":68},"-- %d  digit          %D  non-digit\n",[62,442,443],{"class":64,"line":117},[62,444,445],{"class":68},"-- %s  whitespace     %S  non-whitespace\n",[62,447,448],{"class":64,"line":123},[62,449,450],{"class":68},"-- %w  alphanumeric   %W  non-alphanumeric\n",[62,452,453],{"class":64,"line":144},[62,454,455],{"class":68},"-- %l  lowercase      %u  uppercase\n",[62,457,458],{"class":64,"line":151},[62,459,460],{"class":68},"-- %p  punctuation    %c  control char\n",[62,462,463],{"class":64,"line":157},[62,464,465],{"class":68},"-- %x  hex digit\n",[62,467,468],{"class":64,"line":170},[62,469,470],{"class":68},"-- .  any character\n",[62,472,473],{"class":64,"line":176},[62,474,475],{"class":68},"-- [abc]  char class     [^abc]  negation\n",[62,477,478],{"class":64,"line":182},[62,479,480],{"class":68},"-- [a-z]  range          [%w_]   combined\n",[62,482,483],{"class":64,"line":188},[62,484,148],{"emptyLinePlaceholder":147},[62,486,487],{"class":64,"line":194},[62,488,489],{"class":68},"-- Quantifiers:\n",[62,491,492],{"class":64,"line":200},[62,493,494],{"class":68},"-- +   1 or more (greedy)\n",[62,496,497],{"class":64,"line":206},[62,498,499],{"class":68},"-- *   0 or more (greedy)\n",[62,501,502],{"class":64,"line":219},[62,503,504],{"class":68},"-- -   0 or more (lazy\u002Fnon-greedy)  ← unique to Lua, not in regex\n",[62,506,507],{"class":64,"line":228},[62,508,509],{"class":68},"-- ?   0 or 1\n",[62,511,512],{"class":64,"line":239},[62,513,148],{"emptyLinePlaceholder":147},[62,515,516],{"class":64,"line":244},[62,517,518],{"class":68},"-- Anchors:\n",[62,520,521],{"class":64,"line":250},[62,522,523],{"class":68},"-- ^   start of string (only at pattern start)\n",[62,525,526],{"class":64,"line":262},[62,527,528],{"class":68},"-- $   end of string (only at pattern end)\n",[62,530,531],{"class":64,"line":279},[62,532,148],{"emptyLinePlaceholder":147},[62,534,535],{"class":64,"line":284},[62,536,537],{"class":68},"-- Captures: ( ) groups; %1, %2 backreferences in replacement\n",[62,539,540],{"class":64,"line":290},[62,541,542],{"class":68},"-- Frontier: %f[set] — boundary between non-set and set char (like lookahead for boundary)\n",[62,544,545],{"class":64,"line":301},[62,546,148],{"emptyLinePlaceholder":147},[62,548,549],{"class":64,"line":328},[62,550,551],{"class":68},"-- Key differences from regex:\n",[62,553,554],{"class":64,"line":350},[62,555,556],{"class":68},"-- NO alternation: (cat|dog) doesn't work — use multiple matches or LPeg\n",[62,558,559],{"class":64,"line":371},[62,560,561],{"class":68},"-- NO lookahead\u002Flookbehind\n",[62,563,565],{"class":64,"line":564},29,[62,566,567],{"class":68},"-- NO backreferences in the MATCH pattern (only in gsub replacement)\n",[62,569,571],{"class":64,"line":570},30,[62,572,573],{"class":68},"-- % is the escape, not \\  (use %. for literal dot, %% for literal %)\n",[45,575,577,317,580,317,583,317,586],{"id":576},"stringmatch-stringgmatch-stringfind-stringgsub",[28,578,579],{},"string.match",[28,581,582],{},"string.gmatch",[28,584,585],{},"string.find",[28,587,588],{},"string.gsub",[50,590,591,1124,1129],{"language":52},[54,592,594],{"className":56,"code":593,"language":52,"meta":58,"style":58},"-- string.match(s, pattern, init?) → captures (or whole match if no captures), or nil\nlocal s = \"2024-01-15\"\nlocal y, m, d = s:match(\"(%d+)-(%d+)-(%d+)\")  -- \"2024\", \"01\", \"15\"\nlocal whole = s:match(\"%d+-\")  -- \"2024-\" (no captures → returns whole match)\n\n-- string.find(s, pattern, init?, plain?) → start, end, captures... (or nil)\nlocal start, en = s:find(\"%d+\")  -- 1, 4 (position of \"2024\")\nlocal start2, en2, cap = s:find(\"(%d+)-(%d+)\")  -- 1, 7, \"2024\", \"01\"\n-- Use plain=true for literal search (no pattern interpretation):\nlocal p = \"a.b.c\"\np:find(\".\", 1, true)  -- 2, 2 (literal dot at position 2; not \"any char\")\n\n-- string.gmatch(s, pattern) → iterator over all matches\nlocal csv = \"a,b,c,d\"\nfor col in csv:gmatch(\"([^,]+)\") do  -- capture each non-comma sequence\n  print(col)  -- a \u002F b \u002F c \u002F d\nend\n-- With multiple captures, each iteration yields all captures:\nfor k, v in (\"a=1, b=2\"):gmatch(\"(%w+)=(%w+)\") do\n  print(k, v)  -- a 1 \u002F b 2\nend\n\n-- string.gsub(s, pattern, replacement, n?) → new_string, count\n-- replacement: string (with %1, %0 for captures), function, or table\nlocal result, count = (\"hello world\"):gsub(\"o\", \"0\")  -- \"hell0 w0rld\", 2\nlocal result2 = (\"a=1\"):gsub(\"(%w)=(%w)\", \"%2=%1\")  -- \"1=a\" (swap captures)\nlocal result3 = (\"hello\"):gsub(\"%w\", function(c) return c:upper() end)  -- \"HELLO\"\nlocal result4 = (\"hello\"):gsub(\"l\", function(c) return nil end)  -- \"heo\" (nil → delete)\nlocal result5 = (\"hello\"):gsub(\"%w\", {h = \"H\", e = \"E\", l = \"L\", o = \"O\"})  -- \"HELL0\"\n-- table replacement: matched string → key → replacement value (nil = delete)\n",[28,595,596,601,612,638,663,667,672,698,723,728,740,767,771,776,788,821,832,837,842,872,882,886,890,895,900,934,967,1019,1060,1119],{"__ignoreMap":58},[62,597,598],{"class":64,"line":65},[62,599,600],{"class":68},"-- string.match(s, pattern, init?) → captures (or whole match if no captures), or nil\n",[62,602,603,605,607,609],{"class":64,"line":72},[62,604,76],{"class":75},[62,606,80],{"class":79},[62,608,83],{"class":75},[62,610,611],{"class":86}," \"2024-01-15\"\n",[62,613,614,616,619,621,623,625,628,630,633,635],{"class":64,"line":90},[62,615,76],{"class":75},[62,617,618],{"class":79}," y, m, d ",[62,620,83],{"class":75},[62,622,101],{"class":100},[62,624,104],{"class":79},[62,626,627],{"class":107},"match",[62,629,268],{"class":79},[62,631,632],{"class":86},"\"(%d+)-(%d+)-(%d+)\"",[62,634,138],{"class":79},[62,636,637],{"class":68},"-- \"2024\", \"01\", \"15\"\n",[62,639,640,642,645,647,649,651,653,655,658,660],{"class":64,"line":117},[62,641,76],{"class":75},[62,643,644],{"class":79}," whole ",[62,646,83],{"class":75},[62,648,101],{"class":100},[62,650,104],{"class":79},[62,652,627],{"class":107},[62,654,268],{"class":79},[62,656,657],{"class":86},"\"%d+-\"",[62,659,138],{"class":79},[62,661,662],{"class":68},"-- \"2024-\" (no captures → returns whole match)\n",[62,664,665],{"class":64,"line":123},[62,666,148],{"emptyLinePlaceholder":147},[62,668,669],{"class":64,"line":144},[62,670,671],{"class":68},"-- string.find(s, pattern, init?, plain?) → start, end, captures... (or nil)\n",[62,673,674,676,679,681,683,685,688,690,693,695],{"class":64,"line":151},[62,675,76],{"class":75},[62,677,678],{"class":79}," start, en ",[62,680,83],{"class":75},[62,682,101],{"class":100},[62,684,104],{"class":79},[62,686,687],{"class":107},"find",[62,689,268],{"class":79},[62,691,692],{"class":86},"\"%d+\"",[62,694,138],{"class":79},[62,696,697],{"class":68},"-- 1, 4 (position of \"2024\")\n",[62,699,700,702,705,707,709,711,713,715,718,720],{"class":64,"line":157},[62,701,76],{"class":75},[62,703,704],{"class":79}," start2, en2, cap ",[62,706,83],{"class":75},[62,708,101],{"class":100},[62,710,104],{"class":79},[62,712,687],{"class":107},[62,714,268],{"class":79},[62,716,717],{"class":86},"\"(%d+)-(%d+)\"",[62,719,138],{"class":79},[62,721,722],{"class":68},"-- 1, 7, \"2024\", \"01\"\n",[62,724,725],{"class":64,"line":170},[62,726,727],{"class":68},"-- Use plain=true for literal search (no pattern interpretation):\n",[62,729,730,732,735,737],{"class":64,"line":176},[62,731,76],{"class":75},[62,733,734],{"class":79}," p ",[62,736,83],{"class":75},[62,738,739],{"class":86}," \"a.b.c\"\n",[62,741,742,744,746,748,750,753,755,757,759,762,764],{"class":64,"line":182},[62,743,15],{"class":100},[62,745,104],{"class":79},[62,747,687],{"class":107},[62,749,268],{"class":79},[62,751,752],{"class":86},"\".\"",[62,754,317],{"class":79},[62,756,314],{"class":107},[62,758,317],{"class":79},[62,760,761],{"class":107},"true",[62,763,138],{"class":79},[62,765,766],{"class":68},"-- 2, 2 (literal dot at position 2; not \"any char\")\n",[62,768,769],{"class":64,"line":188},[62,770,148],{"emptyLinePlaceholder":147},[62,772,773],{"class":64,"line":194},[62,774,775],{"class":68},"-- string.gmatch(s, pattern) → iterator over all matches\n",[62,777,778,780,783,785],{"class":64,"line":200},[62,779,76],{"class":75},[62,781,782],{"class":79}," csv ",[62,784,83],{"class":75},[62,786,787],{"class":86}," \"a,b,c,d\"\n",[62,789,790,793,796,799,802,804,807,809,812,815,818],{"class":64,"line":206},[62,791,792],{"class":75},"for",[62,794,795],{"class":79}," col ",[62,797,798],{"class":75},"in",[62,800,801],{"class":100}," csv",[62,803,104],{"class":79},[62,805,806],{"class":107},"gmatch",[62,808,268],{"class":79},[62,810,811],{"class":86},"\"([^,]+)\"",[62,813,814],{"class":79},") ",[62,816,817],{"class":75},"do",[62,819,820],{"class":68},"  -- capture each non-comma sequence\n",[62,822,823,826,829],{"class":64,"line":219},[62,824,825],{"class":107},"  print",[62,827,828],{"class":79},"(col)  ",[62,830,831],{"class":68},"-- a \u002F b \u002F c \u002F d\n",[62,833,834],{"class":64,"line":228},[62,835,836],{"class":75},"end\n",[62,838,839],{"class":64,"line":239},[62,840,841],{"class":68},"-- With multiple captures, each iteration yields all captures:\n",[62,843,844,846,849,851,854,857,860,862,864,867,869],{"class":64,"line":244},[62,845,792],{"class":75},[62,847,848],{"class":79}," k, v ",[62,850,798],{"class":75},[62,852,853],{"class":79}," (",[62,855,856],{"class":86},"\"a=1, b=2\"",[62,858,859],{"class":79},"):",[62,861,806],{"class":107},[62,863,268],{"class":79},[62,865,866],{"class":86},"\"(%w+)=(%w+)\"",[62,868,814],{"class":79},[62,870,871],{"class":75},"do\n",[62,873,874,876,879],{"class":64,"line":250},[62,875,825],{"class":107},[62,877,878],{"class":79},"(k, v)  ",[62,880,881],{"class":68},"-- a 1 \u002F b 2\n",[62,883,884],{"class":64,"line":262},[62,885,836],{"class":75},[62,887,888],{"class":64,"line":279},[62,889,148],{"emptyLinePlaceholder":147},[62,891,892],{"class":64,"line":284},[62,893,894],{"class":68},"-- string.gsub(s, pattern, replacement, n?) → new_string, count\n",[62,896,897],{"class":64,"line":290},[62,898,899],{"class":68},"-- replacement: string (with %1, %0 for captures), function, or table\n",[62,901,902,904,907,909,911,914,916,919,921,924,926,929,931],{"class":64,"line":301},[62,903,76],{"class":75},[62,905,906],{"class":79}," result, count ",[62,908,83],{"class":75},[62,910,853],{"class":79},[62,912,913],{"class":86},"\"hello world\"",[62,915,859],{"class":79},[62,917,918],{"class":107},"gsub",[62,920,268],{"class":79},[62,922,923],{"class":86},"\"o\"",[62,925,317],{"class":79},[62,927,928],{"class":86},"\"0\"",[62,930,138],{"class":79},[62,932,933],{"class":68},"-- \"hell0 w0rld\", 2\n",[62,935,936,938,941,943,945,948,950,952,954,957,959,962,964],{"class":64,"line":328},[62,937,76],{"class":75},[62,939,940],{"class":79}," result2 ",[62,942,83],{"class":75},[62,944,853],{"class":79},[62,946,947],{"class":86},"\"a=1\"",[62,949,859],{"class":79},[62,951,918],{"class":107},[62,953,268],{"class":79},[62,955,956],{"class":86},"\"(%w)=(%w)\"",[62,958,317],{"class":79},[62,960,961],{"class":86},"\"%2=%1\"",[62,963,138],{"class":79},[62,965,966],{"class":68},"-- \"1=a\" (swap captures)\n",[62,968,969,971,974,976,978,981,983,985,987,990,992,995,998,1001,1004,1006,1008,1011,1014,1016],{"class":64,"line":350},[62,970,76],{"class":75},[62,972,973],{"class":79}," result3 ",[62,975,83],{"class":75},[62,977,853],{"class":79},[62,979,980],{"class":86},"\"hello\"",[62,982,859],{"class":79},[62,984,918],{"class":107},[62,986,268],{"class":79},[62,988,989],{"class":86},"\"%w\"",[62,991,317],{"class":79},[62,993,994],{"class":75},"function",[62,996,997],{"class":79},"(c) ",[62,999,1000],{"class":75},"return",[62,1002,1003],{"class":100}," c",[62,1005,104],{"class":79},[62,1007,108],{"class":107},[62,1009,1010],{"class":79},"() ",[62,1012,1013],{"class":75},"end",[62,1015,138],{"class":79},[62,1017,1018],{"class":68},"-- \"HELLO\"\n",[62,1020,1021,1023,1026,1028,1030,1032,1034,1036,1038,1041,1043,1045,1047,1049,1052,1055,1057],{"class":64,"line":371},[62,1022,76],{"class":75},[62,1024,1025],{"class":79}," result4 ",[62,1027,83],{"class":75},[62,1029,853],{"class":79},[62,1031,980],{"class":86},[62,1033,859],{"class":79},[62,1035,918],{"class":107},[62,1037,268],{"class":79},[62,1039,1040],{"class":86},"\"l\"",[62,1042,317],{"class":79},[62,1044,994],{"class":75},[62,1046,997],{"class":79},[62,1048,1000],{"class":75},[62,1050,1051],{"class":107}," nil",[62,1053,1054],{"class":75}," end",[62,1056,138],{"class":79},[62,1058,1059],{"class":68},"-- \"heo\" (nil → delete)\n",[62,1061,1062,1064,1067,1069,1071,1073,1075,1077,1079,1081,1084,1086,1089,1092,1094,1097,1100,1102,1105,1108,1110,1113,1116],{"class":64,"line":564},[62,1063,76],{"class":75},[62,1065,1066],{"class":79}," result5 ",[62,1068,83],{"class":75},[62,1070,853],{"class":79},[62,1072,980],{"class":86},[62,1074,859],{"class":79},[62,1076,918],{"class":107},[62,1078,268],{"class":79},[62,1080,989],{"class":86},[62,1082,1083],{"class":79},", {h ",[62,1085,83],{"class":75},[62,1087,1088],{"class":86}," \"H\"",[62,1090,1091],{"class":79},", e ",[62,1093,83],{"class":75},[62,1095,1096],{"class":86}," \"E\"",[62,1098,1099],{"class":79},", l ",[62,1101,83],{"class":75},[62,1103,1104],{"class":86}," \"L\"",[62,1106,1107],{"class":79},", o ",[62,1109,83],{"class":75},[62,1111,1112],{"class":86}," \"O\"",[62,1114,1115],{"class":79},"})  ",[62,1117,1118],{"class":68},"-- \"HELL0\"\n",[62,1120,1121],{"class":64,"line":570},[62,1122,1123],{"class":68},"-- table replacement: matched string → key → replacement value (nil = delete)\n",[1125,1126,1128],"h3",{"id":1127},"anti-pattern-gsub-with-magic-characters-in-replacement","Anti-pattern: gsub with magic characters in replacement",[50,1130,1131],{"language":52},[54,1132,1134],{"className":56,"code":1133,"language":52,"meta":58,"style":58},"-- BAD: % in replacement string is interpreted as capture backreference\nlocal user_input = \"50%\"\nlocal result = (\"score\"):gsub(\"score\", user_input)  -- ERROR: invalid capture index %'\n-- user_input \"50%\" → % is interpreted as capture reference → errors\n\n-- FIX: escape % as %% in the replacement, OR use a function (no interpretation)\nlocal safe = user_input:gsub(\"%%\", \"%%%%\")  -- \"50%%\" — now gsub won't interpret\nlocal result = (\"score\"):gsub(\"score\", safe)  -- \"50%\" (correct)\n\n-- SAFER: function replacement — return value is used literally, no % interpretation\nlocal result2 = (\"score\"):gsub(\"score\", function() return user_input end)  -- \"50%\"\n",[28,1135,1136,1141,1153,1181,1186,1190,1195,1226,1252,1256,1261],{"__ignoreMap":58},[62,1137,1138],{"class":64,"line":65},[62,1139,1140],{"class":68},"-- BAD: % in replacement string is interpreted as capture backreference\n",[62,1142,1143,1145,1148,1150],{"class":64,"line":72},[62,1144,76],{"class":75},[62,1146,1147],{"class":79}," user_input ",[62,1149,83],{"class":75},[62,1151,1152],{"class":86}," \"50%\"\n",[62,1154,1155,1157,1160,1162,1164,1167,1169,1171,1173,1175,1178],{"class":64,"line":90},[62,1156,76],{"class":75},[62,1158,1159],{"class":79}," result ",[62,1161,83],{"class":75},[62,1163,853],{"class":79},[62,1165,1166],{"class":86},"\"score\"",[62,1168,859],{"class":79},[62,1170,918],{"class":107},[62,1172,268],{"class":79},[62,1174,1166],{"class":86},[62,1176,1177],{"class":79},", user_input)  ",[62,1179,1180],{"class":68},"-- ERROR: invalid capture index %'\n",[62,1182,1183],{"class":64,"line":117},[62,1184,1185],{"class":68},"-- user_input \"50%\" → % is interpreted as capture reference → errors\n",[62,1187,1188],{"class":64,"line":123},[62,1189,148],{"emptyLinePlaceholder":147},[62,1191,1192],{"class":64,"line":144},[62,1193,1194],{"class":68},"-- FIX: escape % as %% in the replacement, OR use a function (no interpretation)\n",[62,1196,1197,1199,1202,1204,1207,1209,1211,1213,1216,1218,1221,1223],{"class":64,"line":151},[62,1198,76],{"class":75},[62,1200,1201],{"class":79}," safe ",[62,1203,83],{"class":75},[62,1205,1206],{"class":100}," user_input",[62,1208,104],{"class":79},[62,1210,918],{"class":107},[62,1212,268],{"class":79},[62,1214,1215],{"class":86},"\"%%\"",[62,1217,317],{"class":79},[62,1219,1220],{"class":86},"\"%%%%\"",[62,1222,138],{"class":79},[62,1224,1225],{"class":68},"-- \"50%%\" — now gsub won't interpret\n",[62,1227,1228,1230,1232,1234,1236,1238,1240,1242,1244,1246,1249],{"class":64,"line":157},[62,1229,76],{"class":75},[62,1231,1159],{"class":79},[62,1233,83],{"class":75},[62,1235,853],{"class":79},[62,1237,1166],{"class":86},[62,1239,859],{"class":79},[62,1241,918],{"class":107},[62,1243,268],{"class":79},[62,1245,1166],{"class":86},[62,1247,1248],{"class":79},", safe)  ",[62,1250,1251],{"class":68},"-- \"50%\" (correct)\n",[62,1253,1254],{"class":64,"line":170},[62,1255,148],{"emptyLinePlaceholder":147},[62,1257,1258],{"class":64,"line":176},[62,1259,1260],{"class":68},"-- SAFER: function replacement — return value is used literally, no % interpretation\n",[62,1262,1263,1265,1267,1269,1271,1273,1275,1277,1279,1281,1283,1285,1287,1289,1291,1293,1295],{"class":64,"line":182},[62,1264,76],{"class":75},[62,1266,940],{"class":79},[62,1268,83],{"class":75},[62,1270,853],{"class":79},[62,1272,1166],{"class":86},[62,1274,859],{"class":79},[62,1276,918],{"class":107},[62,1278,268],{"class":79},[62,1280,1166],{"class":86},[62,1282,317],{"class":79},[62,1284,994],{"class":75},[62,1286,1010],{"class":79},[62,1288,1000],{"class":75},[62,1290,1147],{"class":79},[62,1292,1013],{"class":75},[62,1294,138],{"class":79},[62,1296,1297],{"class":68},"-- \"50%\"\n",[45,1299,1301],{"id":1300},"production-pattern-log-parser","Production Pattern: Log Parser",[50,1303,1304,1517,1521],{"language":52},[54,1305,1307],{"className":56,"code":1306,"language":52,"meta":58,"style":58},"-- Parse Apache\u002Fnginx-style log line:\n-- 192.168.1.1 - - [10\u002FJan\u002F2024:13:55:36 +0000] \"GET \u002Fpath HTTP\u002F1.1\" 200 1234\nlocal function parse_log(line)\n  -- Pattern with captures: IP, date, method, path, status, size\n  local pattern = '^(%S+) %S+ %S+ %[(.-)%] \"(%S+) (%S+) %S+\" (%d+) (%d+)'\n  local ip, date, method, path, status, size = line:match(pattern)\n  if not ip then return nil, \"unparseable: \" .. line end\n  return {\n    ip = ip, date = date, method = method, path = path,\n    status = tonumber(status), size = tonumber(size),\n  }\nend\n\nlocal entry = parse_log('192.168.1.1 - - [10\u002FJan\u002F2024:13:55:36 +0000] \"GET \u002Fapi HTTP\u002F1.1\" 200 1234')\nprint(entry.ip, entry.method, entry.path, entry.status)  -- 192.168.1.1 GET \u002Fapi 200\n",[28,1308,1309,1314,1319,1332,1337,1350,1369,1401,1409,1434,1454,1459,1463,1467,1486],{"__ignoreMap":58},[62,1310,1311],{"class":64,"line":65},[62,1312,1313],{"class":68},"-- Parse Apache\u002Fnginx-style log line:\n",[62,1315,1316],{"class":64,"line":72},[62,1317,1318],{"class":68},"-- 192.168.1.1 - - [10\u002FJan\u002F2024:13:55:36 +0000] \"GET \u002Fpath HTTP\u002F1.1\" 200 1234\n",[62,1320,1321,1323,1326,1329],{"class":64,"line":90},[62,1322,76],{"class":75},[62,1324,1325],{"class":75}," function",[62,1327,1328],{"class":100}," parse_log",[62,1330,1331],{"class":79},"(line)\n",[62,1333,1334],{"class":64,"line":117},[62,1335,1336],{"class":68},"  -- Pattern with captures: IP, date, method, path, status, size\n",[62,1338,1339,1342,1345,1347],{"class":64,"line":123},[62,1340,1341],{"class":75},"  local",[62,1343,1344],{"class":79}," pattern ",[62,1346,83],{"class":75},[62,1348,1349],{"class":86}," '^(%S+) %S+ %S+ %[(.-)%] \"(%S+) (%S+) %S+\" (%d+) (%d+)'\n",[62,1351,1352,1354,1357,1359,1362,1364,1366],{"class":64,"line":144},[62,1353,1341],{"class":75},[62,1355,1356],{"class":79}," ip, date, method, path, status, size ",[62,1358,83],{"class":75},[62,1360,1361],{"class":100}," line",[62,1363,104],{"class":79},[62,1365,627],{"class":107},[62,1367,1368],{"class":79},"(pattern)\n",[62,1370,1371,1374,1377,1380,1383,1386,1388,1390,1393,1396,1399],{"class":64,"line":151},[62,1372,1373],{"class":75},"  if",[62,1375,1376],{"class":75}," not",[62,1378,1379],{"class":79}," ip ",[62,1381,1382],{"class":75},"then",[62,1384,1385],{"class":75}," return",[62,1387,1051],{"class":107},[62,1389,317],{"class":79},[62,1391,1392],{"class":86},"\"unparseable: \" ",[62,1394,1395],{"class":75},"..",[62,1397,1398],{"class":79}," line ",[62,1400,836],{"class":75},[62,1402,1403,1406],{"class":64,"line":157},[62,1404,1405],{"class":75},"  return",[62,1407,1408],{"class":79}," {\n",[62,1410,1411,1414,1416,1419,1421,1424,1426,1429,1431],{"class":64,"line":170},[62,1412,1413],{"class":79},"    ip ",[62,1415,83],{"class":75},[62,1417,1418],{"class":79}," ip, date ",[62,1420,83],{"class":75},[62,1422,1423],{"class":79}," date, method ",[62,1425,83],{"class":75},[62,1427,1428],{"class":79}," method, path ",[62,1430,83],{"class":75},[62,1432,1433],{"class":79}," path,\n",[62,1435,1436,1439,1441,1444,1447,1449,1451],{"class":64,"line":176},[62,1437,1438],{"class":79},"    status ",[62,1440,83],{"class":75},[62,1442,1443],{"class":107}," tonumber",[62,1445,1446],{"class":79},"(status), size ",[62,1448,83],{"class":75},[62,1450,1443],{"class":107},[62,1452,1453],{"class":79},"(size),\n",[62,1455,1456],{"class":64,"line":182},[62,1457,1458],{"class":79},"  }\n",[62,1460,1461],{"class":64,"line":188},[62,1462,836],{"class":75},[62,1464,1465],{"class":64,"line":194},[62,1466,148],{"emptyLinePlaceholder":147},[62,1468,1469,1471,1474,1476,1478,1480,1483],{"class":64,"line":200},[62,1470,76],{"class":75},[62,1472,1473],{"class":79}," entry ",[62,1475,83],{"class":75},[62,1477,1328],{"class":107},[62,1479,268],{"class":79},[62,1481,1482],{"class":86},"'192.168.1.1 - - [10\u002FJan\u002F2024:13:55:36 +0000] \"GET \u002Fapi HTTP\u002F1.1\" 200 1234'",[62,1484,1485],{"class":79},")\n",[62,1487,1488,1490,1493,1496,1499,1502,1504,1507,1509,1512,1514],{"class":64,"line":206},[62,1489,126],{"class":107},[62,1491,1492],{"class":79},"(entry.",[62,1494,1495],{"class":100},"ip",[62,1497,1498],{"class":79},", entry.",[62,1500,1501],{"class":100},"method",[62,1503,1498],{"class":79},[62,1505,1506],{"class":100},"path",[62,1508,1498],{"class":79},[62,1510,1511],{"class":100},"status",[62,1513,138],{"class":79},[62,1515,1516],{"class":68},"-- 192.168.1.1 GET \u002Fapi 200\n",[45,1518,1520],{"id":1519},"production-pattern-csv-parser-handles-quoted-fields","Production Pattern: CSV Parser (handles quoted fields)",[50,1522,1523,1888,1892],{"language":52},[54,1524,1526],{"className":56,"code":1525,"language":52,"meta":58,"style":58},"-- CSV: commas separate fields, quoted fields can contain commas\nlocal function parse_csv(line)\n  local fields = {}\n  local field = \"\"\n  local in_quotes = false\n  local i = 1\n  while i \u003C= #line do\n    local c = line:sub(i, i)\n    if c == '\"' then\n      if in_quotes and line:sub(i + 1, i + 1) == '\"' then\n        field = field .. '\"'  -- escaped quote \"\"\n        i = i + 1\n      else\n        in_quotes = not in_quotes\n      end\n    elseif c == \",\" and not in_quotes then\n      fields[#fields + 1] = field\n      field = \"\"\n    else\n      field = field .. c\n    end\n    i = i + 1\n  end\n  fields[#fields + 1] = field\n  return fields\nend\n\nprint(parse_csv('a,b,\"c,d\",e')[3])  -- \"c,d\" (quoted field with comma preserved)\n",[28,1527,1528,1533,1544,1556,1568,1580,1592,1610,1629,1644,1684,1701,1714,1719,1731,1736,1756,1778,1787,1792,1805,1810,1823,1828,1847,1854,1858,1862],{"__ignoreMap":58},[62,1529,1530],{"class":64,"line":65},[62,1531,1532],{"class":68},"-- CSV: commas separate fields, quoted fields can contain commas\n",[62,1534,1535,1537,1539,1542],{"class":64,"line":72},[62,1536,76],{"class":75},[62,1538,1325],{"class":75},[62,1540,1541],{"class":100}," parse_csv",[62,1543,1331],{"class":79},[62,1545,1546,1548,1551,1553],{"class":64,"line":90},[62,1547,1341],{"class":75},[62,1549,1550],{"class":79}," fields ",[62,1552,83],{"class":75},[62,1554,1555],{"class":79}," {}\n",[62,1557,1558,1560,1563,1565],{"class":64,"line":117},[62,1559,1341],{"class":75},[62,1561,1562],{"class":79}," field ",[62,1564,83],{"class":75},[62,1566,1567],{"class":86}," \"\"\n",[62,1569,1570,1572,1575,1577],{"class":64,"line":123},[62,1571,1341],{"class":75},[62,1573,1574],{"class":79}," in_quotes ",[62,1576,83],{"class":75},[62,1578,1579],{"class":107}," false\n",[62,1581,1582,1584,1587,1589],{"class":64,"line":144},[62,1583,1341],{"class":75},[62,1585,1586],{"class":79}," i ",[62,1588,83],{"class":75},[62,1590,1591],{"class":107}," 1\n",[62,1593,1594,1597,1599,1602,1605,1608],{"class":64,"line":151},[62,1595,1596],{"class":75},"  while",[62,1598,1586],{"class":79},[62,1600,1601],{"class":75},"\u003C=",[62,1603,1604],{"class":75}," #",[62,1606,1607],{"class":79},"line ",[62,1609,871],{"class":75},[62,1611,1612,1615,1618,1620,1622,1624,1626],{"class":64,"line":157},[62,1613,1614],{"class":75},"    local",[62,1616,1617],{"class":79}," c ",[62,1619,83],{"class":75},[62,1621,1361],{"class":100},[62,1623,104],{"class":79},[62,1625,309],{"class":107},[62,1627,1628],{"class":79},"(i, i)\n",[62,1630,1631,1634,1636,1638,1641],{"class":64,"line":170},[62,1632,1633],{"class":75},"    if",[62,1635,1617],{"class":79},[62,1637,132],{"class":75},[62,1639,1640],{"class":86}," '\"' ",[62,1642,1643],{"class":75},"then\n",[62,1645,1646,1649,1651,1654,1656,1658,1660,1663,1666,1669,1672,1674,1676,1678,1680,1682],{"class":64,"line":176},[62,1647,1648],{"class":75},"      if",[62,1650,1574],{"class":79},[62,1652,1653],{"class":75},"and",[62,1655,1361],{"class":100},[62,1657,104],{"class":79},[62,1659,309],{"class":107},[62,1661,1662],{"class":79},"(i ",[62,1664,1665],{"class":75},"+",[62,1667,1668],{"class":107}," 1",[62,1670,1671],{"class":79},", i ",[62,1673,1665],{"class":75},[62,1675,1668],{"class":107},[62,1677,814],{"class":79},[62,1679,132],{"class":75},[62,1681,1640],{"class":86},[62,1683,1643],{"class":75},[62,1685,1686,1689,1691,1693,1695,1698],{"class":64,"line":182},[62,1687,1688],{"class":79},"        field ",[62,1690,83],{"class":75},[62,1692,1562],{"class":79},[62,1694,1395],{"class":75},[62,1696,1697],{"class":86}," '\"'  ",[62,1699,1700],{"class":68},"-- escaped quote \"\"\n",[62,1702,1703,1706,1708,1710,1712],{"class":64,"line":188},[62,1704,1705],{"class":79},"        i ",[62,1707,83],{"class":75},[62,1709,1586],{"class":79},[62,1711,1665],{"class":75},[62,1713,1591],{"class":107},[62,1715,1716],{"class":64,"line":194},[62,1717,1718],{"class":75},"      else\n",[62,1720,1721,1724,1726,1728],{"class":64,"line":200},[62,1722,1723],{"class":79},"        in_quotes ",[62,1725,83],{"class":75},[62,1727,1376],{"class":75},[62,1729,1730],{"class":79}," in_quotes\n",[62,1732,1733],{"class":64,"line":206},[62,1734,1735],{"class":75},"      end\n",[62,1737,1738,1741,1743,1745,1748,1750,1752,1754],{"class":64,"line":219},[62,1739,1740],{"class":75},"    elseif",[62,1742,1617],{"class":79},[62,1744,132],{"class":75},[62,1746,1747],{"class":86}," \",\" ",[62,1749,1653],{"class":75},[62,1751,1376],{"class":75},[62,1753,1574],{"class":79},[62,1755,1643],{"class":75},[62,1757,1758,1761,1763,1766,1768,1770,1773,1775],{"class":64,"line":228},[62,1759,1760],{"class":79},"      fields[",[62,1762,253],{"class":75},[62,1764,1765],{"class":79},"fields ",[62,1767,1665],{"class":75},[62,1769,1668],{"class":107},[62,1771,1772],{"class":79},"] ",[62,1774,83],{"class":75},[62,1776,1777],{"class":79}," field\n",[62,1779,1780,1783,1785],{"class":64,"line":239},[62,1781,1782],{"class":79},"      field ",[62,1784,83],{"class":75},[62,1786,1567],{"class":86},[62,1788,1789],{"class":64,"line":244},[62,1790,1791],{"class":75},"    else\n",[62,1793,1794,1796,1798,1800,1802],{"class":64,"line":250},[62,1795,1782],{"class":79},[62,1797,83],{"class":75},[62,1799,1562],{"class":79},[62,1801,1395],{"class":75},[62,1803,1804],{"class":79}," c\n",[62,1806,1807],{"class":64,"line":262},[62,1808,1809],{"class":75},"    end\n",[62,1811,1812,1815,1817,1819,1821],{"class":64,"line":279},[62,1813,1814],{"class":79},"    i ",[62,1816,83],{"class":75},[62,1818,1586],{"class":79},[62,1820,1665],{"class":75},[62,1822,1591],{"class":107},[62,1824,1825],{"class":64,"line":284},[62,1826,1827],{"class":75},"  end\n",[62,1829,1830,1833,1835,1837,1839,1841,1843,1845],{"class":64,"line":290},[62,1831,1832],{"class":79},"  fields[",[62,1834,253],{"class":75},[62,1836,1765],{"class":79},[62,1838,1665],{"class":75},[62,1840,1668],{"class":107},[62,1842,1772],{"class":79},[62,1844,83],{"class":75},[62,1846,1777],{"class":79},[62,1848,1849,1851],{"class":64,"line":301},[62,1850,1405],{"class":75},[62,1852,1853],{"class":79}," fields\n",[62,1855,1856],{"class":64,"line":328},[62,1857,836],{"class":75},[62,1859,1860],{"class":64,"line":350},[62,1861,148],{"emptyLinePlaceholder":147},[62,1863,1864,1866,1868,1871,1873,1876,1879,1882,1885],{"class":64,"line":371},[62,1865,126],{"class":107},[62,1867,268],{"class":79},[62,1869,1870],{"class":107},"parse_csv",[62,1872,268],{"class":79},[62,1874,1875],{"class":86},"'a,b,\"c,d\",e'",[62,1877,1878],{"class":79},")[",[62,1880,1881],{"class":107},"3",[62,1883,1884],{"class":79},"])  ",[62,1886,1887],{"class":68},"-- \"c,d\" (quoted field with comma preserved)\n",[45,1889,1891],{"id":1890},"production-pattern-ini-config-parser","Production Pattern: INI Config Parser",[50,1893,1894,2323,2331],{"language":52},[54,1895,1897],{"className":56,"code":1896,"language":52,"meta":58,"style":58},"-- INI format: [section] \u002F key = value \u002F ; comment\nlocal function parse_ini(text)\n  local config, section = {}, nil\n  for line in text:gmatch(\"[^\\r\\n]+\") do\n    line = line:gsub(\"^%s*(.-)%s*$\", \"%1\")  -- trim\n    if line:sub(1, 1) == \";\" or line == \"\" then\n      -- comment or empty, skip\n    elseif line:match(\"^%[(.-)%]$\") then\n      section = line:match(\"^%[(.-)%]$\")\n      config[section] = {}\n    elseif section then\n      local key, val = line:match(\"^([^=]+)=(.*)$\")\n      if key and val then\n        key = key:gsub(\"^%s*(.-)%s*$\", \"%1\")  -- trim key\n        -- type coercion: number, true, false, or string\n        val = tonumber(val) or (val == \"true\" and true or (val == \"false\" and false or val))\n        config[section][key] = val\n      end\n    end\n  end\n  return config\nend\n\nlocal ini = parse_ini([[\n[server]\nhost = localhost\nport = 8080\ndebug = true\n]])\nprint(ini.server.host, ini.server.port, ini.server.debug)  -- localhost 8080 true\n",[28,1898,1899,1904,1916,1931,1962,1990,2027,2032,2051,2070,2079,2088,2111,2125,2152,2157,2204,2214,2218,2222,2226,2233,2237,2241,2257,2262,2267,2272,2277,2284],{"__ignoreMap":58},[62,1900,1901],{"class":64,"line":65},[62,1902,1903],{"class":68},"-- INI format: [section] \u002F key = value \u002F ; comment\n",[62,1905,1906,1908,1910,1913],{"class":64,"line":72},[62,1907,76],{"class":75},[62,1909,1325],{"class":75},[62,1911,1912],{"class":100}," parse_ini",[62,1914,1915],{"class":79},"(text)\n",[62,1917,1918,1920,1923,1925,1928],{"class":64,"line":90},[62,1919,1341],{"class":75},[62,1921,1922],{"class":79}," config, section ",[62,1924,83],{"class":75},[62,1926,1927],{"class":79}," {}, ",[62,1929,1930],{"class":107},"nil\n",[62,1932,1933,1936,1938,1940,1943,1945,1947,1949,1952,1955,1958,1960],{"class":64,"line":117},[62,1934,1935],{"class":75},"  for",[62,1937,1398],{"class":79},[62,1939,798],{"class":75},[62,1941,1942],{"class":100}," text",[62,1944,104],{"class":79},[62,1946,806],{"class":107},[62,1948,268],{"class":79},[62,1950,1951],{"class":86},"\"[^",[62,1953,1954],{"class":107},"\\r\\n",[62,1956,1957],{"class":86},"]+\"",[62,1959,814],{"class":79},[62,1961,871],{"class":75},[62,1963,1964,1967,1969,1971,1973,1975,1977,1980,1982,1985,1987],{"class":64,"line":123},[62,1965,1966],{"class":79},"    line ",[62,1968,83],{"class":75},[62,1970,1361],{"class":100},[62,1972,104],{"class":79},[62,1974,918],{"class":107},[62,1976,268],{"class":79},[62,1978,1979],{"class":86},"\"^%s*(.-)%s*$\"",[62,1981,317],{"class":79},[62,1983,1984],{"class":86},"\"%1\"",[62,1986,138],{"class":79},[62,1988,1989],{"class":68},"-- trim\n",[62,1991,1992,1994,1996,1998,2000,2002,2004,2006,2008,2010,2012,2015,2018,2020,2022,2025],{"class":64,"line":144},[62,1993,1633],{"class":75},[62,1995,1361],{"class":100},[62,1997,104],{"class":79},[62,1999,309],{"class":107},[62,2001,268],{"class":79},[62,2003,314],{"class":107},[62,2005,317],{"class":79},[62,2007,314],{"class":107},[62,2009,814],{"class":79},[62,2011,132],{"class":75},[62,2013,2014],{"class":86}," \";\" ",[62,2016,2017],{"class":75},"or",[62,2019,1398],{"class":79},[62,2021,132],{"class":75},[62,2023,2024],{"class":86}," \"\" ",[62,2026,1643],{"class":75},[62,2028,2029],{"class":64,"line":151},[62,2030,2031],{"class":68},"      -- comment or empty, skip\n",[62,2033,2034,2036,2038,2040,2042,2044,2047,2049],{"class":64,"line":157},[62,2035,1740],{"class":75},[62,2037,1361],{"class":100},[62,2039,104],{"class":79},[62,2041,627],{"class":107},[62,2043,268],{"class":79},[62,2045,2046],{"class":86},"\"^%[(.-)%]$\"",[62,2048,814],{"class":79},[62,2050,1643],{"class":75},[62,2052,2053,2056,2058,2060,2062,2064,2066,2068],{"class":64,"line":170},[62,2054,2055],{"class":79},"      section ",[62,2057,83],{"class":75},[62,2059,1361],{"class":100},[62,2061,104],{"class":79},[62,2063,627],{"class":107},[62,2065,268],{"class":79},[62,2067,2046],{"class":86},[62,2069,1485],{"class":79},[62,2071,2072,2075,2077],{"class":64,"line":176},[62,2073,2074],{"class":79},"      config[section] ",[62,2076,83],{"class":75},[62,2078,1555],{"class":79},[62,2080,2081,2083,2086],{"class":64,"line":182},[62,2082,1740],{"class":75},[62,2084,2085],{"class":79}," section ",[62,2087,1643],{"class":75},[62,2089,2090,2093,2096,2098,2100,2102,2104,2106,2109],{"class":64,"line":188},[62,2091,2092],{"class":75},"      local",[62,2094,2095],{"class":79}," key, val ",[62,2097,83],{"class":75},[62,2099,1361],{"class":100},[62,2101,104],{"class":79},[62,2103,627],{"class":107},[62,2105,268],{"class":79},[62,2107,2108],{"class":86},"\"^([^=]+)=(.*)$\"",[62,2110,1485],{"class":79},[62,2112,2113,2115,2118,2120,2123],{"class":64,"line":194},[62,2114,1648],{"class":75},[62,2116,2117],{"class":79}," key ",[62,2119,1653],{"class":75},[62,2121,2122],{"class":79}," val ",[62,2124,1643],{"class":75},[62,2126,2127,2130,2132,2135,2137,2139,2141,2143,2145,2147,2149],{"class":64,"line":200},[62,2128,2129],{"class":79},"        key ",[62,2131,83],{"class":75},[62,2133,2134],{"class":100}," key",[62,2136,104],{"class":79},[62,2138,918],{"class":107},[62,2140,268],{"class":79},[62,2142,1979],{"class":86},[62,2144,317],{"class":79},[62,2146,1984],{"class":86},[62,2148,138],{"class":79},[62,2150,2151],{"class":68},"-- trim key\n",[62,2153,2154],{"class":64,"line":206},[62,2155,2156],{"class":68},"        -- type coercion: number, true, false, or string\n",[62,2158,2159,2162,2164,2166,2169,2171,2174,2176,2179,2181,2184,2187,2189,2191,2194,2196,2199,2201],{"class":64,"line":219},[62,2160,2161],{"class":79},"        val ",[62,2163,83],{"class":75},[62,2165,1443],{"class":107},[62,2167,2168],{"class":79},"(val) ",[62,2170,2017],{"class":75},[62,2172,2173],{"class":79}," (val ",[62,2175,132],{"class":75},[62,2177,2178],{"class":86}," \"true\" ",[62,2180,1653],{"class":75},[62,2182,2183],{"class":107}," true",[62,2185,2186],{"class":75}," or",[62,2188,2173],{"class":79},[62,2190,132],{"class":75},[62,2192,2193],{"class":86}," \"false\" ",[62,2195,1653],{"class":75},[62,2197,2198],{"class":107}," false",[62,2200,2186],{"class":75},[62,2202,2203],{"class":79}," val))\n",[62,2205,2206,2209,2211],{"class":64,"line":228},[62,2207,2208],{"class":79},"        config[section][key] ",[62,2210,83],{"class":75},[62,2212,2213],{"class":79}," val\n",[62,2215,2216],{"class":64,"line":239},[62,2217,1735],{"class":75},[62,2219,2220],{"class":64,"line":244},[62,2221,1809],{"class":75},[62,2223,2224],{"class":64,"line":250},[62,2225,1827],{"class":75},[62,2227,2228,2230],{"class":64,"line":262},[62,2229,1405],{"class":75},[62,2231,2232],{"class":79}," config\n",[62,2234,2235],{"class":64,"line":279},[62,2236,836],{"class":75},[62,2238,2239],{"class":64,"line":284},[62,2240,148],{"emptyLinePlaceholder":147},[62,2242,2243,2245,2248,2250,2252,2254],{"class":64,"line":290},[62,2244,76],{"class":75},[62,2246,2247],{"class":79}," ini ",[62,2249,83],{"class":75},[62,2251,1912],{"class":107},[62,2253,268],{"class":79},[62,2255,2256],{"class":86},"[[\n",[62,2258,2259],{"class":64,"line":301},[62,2260,2261],{"class":86},"[server]\n",[62,2263,2264],{"class":64,"line":328},[62,2265,2266],{"class":86},"host = localhost\n",[62,2268,2269],{"class":64,"line":350},[62,2270,2271],{"class":86},"port = 8080\n",[62,2273,2274],{"class":64,"line":371},[62,2275,2276],{"class":86},"debug = true\n",[62,2278,2279,2282],{"class":64,"line":564},[62,2280,2281],{"class":86},"]]",[62,2283,1485],{"class":79},[62,2285,2286,2288,2291,2294,2296,2299,2302,2304,2306,2309,2311,2313,2315,2318,2320],{"class":64,"line":570},[62,2287,126],{"class":107},[62,2289,2290],{"class":79},"(ini.",[62,2292,2293],{"class":100},"server",[62,2295,418],{"class":79},[62,2297,2298],{"class":100},"host",[62,2300,2301],{"class":79},", ini.",[62,2303,2293],{"class":100},[62,2305,418],{"class":79},[62,2307,2308],{"class":100},"port",[62,2310,2301],{"class":79},[62,2312,2293],{"class":100},[62,2314,418],{"class":79},[62,2316,2317],{"class":100},"debug",[62,2319,138],{"class":79},[62,2321,2322],{"class":68},"-- localhost 8080 true\n",[45,2324,2326,2327,2330],{"id":2325},"frontier-pattern-fset-boundary-detection","Frontier Pattern ",[28,2328,2329],{},"%f[set]"," — Boundary Detection",[50,2332,2333,2444,2448],{"language":52},[54,2334,2336],{"className":56,"code":2335,"language":52,"meta":58,"style":58},"-- %f[set] matches the position where the previous char is NOT in set, and next IS\n-- Like a zero-width boundary assertion (analogous to \\b in regex)\n\n-- Find word boundaries:\nlocal s = \"hello world\"\nfor word in s:gmatch(\"%f[%a]%a+%f[%A]\") do\n  print(word)  -- \"hello\" \u002F \"world\" (words bounded by non-letters)\nend\n\n-- %f[%a]: transition from non-alpha to alpha (start of word)\n-- %f[%A]: transition from alpha to non-alpha (end of word)\n-- Combined: %f[%a]%a+%f[%A] matches a complete word\n\n-- Edge case: frontier at string start\u002Fend\n-- %f[set] at position 1: \"previous char\" is treated as \\0 (not in any set)\n-- So %f[%a] at start of string matches (transition from \\0 to alpha)\n",[28,2337,2338,2343,2348,2352,2357,2368,2392,2402,2406,2410,2415,2420,2425,2429,2434,2439],{"__ignoreMap":58},[62,2339,2340],{"class":64,"line":65},[62,2341,2342],{"class":68},"-- %f[set] matches the position where the previous char is NOT in set, and next IS\n",[62,2344,2345],{"class":64,"line":72},[62,2346,2347],{"class":68},"-- Like a zero-width boundary assertion (analogous to \\b in regex)\n",[62,2349,2350],{"class":64,"line":90},[62,2351,148],{"emptyLinePlaceholder":147},[62,2353,2354],{"class":64,"line":117},[62,2355,2356],{"class":68},"-- Find word boundaries:\n",[62,2358,2359,2361,2363,2365],{"class":64,"line":123},[62,2360,76],{"class":75},[62,2362,80],{"class":79},[62,2364,83],{"class":75},[62,2366,2367],{"class":86}," \"hello world\"\n",[62,2369,2370,2372,2375,2377,2379,2381,2383,2385,2388,2390],{"class":64,"line":144},[62,2371,792],{"class":75},[62,2373,2374],{"class":79}," word ",[62,2376,798],{"class":75},[62,2378,101],{"class":100},[62,2380,104],{"class":79},[62,2382,806],{"class":107},[62,2384,268],{"class":79},[62,2386,2387],{"class":86},"\"%f[%a]%a+%f[%A]\"",[62,2389,814],{"class":79},[62,2391,871],{"class":75},[62,2393,2394,2396,2399],{"class":64,"line":151},[62,2395,825],{"class":107},[62,2397,2398],{"class":79},"(word)  ",[62,2400,2401],{"class":68},"-- \"hello\" \u002F \"world\" (words bounded by non-letters)\n",[62,2403,2404],{"class":64,"line":157},[62,2405,836],{"class":75},[62,2407,2408],{"class":64,"line":170},[62,2409,148],{"emptyLinePlaceholder":147},[62,2411,2412],{"class":64,"line":176},[62,2413,2414],{"class":68},"-- %f[%a]: transition from non-alpha to alpha (start of word)\n",[62,2416,2417],{"class":64,"line":182},[62,2418,2419],{"class":68},"-- %f[%A]: transition from alpha to non-alpha (end of word)\n",[62,2421,2422],{"class":64,"line":188},[62,2423,2424],{"class":68},"-- Combined: %f[%a]%a+%f[%A] matches a complete word\n",[62,2426,2427],{"class":64,"line":194},[62,2428,148],{"emptyLinePlaceholder":147},[62,2430,2431],{"class":64,"line":200},[62,2432,2433],{"class":68},"-- Edge case: frontier at string start\u002Fend\n",[62,2435,2436],{"class":64,"line":206},[62,2437,2438],{"class":68},"-- %f[set] at position 1: \"previous char\" is treated as \\0 (not in any set)\n",[62,2440,2441],{"class":64,"line":219},[62,2442,2443],{"class":68},"-- So %f[%a] at start of string matches (transition from \\0 to alpha)\n",[45,2445,2447],{"id":2446},"utf-8-handling-lua-53","UTF-8 Handling (Lua 5.3+)",[50,2449,2450,2670,2674],{"language":52},[54,2451,2453],{"className":56,"code":2452,"language":52,"meta":58,"style":58},"-- utf8.codes(s) → iterator over (position, codepoint) pairs\nlocal s = \"héllo\"\nfor pos, cp in utf8.codes(s) do\n  print(pos, cp, utf8.char(cp))  -- byte position, codepoint number, char\nend\n-- 1  104 h\n-- 2  233 é\n-- 4  108 l  (note: position jumped by 2 — é is 2 bytes)\n-- ...\n\n-- utf8.codepoint(s, i?) → codepoint at byte position\nutf8.codepoint(\"é\", 1)  -- 233\n\n-- utf8.char(cp, ...) → string from codepoints\nutf8.char(233)  -- \"é\"\nutf8.char(104, 233, 108)  -- \"hél\"\n\n-- utf8.len(s) → number of codepoints, or nil + position if invalid\nutf8.len(\"héllo\")  -- 5\nutf8.len(\"\\xFF\")    -- nil, 1 (invalid byte at position 1)\n\n-- utf8.offset(s, n, i?) → byte position of the n-th character\nutf8.offset(\"héllo\", 3)  -- 4 (3rd char \"l\" starts at byte 4 — é took 2 bytes)\n",[28,2454,2455,2460,2471,2488,2504,2508,2513,2518,2523,2528,2532,2537,2556,2560,2565,2579,2602,2606,2611,2624,2643,2647,2652],{"__ignoreMap":58},[62,2456,2457],{"class":64,"line":65},[62,2458,2459],{"class":68},"-- utf8.codes(s) → iterator over (position, codepoint) pairs\n",[62,2461,2462,2464,2466,2468],{"class":64,"line":72},[62,2463,76],{"class":75},[62,2465,80],{"class":79},[62,2467,83],{"class":75},[62,2469,2470],{"class":86}," \"héllo\"\n",[62,2472,2473,2475,2478,2480,2483,2486],{"class":64,"line":90},[62,2474,792],{"class":75},[62,2476,2477],{"class":79}," pos, cp ",[62,2479,798],{"class":75},[62,2481,2482],{"class":107}," utf8.codes",[62,2484,2485],{"class":79},"(s) ",[62,2487,871],{"class":75},[62,2489,2490,2492,2495,2498,2501],{"class":64,"line":117},[62,2491,825],{"class":107},[62,2493,2494],{"class":79},"(pos, cp, ",[62,2496,2497],{"class":107},"utf8.char",[62,2499,2500],{"class":79},"(cp))  ",[62,2502,2503],{"class":68},"-- byte position, codepoint number, char\n",[62,2505,2506],{"class":64,"line":123},[62,2507,836],{"class":75},[62,2509,2510],{"class":64,"line":144},[62,2511,2512],{"class":68},"-- 1  104 h\n",[62,2514,2515],{"class":64,"line":151},[62,2516,2517],{"class":68},"-- 2  233 é\n",[62,2519,2520],{"class":64,"line":157},[62,2521,2522],{"class":68},"-- 4  108 l  (note: position jumped by 2 — é is 2 bytes)\n",[62,2524,2525],{"class":64,"line":170},[62,2526,2527],{"class":68},"-- ...\n",[62,2529,2530],{"class":64,"line":176},[62,2531,148],{"emptyLinePlaceholder":147},[62,2533,2534],{"class":64,"line":182},[62,2535,2536],{"class":68},"-- utf8.codepoint(s, i?) → codepoint at byte position\n",[62,2538,2539,2542,2544,2547,2549,2551,2553],{"class":64,"line":188},[62,2540,2541],{"class":107},"utf8.codepoint",[62,2543,268],{"class":79},[62,2545,2546],{"class":86},"\"é\"",[62,2548,317],{"class":79},[62,2550,314],{"class":107},[62,2552,138],{"class":79},[62,2554,2555],{"class":68},"-- 233\n",[62,2557,2558],{"class":64,"line":194},[62,2559,148],{"emptyLinePlaceholder":147},[62,2561,2562],{"class":64,"line":200},[62,2563,2564],{"class":68},"-- utf8.char(cp, ...) → string from codepoints\n",[62,2566,2567,2569,2571,2574,2576],{"class":64,"line":206},[62,2568,2497],{"class":107},[62,2570,268],{"class":79},[62,2572,2573],{"class":107},"233",[62,2575,138],{"class":79},[62,2577,2578],{"class":68},"-- \"é\"\n",[62,2580,2581,2583,2585,2588,2590,2592,2594,2597,2599],{"class":64,"line":219},[62,2582,2497],{"class":107},[62,2584,268],{"class":79},[62,2586,2587],{"class":107},"104",[62,2589,317],{"class":79},[62,2591,2573],{"class":107},[62,2593,317],{"class":79},[62,2595,2596],{"class":107},"108",[62,2598,138],{"class":79},[62,2600,2601],{"class":68},"-- \"hél\"\n",[62,2603,2604],{"class":64,"line":228},[62,2605,148],{"emptyLinePlaceholder":147},[62,2607,2608],{"class":64,"line":239},[62,2609,2610],{"class":68},"-- utf8.len(s) → number of codepoints, or nil + position if invalid\n",[62,2612,2613,2615,2617,2619,2621],{"class":64,"line":244},[62,2614,265],{"class":107},[62,2616,268],{"class":79},[62,2618,271],{"class":86},[62,2620,138],{"class":79},[62,2622,2623],{"class":68},"-- 5\n",[62,2625,2626,2628,2630,2633,2636,2638,2640],{"class":64,"line":250},[62,2627,265],{"class":107},[62,2629,268],{"class":79},[62,2631,2632],{"class":86},"\"",[62,2634,2635],{"class":107},"\\xFF",[62,2637,2632],{"class":86},[62,2639,322],{"class":79},[62,2641,2642],{"class":68},"-- nil, 1 (invalid byte at position 1)\n",[62,2644,2645],{"class":64,"line":262},[62,2646,148],{"emptyLinePlaceholder":147},[62,2648,2649],{"class":64,"line":279},[62,2650,2651],{"class":68},"-- utf8.offset(s, n, i?) → byte position of the n-th character\n",[62,2653,2654,2657,2659,2661,2663,2665,2667],{"class":64,"line":284},[62,2655,2656],{"class":107},"utf8.offset",[62,2658,268],{"class":79},[62,2660,271],{"class":86},[62,2662,317],{"class":79},[62,2664,1881],{"class":107},[62,2666,138],{"class":79},[62,2668,2669],{"class":68},"-- 4 (3rd char \"l\" starts at byte 4 — é took 2 bytes)\n",[1125,2671,2673],{"id":2672},"production-utf-8-safe-string-operations","Production: UTF-8 safe string operations",[50,2675,2676,2920,2931],{"language":52},[54,2677,2679],{"className":56,"code":2678,"language":52,"meta":58,"style":58},"-- string.sub operates on BYTES, not characters. For UTF-8, use utf8.offset.\nlocal function utf8_sub(s, start_char, end_char)\n  local start_byte = utf8.offset(s, start_char)\n  local end_byte = end_char and (utf8.offset(s, end_char + 1) - 1) or #s\n  return s:sub(start_byte, end_byte)\nend\n\nlocal s = \"héllo\"\nutf8_sub(s, 1, 3)  -- \"hél\" (first 3 characters, not first 3 bytes)\n\n-- UTF-8 safe reverse\nlocal function utf8_reverse(s)\n  local chars = {}\n  for _, cp in utf8.codes(s) do\n    table.insert(chars, 1, utf8.char(cp))  -- prepend each codepoint\n  end\n  return table.concat(chars)\nend\n\nprint(utf8_reverse(\"héllo\"))  -- \"olléh\" (correct character reversal)\n-- s:reverse() would give \"olléh\" too for this case, but breaks on multi-byte\n-- sequences where byte order matters (e.g., combining marks)\n",[28,2680,2681,2686,2698,2713,2753,2766,2770,2774,2784,2803,2807,2812,2824,2835,2850,2869,2873,2883,2887,2891,2910,2915],{"__ignoreMap":58},[62,2682,2683],{"class":64,"line":65},[62,2684,2685],{"class":68},"-- string.sub operates on BYTES, not characters. For UTF-8, use utf8.offset.\n",[62,2687,2688,2690,2692,2695],{"class":64,"line":72},[62,2689,76],{"class":75},[62,2691,1325],{"class":75},[62,2693,2694],{"class":100}," utf8_sub",[62,2696,2697],{"class":79},"(s, start_char, end_char)\n",[62,2699,2700,2702,2705,2707,2710],{"class":64,"line":90},[62,2701,1341],{"class":75},[62,2703,2704],{"class":79}," start_byte ",[62,2706,83],{"class":75},[62,2708,2709],{"class":107}," utf8.offset",[62,2711,2712],{"class":79},"(s, start_char)\n",[62,2714,2715,2717,2720,2722,2725,2727,2729,2731,2734,2736,2738,2740,2742,2744,2746,2748,2750],{"class":64,"line":117},[62,2716,1341],{"class":75},[62,2718,2719],{"class":79}," end_byte ",[62,2721,83],{"class":75},[62,2723,2724],{"class":79}," end_char ",[62,2726,1653],{"class":75},[62,2728,853],{"class":79},[62,2730,2656],{"class":107},[62,2732,2733],{"class":79},"(s, end_char ",[62,2735,1665],{"class":75},[62,2737,1668],{"class":107},[62,2739,814],{"class":79},[62,2741,339],{"class":75},[62,2743,1668],{"class":107},[62,2745,814],{"class":79},[62,2747,2017],{"class":75},[62,2749,1604],{"class":75},[62,2751,2752],{"class":79},"s\n",[62,2754,2755,2757,2759,2761,2763],{"class":64,"line":123},[62,2756,1405],{"class":75},[62,2758,101],{"class":100},[62,2760,104],{"class":79},[62,2762,309],{"class":107},[62,2764,2765],{"class":79},"(start_byte, end_byte)\n",[62,2767,2768],{"class":64,"line":144},[62,2769,836],{"class":75},[62,2771,2772],{"class":64,"line":151},[62,2773,148],{"emptyLinePlaceholder":147},[62,2775,2776,2778,2780,2782],{"class":64,"line":157},[62,2777,76],{"class":75},[62,2779,80],{"class":79},[62,2781,83],{"class":75},[62,2783,2470],{"class":86},[62,2785,2786,2789,2792,2794,2796,2798,2800],{"class":64,"line":170},[62,2787,2788],{"class":107},"utf8_sub",[62,2790,2791],{"class":79},"(s, ",[62,2793,314],{"class":107},[62,2795,317],{"class":79},[62,2797,1881],{"class":107},[62,2799,138],{"class":79},[62,2801,2802],{"class":68},"-- \"hél\" (first 3 characters, not first 3 bytes)\n",[62,2804,2805],{"class":64,"line":176},[62,2806,148],{"emptyLinePlaceholder":147},[62,2808,2809],{"class":64,"line":182},[62,2810,2811],{"class":68},"-- UTF-8 safe reverse\n",[62,2813,2814,2816,2818,2821],{"class":64,"line":188},[62,2815,76],{"class":75},[62,2817,1325],{"class":75},[62,2819,2820],{"class":100}," utf8_reverse",[62,2822,2823],{"class":79},"(s)\n",[62,2825,2826,2828,2831,2833],{"class":64,"line":194},[62,2827,1341],{"class":75},[62,2829,2830],{"class":79}," chars ",[62,2832,83],{"class":75},[62,2834,1555],{"class":79},[62,2836,2837,2839,2842,2844,2846,2848],{"class":64,"line":200},[62,2838,1935],{"class":75},[62,2840,2841],{"class":79}," _, cp ",[62,2843,798],{"class":75},[62,2845,2482],{"class":107},[62,2847,2485],{"class":79},[62,2849,871],{"class":75},[62,2851,2852,2855,2858,2860,2862,2864,2866],{"class":64,"line":206},[62,2853,2854],{"class":107},"    table.insert",[62,2856,2857],{"class":79},"(chars, ",[62,2859,314],{"class":107},[62,2861,317],{"class":79},[62,2863,2497],{"class":107},[62,2865,2500],{"class":79},[62,2867,2868],{"class":68},"-- prepend each codepoint\n",[62,2870,2871],{"class":64,"line":219},[62,2872,1827],{"class":75},[62,2874,2875,2877,2880],{"class":64,"line":228},[62,2876,1405],{"class":75},[62,2878,2879],{"class":107}," table.concat",[62,2881,2882],{"class":79},"(chars)\n",[62,2884,2885],{"class":64,"line":239},[62,2886,836],{"class":75},[62,2888,2889],{"class":64,"line":244},[62,2890,148],{"emptyLinePlaceholder":147},[62,2892,2893,2895,2897,2900,2902,2904,2907],{"class":64,"line":250},[62,2894,126],{"class":107},[62,2896,268],{"class":79},[62,2898,2899],{"class":107},"utf8_reverse",[62,2901,268],{"class":79},[62,2903,271],{"class":86},[62,2905,2906],{"class":79},"))  ",[62,2908,2909],{"class":68},"-- \"olléh\" (correct character reversal)\n",[62,2911,2912],{"class":64,"line":262},[62,2913,2914],{"class":68},"-- s:reverse() would give \"olléh\" too for this case, but breaks on multi-byte\n",[62,2916,2917],{"class":64,"line":279},[62,2918,2919],{"class":68},"-- sequences where byte order matters (e.g., combining marks)\n",[45,2921,2923,2924,2927,2928],{"id":2922},"binary-protocol-parsing-with-stringbytestringchar","Binary Protocol Parsing with ",[28,2925,2926],{},"string.byte","\u002F",[28,2929,2930],{},"string.char",[50,2932,2933,3497,3508],{"language":52},[54,2934,2936],{"className":56,"code":2935,"language":52,"meta":58,"style":58},"-- string.byte(s, i, j) → byte values at positions i..j\n-- string.char(b1, b2, ...) → string from byte values\n\nlocal function read_uint16_be(data, offset)  -- big-endian 16-bit\n  local b1, b2 = data:byte(offset, offset + 1)\n  return b1 * 256 + b2, offset + 2  -- value, new offset\nend\n\nlocal function read_uint32_le(data, offset)  -- little-endian 32-bit\n  local b1, b2, b3, b4 = data:byte(offset, offset + 3)\n  return b1 + b2 * 256 + b3 * 65536 + b4 * 16777216, offset + 4\nend\n\nlocal function write_uint16_be(value)\n  return string.char(math.floor(value \u002F 256), value % 256)\nend\n\nlocal function write_uint32_le(value)\n  return string.char(\n    value % 256,\n    math.floor(value \u002F 256) % 256,\n    math.floor(value \u002F 65536) % 256,\n    math.floor(value \u002F 16777216) % 256\n  )\nend\n\n-- Production: binary protocol parser (e.g., DNS header)\nlocal function parse_dns_header(data)\n  local offset = 1\n  local id, offset = read_uint16_be(data, offset)\n  local flags, offset = read_uint16_be(data, offset)\n  local qdcount, offset = read_uint16_be(data, offset)\n  local ancount, offset = read_uint16_be(data, offset)\n  local nscount, offset = read_uint16_be(data, offset)\n  local arcount, offset = read_uint16_be(data, offset)\n  return {\n    id = id, flags = flags,\n    qdcount = qdcount, ancount = ancount,\n    nscount = nscount, arcount = arcount,\n  }\nend\n\n-- Build a binary message:\nlocal msg = write_uint16_be(0x1234) .. write_uint32_le(50000)\nprint(#msg)  -- 6 (2 + 4 bytes)\n",[28,2937,2938,2943,2948,2952,2967,2993,3020,3024,3028,3042,3066,3109,3113,3117,3129,3157,3161,3165,3176,3185,3197,3216,3234,3251,3256,3260,3264,3269,3281,3292,3306,3320,3334,3348,3362,3376,3383,3399,3415,3431,3436,3441,3446,3452,3482],{"__ignoreMap":58},[62,2939,2940],{"class":64,"line":65},[62,2941,2942],{"class":68},"-- string.byte(s, i, j) → byte values at positions i..j\n",[62,2944,2945],{"class":64,"line":72},[62,2946,2947],{"class":68},"-- string.char(b1, b2, ...) → string from byte values\n",[62,2949,2950],{"class":64,"line":90},[62,2951,148],{"emptyLinePlaceholder":147},[62,2953,2954,2956,2958,2961,2964],{"class":64,"line":117},[62,2955,76],{"class":75},[62,2957,1325],{"class":75},[62,2959,2960],{"class":100}," read_uint16_be",[62,2962,2963],{"class":79},"(data, offset)  ",[62,2965,2966],{"class":68},"-- big-endian 16-bit\n",[62,2968,2969,2971,2974,2976,2979,2981,2984,2987,2989,2991],{"class":64,"line":123},[62,2970,1341],{"class":75},[62,2972,2973],{"class":79}," b1, b2 ",[62,2975,83],{"class":75},[62,2977,2978],{"class":100}," data",[62,2980,104],{"class":79},[62,2982,2983],{"class":107},"byte",[62,2985,2986],{"class":79},"(offset, offset ",[62,2988,1665],{"class":75},[62,2990,1668],{"class":107},[62,2992,1485],{"class":79},[62,2994,2995,2997,3000,3003,3006,3009,3012,3014,3017],{"class":64,"line":144},[62,2996,1405],{"class":75},[62,2998,2999],{"class":79}," b1 ",[62,3001,3002],{"class":75},"*",[62,3004,3005],{"class":107}," 256",[62,3007,3008],{"class":75}," +",[62,3010,3011],{"class":79}," b2, offset ",[62,3013,1665],{"class":75},[62,3015,3016],{"class":107}," 2",[62,3018,3019],{"class":68},"  -- value, new offset\n",[62,3021,3022],{"class":64,"line":151},[62,3023,836],{"class":75},[62,3025,3026],{"class":64,"line":157},[62,3027,148],{"emptyLinePlaceholder":147},[62,3029,3030,3032,3034,3037,3039],{"class":64,"line":170},[62,3031,76],{"class":75},[62,3033,1325],{"class":75},[62,3035,3036],{"class":100}," read_uint32_le",[62,3038,2963],{"class":79},[62,3040,3041],{"class":68},"-- little-endian 32-bit\n",[62,3043,3044,3046,3049,3051,3053,3055,3057,3059,3061,3064],{"class":64,"line":176},[62,3045,1341],{"class":75},[62,3047,3048],{"class":79}," b1, b2, b3, b4 ",[62,3050,83],{"class":75},[62,3052,2978],{"class":100},[62,3054,104],{"class":79},[62,3056,2983],{"class":107},[62,3058,2986],{"class":79},[62,3060,1665],{"class":75},[62,3062,3063],{"class":107}," 3",[62,3065,1485],{"class":79},[62,3067,3068,3070,3072,3074,3077,3079,3081,3083,3086,3088,3091,3093,3096,3098,3101,3104,3106],{"class":64,"line":182},[62,3069,1405],{"class":75},[62,3071,2999],{"class":79},[62,3073,1665],{"class":75},[62,3075,3076],{"class":79}," b2 ",[62,3078,3002],{"class":75},[62,3080,3005],{"class":107},[62,3082,3008],{"class":75},[62,3084,3085],{"class":79}," b3 ",[62,3087,3002],{"class":75},[62,3089,3090],{"class":107}," 65536",[62,3092,3008],{"class":75},[62,3094,3095],{"class":79}," b4 ",[62,3097,3002],{"class":75},[62,3099,3100],{"class":107}," 16777216",[62,3102,3103],{"class":79},", offset ",[62,3105,1665],{"class":75},[62,3107,3108],{"class":107}," 4\n",[62,3110,3111],{"class":64,"line":188},[62,3112,836],{"class":75},[62,3114,3115],{"class":64,"line":194},[62,3116,148],{"emptyLinePlaceholder":147},[62,3118,3119,3121,3123,3126],{"class":64,"line":200},[62,3120,76],{"class":75},[62,3122,1325],{"class":75},[62,3124,3125],{"class":100}," write_uint16_be",[62,3127,3128],{"class":79},"(value)\n",[62,3130,3131,3133,3136,3138,3141,3144,3146,3148,3151,3153,3155],{"class":64,"line":206},[62,3132,1405],{"class":75},[62,3134,3135],{"class":107}," string.char",[62,3137,268],{"class":79},[62,3139,3140],{"class":107},"math.floor",[62,3142,3143],{"class":79},"(value ",[62,3145,2927],{"class":75},[62,3147,3005],{"class":107},[62,3149,3150],{"class":79},"), value ",[62,3152,410],{"class":75},[62,3154,3005],{"class":107},[62,3156,1485],{"class":79},[62,3158,3159],{"class":64,"line":219},[62,3160,836],{"class":75},[62,3162,3163],{"class":64,"line":228},[62,3164,148],{"emptyLinePlaceholder":147},[62,3166,3167,3169,3171,3174],{"class":64,"line":239},[62,3168,76],{"class":75},[62,3170,1325],{"class":75},[62,3172,3173],{"class":100}," write_uint32_le",[62,3175,3128],{"class":79},[62,3177,3178,3180,3182],{"class":64,"line":244},[62,3179,1405],{"class":75},[62,3181,3135],{"class":107},[62,3183,3184],{"class":79},"(\n",[62,3186,3187,3190,3192,3194],{"class":64,"line":250},[62,3188,3189],{"class":79},"    value ",[62,3191,410],{"class":75},[62,3193,3005],{"class":107},[62,3195,3196],{"class":79},",\n",[62,3198,3199,3202,3204,3206,3208,3210,3212,3214],{"class":64,"line":262},[62,3200,3201],{"class":107},"    math.floor",[62,3203,3143],{"class":79},[62,3205,2927],{"class":75},[62,3207,3005],{"class":107},[62,3209,814],{"class":79},[62,3211,410],{"class":75},[62,3213,3005],{"class":107},[62,3215,3196],{"class":79},[62,3217,3218,3220,3222,3224,3226,3228,3230,3232],{"class":64,"line":279},[62,3219,3201],{"class":107},[62,3221,3143],{"class":79},[62,3223,2927],{"class":75},[62,3225,3090],{"class":107},[62,3227,814],{"class":79},[62,3229,410],{"class":75},[62,3231,3005],{"class":107},[62,3233,3196],{"class":79},[62,3235,3236,3238,3240,3242,3244,3246,3248],{"class":64,"line":284},[62,3237,3201],{"class":107},[62,3239,3143],{"class":79},[62,3241,2927],{"class":75},[62,3243,3100],{"class":107},[62,3245,814],{"class":79},[62,3247,410],{"class":75},[62,3249,3250],{"class":107}," 256\n",[62,3252,3253],{"class":64,"line":290},[62,3254,3255],{"class":79},"  )\n",[62,3257,3258],{"class":64,"line":301},[62,3259,836],{"class":75},[62,3261,3262],{"class":64,"line":328},[62,3263,148],{"emptyLinePlaceholder":147},[62,3265,3266],{"class":64,"line":350},[62,3267,3268],{"class":68},"-- Production: binary protocol parser (e.g., DNS header)\n",[62,3270,3271,3273,3275,3278],{"class":64,"line":371},[62,3272,76],{"class":75},[62,3274,1325],{"class":75},[62,3276,3277],{"class":100}," parse_dns_header",[62,3279,3280],{"class":79},"(data)\n",[62,3282,3283,3285,3288,3290],{"class":64,"line":564},[62,3284,1341],{"class":75},[62,3286,3287],{"class":79}," offset ",[62,3289,83],{"class":75},[62,3291,1591],{"class":107},[62,3293,3294,3296,3299,3301,3303],{"class":64,"line":570},[62,3295,1341],{"class":75},[62,3297,3298],{"class":79}," id, offset ",[62,3300,83],{"class":75},[62,3302,2960],{"class":107},[62,3304,3305],{"class":79},"(data, offset)\n",[62,3307,3309,3311,3314,3316,3318],{"class":64,"line":3308},31,[62,3310,1341],{"class":75},[62,3312,3313],{"class":79}," flags, offset ",[62,3315,83],{"class":75},[62,3317,2960],{"class":107},[62,3319,3305],{"class":79},[62,3321,3323,3325,3328,3330,3332],{"class":64,"line":3322},32,[62,3324,1341],{"class":75},[62,3326,3327],{"class":79}," qdcount, offset ",[62,3329,83],{"class":75},[62,3331,2960],{"class":107},[62,3333,3305],{"class":79},[62,3335,3337,3339,3342,3344,3346],{"class":64,"line":3336},33,[62,3338,1341],{"class":75},[62,3340,3341],{"class":79}," ancount, offset ",[62,3343,83],{"class":75},[62,3345,2960],{"class":107},[62,3347,3305],{"class":79},[62,3349,3351,3353,3356,3358,3360],{"class":64,"line":3350},34,[62,3352,1341],{"class":75},[62,3354,3355],{"class":79}," nscount, offset ",[62,3357,83],{"class":75},[62,3359,2960],{"class":107},[62,3361,3305],{"class":79},[62,3363,3365,3367,3370,3372,3374],{"class":64,"line":3364},35,[62,3366,1341],{"class":75},[62,3368,3369],{"class":79}," arcount, offset ",[62,3371,83],{"class":75},[62,3373,2960],{"class":107},[62,3375,3305],{"class":79},[62,3377,3379,3381],{"class":64,"line":3378},36,[62,3380,1405],{"class":75},[62,3382,1408],{"class":79},[62,3384,3386,3389,3391,3394,3396],{"class":64,"line":3385},37,[62,3387,3388],{"class":79},"    id ",[62,3390,83],{"class":75},[62,3392,3393],{"class":79}," id, flags ",[62,3395,83],{"class":75},[62,3397,3398],{"class":79}," flags,\n",[62,3400,3402,3405,3407,3410,3412],{"class":64,"line":3401},38,[62,3403,3404],{"class":79},"    qdcount ",[62,3406,83],{"class":75},[62,3408,3409],{"class":79}," qdcount, ancount ",[62,3411,83],{"class":75},[62,3413,3414],{"class":79}," ancount,\n",[62,3416,3418,3421,3423,3426,3428],{"class":64,"line":3417},39,[62,3419,3420],{"class":79},"    nscount ",[62,3422,83],{"class":75},[62,3424,3425],{"class":79}," nscount, arcount ",[62,3427,83],{"class":75},[62,3429,3430],{"class":79}," arcount,\n",[62,3432,3434],{"class":64,"line":3433},40,[62,3435,1458],{"class":79},[62,3437,3439],{"class":64,"line":3438},41,[62,3440,836],{"class":75},[62,3442,3444],{"class":64,"line":3443},42,[62,3445,148],{"emptyLinePlaceholder":147},[62,3447,3449],{"class":64,"line":3448},43,[62,3450,3451],{"class":68},"-- Build a binary message:\n",[62,3453,3455,3457,3460,3462,3464,3466,3469,3471,3473,3475,3477,3480],{"class":64,"line":3454},44,[62,3456,76],{"class":75},[62,3458,3459],{"class":79}," msg ",[62,3461,83],{"class":75},[62,3463,3125],{"class":107},[62,3465,268],{"class":79},[62,3467,3468],{"class":107},"0x1234",[62,3470,814],{"class":79},[62,3472,1395],{"class":75},[62,3474,3173],{"class":107},[62,3476,268],{"class":79},[62,3478,3479],{"class":107},"50000",[62,3481,1485],{"class":79},[62,3483,3485,3487,3489,3491,3494],{"class":64,"line":3484},45,[62,3486,126],{"class":107},[62,3488,268],{"class":79},[62,3490,253],{"class":75},[62,3492,3493],{"class":79},"msg)  ",[62,3495,3496],{"class":68},"-- 6 (2 + 4 bytes)\n",[45,3498,3500,3503,3504,3507],{"id":3499},"stringformat-c-printf-compatible",[28,3501,3502],{},"string.format"," — C ",[28,3505,3506],{},"printf"," Compatible",[50,3509,3510,3922,3926,3938],{"language":52},[54,3511,3513],{"className":56,"code":3512,"language":52,"meta":58,"style":58},"-- %s string, %d integer, %f float, %x hex, %o octal, %c char\n-- %5d right-padded width 5, %-5d left-padded\n-- %.2f 2 decimal places, %05d zero-padded width 5\n-- %q: Lua-safe quoted string (escapes for embedding in Lua source)\n\nstring.format(\"%d\", 42)        -- \"42\"\nstring.format(\"%5d\", 42)       -- \"   42\" (right-aligned)\nstring.format(\"%-5d|\", 42)     -- \"42   |\" (left-aligned)\nstring.format(\"%05d\", 42)       -- \"00042\" (zero-padded)\nstring.format(\"%.2f\", 3.14159) -- \"3.14\"\nstring.format(\"%x\", 255)       -- \"ff\" (hex)\nstring.format(\"%X\", 255)       -- \"FF\"\nstring.format(\"%q\", \"he\\\"llo\") -- '\"he\\\\\"llo\"' (escaped for Lua source)\nstring.format(\"%q\", \"tab\\there\") -- '\"tab\\\\there\"'\n\n-- Edge case: %s coerces via tostring, but numbers get full precision\nstring.format(\"%s\", 3.14)      -- \"3.14\"\nstring.format(\"%s\", true)      -- \"true\"\ntostring(3.14)                  -- \"3.14\" (same)\n\n-- Integer formatting with thousands separator (manual — no built-in)\nlocal function format_int(n)\n  local s = tostring(math.abs(n))\n  s = s:reverse():gsub(\"(%d%d%d)\", \"%1,\"):reverse():gsub(\"^,\", \"\")\n  return (n \u003C 0 and \"-\" or \"\") .. s\nend\nprint(format_int(1234567))  -- \"1,234,567\"\n",[28,3514,3515,3520,3525,3530,3535,3539,3559,3578,3597,3615,3634,3653,3671,3696,3720,3724,3729,3747,3764,3779,3783,3788,3800,3819,3868,3899,3903],{"__ignoreMap":58},[62,3516,3517],{"class":64,"line":65},[62,3518,3519],{"class":68},"-- %s string, %d integer, %f float, %x hex, %o octal, %c char\n",[62,3521,3522],{"class":64,"line":72},[62,3523,3524],{"class":68},"-- %5d right-padded width 5, %-5d left-padded\n",[62,3526,3527],{"class":64,"line":90},[62,3528,3529],{"class":68},"-- %.2f 2 decimal places, %05d zero-padded width 5\n",[62,3531,3532],{"class":64,"line":117},[62,3533,3534],{"class":68},"-- %q: Lua-safe quoted string (escapes for embedding in Lua source)\n",[62,3536,3537],{"class":64,"line":123},[62,3538,148],{"emptyLinePlaceholder":147},[62,3540,3541,3543,3545,3548,3550,3553,3556],{"class":64,"line":144},[62,3542,3502],{"class":107},[62,3544,268],{"class":79},[62,3546,3547],{"class":86},"\"%d\"",[62,3549,317],{"class":79},[62,3551,3552],{"class":107},"42",[62,3554,3555],{"class":79},")        ",[62,3557,3558],{"class":68},"-- \"42\"\n",[62,3560,3561,3563,3565,3568,3570,3572,3575],{"class":64,"line":151},[62,3562,3502],{"class":107},[62,3564,268],{"class":79},[62,3566,3567],{"class":86},"\"%5d\"",[62,3569,317],{"class":79},[62,3571,3552],{"class":107},[62,3573,3574],{"class":79},")       ",[62,3576,3577],{"class":68},"-- \"   42\" (right-aligned)\n",[62,3579,3580,3582,3584,3587,3589,3591,3594],{"class":64,"line":157},[62,3581,3502],{"class":107},[62,3583,268],{"class":79},[62,3585,3586],{"class":86},"\"%-5d|\"",[62,3588,317],{"class":79},[62,3590,3552],{"class":107},[62,3592,3593],{"class":79},")     ",[62,3595,3596],{"class":68},"-- \"42   |\" (left-aligned)\n",[62,3598,3599,3601,3603,3606,3608,3610,3612],{"class":64,"line":170},[62,3600,3502],{"class":107},[62,3602,268],{"class":79},[62,3604,3605],{"class":86},"\"%05d\"",[62,3607,317],{"class":79},[62,3609,3552],{"class":107},[62,3611,3574],{"class":79},[62,3613,3614],{"class":68},"-- \"00042\" (zero-padded)\n",[62,3616,3617,3619,3621,3624,3626,3629,3631],{"class":64,"line":176},[62,3618,3502],{"class":107},[62,3620,268],{"class":79},[62,3622,3623],{"class":86},"\"%.2f\"",[62,3625,317],{"class":79},[62,3627,3628],{"class":107},"3.14159",[62,3630,814],{"class":79},[62,3632,3633],{"class":68},"-- \"3.14\"\n",[62,3635,3636,3638,3640,3643,3645,3648,3650],{"class":64,"line":182},[62,3637,3502],{"class":107},[62,3639,268],{"class":79},[62,3641,3642],{"class":86},"\"%x\"",[62,3644,317],{"class":79},[62,3646,3647],{"class":107},"255",[62,3649,3574],{"class":79},[62,3651,3652],{"class":68},"-- \"ff\" (hex)\n",[62,3654,3655,3657,3659,3662,3664,3666,3668],{"class":64,"line":188},[62,3656,3502],{"class":107},[62,3658,268],{"class":79},[62,3660,3661],{"class":86},"\"%X\"",[62,3663,317],{"class":79},[62,3665,3647],{"class":107},[62,3667,3574],{"class":79},[62,3669,3670],{"class":68},"-- \"FF\"\n",[62,3672,3673,3675,3677,3680,3682,3685,3688,3691,3693],{"class":64,"line":194},[62,3674,3502],{"class":107},[62,3676,268],{"class":79},[62,3678,3679],{"class":86},"\"%q\"",[62,3681,317],{"class":79},[62,3683,3684],{"class":86},"\"he",[62,3686,3687],{"class":107},"\\\"",[62,3689,3690],{"class":86},"llo\"",[62,3692,814],{"class":79},[62,3694,3695],{"class":68},"-- '\"he\\\\\"llo\"' (escaped for Lua source)\n",[62,3697,3698,3700,3702,3704,3706,3709,3712,3715,3717],{"class":64,"line":200},[62,3699,3502],{"class":107},[62,3701,268],{"class":79},[62,3703,3679],{"class":86},[62,3705,317],{"class":79},[62,3707,3708],{"class":86},"\"tab",[62,3710,3711],{"class":107},"\\t",[62,3713,3714],{"class":86},"here\"",[62,3716,814],{"class":79},[62,3718,3719],{"class":68},"-- '\"tab\\\\there\"'\n",[62,3721,3722],{"class":64,"line":206},[62,3723,148],{"emptyLinePlaceholder":147},[62,3725,3726],{"class":64,"line":219},[62,3727,3728],{"class":68},"-- Edge case: %s coerces via tostring, but numbers get full precision\n",[62,3730,3731,3733,3735,3738,3740,3743,3745],{"class":64,"line":228},[62,3732,3502],{"class":107},[62,3734,268],{"class":79},[62,3736,3737],{"class":86},"\"%s\"",[62,3739,317],{"class":79},[62,3741,3742],{"class":107},"3.14",[62,3744,344],{"class":79},[62,3746,3633],{"class":68},[62,3748,3749,3751,3753,3755,3757,3759,3761],{"class":64,"line":239},[62,3750,3502],{"class":107},[62,3752,268],{"class":79},[62,3754,3737],{"class":86},[62,3756,317],{"class":79},[62,3758,761],{"class":107},[62,3760,344],{"class":79},[62,3762,3763],{"class":68},"-- \"true\"\n",[62,3765,3766,3769,3771,3773,3776],{"class":64,"line":244},[62,3767,3768],{"class":107},"tostring",[62,3770,268],{"class":79},[62,3772,3742],{"class":107},[62,3774,3775],{"class":79},")                  ",[62,3777,3778],{"class":68},"-- \"3.14\" (same)\n",[62,3780,3781],{"class":64,"line":250},[62,3782,148],{"emptyLinePlaceholder":147},[62,3784,3785],{"class":64,"line":262},[62,3786,3787],{"class":68},"-- Integer formatting with thousands separator (manual — no built-in)\n",[62,3789,3790,3792,3794,3797],{"class":64,"line":279},[62,3791,76],{"class":75},[62,3793,1325],{"class":75},[62,3795,3796],{"class":100}," format_int",[62,3798,3799],{"class":79},"(n)\n",[62,3801,3802,3804,3806,3808,3811,3813,3816],{"class":64,"line":284},[62,3803,1341],{"class":75},[62,3805,80],{"class":79},[62,3807,83],{"class":75},[62,3809,3810],{"class":107}," tostring",[62,3812,268],{"class":79},[62,3814,3815],{"class":107},"math.abs",[62,3817,3818],{"class":79},"(n))\n",[62,3820,3821,3824,3826,3828,3830,3833,3836,3838,3840,3843,3845,3848,3850,3852,3854,3856,3858,3861,3863,3866],{"class":64,"line":290},[62,3822,3823],{"class":79},"  s ",[62,3825,83],{"class":75},[62,3827,101],{"class":100},[62,3829,104],{"class":79},[62,3831,3832],{"class":107},"reverse",[62,3834,3835],{"class":79},"():",[62,3837,918],{"class":107},[62,3839,268],{"class":79},[62,3841,3842],{"class":86},"\"(%d%d%d)\"",[62,3844,317],{"class":79},[62,3846,3847],{"class":86},"\"%1,\"",[62,3849,859],{"class":79},[62,3851,3832],{"class":107},[62,3853,3835],{"class":79},[62,3855,918],{"class":107},[62,3857,268],{"class":79},[62,3859,3860],{"class":86},"\"^,\"",[62,3862,317],{"class":79},[62,3864,3865],{"class":86},"\"\"",[62,3867,1485],{"class":79},[62,3869,3870,3872,3875,3878,3881,3884,3887,3889,3892,3894,3896],{"class":64,"line":301},[62,3871,1405],{"class":75},[62,3873,3874],{"class":79}," (n ",[62,3876,3877],{"class":75},"\u003C",[62,3879,3880],{"class":107}," 0",[62,3882,3883],{"class":75}," and",[62,3885,3886],{"class":86}," \"-\" ",[62,3888,2017],{"class":75},[62,3890,3891],{"class":86}," \"\"",[62,3893,814],{"class":79},[62,3895,1395],{"class":75},[62,3897,3898],{"class":79}," s\n",[62,3900,3901],{"class":64,"line":328},[62,3902,836],{"class":75},[62,3904,3905,3907,3909,3912,3914,3917,3919],{"class":64,"line":350},[62,3906,126],{"class":107},[62,3908,268],{"class":79},[62,3910,3911],{"class":107},"format_int",[62,3913,268],{"class":79},[62,3915,3916],{"class":107},"1234567",[62,3918,2906],{"class":79},[62,3920,3921],{"class":68},"-- \"1,234,567\"\n",[45,3923,3925],{"id":3924},"tips-tricks","💡 Tips & Tricks",[15,3927,3928,3937],{},[19,3929,3930,3931,3933,3934,3936],{},"Use ",[28,3932,3502],{}," for building structured strings, not ",[28,3935,1395],{}," chains",": More readable, handles type coercion.",[50,3939,3940],{"language":52},[54,3941,3943],{"className":56,"code":3942,"language":52,"meta":58,"style":58},"-- Instead of: \"x=\" .. tostring(x) .. \", y=\" .. tostring(y)\nlocal s = string.format(\"x=%s, y=%s\", x, y)\n-- Faster for complex formatting; same performance for simple concat\n",[28,3944,3945,3950,3969],{"__ignoreMap":58},[62,3946,3947],{"class":64,"line":65},[62,3948,3949],{"class":68},"-- Instead of: \"x=\" .. tostring(x) .. \", y=\" .. tostring(y)\n",[62,3951,3952,3954,3956,3958,3961,3963,3966],{"class":64,"line":72},[62,3953,76],{"class":75},[62,3955,80],{"class":79},[62,3957,83],{"class":75},[62,3959,3960],{"class":107}," string.format",[62,3962,268],{"class":79},[62,3964,3965],{"class":86},"\"x=%s, y=%s\"",[62,3967,3968],{"class":79},", x, y)\n",[62,3970,3971],{"class":64,"line":90},[62,3972,3973],{"class":68},"-- Faster for complex formatting; same performance for simple concat\n",[15,3975,3976,3982],{},[19,3977,3978,3981],{},[28,3979,3980],{},"string.rep(s, n, sep)"," with separator (5.2+)",": Build repeated patterns with separators.",[50,3984,3985],{"language":52},[54,3986,3988],{"className":56,"code":3987,"language":52,"meta":58,"style":58},"string.rep(\"ab\", 3, \"-\")  -- \"ab-ab-ab\" (sep between repetitions)\n",[28,3989,3990],{"__ignoreMap":58},[62,3991,3992,3995,3997,4000,4002,4004,4006,4009,4011],{"class":64,"line":65},[62,3993,3994],{"class":107},"string.rep",[62,3996,268],{"class":79},[62,3998,3999],{"class":86},"\"ab\"",[62,4001,317],{"class":79},[62,4003,1881],{"class":107},[62,4005,317],{"class":79},[62,4007,4008],{"class":86},"\"-\"",[62,4010,138],{"class":79},[62,4012,4013],{"class":68},"-- \"ab-ab-ab\" (sep between repetitions)\n",[15,4015,4016,4027],{},[19,4017,4018,4019,4022,4023,4026],{},"Pattern optimization: anchor with ",[28,4020,4021],{},"^"," and ",[28,4024,4025],{},"$"," for early exit",": Unanchored patterns scan the whole string; anchored patterns can fail fast at the start.",[50,4029,4030],{"language":52},[54,4031,4033],{"className":56,"code":4032,"language":52,"meta":58,"style":58},"-- SLOWER on non-matching input (scans entire string before failing)\n(\"hello\"):match(\"world\")\n\n-- FASTER (fails immediately at position 1 — ^ forces start match)\n(\"hello\"):match(\"^world\")\n",[28,4034,4035,4040,4057,4061,4066],{"__ignoreMap":58},[62,4036,4037],{"class":64,"line":65},[62,4038,4039],{"class":68},"-- SLOWER on non-matching input (scans entire string before failing)\n",[62,4041,4042,4044,4046,4048,4050,4052,4055],{"class":64,"line":72},[62,4043,268],{"class":79},[62,4045,980],{"class":86},[62,4047,859],{"class":79},[62,4049,627],{"class":107},[62,4051,268],{"class":79},[62,4053,4054],{"class":86},"\"world\"",[62,4056,1485],{"class":79},[62,4058,4059],{"class":64,"line":90},[62,4060,148],{"emptyLinePlaceholder":147},[62,4062,4063],{"class":64,"line":117},[62,4064,4065],{"class":68},"-- FASTER (fails immediately at position 1 — ^ forces start match)\n",[62,4067,4068,4070,4072,4074,4076,4078,4081],{"class":64,"line":123},[62,4069,268],{"class":79},[62,4071,980],{"class":86},[62,4073,859],{"class":79},[62,4075,627],{"class":107},[62,4077,268],{"class":79},[62,4079,4080],{"class":86},"\"^world\"",[62,4082,1485],{"class":79},[15,4084,4085,4090],{},[19,4086,4087,4089],{},[28,4088,588],{}," with function for transformations",": More flexible than string replacements.",[50,4092,4093],{"language":52},[54,4094,4096],{"className":56,"code":4095,"language":52,"meta":58,"style":58},"-- URL-encode: replace special chars with %XX\nlocal function url_encode(s)\n  s = s:gsub(\"([^%w])\", function(c)\n    return string.format(\"%%%02X\", string.byte(c))\n  end)\n  return s\nend\nprint(url_encode(\"hello world&foo\"))  -- \"hello%20world%26foo\"\n\n-- URL-decode:\nlocal function url_decode(s)\n  s = s:gsub(\"%%(%x%x)\", function(hex)\n    return string.char(tonumber(hex, 16))\n  end)\n  return s\nend\nprint(url_decode(\"hello%20world%26foo\"))  -- \"hello world&foo\"\n",[28,4097,4098,4103,4114,4138,4157,4164,4170,4174,4193,4197,4202,4213,4237,4257,4263,4269,4273],{"__ignoreMap":58},[62,4099,4100],{"class":64,"line":65},[62,4101,4102],{"class":68},"-- URL-encode: replace special chars with %XX\n",[62,4104,4105,4107,4109,4112],{"class":64,"line":72},[62,4106,76],{"class":75},[62,4108,1325],{"class":75},[62,4110,4111],{"class":100}," url_encode",[62,4113,2823],{"class":79},[62,4115,4116,4118,4120,4122,4124,4126,4128,4131,4133,4135],{"class":64,"line":90},[62,4117,3823],{"class":79},[62,4119,83],{"class":75},[62,4121,101],{"class":100},[62,4123,104],{"class":79},[62,4125,918],{"class":107},[62,4127,268],{"class":79},[62,4129,4130],{"class":86},"\"([^%w])\"",[62,4132,317],{"class":79},[62,4134,994],{"class":75},[62,4136,4137],{"class":79},"(c)\n",[62,4139,4140,4143,4145,4147,4150,4152,4154],{"class":64,"line":117},[62,4141,4142],{"class":75},"    return",[62,4144,3960],{"class":107},[62,4146,268],{"class":79},[62,4148,4149],{"class":86},"\"%%%02X\"",[62,4151,317],{"class":79},[62,4153,2926],{"class":107},[62,4155,4156],{"class":79},"(c))\n",[62,4158,4159,4162],{"class":64,"line":123},[62,4160,4161],{"class":75},"  end",[62,4163,1485],{"class":79},[62,4165,4166,4168],{"class":64,"line":144},[62,4167,1405],{"class":75},[62,4169,3898],{"class":79},[62,4171,4172],{"class":64,"line":151},[62,4173,836],{"class":75},[62,4175,4176,4178,4180,4183,4185,4188,4190],{"class":64,"line":157},[62,4177,126],{"class":107},[62,4179,268],{"class":79},[62,4181,4182],{"class":107},"url_encode",[62,4184,268],{"class":79},[62,4186,4187],{"class":86},"\"hello world&foo\"",[62,4189,2906],{"class":79},[62,4191,4192],{"class":68},"-- \"hello%20world%26foo\"\n",[62,4194,4195],{"class":64,"line":170},[62,4196,148],{"emptyLinePlaceholder":147},[62,4198,4199],{"class":64,"line":176},[62,4200,4201],{"class":68},"-- URL-decode:\n",[62,4203,4204,4206,4208,4211],{"class":64,"line":182},[62,4205,76],{"class":75},[62,4207,1325],{"class":75},[62,4209,4210],{"class":100}," url_decode",[62,4212,2823],{"class":79},[62,4214,4215,4217,4219,4221,4223,4225,4227,4230,4232,4234],{"class":64,"line":188},[62,4216,3823],{"class":79},[62,4218,83],{"class":75},[62,4220,101],{"class":100},[62,4222,104],{"class":79},[62,4224,918],{"class":107},[62,4226,268],{"class":79},[62,4228,4229],{"class":86},"\"%%(%x%x)\"",[62,4231,317],{"class":79},[62,4233,994],{"class":75},[62,4235,4236],{"class":79},"(hex)\n",[62,4238,4239,4241,4243,4245,4248,4251,4254],{"class":64,"line":194},[62,4240,4142],{"class":75},[62,4242,3135],{"class":107},[62,4244,268],{"class":79},[62,4246,4247],{"class":107},"tonumber",[62,4249,4250],{"class":79},"(hex, ",[62,4252,4253],{"class":107},"16",[62,4255,4256],{"class":79},"))\n",[62,4258,4259,4261],{"class":64,"line":200},[62,4260,4161],{"class":75},[62,4262,1485],{"class":79},[62,4264,4265,4267],{"class":64,"line":206},[62,4266,1405],{"class":75},[62,4268,3898],{"class":79},[62,4270,4271],{"class":64,"line":219},[62,4272,836],{"class":75},[62,4274,4275,4277,4279,4282,4284,4287,4289],{"class":64,"line":228},[62,4276,126],{"class":107},[62,4278,268],{"class":79},[62,4280,4281],{"class":107},"url_decode",[62,4283,268],{"class":79},[62,4285,4286],{"class":86},"\"hello%20world%26foo\"",[62,4288,2906],{"class":79},[62,4290,4291],{"class":68},"-- \"hello world&foo\"\n",[45,4293,4295],{"id":4294},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[15,4297,4298,4306,4307,4310,4311,4314],{},[19,4299,4300,4302,4303],{},[28,4301,410],{}," is the escape character, not ",[28,4304,4305],{},"\\",": ",[28,4308,4309],{},"%."," matches literal dot, ",[28,4312,4313],{},"%$"," matches literal dollar.",[50,4316,4317],{"language":52},[54,4318,4320],{"className":56,"code":4319,"language":52,"meta":58,"style":58},"\"price: $5\".match(\"price: $5\", \"%$%d\")  -- error: .match is not a method\n(\"price: $5\"):match(\"%$%d\")  -- \"$5\" (escaped $ as literal)\n(\"a.b.c\"):match(\"%.\")  -- \".\" (first literal dot, at position 2)\n(\"a.b.c\"):match(\".\", 1, true)  -- not valid; use plain find for literal\n",[28,4321,4322,4345,4364,4385],{"__ignoreMap":58},[62,4323,4324,4327,4329,4331,4333,4335,4337,4340,4342],{"class":64,"line":65},[62,4325,4326],{"class":86},"\"price: $5\"",[62,4328,418],{"class":79},[62,4330,627],{"class":107},[62,4332,268],{"class":79},[62,4334,4326],{"class":86},[62,4336,317],{"class":79},[62,4338,4339],{"class":86},"\"%$%d\"",[62,4341,138],{"class":79},[62,4343,4344],{"class":68},"-- error: .match is not a method\n",[62,4346,4347,4349,4351,4353,4355,4357,4359,4361],{"class":64,"line":72},[62,4348,268],{"class":79},[62,4350,4326],{"class":86},[62,4352,859],{"class":79},[62,4354,627],{"class":107},[62,4356,268],{"class":79},[62,4358,4339],{"class":86},[62,4360,138],{"class":79},[62,4362,4363],{"class":68},"-- \"$5\" (escaped $ as literal)\n",[62,4365,4366,4368,4371,4373,4375,4377,4380,4382],{"class":64,"line":90},[62,4367,268],{"class":79},[62,4369,4370],{"class":86},"\"a.b.c\"",[62,4372,859],{"class":79},[62,4374,627],{"class":107},[62,4376,268],{"class":79},[62,4378,4379],{"class":86},"\"%.\"",[62,4381,138],{"class":79},[62,4383,4384],{"class":68},"-- \".\" (first literal dot, at position 2)\n",[62,4386,4387,4389,4391,4393,4395,4397,4399,4401,4403,4405,4407,4409],{"class":64,"line":117},[62,4388,268],{"class":79},[62,4390,4370],{"class":86},[62,4392,859],{"class":79},[62,4394,627],{"class":107},[62,4396,268],{"class":79},[62,4398,752],{"class":86},[62,4400,317],{"class":79},[62,4402,314],{"class":107},[62,4404,317],{"class":79},[62,4406,761],{"class":107},[62,4408,138],{"class":79},[62,4410,4411],{"class":68},"-- not valid; use plain find for literal\n",[15,4413,4414,4427,4428,317,4431,4434],{},[19,4415,4416,4418,4419,4422,4423,4426],{},[28,4417,3502],{}," ",[28,4420,4421],{},"%s"," with ",[28,4424,4425],{},"nil"," errors",": Unlike ",[28,4429,4430],{},"print()",[28,4432,4433],{},"format"," doesn't auto-handle nil.",[50,4436,4437],{"language":52},[54,4438,4440],{"className":56,"code":4439,"language":52,"meta":58,"style":58},"string.format(\"value: %s\", nil)  -- ERROR: bad argument #2 to 'format' (string expected, got nil)\n-- Fix: tostring explicitly\nstring.format(\"value: %s\", tostring(nil))  -- \"value: nil\"\n",[28,4441,4442,4460,4465],{"__ignoreMap":58},[62,4443,4444,4446,4448,4451,4453,4455,4457],{"class":64,"line":65},[62,4445,3502],{"class":107},[62,4447,268],{"class":79},[62,4449,4450],{"class":86},"\"value: %s\"",[62,4452,317],{"class":79},[62,4454,4425],{"class":107},[62,4456,138],{"class":79},[62,4458,4459],{"class":68},"-- ERROR: bad argument #2 to 'format' (string expected, got nil)\n",[62,4461,4462],{"class":64,"line":72},[62,4463,4464],{"class":68},"-- Fix: tostring explicitly\n",[62,4466,4467,4469,4471,4473,4475,4477,4479,4481,4483],{"class":64,"line":90},[62,4468,3502],{"class":107},[62,4470,268],{"class":79},[62,4472,4450],{"class":86},[62,4474,317],{"class":79},[62,4476,3768],{"class":107},[62,4478,268],{"class":79},[62,4480,4425],{"class":107},[62,4482,2906],{"class":79},[62,4484,4485],{"class":68},"-- \"value: nil\"\n",[15,4487,4488,4306,4491,4494,4495,4498,4499,4502,4503,4506,4507,418],{},[19,4489,4490],{},"Patterns are case-sensitive",[28,4492,4493],{},"%a"," is letters; ",[28,4496,4497],{},"%A"," is non-letters. ",[28,4500,4501],{},"[a-z]"," won't match ",[28,4504,4505],{},"A",". Use character classes or ",[28,4508,4509],{},"[a-zA-Z]",[15,4511,4512,4518,4519,4522,4523,418],{},[19,4513,4514,4515,1325],{},"No built-in ",[28,4516,4517],{},"split",": Lua has no ",[28,4520,4521],{},"string.split",". Build one with ",[28,4524,806],{},[50,4526,4527],{"language":52},[54,4528,4530],{"className":56,"code":4529,"language":52,"meta":58,"style":58},"local function split(s, sep)\n  sep = sep or \"%s\"  -- default: whitespace\n  local parts = {}\n  for part in s:gmatch(\"([^\" .. sep .. \"]+)\") do\n    parts[#parts + 1] = part\n  end\n  return parts\nend\n-- WARNING: if sep contains pattern magic chars (., +, *, etc.), escape them!\nlocal function split_literal(s, sep)\n  sep = sep:gsub(\"([%%%.%+%-%*%?%[%]%(%)%$%^])\", \"%%%1\")  -- escape magic chars\n  local parts = {}\n  for part in s:gmatch(\"([^\" .. sep .. \"]+)\") do\n    parts[#parts + 1] = part\n  end\n  return parts\nend\nprint(split_literal(\"a.b.c.d\", \".\"))  -- {\"a\", \"b\", \"c\", \"d\"} (literal dot split)\n",[28,4531,4532,4544,4562,4573,4606,4627,4631,4638,4642,4647,4658,4686,4696,4726,4744,4748,4754,4758],{"__ignoreMap":58},[62,4533,4534,4536,4538,4541],{"class":64,"line":65},[62,4535,76],{"class":75},[62,4537,1325],{"class":75},[62,4539,4540],{"class":100}," split",[62,4542,4543],{"class":79},"(s, sep)\n",[62,4545,4546,4549,4551,4554,4556,4559],{"class":64,"line":72},[62,4547,4548],{"class":79},"  sep ",[62,4550,83],{"class":75},[62,4552,4553],{"class":79}," sep ",[62,4555,2017],{"class":75},[62,4557,4558],{"class":86}," \"%s\"  ",[62,4560,4561],{"class":68},"-- default: whitespace\n",[62,4563,4564,4566,4569,4571],{"class":64,"line":90},[62,4565,1341],{"class":75},[62,4567,4568],{"class":79}," parts ",[62,4570,83],{"class":75},[62,4572,1555],{"class":79},[62,4574,4575,4577,4580,4582,4584,4586,4588,4590,4593,4595,4597,4599,4602,4604],{"class":64,"line":117},[62,4576,1935],{"class":75},[62,4578,4579],{"class":79}," part ",[62,4581,798],{"class":75},[62,4583,101],{"class":100},[62,4585,104],{"class":79},[62,4587,806],{"class":107},[62,4589,268],{"class":79},[62,4591,4592],{"class":86},"\"([^\" ",[62,4594,1395],{"class":75},[62,4596,4553],{"class":79},[62,4598,1395],{"class":75},[62,4600,4601],{"class":86}," \"]+)\"",[62,4603,814],{"class":79},[62,4605,871],{"class":75},[62,4607,4608,4611,4613,4616,4618,4620,4622,4624],{"class":64,"line":123},[62,4609,4610],{"class":79},"    parts[",[62,4612,253],{"class":75},[62,4614,4615],{"class":79},"parts ",[62,4617,1665],{"class":75},[62,4619,1668],{"class":107},[62,4621,1772],{"class":79},[62,4623,83],{"class":75},[62,4625,4626],{"class":79}," part\n",[62,4628,4629],{"class":64,"line":144},[62,4630,1827],{"class":75},[62,4632,4633,4635],{"class":64,"line":151},[62,4634,1405],{"class":75},[62,4636,4637],{"class":79}," parts\n",[62,4639,4640],{"class":64,"line":157},[62,4641,836],{"class":75},[62,4643,4644],{"class":64,"line":170},[62,4645,4646],{"class":68},"-- WARNING: if sep contains pattern magic chars (., +, *, etc.), escape them!\n",[62,4648,4649,4651,4653,4656],{"class":64,"line":176},[62,4650,76],{"class":75},[62,4652,1325],{"class":75},[62,4654,4655],{"class":100}," split_literal",[62,4657,4543],{"class":79},[62,4659,4660,4662,4664,4667,4669,4671,4673,4676,4678,4681,4683],{"class":64,"line":182},[62,4661,4548],{"class":79},[62,4663,83],{"class":75},[62,4665,4666],{"class":100}," sep",[62,4668,104],{"class":79},[62,4670,918],{"class":107},[62,4672,268],{"class":79},[62,4674,4675],{"class":86},"\"([%%%.%+%-%*%?%[%]%(%)%$%^])\"",[62,4677,317],{"class":79},[62,4679,4680],{"class":86},"\"%%%1\"",[62,4682,138],{"class":79},[62,4684,4685],{"class":68},"-- escape magic chars\n",[62,4687,4688,4690,4692,4694],{"class":64,"line":188},[62,4689,1341],{"class":75},[62,4691,4568],{"class":79},[62,4693,83],{"class":75},[62,4695,1555],{"class":79},[62,4697,4698,4700,4702,4704,4706,4708,4710,4712,4714,4716,4718,4720,4722,4724],{"class":64,"line":194},[62,4699,1935],{"class":75},[62,4701,4579],{"class":79},[62,4703,798],{"class":75},[62,4705,101],{"class":100},[62,4707,104],{"class":79},[62,4709,806],{"class":107},[62,4711,268],{"class":79},[62,4713,4592],{"class":86},[62,4715,1395],{"class":75},[62,4717,4553],{"class":79},[62,4719,1395],{"class":75},[62,4721,4601],{"class":86},[62,4723,814],{"class":79},[62,4725,871],{"class":75},[62,4727,4728,4730,4732,4734,4736,4738,4740,4742],{"class":64,"line":200},[62,4729,4610],{"class":79},[62,4731,253],{"class":75},[62,4733,4615],{"class":79},[62,4735,1665],{"class":75},[62,4737,1668],{"class":107},[62,4739,1772],{"class":79},[62,4741,83],{"class":75},[62,4743,4626],{"class":79},[62,4745,4746],{"class":64,"line":206},[62,4747,1827],{"class":75},[62,4749,4750,4752],{"class":64,"line":219},[62,4751,1405],{"class":75},[62,4753,4637],{"class":79},[62,4755,4756],{"class":64,"line":228},[62,4757,836],{"class":75},[62,4759,4760,4762,4764,4767,4769,4772,4774,4776,4778],{"class":64,"line":239},[62,4761,126],{"class":107},[62,4763,268],{"class":79},[62,4765,4766],{"class":107},"split_literal",[62,4768,268],{"class":79},[62,4770,4771],{"class":86},"\"a.b.c.d\"",[62,4773,317],{"class":79},[62,4775,752],{"class":86},[62,4777,2906],{"class":79},[62,4779,4780],{"class":68},"-- {\"a\", \"b\", \"c\", \"d\"} (literal dot split)\n",[45,4782,4784],{"id":4783},"spot-the-bug","🧠 Spot the Bug",[50,4786,4787,4834],{"language":52},[54,4788,4790],{"className":56,"code":4789,"language":52,"meta":58,"style":58},"local s = \"a.b.c\"\nlocal result = s:gsub(\".\", \"X\")\nprint(result)\n",[28,4791,4792,4802,4827],{"__ignoreMap":58},[62,4793,4794,4796,4798,4800],{"class":64,"line":65},[62,4795,76],{"class":75},[62,4797,80],{"class":79},[62,4799,83],{"class":75},[62,4801,739],{"class":86},[62,4803,4804,4806,4808,4810,4812,4814,4816,4818,4820,4822,4825],{"class":64,"line":72},[62,4805,76],{"class":75},[62,4807,1159],{"class":79},[62,4809,83],{"class":75},[62,4811,101],{"class":100},[62,4813,104],{"class":79},[62,4815,918],{"class":107},[62,4817,268],{"class":79},[62,4819,752],{"class":86},[62,4821,317],{"class":79},[62,4823,4824],{"class":86},"\"X\"",[62,4826,1485],{"class":79},[62,4828,4829,4831],{"class":64,"line":90},[62,4830,126],{"class":107},[62,4832,4833],{"class":79},"(result)\n",[4835,4836,4837,4841,4851,4867,4899],"details",{},[4838,4839,4840],"summary",{},"Answer",[15,4842,4843,4844,4847,4848,418],{},"Prints ",[28,4845,4846],{},"XXXXX"," (5 X's), not ",[28,4849,4850],{},"aXbXc",[15,4852,4853,4854,4856,4857,4860,4861,4864,4865,104],{},"In Lua patterns, ",[28,4855,418],{}," means \"any character\", not a literal dot. So ",[28,4858,4859],{},"gsub(\".\", \"X\")"," replaces EVERY character with ",[28,4862,4863],{},"X",". To match a literal dot, escape it: ",[28,4866,4309],{},[54,4868,4870],{"className":56,"code":4869,"language":52,"meta":58,"style":58},"local result = s:gsub(\"%.\", \"X\")  -- \"aXbXc\" (correct)\n",[28,4871,4872],{"__ignoreMap":58},[62,4873,4874,4876,4878,4880,4882,4884,4886,4888,4890,4892,4894,4896],{"class":64,"line":65},[62,4875,76],{"class":75},[62,4877,1159],{"class":79},[62,4879,83],{"class":75},[62,4881,101],{"class":100},[62,4883,104],{"class":79},[62,4885,918],{"class":107},[62,4887,268],{"class":79},[62,4889,4379],{"class":86},[62,4891,317],{"class":79},[62,4893,4824],{"class":86},[62,4895,138],{"class":79},[62,4897,4898],{"class":68},"-- \"aXbXc\" (correct)\n",[15,4900,4901,4902,4905,4906,4908,4909,4911,4912,418],{},"The trap: regex users expect ",[28,4903,4904],{},"\\."," but Lua uses ",[28,4907,4309],{},". The escape character is ",[28,4910,410],{},", not ",[28,4913,4305],{},[4915,4916,4917],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}",{"title":58,"searchDepth":72,"depth":72,"links":4919},[4920,4921,4922,4923,4924],{"id":47,"depth":72,"text":48},{"id":396,"depth":72,"text":397},{"id":1300,"depth":72,"text":1301},{"id":4294,"depth":72,"text":4295},{"id":4783,"depth":72,"text":4784},"Deep-dive into Lua's immutable interned strings, the pattern matching engine (captures, anchors, character classes, frontier), gsub with replacement functions, UTF-8 handling, and production patterns for log parsing, CSV, config files, and binary protocols.","md",{},"\u002Flua\u002F06-string-manipulation",{"title":5,"description":4925},"lua\u002F06-string-manipulation","fL--je-9-6nhqKR1VAkpcLo9_7g2fxN5fs_H1JS7RKA",1789924650317]