[{"data":1,"prerenderedAt":6086},["ShallowReactive",2],{"page-\u002Flua\u002F07-metatables-and-metamethods":3},{"id":4,"title":5,"body":6,"description":6077,"extension":6078,"meta":6079,"navigation":82,"path":6080,"seo":6081,"stem":6084,"__hash__":6085},"content\u002Flua\u002F07-metatables-and-metamethods.md","Lua 07 — Metatables, Metamethods & Proxies",{"type":7,"value":8,"toc":6055},"minimark",[9,14,39,44,1185,1191,2863,2869,3345,3354,3780,3786,5552,5556,5566,5703,5721,5730,5849,5866,5878,5882,6051],[10,11,13],"h1",{"id":12},"_07-metatables-metamethods-proxies","07 — Metatables, Metamethods & Proxies",[15,16,17,18,22,23,26,27,31,32,31,35,38],"p",{},"A ",[19,20,21],"strong",{},"metatable"," is a table attached to another table (or userdata) that defines behavior for operations on that table. The functions in the metatable are ",[19,24,25],{},"metamethods",", keyed by event names (",[28,29,30],"code",{},"__index",", ",[28,33,34],{},"__add",[28,36,37],{},"__eq",", etc.). This is Lua's mechanism for operator overloading, OOP, prototype inheritance, lazy evaluation, property observation, and resource cleanup — all built from one simple primitive.",[40,41,43],"h2",{"id":42},"metatable-basics","Metatable Basics",[45,46,48,227,231,715,721],"code-wrapper",{"language":47},"lua",[49,50,54],"pre",{"className":51,"code":52,"language":47,"meta":53,"style":53},"language-lua shiki shiki-themes github-light github-dark","-- setmetatable(t, mt): attach mt to t. Returns t.\n-- getmetatable(t): returns t's metatable (or nil if none)\n-- rawget\u002Frawset: bypass metamethods entirely\n\nlocal t = {}\nlocal mt = {\n  __tostring = function(self) return \"MyTable\" end,\n  __index = function(self, key) return \"default\" end,\n}\nsetmetatable(t, mt)\nprint(t)           -- \"MyTable\" (print calls tostring → __tostring)\nprint(t.anything)  -- \"default\" (key not in t → __index called)\nrawget(t, \"anything\")  -- nil (bypass __index)\n","",[28,55,56,65,71,77,84,101,114,143,165,171,181,193,210],{"__ignoreMap":53},[57,58,61],"span",{"class":59,"line":60},"line",1,[57,62,64],{"class":63},"sdCPZ","-- setmetatable(t, mt): attach mt to t. Returns t.\n",[57,66,68],{"class":59,"line":67},2,[57,69,70],{"class":63},"-- getmetatable(t): returns t's metatable (or nil if none)\n",[57,72,74],{"class":59,"line":73},3,[57,75,76],{"class":63},"-- rawget\u002Frawset: bypass metamethods entirely\n",[57,78,80],{"class":59,"line":79},4,[57,81,83],{"emptyLinePlaceholder":82},true,"\n",[57,85,87,91,95,98],{"class":59,"line":86},5,[57,88,90],{"class":89},"svdQ7","local",[57,92,94],{"class":93},"ssxIu"," t ",[57,96,97],{"class":89},"=",[57,99,100],{"class":93}," {}\n",[57,102,104,106,109,111],{"class":59,"line":103},6,[57,105,90],{"class":89},[57,107,108],{"class":93}," mt ",[57,110,97],{"class":89},[57,112,113],{"class":93}," {\n",[57,115,117,121,124,127,130,133,137,140],{"class":59,"line":116},7,[57,118,120],{"class":119},"sIsaT","  __tostring",[57,122,123],{"class":89}," =",[57,125,126],{"class":89}," function",[57,128,129],{"class":93},"(self) ",[57,131,132],{"class":89},"return",[57,134,136],{"class":135},"sJ6F3"," \"MyTable\" ",[57,138,139],{"class":89},"end",[57,141,142],{"class":93},",\n",[57,144,146,149,151,153,156,158,161,163],{"class":59,"line":145},8,[57,147,148],{"class":119},"  __index",[57,150,123],{"class":89},[57,152,126],{"class":89},[57,154,155],{"class":93},"(self, key) ",[57,157,132],{"class":89},[57,159,160],{"class":135}," \"default\" ",[57,162,139],{"class":89},[57,164,142],{"class":93},[57,166,168],{"class":59,"line":167},9,[57,169,170],{"class":93},"}\n",[57,172,174,178],{"class":59,"line":173},10,[57,175,177],{"class":176},"snvgF","setmetatable",[57,179,180],{"class":93},"(t, mt)\n",[57,182,184,187,190],{"class":59,"line":183},11,[57,185,186],{"class":176},"print",[57,188,189],{"class":93},"(t)           ",[57,191,192],{"class":63},"-- \"MyTable\" (print calls tostring → __tostring)\n",[57,194,196,198,201,204,207],{"class":59,"line":195},12,[57,197,186],{"class":176},[57,199,200],{"class":93},"(t.",[57,202,203],{"class":119},"anything",[57,205,206],{"class":93},")  ",[57,208,209],{"class":63},"-- \"default\" (key not in t → __index called)\n",[57,211,213,216,219,222,224],{"class":59,"line":212},13,[57,214,215],{"class":176},"rawget",[57,217,218],{"class":93},"(t, ",[57,220,221],{"class":135},"\"anything\"",[57,223,206],{"class":93},[57,225,226],{"class":63},"-- nil (bypass __index)\n",[40,228,230],{"id":229},"full-metamethod-reference","Full Metamethod Reference",[232,233,234,250],"table",{},[235,236,237],"thead",{},[238,239,240,244,247],"tr",{},[241,242,243],"th",{},"Event",[241,245,246],{},"Metamethod",[241,248,249],{},"Triggered by",[251,252,253,276,295,311,327,344,361,378,396,413,430,448,465,482,499,516,533,549,566,583,600,617,635,653,670,683,699],"tbody",{},[238,254,255,259,263],{},[256,257,258],"td",{},"Table access",[256,260,261],{},[28,262,30],{},[256,264,265,268,269,272,273],{},[28,266,267],{},"t[key]"," when ",[28,270,271],{},"key"," not in ",[28,274,275],{},"t",[238,277,278,281,286],{},[256,279,280],{},"Table assignment",[256,282,283],{},[28,284,285],{},"__newindex",[256,287,288,268,291,272,293],{},[28,289,290],{},"t[key] = v",[28,292,271],{},[28,294,275],{},[238,296,297,300,305],{},[256,298,299],{},"Call",[256,301,302],{},[28,303,304],{},"__call",[256,306,307,310],{},[28,308,309],{},"t(args)"," — table used as function",[238,312,313,318,322],{},[256,314,315],{},[28,316,317],{},"+",[256,319,320],{},[28,321,34],{},[256,323,324],{},[28,325,326],{},"a + b",[238,328,329,334,339],{},[256,330,331],{},[28,332,333],{},"-",[256,335,336],{},[28,337,338],{},"__sub",[256,340,341],{},[28,342,343],{},"a - b",[238,345,346,351,356],{},[256,347,348],{},[28,349,350],{},"*",[256,352,353],{},[28,354,355],{},"__mul",[256,357,358],{},[28,359,360],{},"a * b",[238,362,363,368,373],{},[256,364,365],{},[28,366,367],{},"\u002F",[256,369,370],{},[28,371,372],{},"__div",[256,374,375],{},[28,376,377],{},"a \u002F b",[238,379,380,385,390],{},[256,381,382],{},[28,383,384],{},"\u002F\u002F",[256,386,387],{},[28,388,389],{},"__idiv",[256,391,392,395],{},[28,393,394],{},"a \u002F\u002F b"," (Lua 5.3+)",[238,397,398,403,408],{},[256,399,400],{},[28,401,402],{},"%",[256,404,405],{},[28,406,407],{},"__mod",[256,409,410],{},[28,411,412],{},"a % b",[238,414,415,420,425],{},[256,416,417],{},[28,418,419],{},"^",[256,421,422],{},[28,423,424],{},"__pow",[256,426,427],{},[28,428,429],{},"a ^ b",[238,431,432,437,442],{},[256,433,434],{},[28,435,436],{},"unm",[256,438,439],{},[28,440,441],{},"__unm",[256,443,444,447],{},[28,445,446],{},"-a"," (unary minus)",[238,449,450,455,460],{},[256,451,452],{},[28,453,454],{},"&",[256,456,457],{},[28,458,459],{},"__band",[256,461,462,395],{},[28,463,464],{},"a & b",[238,466,467,472,477],{},[256,468,469],{},[28,470,471],{},"|",[256,473,474],{},[28,475,476],{},"__bor",[256,478,479,395],{},[28,480,481],{},"a | b",[238,483,484,489,494],{},[256,485,486],{},[28,487,488],{},"~",[256,490,491],{},[28,492,493],{},"__bxor",[256,495,496,395],{},[28,497,498],{},"a ~ b",[238,500,501,506,511],{},[256,502,503],{},[28,504,505],{},"\u003C\u003C",[256,507,508],{},[28,509,510],{},"__shl",[256,512,513,395],{},[28,514,515],{},"a \u003C\u003C b",[238,517,518,523,528],{},[256,519,520],{},[28,521,522],{},">>",[256,524,525],{},[28,526,527],{},"__shr",[256,529,530,395],{},[28,531,532],{},"a >> b",[238,534,535,540,544],{},[256,536,537],{},[28,538,539],{},"==",[256,541,542],{},[28,543,37],{},[256,545,546],{},[28,547,548],{},"a == b",[238,550,551,556,561],{},[256,552,553],{},[28,554,555],{},"\u003C",[256,557,558],{},[28,559,560],{},"__lt",[256,562,563],{},[28,564,565],{},"a \u003C b",[238,567,568,573,578],{},[256,569,570],{},[28,571,572],{},"\u003C=",[256,574,575],{},[28,576,577],{},"__le",[256,579,580],{},[28,581,582],{},"a \u003C= b",[238,584,585,590,595],{},[256,586,587],{},[28,588,589],{},"..",[256,591,592],{},[28,593,594],{},"__concat",[256,596,597],{},[28,598,599],{},"a .. b",[238,601,602,607,612],{},[256,603,604],{},[28,605,606],{},"#",[256,608,609],{},[28,610,611],{},"__len",[256,613,614],{},[28,615,616],{},"#t",[238,618,619,624,629],{},[256,620,621],{},[28,622,623],{},"pairs",[256,625,626],{},[28,627,628],{},"__pairs",[256,630,631,634],{},[28,632,633],{},"pairs(t)"," (Lua 5.2+)",[238,636,637,642,647],{},[256,638,639],{},[28,640,641],{},"ipairs",[256,643,644],{},[28,645,646],{},"__ipairs",[256,648,649,652],{},[28,650,651],{},"ipairs(t)"," (Lua 5.2+, removed 5.3)",[238,654,655,660,665],{},[256,656,657],{},[28,658,659],{},"tostring",[256,661,662],{},[28,663,664],{},"__tostring",[256,666,667],{},[28,668,669],{},"tostring(t)",[238,671,672,675,680],{},[256,673,674],{},"GC",[256,676,677],{},[28,678,679],{},"__gc",[256,681,682],{},"garbage collection of userdata\u002Ftable (5.4+)",[238,684,685,688,693],{},[256,686,687],{},"Close",[256,689,690],{},[28,691,692],{},"__close",[256,694,695,698],{},[28,696,697],{},"t \u003Cclose>"," goes out of scope (5.4+)",[238,700,701,706,710],{},[256,702,703],{},[28,704,705],{},"__name",[256,707,708],{},[28,709,705],{},[256,711,712,714],{},[28,713,659],{}," for userdata type name (5.4+)",[40,716,718,720],{"id":717},"__index-fallback-lookup",[28,719,30],{}," — Fallback Lookup",[45,722,723,930,938],{"language":47},[49,724,726],{"className":51,"code":725,"language":47,"meta":53,"style":53},"-- __index can be a FUNCTION (called on every miss) or a TABLE (direct lookup)\n-- Table form is faster — no function call overhead\n\n-- As table: used for OOP method inheritance\nlocal Class = {greet = function(self) return \"hi\" end}\nlocal instance = setmetatable({}, {__index = Class})\nprint(instance:greet())  -- \"hi\" (greet not in instance → __index → Class → found)\n\n-- As function: used for lazy computation, defaults, property observation\nlocal lazy_config = setmetatable({}, {\n  __index = function(self, key)\n    print(\"loading:\", key)\n    local val = load_from_disk(key)  -- simulate\n    rawset(self, key, val)  -- cache for next time (avoid __index on repeat)\n    return val\n  end,\n})\n-- First access triggers load; subsequent accesses hit the cached value in self\n",[28,727,728,733,738,742,747,777,797,818,822,827,841,852,865,884,901,910,918,924],{"__ignoreMap":53},[57,729,730],{"class":59,"line":60},[57,731,732],{"class":63},"-- __index can be a FUNCTION (called on every miss) or a TABLE (direct lookup)\n",[57,734,735],{"class":59,"line":67},[57,736,737],{"class":63},"-- Table form is faster — no function call overhead\n",[57,739,740],{"class":59,"line":73},[57,741,83],{"emptyLinePlaceholder":82},[57,743,744],{"class":59,"line":79},[57,745,746],{"class":63},"-- As table: used for OOP method inheritance\n",[57,748,749,751,754,756,759,762,764,766,768,770,773,775],{"class":59,"line":86},[57,750,90],{"class":89},[57,752,753],{"class":93}," Class ",[57,755,97],{"class":89},[57,757,758],{"class":93}," {",[57,760,761],{"class":119},"greet",[57,763,123],{"class":89},[57,765,126],{"class":89},[57,767,129],{"class":93},[57,769,132],{"class":89},[57,771,772],{"class":135}," \"hi\" ",[57,774,139],{"class":89},[57,776,170],{"class":93},[57,778,779,781,784,786,789,792,794],{"class":59,"line":103},[57,780,90],{"class":89},[57,782,783],{"class":93}," instance ",[57,785,97],{"class":89},[57,787,788],{"class":176}," setmetatable",[57,790,791],{"class":93},"({}, {__index ",[57,793,97],{"class":89},[57,795,796],{"class":93}," Class})\n",[57,798,799,801,804,807,810,812,815],{"class":59,"line":116},[57,800,186],{"class":176},[57,802,803],{"class":93},"(",[57,805,806],{"class":119},"instance",[57,808,809],{"class":93},":",[57,811,761],{"class":176},[57,813,814],{"class":93},"())  ",[57,816,817],{"class":63},"-- \"hi\" (greet not in instance → __index → Class → found)\n",[57,819,820],{"class":59,"line":145},[57,821,83],{"emptyLinePlaceholder":82},[57,823,824],{"class":59,"line":167},[57,825,826],{"class":63},"-- As function: used for lazy computation, defaults, property observation\n",[57,828,829,831,834,836,838],{"class":59,"line":173},[57,830,90],{"class":89},[57,832,833],{"class":93}," lazy_config ",[57,835,97],{"class":89},[57,837,788],{"class":176},[57,839,840],{"class":93},"({}, {\n",[57,842,843,845,847,849],{"class":59,"line":183},[57,844,148],{"class":119},[57,846,123],{"class":89},[57,848,126],{"class":89},[57,850,851],{"class":93},"(self, key)\n",[57,853,854,857,859,862],{"class":59,"line":195},[57,855,856],{"class":176},"    print",[57,858,803],{"class":93},[57,860,861],{"class":135},"\"loading:\"",[57,863,864],{"class":93},", key)\n",[57,866,867,870,873,875,878,881],{"class":59,"line":212},[57,868,869],{"class":89},"    local",[57,871,872],{"class":93}," val ",[57,874,97],{"class":89},[57,876,877],{"class":176}," load_from_disk",[57,879,880],{"class":93},"(key)  ",[57,882,883],{"class":63},"-- simulate\n",[57,885,887,890,892,895,898],{"class":59,"line":886},14,[57,888,889],{"class":176},"    rawset",[57,891,803],{"class":93},[57,893,894],{"class":176},"self",[57,896,897],{"class":93},", key, val)  ",[57,899,900],{"class":63},"-- cache for next time (avoid __index on repeat)\n",[57,902,904,907],{"class":59,"line":903},15,[57,905,906],{"class":89},"    return",[57,908,909],{"class":93}," val\n",[57,911,913,916],{"class":59,"line":912},16,[57,914,915],{"class":89},"  end",[57,917,142],{"class":93},[57,919,921],{"class":59,"line":920},17,[57,922,923],{"class":93},"})\n",[57,925,927],{"class":59,"line":926},18,[57,928,929],{"class":63},"-- First access triggers load; subsequent accesses hit the cached value in self\n",[931,932,934,935,937],"h3",{"id":933},"chained-__index-for-multi-level-inheritance","Chained ",[28,936,30],{}," for Multi-Level Inheritance",[45,939,940],{"language":47},[49,941,943],{"className":51,"code":942,"language":47,"meta":53,"style":53},"-- A → B → C: each class's __index points to its parent\nlocal A = {a_method = function() return \"A\" end}\nA.__index = A\n\nlocal B = setmetatable({b_method = function() return \"B\" end}, {__index = A})\nB.__index = B\n\nlocal C = setmetatable({c_method = function() return \"C\" end}, {__index = B})\nC.__index = C\n\nlocal instance = setmetatable({}, C)\nprint(instance:a_method())  -- \"A\" (lookup: instance → C → B → A → found)\nprint(instance:b_method())  -- \"B\" (lookup: instance → C → B → found)\nprint(instance:c_method())  -- \"C\" (lookup: instance → C → found)\nprint(instance:missing())   -- error: attempt to call nil value (chain exhausted)\n",[28,944,945,950,980,992,996,1034,1046,1050,1086,1098,1102,1115,1132,1149,1166],{"__ignoreMap":53},[57,946,947],{"class":59,"line":60},[57,948,949],{"class":63},"-- A → B → C: each class's __index points to its parent\n",[57,951,952,954,957,959,961,964,966,968,971,973,976,978],{"class":59,"line":67},[57,953,90],{"class":89},[57,955,956],{"class":93}," A ",[57,958,97],{"class":89},[57,960,758],{"class":93},[57,962,963],{"class":119},"a_method",[57,965,123],{"class":89},[57,967,126],{"class":89},[57,969,970],{"class":93},"() ",[57,972,132],{"class":89},[57,974,975],{"class":135}," \"A\" ",[57,977,139],{"class":89},[57,979,170],{"class":93},[57,981,982,985,987,989],{"class":59,"line":73},[57,983,984],{"class":93},"A.",[57,986,30],{"class":119},[57,988,123],{"class":89},[57,990,991],{"class":93}," A\n",[57,993,994],{"class":59,"line":79},[57,995,83],{"emptyLinePlaceholder":82},[57,997,998,1000,1003,1005,1007,1010,1013,1015,1017,1019,1021,1024,1026,1029,1031],{"class":59,"line":86},[57,999,90],{"class":89},[57,1001,1002],{"class":93}," B ",[57,1004,97],{"class":89},[57,1006,788],{"class":176},[57,1008,1009],{"class":93},"({",[57,1011,1012],{"class":119},"b_method",[57,1014,123],{"class":89},[57,1016,126],{"class":89},[57,1018,970],{"class":93},[57,1020,132],{"class":89},[57,1022,1023],{"class":135}," \"B\" ",[57,1025,139],{"class":89},[57,1027,1028],{"class":93},"}, {__index ",[57,1030,97],{"class":89},[57,1032,1033],{"class":93}," A})\n",[57,1035,1036,1039,1041,1043],{"class":59,"line":103},[57,1037,1038],{"class":93},"B.",[57,1040,30],{"class":119},[57,1042,123],{"class":89},[57,1044,1045],{"class":93}," B\n",[57,1047,1048],{"class":59,"line":116},[57,1049,83],{"emptyLinePlaceholder":82},[57,1051,1052,1054,1057,1059,1061,1063,1066,1068,1070,1072,1074,1077,1079,1081,1083],{"class":59,"line":145},[57,1053,90],{"class":89},[57,1055,1056],{"class":93}," C ",[57,1058,97],{"class":89},[57,1060,788],{"class":176},[57,1062,1009],{"class":93},[57,1064,1065],{"class":119},"c_method",[57,1067,123],{"class":89},[57,1069,126],{"class":89},[57,1071,970],{"class":93},[57,1073,132],{"class":89},[57,1075,1076],{"class":135}," \"C\" ",[57,1078,139],{"class":89},[57,1080,1028],{"class":93},[57,1082,97],{"class":89},[57,1084,1085],{"class":93}," B})\n",[57,1087,1088,1091,1093,1095],{"class":59,"line":167},[57,1089,1090],{"class":93},"C.",[57,1092,30],{"class":119},[57,1094,123],{"class":89},[57,1096,1097],{"class":93}," C\n",[57,1099,1100],{"class":59,"line":173},[57,1101,83],{"emptyLinePlaceholder":82},[57,1103,1104,1106,1108,1110,1112],{"class":59,"line":183},[57,1105,90],{"class":89},[57,1107,783],{"class":93},[57,1109,97],{"class":89},[57,1111,788],{"class":176},[57,1113,1114],{"class":93},"({}, C)\n",[57,1116,1117,1119,1121,1123,1125,1127,1129],{"class":59,"line":195},[57,1118,186],{"class":176},[57,1120,803],{"class":93},[57,1122,806],{"class":119},[57,1124,809],{"class":93},[57,1126,963],{"class":176},[57,1128,814],{"class":93},[57,1130,1131],{"class":63},"-- \"A\" (lookup: instance → C → B → A → found)\n",[57,1133,1134,1136,1138,1140,1142,1144,1146],{"class":59,"line":212},[57,1135,186],{"class":176},[57,1137,803],{"class":93},[57,1139,806],{"class":119},[57,1141,809],{"class":93},[57,1143,1012],{"class":176},[57,1145,814],{"class":93},[57,1147,1148],{"class":63},"-- \"B\" (lookup: instance → C → B → found)\n",[57,1150,1151,1153,1155,1157,1159,1161,1163],{"class":59,"line":886},[57,1152,186],{"class":176},[57,1154,803],{"class":93},[57,1156,806],{"class":119},[57,1158,809],{"class":93},[57,1160,1065],{"class":176},[57,1162,814],{"class":93},[57,1164,1165],{"class":63},"-- \"C\" (lookup: instance → C → found)\n",[57,1167,1168,1170,1172,1174,1176,1179,1182],{"class":59,"line":903},[57,1169,186],{"class":176},[57,1171,803],{"class":93},[57,1173,806],{"class":119},[57,1175,809],{"class":93},[57,1177,1178],{"class":176},"missing",[57,1180,1181],{"class":93},"())   ",[57,1183,1184],{"class":63},"-- error: attempt to call nil value (chain exhausted)\n",[40,1186,1188,1190],{"id":1187},"__newindex-intercept-assignment",[28,1189,285],{}," — Intercept Assignment",[45,1192,1193,1313,1317,1578,1582,1782,1786],{"language":47},[49,1194,1196],{"className":51,"code":1195,"language":47,"meta":53,"style":53},"-- __newindex is called ONLY when the key doesn't exist in the table\n-- If key exists, __newindex is NOT called — direct assignment happens\n-- Use rawset inside __newindex to actually set (avoid infinite recursion)\n\nlocal observed = setmetatable({}, {\n  __newindex = function(self, key, value)\n    print(string.format(\"SET %s = %s\", key, value))\n    rawset(self, key, value)  -- actually store it; rawset bypasses __newindex\n  end,\n})\nobserved.x = 10  -- prints \"SET x = 10\"\nobserved.x = 20  -- NO print (key x already exists → __newindex skipped)\n",[28,1197,1198,1203,1208,1213,1217,1230,1242,1259,1273,1279,1283,1299],{"__ignoreMap":53},[57,1199,1200],{"class":59,"line":60},[57,1201,1202],{"class":63},"-- __newindex is called ONLY when the key doesn't exist in the table\n",[57,1204,1205],{"class":59,"line":67},[57,1206,1207],{"class":63},"-- If key exists, __newindex is NOT called — direct assignment happens\n",[57,1209,1210],{"class":59,"line":73},[57,1211,1212],{"class":63},"-- Use rawset inside __newindex to actually set (avoid infinite recursion)\n",[57,1214,1215],{"class":59,"line":79},[57,1216,83],{"emptyLinePlaceholder":82},[57,1218,1219,1221,1224,1226,1228],{"class":59,"line":86},[57,1220,90],{"class":89},[57,1222,1223],{"class":93}," observed ",[57,1225,97],{"class":89},[57,1227,788],{"class":176},[57,1229,840],{"class":93},[57,1231,1232,1235,1237,1239],{"class":59,"line":103},[57,1233,1234],{"class":119},"  __newindex",[57,1236,123],{"class":89},[57,1238,126],{"class":89},[57,1240,1241],{"class":93},"(self, key, value)\n",[57,1243,1244,1246,1248,1251,1253,1256],{"class":59,"line":116},[57,1245,856],{"class":176},[57,1247,803],{"class":93},[57,1249,1250],{"class":176},"string.format",[57,1252,803],{"class":93},[57,1254,1255],{"class":135},"\"SET %s = %s\"",[57,1257,1258],{"class":93},", key, value))\n",[57,1260,1261,1263,1265,1267,1270],{"class":59,"line":145},[57,1262,889],{"class":176},[57,1264,803],{"class":93},[57,1266,894],{"class":176},[57,1268,1269],{"class":93},", key, value)  ",[57,1271,1272],{"class":63},"-- actually store it; rawset bypasses __newindex\n",[57,1274,1275,1277],{"class":59,"line":167},[57,1276,915],{"class":89},[57,1278,142],{"class":93},[57,1280,1281],{"class":59,"line":173},[57,1282,923],{"class":93},[57,1284,1285,1288,1291,1293,1296],{"class":59,"line":183},[57,1286,1287],{"class":93},"observed.",[57,1289,1290],{"class":119},"x",[57,1292,123],{"class":89},[57,1294,1295],{"class":176}," 10",[57,1297,1298],{"class":63},"  -- prints \"SET x = 10\"\n",[57,1300,1301,1303,1305,1307,1310],{"class":59,"line":195},[57,1302,1287],{"class":93},[57,1304,1290],{"class":119},[57,1306,123],{"class":89},[57,1308,1309],{"class":176}," 20",[57,1311,1312],{"class":63},"  -- NO print (key x already exists → __newindex skipped)\n",[931,1314,1316],{"id":1315},"read-only-proxy-pattern","Read-Only Proxy Pattern",[45,1318,1319],{"language":47},[49,1320,1322],{"className":51,"code":1321,"language":47,"meta":53,"style":53},"-- Proxy: an empty table with a metatable that forwards reads to an internal table\n-- and blocks writes (or logs them). The real data is hidden in a closure.\nlocal function readonly(t)\n  local proxy = {}  -- empty — all access goes through metatable\n  local mt = {\n    __index = function(self, k) return t[k] end,      -- read: forward to real table\n    __newindex = function(self, k, v)\n      error(\"attempt to modify read-only table: \" .. tostring(k), 2)\n    end,\n    __pairs = function(self) return pairs(t) end,     -- iteration: forward\n    __len = function(self) return #t end,             -- length: forward\n  }\n  return setmetatable(proxy, mt)\nend\n\nlocal config = readonly({host = \"localhost\", port = 8080})\nprint(config.host)  -- \"localhost\"\nconfig.port = 9090  -- ERROR: attempt to modify read-only table: port\n",[28,1323,1324,1329,1334,1346,1362,1372,1397,1409,1433,1440,1467,1494,1499,1509,1514,1518,1547,1562],{"__ignoreMap":53},[57,1325,1326],{"class":59,"line":60},[57,1327,1328],{"class":63},"-- Proxy: an empty table with a metatable that forwards reads to an internal table\n",[57,1330,1331],{"class":59,"line":67},[57,1332,1333],{"class":63},"-- and blocks writes (or logs them). The real data is hidden in a closure.\n",[57,1335,1336,1338,1340,1343],{"class":59,"line":73},[57,1337,90],{"class":89},[57,1339,126],{"class":89},[57,1341,1342],{"class":119}," readonly",[57,1344,1345],{"class":93},"(t)\n",[57,1347,1348,1351,1354,1356,1359],{"class":59,"line":79},[57,1349,1350],{"class":89},"  local",[57,1352,1353],{"class":93}," proxy ",[57,1355,97],{"class":89},[57,1357,1358],{"class":93}," {}  ",[57,1360,1361],{"class":63},"-- empty — all access goes through metatable\n",[57,1363,1364,1366,1368,1370],{"class":59,"line":86},[57,1365,1350],{"class":89},[57,1367,108],{"class":93},[57,1369,97],{"class":89},[57,1371,113],{"class":93},[57,1373,1374,1377,1379,1381,1384,1386,1389,1391,1394],{"class":59,"line":103},[57,1375,1376],{"class":119},"    __index",[57,1378,123],{"class":89},[57,1380,126],{"class":89},[57,1382,1383],{"class":93},"(self, k) ",[57,1385,132],{"class":89},[57,1387,1388],{"class":93}," t[k] ",[57,1390,139],{"class":89},[57,1392,1393],{"class":93},",      ",[57,1395,1396],{"class":63},"-- read: forward to real table\n",[57,1398,1399,1402,1404,1406],{"class":59,"line":116},[57,1400,1401],{"class":119},"    __newindex",[57,1403,123],{"class":89},[57,1405,126],{"class":89},[57,1407,1408],{"class":93},"(self, k, v)\n",[57,1410,1411,1414,1416,1419,1421,1424,1427,1430],{"class":59,"line":145},[57,1412,1413],{"class":176},"      error",[57,1415,803],{"class":93},[57,1417,1418],{"class":135},"\"attempt to modify read-only table: \" ",[57,1420,589],{"class":89},[57,1422,1423],{"class":176}," tostring",[57,1425,1426],{"class":93},"(k), ",[57,1428,1429],{"class":176},"2",[57,1431,1432],{"class":93},")\n",[57,1434,1435,1438],{"class":59,"line":167},[57,1436,1437],{"class":89},"    end",[57,1439,142],{"class":93},[57,1441,1442,1445,1447,1449,1451,1453,1456,1459,1461,1464],{"class":59,"line":173},[57,1443,1444],{"class":119},"    __pairs",[57,1446,123],{"class":89},[57,1448,126],{"class":89},[57,1450,129],{"class":93},[57,1452,132],{"class":89},[57,1454,1455],{"class":176}," pairs",[57,1457,1458],{"class":93},"(t) ",[57,1460,139],{"class":89},[57,1462,1463],{"class":93},",     ",[57,1465,1466],{"class":63},"-- iteration: forward\n",[57,1468,1469,1472,1474,1476,1478,1480,1483,1486,1488,1491],{"class":59,"line":183},[57,1470,1471],{"class":119},"    __len",[57,1473,123],{"class":89},[57,1475,126],{"class":89},[57,1477,129],{"class":93},[57,1479,132],{"class":89},[57,1481,1482],{"class":89}," #",[57,1484,1485],{"class":93},"t ",[57,1487,139],{"class":89},[57,1489,1490],{"class":93},",             ",[57,1492,1493],{"class":63},"-- length: forward\n",[57,1495,1496],{"class":59,"line":195},[57,1497,1498],{"class":93},"  }\n",[57,1500,1501,1504,1506],{"class":59,"line":212},[57,1502,1503],{"class":89},"  return",[57,1505,788],{"class":176},[57,1507,1508],{"class":93},"(proxy, mt)\n",[57,1510,1511],{"class":59,"line":886},[57,1512,1513],{"class":89},"end\n",[57,1515,1516],{"class":59,"line":903},[57,1517,83],{"emptyLinePlaceholder":82},[57,1519,1520,1522,1525,1527,1529,1532,1534,1537,1540,1542,1545],{"class":59,"line":912},[57,1521,90],{"class":89},[57,1523,1524],{"class":93}," config ",[57,1526,97],{"class":89},[57,1528,1342],{"class":176},[57,1530,1531],{"class":93},"({host ",[57,1533,97],{"class":89},[57,1535,1536],{"class":135}," \"localhost\"",[57,1538,1539],{"class":93},", port ",[57,1541,97],{"class":89},[57,1543,1544],{"class":176}," 8080",[57,1546,923],{"class":93},[57,1548,1549,1551,1554,1557,1559],{"class":59,"line":920},[57,1550,186],{"class":176},[57,1552,1553],{"class":93},"(config.",[57,1555,1556],{"class":119},"host",[57,1558,206],{"class":93},[57,1560,1561],{"class":63},"-- \"localhost\"\n",[57,1563,1564,1567,1570,1572,1575],{"class":59,"line":926},[57,1565,1566],{"class":93},"config.",[57,1568,1569],{"class":119},"port",[57,1571,123],{"class":89},[57,1573,1574],{"class":176}," 9090",[57,1576,1577],{"class":63},"  -- ERROR: attempt to modify read-only table: port\n",[931,1579,1581],{"id":1580},"logging-proxy-audit-all-access","Logging Proxy (Audit All Access)",[45,1583,1584],{"language":47},[49,1585,1587],{"className":51,"code":1586,"language":47,"meta":53,"style":53},"local function logged_table(name)\n  local real = {}\n  return setmetatable({}, {\n    __index = function(self, k)\n      local v = real[k]\n      print(string.format(\"[%s] READ %s = %s\", name, k, v))\n      return v\n    end,\n    __newindex = function(self, k, v)\n      print(string.format(\"[%s] WRITE %s = %s\", name, k, v))\n      real[k] = v  -- store in the hidden table (not self — would cause recursion)\n    end,\n  })\nend\n\nlocal store = logged_table(\"DB\")\nstore.x = 1  -- [DB] WRITE x = 1\nlocal _ = store.x  -- [DB] READ x = 1\n",[28,1588,1589,1601,1612,1620,1631,1644,1661,1669,1675,1685,1700,1713,1719,1724,1728,1732,1750,1765],{"__ignoreMap":53},[57,1590,1591,1593,1595,1598],{"class":59,"line":60},[57,1592,90],{"class":89},[57,1594,126],{"class":89},[57,1596,1597],{"class":119}," logged_table",[57,1599,1600],{"class":93},"(name)\n",[57,1602,1603,1605,1608,1610],{"class":59,"line":67},[57,1604,1350],{"class":89},[57,1606,1607],{"class":93}," real ",[57,1609,97],{"class":89},[57,1611,100],{"class":93},[57,1613,1614,1616,1618],{"class":59,"line":73},[57,1615,1503],{"class":89},[57,1617,788],{"class":176},[57,1619,840],{"class":93},[57,1621,1622,1624,1626,1628],{"class":59,"line":79},[57,1623,1376],{"class":119},[57,1625,123],{"class":89},[57,1627,126],{"class":89},[57,1629,1630],{"class":93},"(self, k)\n",[57,1632,1633,1636,1639,1641],{"class":59,"line":86},[57,1634,1635],{"class":89},"      local",[57,1637,1638],{"class":93}," v ",[57,1640,97],{"class":89},[57,1642,1643],{"class":93}," real[k]\n",[57,1645,1646,1649,1651,1653,1655,1658],{"class":59,"line":103},[57,1647,1648],{"class":176},"      print",[57,1650,803],{"class":93},[57,1652,1250],{"class":176},[57,1654,803],{"class":93},[57,1656,1657],{"class":135},"\"[%s] READ %s = %s\"",[57,1659,1660],{"class":93},", name, k, v))\n",[57,1662,1663,1666],{"class":59,"line":116},[57,1664,1665],{"class":89},"      return",[57,1667,1668],{"class":93}," v\n",[57,1670,1671,1673],{"class":59,"line":145},[57,1672,1437],{"class":89},[57,1674,142],{"class":93},[57,1676,1677,1679,1681,1683],{"class":59,"line":167},[57,1678,1401],{"class":119},[57,1680,123],{"class":89},[57,1682,126],{"class":89},[57,1684,1408],{"class":93},[57,1686,1687,1689,1691,1693,1695,1698],{"class":59,"line":173},[57,1688,1648],{"class":176},[57,1690,803],{"class":93},[57,1692,1250],{"class":176},[57,1694,803],{"class":93},[57,1696,1697],{"class":135},"\"[%s] WRITE %s = %s\"",[57,1699,1660],{"class":93},[57,1701,1702,1705,1707,1710],{"class":59,"line":183},[57,1703,1704],{"class":93},"      real[k] ",[57,1706,97],{"class":89},[57,1708,1709],{"class":93}," v  ",[57,1711,1712],{"class":63},"-- store in the hidden table (not self — would cause recursion)\n",[57,1714,1715,1717],{"class":59,"line":195},[57,1716,1437],{"class":89},[57,1718,142],{"class":93},[57,1720,1721],{"class":59,"line":212},[57,1722,1723],{"class":93},"  })\n",[57,1725,1726],{"class":59,"line":886},[57,1727,1513],{"class":89},[57,1729,1730],{"class":59,"line":903},[57,1731,83],{"emptyLinePlaceholder":82},[57,1733,1734,1736,1739,1741,1743,1745,1748],{"class":59,"line":912},[57,1735,90],{"class":89},[57,1737,1738],{"class":93}," store ",[57,1740,97],{"class":89},[57,1742,1597],{"class":176},[57,1744,803],{"class":93},[57,1746,1747],{"class":135},"\"DB\"",[57,1749,1432],{"class":93},[57,1751,1752,1755,1757,1759,1762],{"class":59,"line":920},[57,1753,1754],{"class":93},"store.",[57,1756,1290],{"class":119},[57,1758,123],{"class":89},[57,1760,1761],{"class":176}," 1",[57,1763,1764],{"class":63},"  -- [DB] WRITE x = 1\n",[57,1766,1767,1769,1772,1774,1777,1779],{"class":59,"line":926},[57,1768,90],{"class":89},[57,1770,1771],{"class":93}," _ ",[57,1773,97],{"class":89},[57,1775,1776],{"class":93}," store.",[57,1778,1290],{"class":119},[57,1780,1781],{"class":63},"  -- [DB] READ x = 1\n",[40,1783,1785],{"id":1784},"operator-overloading-math-types","Operator Overloading: Math Types",[45,1787,1789,2663,2670],{"language":47,"filename":1788},"vector.lua",[49,1790,1792],{"className":51,"code":1791,"language":47,"meta":53,"style":53},"-- Production: 2D vector with full operator set\nlocal Vector = {}\nVector.__index = Vector\nVector.__tostring = function(v) return string.format(\"(%g, %g)\", v.x, v.y) end\n\nfunction Vector.new(x, y)\n  return setmetatable({x = x or 0, y = y or 0}, Vector)\nend\n\n-- Arithmetic: return new Vector (immutable style)\nVector.__add  = function(a, b) return Vector.new(a.x + b.x, a.y + b.y) end\nVector.__sub  = function(a, b) return Vector.new(a.x - b.x, a.y - b.y) end\nVector.__mul  = function(a, s)\n  if type(s) == \"number\" then return Vector.new(a.x * s, a.y * s) end  -- scalar mult\n  return Vector.new(a.x * s.x, a.y * s.y)  -- component-wise (if both vectors)\nend\nVector.__div  = function(a, s) return Vector.new(a.x \u002F s, a.y \u002F s) end\nVector.__unm  = function(a) return Vector.new(-a.x, -a.y) end\nVector.__eq   = function(a, b) return a.x == b.x and a.y == b.y end\nVector.__lt   = function(a, b) return (a.x^2 + a.y^2) \u003C (b.x^2 + b.y^2) end  -- by magnitude\nVector.__le   = function(a, b) return (a.x^2 + a.y^2) \u003C= (b.x^2 + b.y^2) end\n\n-- Metamethods for mixed-type operations (commutativity)\n-- __mul handles both Vector * scalar and scalar * Vector? NO — __mul is only called\n-- when the LEFT operand has the metamethod. For scalar * Vector, we need __mul on\n-- the number's metatable — but we can't modify numbers. Solution: only support\n-- Vector * scalar, document it.\n\nlocal v1 = Vector.new(1, 2)\nlocal v2 = Vector.new(3, 4)\nprint(v1 + v2)   -- (4, 6)\nprint(v2 - v1)  -- (2, 2)\nprint(v1 * 3)    -- (3, 6)\nprint(-v1)       -- (-1, -2)\nprint(v1 == Vector.new(1, 2))  -- true\nprint(v1 \u003C v2)   -- true (magnitude: 5 \u003C 25)\n\n-- Sorting vectors by magnitude\nlocal vecs = {Vector.new(3, 4), Vector.new(1, 1), Vector.new(0, 1)}\ntable.sort(vecs)  -- uses __lt: {0,1}, {1,1}, {3,4}\nfor _, v in ipairs(vecs) do print(v) end\n",[28,1793,1794,1799,1810,1822,1860,1864,1881,1916,1920,1924,1929,1978,2021,2034,2082,2116,2120,2156,2196,2240,2304,2363,2368,2374,2380,2386,2392,2398,2403,2428,2454,2470,2486,2504,2519,2546,2560,2565,2571,2623,2635],{"__ignoreMap":53},[57,1795,1796],{"class":59,"line":60},[57,1797,1798],{"class":63},"-- Production: 2D vector with full operator set\n",[57,1800,1801,1803,1806,1808],{"class":59,"line":67},[57,1802,90],{"class":89},[57,1804,1805],{"class":93}," Vector ",[57,1807,97],{"class":89},[57,1809,100],{"class":93},[57,1811,1812,1815,1817,1819],{"class":59,"line":73},[57,1813,1814],{"class":93},"Vector.",[57,1816,30],{"class":119},[57,1818,123],{"class":89},[57,1820,1821],{"class":93}," Vector\n",[57,1823,1824,1826,1828,1830,1832,1835,1837,1840,1842,1845,1848,1850,1852,1855,1858],{"class":59,"line":79},[57,1825,1814],{"class":93},[57,1827,664],{"class":119},[57,1829,123],{"class":89},[57,1831,126],{"class":89},[57,1833,1834],{"class":93},"(v) ",[57,1836,132],{"class":89},[57,1838,1839],{"class":176}," string.format",[57,1841,803],{"class":93},[57,1843,1844],{"class":135},"\"(%g, %g)\"",[57,1846,1847],{"class":93},", v.",[57,1849,1290],{"class":119},[57,1851,1847],{"class":93},[57,1853,1854],{"class":119},"y",[57,1856,1857],{"class":93},") ",[57,1859,1513],{"class":89},[57,1861,1862],{"class":59,"line":86},[57,1863,83],{"emptyLinePlaceholder":82},[57,1865,1866,1869,1872,1875,1878],{"class":59,"line":103},[57,1867,1868],{"class":89},"function",[57,1870,1871],{"class":119}," Vector",[57,1873,1874],{"class":93},".",[57,1876,1877],{"class":119},"new",[57,1879,1880],{"class":93},"(x, y)\n",[57,1882,1883,1885,1887,1890,1892,1895,1898,1901,1904,1906,1909,1911,1913],{"class":59,"line":116},[57,1884,1503],{"class":89},[57,1886,788],{"class":176},[57,1888,1889],{"class":93},"({x ",[57,1891,97],{"class":89},[57,1893,1894],{"class":93}," x ",[57,1896,1897],{"class":89},"or",[57,1899,1900],{"class":176}," 0",[57,1902,1903],{"class":93},", y ",[57,1905,97],{"class":89},[57,1907,1908],{"class":93}," y ",[57,1910,1897],{"class":89},[57,1912,1900],{"class":176},[57,1914,1915],{"class":93},"}, Vector)\n",[57,1917,1918],{"class":59,"line":145},[57,1919,1513],{"class":89},[57,1921,1922],{"class":59,"line":167},[57,1923,83],{"emptyLinePlaceholder":82},[57,1925,1926],{"class":59,"line":173},[57,1927,1928],{"class":63},"-- Arithmetic: return new Vector (immutable style)\n",[57,1930,1931,1933,1935,1938,1940,1943,1945,1948,1950,1953,1955,1958,1961,1963,1966,1968,1970,1972,1974,1976],{"class":59,"line":183},[57,1932,1814],{"class":93},[57,1934,34],{"class":119},[57,1936,1937],{"class":89},"  =",[57,1939,126],{"class":89},[57,1941,1942],{"class":93},"(a, b) ",[57,1944,132],{"class":89},[57,1946,1947],{"class":93}," Vector.",[57,1949,1877],{"class":176},[57,1951,1952],{"class":93},"(a.",[57,1954,1290],{"class":119},[57,1956,1957],{"class":89}," +",[57,1959,1960],{"class":93}," b.",[57,1962,1290],{"class":119},[57,1964,1965],{"class":93},", a.",[57,1967,1854],{"class":119},[57,1969,1957],{"class":89},[57,1971,1960],{"class":93},[57,1973,1854],{"class":119},[57,1975,1857],{"class":93},[57,1977,1513],{"class":89},[57,1979,1980,1982,1984,1986,1988,1990,1992,1994,1996,1998,2000,2003,2005,2007,2009,2011,2013,2015,2017,2019],{"class":59,"line":195},[57,1981,1814],{"class":93},[57,1983,338],{"class":119},[57,1985,1937],{"class":89},[57,1987,126],{"class":89},[57,1989,1942],{"class":93},[57,1991,132],{"class":89},[57,1993,1947],{"class":93},[57,1995,1877],{"class":176},[57,1997,1952],{"class":93},[57,1999,1290],{"class":119},[57,2001,2002],{"class":89}," -",[57,2004,1960],{"class":93},[57,2006,1290],{"class":119},[57,2008,1965],{"class":93},[57,2010,1854],{"class":119},[57,2012,2002],{"class":89},[57,2014,1960],{"class":93},[57,2016,1854],{"class":119},[57,2018,1857],{"class":93},[57,2020,1513],{"class":89},[57,2022,2023,2025,2027,2029,2031],{"class":59,"line":212},[57,2024,1814],{"class":93},[57,2026,355],{"class":119},[57,2028,1937],{"class":89},[57,2030,126],{"class":89},[57,2032,2033],{"class":93},"(a, s)\n",[57,2035,2036,2039,2042,2045,2047,2050,2053,2056,2058,2060,2062,2064,2067,2070,2072,2074,2077,2079],{"class":59,"line":886},[57,2037,2038],{"class":89},"  if",[57,2040,2041],{"class":176}," type",[57,2043,2044],{"class":93},"(s) ",[57,2046,539],{"class":89},[57,2048,2049],{"class":135}," \"number\" ",[57,2051,2052],{"class":89},"then",[57,2054,2055],{"class":89}," return",[57,2057,1947],{"class":93},[57,2059,1877],{"class":176},[57,2061,1952],{"class":93},[57,2063,1290],{"class":119},[57,2065,2066],{"class":89}," *",[57,2068,2069],{"class":93}," s, a.",[57,2071,1854],{"class":119},[57,2073,2066],{"class":89},[57,2075,2076],{"class":93}," s) ",[57,2078,139],{"class":89},[57,2080,2081],{"class":63},"  -- scalar mult\n",[57,2083,2084,2086,2088,2090,2092,2094,2096,2099,2101,2103,2105,2107,2109,2111,2113],{"class":59,"line":903},[57,2085,1503],{"class":89},[57,2087,1947],{"class":93},[57,2089,1877],{"class":176},[57,2091,1952],{"class":93},[57,2093,1290],{"class":119},[57,2095,2066],{"class":89},[57,2097,2098],{"class":93}," s.",[57,2100,1290],{"class":119},[57,2102,1965],{"class":93},[57,2104,1854],{"class":119},[57,2106,2066],{"class":89},[57,2108,2098],{"class":93},[57,2110,1854],{"class":119},[57,2112,206],{"class":93},[57,2114,2115],{"class":63},"-- component-wise (if both vectors)\n",[57,2117,2118],{"class":59,"line":912},[57,2119,1513],{"class":89},[57,2121,2122,2124,2126,2128,2130,2133,2135,2137,2139,2141,2143,2146,2148,2150,2152,2154],{"class":59,"line":920},[57,2123,1814],{"class":93},[57,2125,372],{"class":119},[57,2127,1937],{"class":89},[57,2129,126],{"class":89},[57,2131,2132],{"class":93},"(a, s) ",[57,2134,132],{"class":89},[57,2136,1947],{"class":93},[57,2138,1877],{"class":176},[57,2140,1952],{"class":93},[57,2142,1290],{"class":119},[57,2144,2145],{"class":89}," \u002F",[57,2147,2069],{"class":93},[57,2149,1854],{"class":119},[57,2151,2145],{"class":89},[57,2153,2076],{"class":93},[57,2155,1513],{"class":89},[57,2157,2158,2160,2162,2164,2166,2169,2171,2173,2175,2177,2179,2182,2184,2186,2188,2190,2192,2194],{"class":59,"line":926},[57,2159,1814],{"class":93},[57,2161,441],{"class":119},[57,2163,1937],{"class":89},[57,2165,126],{"class":89},[57,2167,2168],{"class":93},"(a) ",[57,2170,132],{"class":89},[57,2172,1947],{"class":93},[57,2174,1877],{"class":176},[57,2176,803],{"class":93},[57,2178,333],{"class":89},[57,2180,2181],{"class":93},"a.",[57,2183,1290],{"class":119},[57,2185,31],{"class":93},[57,2187,333],{"class":89},[57,2189,2181],{"class":93},[57,2191,1854],{"class":119},[57,2193,1857],{"class":93},[57,2195,1513],{"class":89},[57,2197,2199,2201,2203,2206,2208,2210,2212,2215,2217,2220,2222,2224,2227,2229,2231,2233,2235,2237],{"class":59,"line":2198},19,[57,2200,1814],{"class":93},[57,2202,37],{"class":119},[57,2204,2205],{"class":89},"   =",[57,2207,126],{"class":89},[57,2209,1942],{"class":93},[57,2211,132],{"class":89},[57,2213,2214],{"class":93}," a.",[57,2216,1290],{"class":119},[57,2218,2219],{"class":89}," ==",[57,2221,1960],{"class":93},[57,2223,1290],{"class":119},[57,2225,2226],{"class":89}," and",[57,2228,2214],{"class":93},[57,2230,1854],{"class":119},[57,2232,2219],{"class":89},[57,2234,1960],{"class":93},[57,2236,1854],{"class":119},[57,2238,2239],{"class":89}," end\n",[57,2241,2243,2245,2247,2249,2251,2253,2255,2258,2260,2262,2264,2266,2268,2270,2272,2274,2276,2278,2281,2283,2285,2287,2289,2291,2293,2295,2297,2299,2301],{"class":59,"line":2242},20,[57,2244,1814],{"class":93},[57,2246,560],{"class":119},[57,2248,2205],{"class":89},[57,2250,126],{"class":89},[57,2252,1942],{"class":93},[57,2254,132],{"class":89},[57,2256,2257],{"class":93}," (a.",[57,2259,1290],{"class":119},[57,2261,419],{"class":89},[57,2263,1429],{"class":176},[57,2265,1957],{"class":89},[57,2267,2214],{"class":93},[57,2269,1854],{"class":119},[57,2271,419],{"class":89},[57,2273,1429],{"class":176},[57,2275,1857],{"class":93},[57,2277,555],{"class":89},[57,2279,2280],{"class":93}," (b.",[57,2282,1290],{"class":119},[57,2284,419],{"class":89},[57,2286,1429],{"class":176},[57,2288,1957],{"class":89},[57,2290,1960],{"class":93},[57,2292,1854],{"class":119},[57,2294,419],{"class":89},[57,2296,1429],{"class":176},[57,2298,1857],{"class":93},[57,2300,139],{"class":89},[57,2302,2303],{"class":63},"  -- by magnitude\n",[57,2305,2307,2309,2311,2313,2315,2317,2319,2321,2323,2325,2327,2329,2331,2333,2335,2337,2339,2341,2343,2345,2347,2349,2351,2353,2355,2357,2359,2361],{"class":59,"line":2306},21,[57,2308,1814],{"class":93},[57,2310,577],{"class":119},[57,2312,2205],{"class":89},[57,2314,126],{"class":89},[57,2316,1942],{"class":93},[57,2318,132],{"class":89},[57,2320,2257],{"class":93},[57,2322,1290],{"class":119},[57,2324,419],{"class":89},[57,2326,1429],{"class":176},[57,2328,1957],{"class":89},[57,2330,2214],{"class":93},[57,2332,1854],{"class":119},[57,2334,419],{"class":89},[57,2336,1429],{"class":176},[57,2338,1857],{"class":93},[57,2340,572],{"class":89},[57,2342,2280],{"class":93},[57,2344,1290],{"class":119},[57,2346,419],{"class":89},[57,2348,1429],{"class":176},[57,2350,1957],{"class":89},[57,2352,1960],{"class":93},[57,2354,1854],{"class":119},[57,2356,419],{"class":89},[57,2358,1429],{"class":176},[57,2360,1857],{"class":93},[57,2362,1513],{"class":89},[57,2364,2366],{"class":59,"line":2365},22,[57,2367,83],{"emptyLinePlaceholder":82},[57,2369,2371],{"class":59,"line":2370},23,[57,2372,2373],{"class":63},"-- Metamethods for mixed-type operations (commutativity)\n",[57,2375,2377],{"class":59,"line":2376},24,[57,2378,2379],{"class":63},"-- __mul handles both Vector * scalar and scalar * Vector? NO — __mul is only called\n",[57,2381,2383],{"class":59,"line":2382},25,[57,2384,2385],{"class":63},"-- when the LEFT operand has the metamethod. For scalar * Vector, we need __mul on\n",[57,2387,2389],{"class":59,"line":2388},26,[57,2390,2391],{"class":63},"-- the number's metatable — but we can't modify numbers. Solution: only support\n",[57,2393,2395],{"class":59,"line":2394},27,[57,2396,2397],{"class":63},"-- Vector * scalar, document it.\n",[57,2399,2401],{"class":59,"line":2400},28,[57,2402,83],{"emptyLinePlaceholder":82},[57,2404,2406,2408,2411,2413,2415,2417,2419,2422,2424,2426],{"class":59,"line":2405},29,[57,2407,90],{"class":89},[57,2409,2410],{"class":93}," v1 ",[57,2412,97],{"class":89},[57,2414,1947],{"class":93},[57,2416,1877],{"class":176},[57,2418,803],{"class":93},[57,2420,2421],{"class":176},"1",[57,2423,31],{"class":93},[57,2425,1429],{"class":176},[57,2427,1432],{"class":93},[57,2429,2431,2433,2436,2438,2440,2442,2444,2447,2449,2452],{"class":59,"line":2430},30,[57,2432,90],{"class":89},[57,2434,2435],{"class":93}," v2 ",[57,2437,97],{"class":89},[57,2439,1947],{"class":93},[57,2441,1877],{"class":176},[57,2443,803],{"class":93},[57,2445,2446],{"class":176},"3",[57,2448,31],{"class":93},[57,2450,2451],{"class":176},"4",[57,2453,1432],{"class":93},[57,2455,2457,2459,2462,2464,2467],{"class":59,"line":2456},31,[57,2458,186],{"class":176},[57,2460,2461],{"class":93},"(v1 ",[57,2463,317],{"class":89},[57,2465,2466],{"class":93}," v2)   ",[57,2468,2469],{"class":63},"-- (4, 6)\n",[57,2471,2473,2475,2478,2480,2483],{"class":59,"line":2472},32,[57,2474,186],{"class":176},[57,2476,2477],{"class":93},"(v2 ",[57,2479,333],{"class":89},[57,2481,2482],{"class":93}," v1)  ",[57,2484,2485],{"class":63},"-- (2, 2)\n",[57,2487,2489,2491,2493,2495,2498,2501],{"class":59,"line":2488},33,[57,2490,186],{"class":176},[57,2492,2461],{"class":93},[57,2494,350],{"class":89},[57,2496,2497],{"class":176}," 3",[57,2499,2500],{"class":93},")    ",[57,2502,2503],{"class":63},"-- (3, 6)\n",[57,2505,2507,2509,2511,2513,2516],{"class":59,"line":2506},34,[57,2508,186],{"class":176},[57,2510,803],{"class":93},[57,2512,333],{"class":89},[57,2514,2515],{"class":93},"v1)       ",[57,2517,2518],{"class":63},"-- (-1, -2)\n",[57,2520,2522,2524,2526,2528,2530,2532,2534,2536,2538,2540,2543],{"class":59,"line":2521},35,[57,2523,186],{"class":176},[57,2525,2461],{"class":93},[57,2527,539],{"class":89},[57,2529,1947],{"class":93},[57,2531,1877],{"class":176},[57,2533,803],{"class":93},[57,2535,2421],{"class":176},[57,2537,31],{"class":93},[57,2539,1429],{"class":176},[57,2541,2542],{"class":93},"))  ",[57,2544,2545],{"class":63},"-- true\n",[57,2547,2549,2551,2553,2555,2557],{"class":59,"line":2548},36,[57,2550,186],{"class":176},[57,2552,2461],{"class":93},[57,2554,555],{"class":89},[57,2556,2466],{"class":93},[57,2558,2559],{"class":63},"-- true (magnitude: 5 \u003C 25)\n",[57,2561,2563],{"class":59,"line":2562},37,[57,2564,83],{"emptyLinePlaceholder":82},[57,2566,2568],{"class":59,"line":2567},38,[57,2569,2570],{"class":63},"-- Sorting vectors by magnitude\n",[57,2572,2574,2576,2579,2581,2584,2586,2588,2590,2592,2594,2597,2599,2601,2603,2605,2607,2609,2611,2613,2616,2618,2620],{"class":59,"line":2573},39,[57,2575,90],{"class":89},[57,2577,2578],{"class":93}," vecs ",[57,2580,97],{"class":89},[57,2582,2583],{"class":93}," {Vector.",[57,2585,1877],{"class":176},[57,2587,803],{"class":93},[57,2589,2446],{"class":176},[57,2591,31],{"class":93},[57,2593,2451],{"class":176},[57,2595,2596],{"class":93},"), Vector.",[57,2598,1877],{"class":176},[57,2600,803],{"class":93},[57,2602,2421],{"class":176},[57,2604,31],{"class":93},[57,2606,2421],{"class":176},[57,2608,2596],{"class":93},[57,2610,1877],{"class":176},[57,2612,803],{"class":93},[57,2614,2615],{"class":176},"0",[57,2617,31],{"class":93},[57,2619,2421],{"class":176},[57,2621,2622],{"class":93},")}\n",[57,2624,2626,2629,2632],{"class":59,"line":2625},40,[57,2627,2628],{"class":176},"table.sort",[57,2630,2631],{"class":93},"(vecs)  ",[57,2633,2634],{"class":63},"-- uses __lt: {0,1}, {1,1}, {3,4}\n",[57,2636,2638,2641,2644,2647,2650,2653,2656,2659,2661],{"class":59,"line":2637},41,[57,2639,2640],{"class":89},"for",[57,2642,2643],{"class":93}," _, v ",[57,2645,2646],{"class":89},"in",[57,2648,2649],{"class":176}," ipairs",[57,2651,2652],{"class":93},"(vecs) ",[57,2654,2655],{"class":89},"do",[57,2657,2658],{"class":176}," print",[57,2660,1834],{"class":93},[57,2662,1513],{"class":89},[931,2664,2666,2667,2669],{"id":2665},"edge-case-__eq-only-called-for-same-type-operands","Edge case: ",[28,2668,37],{}," only called for same-type operands",[45,2671,2672],{"language":47},[49,2673,2675],{"className":51,"code":2674,"language":47,"meta":53,"style":53},"-- __eq is ONLY called if BOTH operands are tables (or userdata),\n-- AND they are not the same object, AND both have __eq with the SAME metamethod.\n-- (If one operand is a number\u002Fstring, == does normal comparison, no metamethod.)\n\nlocal T = setmetatable({}, {__eq = function() return true end})\nlocal U = setmetatable({}, {__eq = function() return true end})\nprint(T == T)  -- true (same object — __eq NOT called, direct identity)\nprint(T == U)  -- false (different metatables — __eq NOT called; default: different objects)\n\n-- For __eq to fire, both must have the SAME __eq function:\nlocal eq_fn = function() return true end\nlocal A = setmetatable({}, {__eq = eq_fn})\nlocal B = setmetatable({}, {__eq = eq_fn})\nprint(A == B)  -- true (same __eq function → metamethod called → returns true)\n",[28,2676,2677,2682,2687,2692,2696,2728,2757,2772,2786,2790,2795,2814,2832,2848],{"__ignoreMap":53},[57,2678,2679],{"class":59,"line":60},[57,2680,2681],{"class":63},"-- __eq is ONLY called if BOTH operands are tables (or userdata),\n",[57,2683,2684],{"class":59,"line":67},[57,2685,2686],{"class":63},"-- AND they are not the same object, AND both have __eq with the SAME metamethod.\n",[57,2688,2689],{"class":59,"line":73},[57,2690,2691],{"class":63},"-- (If one operand is a number\u002Fstring, == does normal comparison, no metamethod.)\n",[57,2693,2694],{"class":59,"line":79},[57,2695,83],{"emptyLinePlaceholder":82},[57,2697,2698,2700,2703,2705,2707,2710,2712,2714,2716,2718,2720,2723,2726],{"class":59,"line":86},[57,2699,90],{"class":89},[57,2701,2702],{"class":93}," T ",[57,2704,97],{"class":89},[57,2706,788],{"class":176},[57,2708,2709],{"class":93},"({}, {",[57,2711,37],{"class":119},[57,2713,123],{"class":89},[57,2715,126],{"class":89},[57,2717,970],{"class":93},[57,2719,132],{"class":89},[57,2721,2722],{"class":176}," true",[57,2724,2725],{"class":89}," end",[57,2727,923],{"class":93},[57,2729,2730,2732,2735,2737,2739,2741,2743,2745,2747,2749,2751,2753,2755],{"class":59,"line":103},[57,2731,90],{"class":89},[57,2733,2734],{"class":93}," U ",[57,2736,97],{"class":89},[57,2738,788],{"class":176},[57,2740,2709],{"class":93},[57,2742,37],{"class":119},[57,2744,123],{"class":89},[57,2746,126],{"class":89},[57,2748,970],{"class":93},[57,2750,132],{"class":89},[57,2752,2722],{"class":176},[57,2754,2725],{"class":89},[57,2756,923],{"class":93},[57,2758,2759,2761,2764,2766,2769],{"class":59,"line":116},[57,2760,186],{"class":176},[57,2762,2763],{"class":93},"(T ",[57,2765,539],{"class":89},[57,2767,2768],{"class":93}," T)  ",[57,2770,2771],{"class":63},"-- true (same object — __eq NOT called, direct identity)\n",[57,2773,2774,2776,2778,2780,2783],{"class":59,"line":145},[57,2775,186],{"class":176},[57,2777,2763],{"class":93},[57,2779,539],{"class":89},[57,2781,2782],{"class":93}," U)  ",[57,2784,2785],{"class":63},"-- false (different metatables — __eq NOT called; default: different objects)\n",[57,2787,2788],{"class":59,"line":167},[57,2789,83],{"emptyLinePlaceholder":82},[57,2791,2792],{"class":59,"line":173},[57,2793,2794],{"class":63},"-- For __eq to fire, both must have the SAME __eq function:\n",[57,2796,2797,2799,2802,2804,2806,2808,2810,2812],{"class":59,"line":183},[57,2798,90],{"class":89},[57,2800,2801],{"class":119}," eq_fn",[57,2803,123],{"class":89},[57,2805,126],{"class":89},[57,2807,970],{"class":93},[57,2809,132],{"class":89},[57,2811,2722],{"class":176},[57,2813,2239],{"class":89},[57,2815,2816,2818,2820,2822,2824,2827,2829],{"class":59,"line":195},[57,2817,90],{"class":89},[57,2819,956],{"class":93},[57,2821,97],{"class":89},[57,2823,788],{"class":176},[57,2825,2826],{"class":93},"({}, {__eq ",[57,2828,97],{"class":89},[57,2830,2831],{"class":93}," eq_fn})\n",[57,2833,2834,2836,2838,2840,2842,2844,2846],{"class":59,"line":212},[57,2835,90],{"class":89},[57,2837,1002],{"class":93},[57,2839,97],{"class":89},[57,2841,788],{"class":176},[57,2843,2826],{"class":93},[57,2845,97],{"class":89},[57,2847,2831],{"class":93},[57,2849,2850,2852,2855,2857,2860],{"class":59,"line":886},[57,2851,186],{"class":176},[57,2853,2854],{"class":93},"(A ",[57,2856,539],{"class":89},[57,2858,2859],{"class":93}," B)  ",[57,2861,2862],{"class":63},"-- true (same __eq function → metamethod called → returns true)\n",[40,2864,2866,2868],{"id":2865},"__call-callable-tables",[28,2867,304],{}," — Callable Tables",[45,2870,2871],{"language":47},[49,2872,2874],{"className":51,"code":2873,"language":47,"meta":53,"style":53},"-- Make a table behave like a function\nlocal function memoize(fn)\n  local cache = {}\n  local mt = {\n    __call = function(self, x)\n      if cache[x] ~= nil then return cache[x] end\n      local result = fn(x)\n      cache[x] = result\n      return result\n    end,\n  }\n  return setmetatable({}, mt)\nend\n\nlocal square = memoize(function(x) return x * x end)\nprint(square(5))  -- 25 (computed)\nprint(square(5))  -- 25 (cached)\n-- square is a TABLE, but called like a function\n\n-- Production: builder DSL via __call\nlocal HTML = {}\nHTML.__index = HTML\nHTML.__call = function(self, tag, attrs, children)\n  local attr_str = \"\"\n  if attrs then\n    for k, v in pairs(attrs) do\n      attr_str = attr_str .. string.format(' %s=\"%s\"', k, v)\n    end\n  end\n  local inner = type(children) == \"string\" and children or \"\"\n  if type(children) == \"table\" then\n    for _, c in ipairs(children) do inner = inner .. c end\n  end\n  return string.format(\"\u003C%s%s>%s\u003C\u002F%s>\", tag, attr_str, inner, tag)\nend\nsetmetatable(HTML, HTML)\n\nprint(HTML(\"div\", {class = \"card\"}, {HTML(\"p\", {}, \"hello\")}))\n-- \u003Cdiv class=\"card\">\u003Cp>hello\u003C\u002Fp>\u003C\u002Fdiv>\n",[28,2875,2876,2881,2893,2904,2914,2926,2949,2964,2974,2980,2986,2990,2999,3003,3007,3037,3056,3073,3078,3082,3087,3098,3110,3123,3135,3145,3163,3184,3189,3194,3223,3238,3266,3270,3284,3288,3295,3299,3340],{"__ignoreMap":53},[57,2877,2878],{"class":59,"line":60},[57,2879,2880],{"class":63},"-- Make a table behave like a function\n",[57,2882,2883,2885,2887,2890],{"class":59,"line":67},[57,2884,90],{"class":89},[57,2886,126],{"class":89},[57,2888,2889],{"class":119}," memoize",[57,2891,2892],{"class":93},"(fn)\n",[57,2894,2895,2897,2900,2902],{"class":59,"line":73},[57,2896,1350],{"class":89},[57,2898,2899],{"class":93}," cache ",[57,2901,97],{"class":89},[57,2903,100],{"class":93},[57,2905,2906,2908,2910,2912],{"class":59,"line":79},[57,2907,1350],{"class":89},[57,2909,108],{"class":93},[57,2911,97],{"class":89},[57,2913,113],{"class":93},[57,2915,2916,2919,2921,2923],{"class":59,"line":86},[57,2917,2918],{"class":119},"    __call",[57,2920,123],{"class":89},[57,2922,126],{"class":89},[57,2924,2925],{"class":93},"(self, x)\n",[57,2927,2928,2931,2934,2937,2940,2943,2945,2947],{"class":59,"line":103},[57,2929,2930],{"class":89},"      if",[57,2932,2933],{"class":93}," cache[x] ",[57,2935,2936],{"class":89},"~=",[57,2938,2939],{"class":176}," nil",[57,2941,2942],{"class":89}," then",[57,2944,2055],{"class":89},[57,2946,2933],{"class":93},[57,2948,1513],{"class":89},[57,2950,2951,2953,2956,2958,2961],{"class":59,"line":116},[57,2952,1635],{"class":89},[57,2954,2955],{"class":93}," result ",[57,2957,97],{"class":89},[57,2959,2960],{"class":176}," fn",[57,2962,2963],{"class":93},"(x)\n",[57,2965,2966,2969,2971],{"class":59,"line":145},[57,2967,2968],{"class":93},"      cache[x] ",[57,2970,97],{"class":89},[57,2972,2973],{"class":93}," result\n",[57,2975,2976,2978],{"class":59,"line":167},[57,2977,1665],{"class":89},[57,2979,2973],{"class":93},[57,2981,2982,2984],{"class":59,"line":173},[57,2983,1437],{"class":89},[57,2985,142],{"class":93},[57,2987,2988],{"class":59,"line":183},[57,2989,1498],{"class":93},[57,2991,2992,2994,2996],{"class":59,"line":195},[57,2993,1503],{"class":89},[57,2995,788],{"class":176},[57,2997,2998],{"class":93},"({}, mt)\n",[57,3000,3001],{"class":59,"line":212},[57,3002,1513],{"class":89},[57,3004,3005],{"class":59,"line":886},[57,3006,83],{"emptyLinePlaceholder":82},[57,3008,3009,3011,3014,3016,3018,3020,3022,3025,3027,3029,3031,3033,3035],{"class":59,"line":903},[57,3010,90],{"class":89},[57,3012,3013],{"class":93}," square ",[57,3015,97],{"class":89},[57,3017,2889],{"class":176},[57,3019,803],{"class":93},[57,3021,1868],{"class":89},[57,3023,3024],{"class":93},"(x) ",[57,3026,132],{"class":89},[57,3028,1894],{"class":93},[57,3030,350],{"class":89},[57,3032,1894],{"class":93},[57,3034,139],{"class":89},[57,3036,1432],{"class":93},[57,3038,3039,3041,3043,3046,3048,3051,3053],{"class":59,"line":912},[57,3040,186],{"class":176},[57,3042,803],{"class":93},[57,3044,3045],{"class":176},"square",[57,3047,803],{"class":93},[57,3049,3050],{"class":176},"5",[57,3052,2542],{"class":93},[57,3054,3055],{"class":63},"-- 25 (computed)\n",[57,3057,3058,3060,3062,3064,3066,3068,3070],{"class":59,"line":920},[57,3059,186],{"class":176},[57,3061,803],{"class":93},[57,3063,3045],{"class":176},[57,3065,803],{"class":93},[57,3067,3050],{"class":176},[57,3069,2542],{"class":93},[57,3071,3072],{"class":63},"-- 25 (cached)\n",[57,3074,3075],{"class":59,"line":926},[57,3076,3077],{"class":63},"-- square is a TABLE, but called like a function\n",[57,3079,3080],{"class":59,"line":2198},[57,3081,83],{"emptyLinePlaceholder":82},[57,3083,3084],{"class":59,"line":2242},[57,3085,3086],{"class":63},"-- Production: builder DSL via __call\n",[57,3088,3089,3091,3094,3096],{"class":59,"line":2306},[57,3090,90],{"class":89},[57,3092,3093],{"class":93}," HTML ",[57,3095,97],{"class":89},[57,3097,100],{"class":93},[57,3099,3100,3103,3105,3107],{"class":59,"line":2365},[57,3101,3102],{"class":93},"HTML.",[57,3104,30],{"class":119},[57,3106,123],{"class":89},[57,3108,3109],{"class":93}," HTML\n",[57,3111,3112,3114,3116,3118,3120],{"class":59,"line":2370},[57,3113,3102],{"class":93},[57,3115,304],{"class":119},[57,3117,123],{"class":89},[57,3119,126],{"class":89},[57,3121,3122],{"class":93},"(self, tag, attrs, children)\n",[57,3124,3125,3127,3130,3132],{"class":59,"line":2376},[57,3126,1350],{"class":89},[57,3128,3129],{"class":93}," attr_str ",[57,3131,97],{"class":89},[57,3133,3134],{"class":135}," \"\"\n",[57,3136,3137,3139,3142],{"class":59,"line":2382},[57,3138,2038],{"class":89},[57,3140,3141],{"class":93}," attrs ",[57,3143,3144],{"class":89},"then\n",[57,3146,3147,3150,3153,3155,3157,3160],{"class":59,"line":2388},[57,3148,3149],{"class":89},"    for",[57,3151,3152],{"class":93}," k, v ",[57,3154,2646],{"class":89},[57,3156,1455],{"class":176},[57,3158,3159],{"class":93},"(attrs) ",[57,3161,3162],{"class":89},"do\n",[57,3164,3165,3168,3170,3172,3174,3176,3178,3181],{"class":59,"line":2394},[57,3166,3167],{"class":93},"      attr_str ",[57,3169,97],{"class":89},[57,3171,3129],{"class":93},[57,3173,589],{"class":89},[57,3175,1839],{"class":176},[57,3177,803],{"class":93},[57,3179,3180],{"class":135},"' %s=\"%s\"'",[57,3182,3183],{"class":93},", k, v)\n",[57,3185,3186],{"class":59,"line":2400},[57,3187,3188],{"class":89},"    end\n",[57,3190,3191],{"class":59,"line":2405},[57,3192,3193],{"class":89},"  end\n",[57,3195,3196,3198,3201,3203,3205,3208,3210,3213,3216,3219,3221],{"class":59,"line":2430},[57,3197,1350],{"class":89},[57,3199,3200],{"class":93}," inner ",[57,3202,97],{"class":89},[57,3204,2041],{"class":176},[57,3206,3207],{"class":93},"(children) ",[57,3209,539],{"class":89},[57,3211,3212],{"class":135}," \"string\" ",[57,3214,3215],{"class":89},"and",[57,3217,3218],{"class":93}," children ",[57,3220,1897],{"class":89},[57,3222,3134],{"class":135},[57,3224,3225,3227,3229,3231,3233,3236],{"class":59,"line":2456},[57,3226,2038],{"class":89},[57,3228,2041],{"class":176},[57,3230,3207],{"class":93},[57,3232,539],{"class":89},[57,3234,3235],{"class":135}," \"table\" ",[57,3237,3144],{"class":89},[57,3239,3240,3242,3245,3247,3249,3251,3253,3255,3257,3259,3261,3264],{"class":59,"line":2472},[57,3241,3149],{"class":89},[57,3243,3244],{"class":93}," _, c ",[57,3246,2646],{"class":89},[57,3248,2649],{"class":176},[57,3250,3207],{"class":93},[57,3252,2655],{"class":89},[57,3254,3200],{"class":93},[57,3256,97],{"class":89},[57,3258,3200],{"class":93},[57,3260,589],{"class":89},[57,3262,3263],{"class":93}," c ",[57,3265,1513],{"class":89},[57,3267,3268],{"class":59,"line":2488},[57,3269,3193],{"class":89},[57,3271,3272,3274,3276,3278,3281],{"class":59,"line":2506},[57,3273,1503],{"class":89},[57,3275,1839],{"class":176},[57,3277,803],{"class":93},[57,3279,3280],{"class":135},"\"\u003C%s%s>%s\u003C\u002F%s>\"",[57,3282,3283],{"class":93},", tag, attr_str, inner, tag)\n",[57,3285,3286],{"class":59,"line":2521},[57,3287,1513],{"class":89},[57,3289,3290,3292],{"class":59,"line":2548},[57,3291,177],{"class":176},[57,3293,3294],{"class":93},"(HTML, HTML)\n",[57,3296,3297],{"class":59,"line":2562},[57,3298,83],{"emptyLinePlaceholder":82},[57,3300,3301,3303,3305,3308,3310,3313,3316,3318,3321,3324,3326,3328,3331,3334,3337],{"class":59,"line":2567},[57,3302,186],{"class":176},[57,3304,803],{"class":93},[57,3306,3307],{"class":176},"HTML",[57,3309,803],{"class":93},[57,3311,3312],{"class":135},"\"div\"",[57,3314,3315],{"class":93},", {class ",[57,3317,97],{"class":89},[57,3319,3320],{"class":135}," \"card\"",[57,3322,3323],{"class":93},"}, {",[57,3325,3307],{"class":176},[57,3327,803],{"class":93},[57,3329,3330],{"class":135},"\"p\"",[57,3332,3333],{"class":93},", {}, ",[57,3335,3336],{"class":135},"\"hello\"",[57,3338,3339],{"class":93},")}))\n",[57,3341,3342],{"class":59,"line":2573},[57,3343,3344],{"class":63},"-- \u003Cdiv class=\"card\">\u003Cp>hello\u003C\u002Fp>\u003C\u002Fdiv>\n",[40,3346,3348,3350,3351,3353],{"id":3347},"__pairs-__len-custom-iteration-length",[28,3349,628],{}," \u002F ",[28,3352,611],{}," — Custom Iteration & Length",[45,3355,3356],{"language":47},[49,3357,3359],{"className":51,"code":3358,"language":47,"meta":53,"style":53},"-- __pairs: custom pairs() behavior (Lua 5.2+)\n-- __len: custom # operator\n\nlocal OrderedMap = {}\nOrderedMap.__index = OrderedMap\nOrderedMap.__pairs = function(self)\n  local i = 0\n  return function()\n    i = i + 1\n    local key = self._order[i]\n    if key then return key, self._data[key] end\n  end\nend\nOrderedMap.__len = function(self) return #self._order end\n\nfunction OrderedMap.new()\n  return setmetatable({_data = {}, _order = {}}, OrderedMap)\nend\n\nfunction OrderedMap:set(key, value)\n  if self._data[key] == nil then  -- new key\n    self._order[#self._order + 1] = key  -- track insertion order\n  end\n  self._data[key] = value\nend\n\nlocal om = OrderedMap.new()\nom:set(\"c\", 3):set(\"a\", 1):set(\"b\", 2)  -- (need :set to return self for chaining)\n-- Actually :set returns nil; fix: return self at end of set\n\nfor k, v in pairs(om) do print(k, v) end  -- c 3 \u002F a 1 \u002F b 2 (insertion order!)\nprint(#om)  -- 3 (custom __len)\n",[28,3360,3361,3366,3371,3375,3386,3398,3411,3423,3432,3446,3466,3492,3496,3500,3524,3528,3541,3560,3564,3568,3582,3603,3638,3642,3658,3662,3666,3682,3732,3737,3741,3766],{"__ignoreMap":53},[57,3362,3363],{"class":59,"line":60},[57,3364,3365],{"class":63},"-- __pairs: custom pairs() behavior (Lua 5.2+)\n",[57,3367,3368],{"class":59,"line":67},[57,3369,3370],{"class":63},"-- __len: custom # operator\n",[57,3372,3373],{"class":59,"line":73},[57,3374,83],{"emptyLinePlaceholder":82},[57,3376,3377,3379,3382,3384],{"class":59,"line":79},[57,3378,90],{"class":89},[57,3380,3381],{"class":93}," OrderedMap ",[57,3383,97],{"class":89},[57,3385,100],{"class":93},[57,3387,3388,3391,3393,3395],{"class":59,"line":86},[57,3389,3390],{"class":93},"OrderedMap.",[57,3392,30],{"class":119},[57,3394,123],{"class":89},[57,3396,3397],{"class":93}," OrderedMap\n",[57,3399,3400,3402,3404,3406,3408],{"class":59,"line":103},[57,3401,3390],{"class":93},[57,3403,628],{"class":119},[57,3405,123],{"class":89},[57,3407,126],{"class":89},[57,3409,3410],{"class":93},"(self)\n",[57,3412,3413,3415,3418,3420],{"class":59,"line":116},[57,3414,1350],{"class":89},[57,3416,3417],{"class":93}," i ",[57,3419,97],{"class":89},[57,3421,3422],{"class":176}," 0\n",[57,3424,3425,3427,3429],{"class":59,"line":145},[57,3426,1503],{"class":89},[57,3428,126],{"class":89},[57,3430,3431],{"class":93},"()\n",[57,3433,3434,3437,3439,3441,3443],{"class":59,"line":167},[57,3435,3436],{"class":93},"    i ",[57,3438,97],{"class":89},[57,3440,3417],{"class":93},[57,3442,317],{"class":89},[57,3444,3445],{"class":176}," 1\n",[57,3447,3448,3450,3453,3455,3458,3460,3463],{"class":59,"line":173},[57,3449,869],{"class":89},[57,3451,3452],{"class":93}," key ",[57,3454,97],{"class":89},[57,3456,3457],{"class":176}," self",[57,3459,1874],{"class":93},[57,3461,3462],{"class":119},"_order",[57,3464,3465],{"class":93},"[i]\n",[57,3467,3468,3471,3473,3475,3477,3480,3482,3484,3487,3490],{"class":59,"line":183},[57,3469,3470],{"class":89},"    if",[57,3472,3452],{"class":93},[57,3474,2052],{"class":89},[57,3476,2055],{"class":89},[57,3478,3479],{"class":93}," key, ",[57,3481,894],{"class":176},[57,3483,1874],{"class":93},[57,3485,3486],{"class":119},"_data",[57,3488,3489],{"class":93},"[key] ",[57,3491,1513],{"class":89},[57,3493,3494],{"class":59,"line":195},[57,3495,3193],{"class":89},[57,3497,3498],{"class":59,"line":212},[57,3499,1513],{"class":89},[57,3501,3502,3504,3506,3508,3510,3512,3514,3516,3518,3520,3522],{"class":59,"line":886},[57,3503,3390],{"class":93},[57,3505,611],{"class":119},[57,3507,123],{"class":89},[57,3509,126],{"class":89},[57,3511,129],{"class":93},[57,3513,132],{"class":89},[57,3515,1482],{"class":89},[57,3517,894],{"class":176},[57,3519,1874],{"class":93},[57,3521,3462],{"class":119},[57,3523,2239],{"class":89},[57,3525,3526],{"class":59,"line":903},[57,3527,83],{"emptyLinePlaceholder":82},[57,3529,3530,3532,3535,3537,3539],{"class":59,"line":912},[57,3531,1868],{"class":89},[57,3533,3534],{"class":119}," OrderedMap",[57,3536,1874],{"class":93},[57,3538,1877],{"class":119},[57,3540,3431],{"class":93},[57,3542,3543,3545,3547,3550,3552,3555,3557],{"class":59,"line":920},[57,3544,1503],{"class":89},[57,3546,788],{"class":176},[57,3548,3549],{"class":93},"({_data ",[57,3551,97],{"class":89},[57,3553,3554],{"class":93}," {}, _order ",[57,3556,97],{"class":89},[57,3558,3559],{"class":93}," {}}, OrderedMap)\n",[57,3561,3562],{"class":59,"line":926},[57,3563,1513],{"class":89},[57,3565,3566],{"class":59,"line":2198},[57,3567,83],{"emptyLinePlaceholder":82},[57,3569,3570,3572,3574,3576,3579],{"class":59,"line":2242},[57,3571,1868],{"class":89},[57,3573,3534],{"class":119},[57,3575,809],{"class":93},[57,3577,3578],{"class":119},"set",[57,3580,3581],{"class":93},"(key, value)\n",[57,3583,3584,3586,3588,3590,3592,3594,3596,3598,3600],{"class":59,"line":2306},[57,3585,2038],{"class":89},[57,3587,3457],{"class":176},[57,3589,1874],{"class":93},[57,3591,3486],{"class":119},[57,3593,3489],{"class":93},[57,3595,539],{"class":89},[57,3597,2939],{"class":176},[57,3599,2942],{"class":89},[57,3601,3602],{"class":63},"  -- new key\n",[57,3604,3605,3608,3610,3612,3615,3617,3619,3621,3623,3625,3627,3630,3632,3635],{"class":59,"line":2365},[57,3606,3607],{"class":176},"    self",[57,3609,1874],{"class":93},[57,3611,3462],{"class":119},[57,3613,3614],{"class":93},"[",[57,3616,606],{"class":89},[57,3618,894],{"class":176},[57,3620,1874],{"class":93},[57,3622,3462],{"class":119},[57,3624,1957],{"class":89},[57,3626,1761],{"class":176},[57,3628,3629],{"class":93},"] ",[57,3631,97],{"class":89},[57,3633,3634],{"class":93}," key  ",[57,3636,3637],{"class":63},"-- track insertion order\n",[57,3639,3640],{"class":59,"line":2370},[57,3641,3193],{"class":89},[57,3643,3644,3647,3649,3651,3653,3655],{"class":59,"line":2376},[57,3645,3646],{"class":176},"  self",[57,3648,1874],{"class":93},[57,3650,3486],{"class":119},[57,3652,3489],{"class":93},[57,3654,97],{"class":89},[57,3656,3657],{"class":93}," value\n",[57,3659,3660],{"class":59,"line":2382},[57,3661,1513],{"class":89},[57,3663,3664],{"class":59,"line":2388},[57,3665,83],{"emptyLinePlaceholder":82},[57,3667,3668,3670,3673,3675,3678,3680],{"class":59,"line":2394},[57,3669,90],{"class":89},[57,3671,3672],{"class":93}," om ",[57,3674,97],{"class":89},[57,3676,3677],{"class":93}," OrderedMap.",[57,3679,1877],{"class":176},[57,3681,3431],{"class":93},[57,3683,3684,3687,3689,3691,3693,3696,3698,3700,3703,3705,3707,3710,3712,3714,3716,3718,3720,3723,3725,3727,3729],{"class":59,"line":2400},[57,3685,3686],{"class":119},"om",[57,3688,809],{"class":93},[57,3690,3578],{"class":176},[57,3692,803],{"class":93},[57,3694,3695],{"class":135},"\"c\"",[57,3697,31],{"class":93},[57,3699,2446],{"class":176},[57,3701,3702],{"class":93},"):",[57,3704,3578],{"class":176},[57,3706,803],{"class":93},[57,3708,3709],{"class":135},"\"a\"",[57,3711,31],{"class":93},[57,3713,2421],{"class":176},[57,3715,3702],{"class":93},[57,3717,3578],{"class":176},[57,3719,803],{"class":93},[57,3721,3722],{"class":135},"\"b\"",[57,3724,31],{"class":93},[57,3726,1429],{"class":176},[57,3728,206],{"class":93},[57,3730,3731],{"class":63},"-- (need :set to return self for chaining)\n",[57,3733,3734],{"class":59,"line":2405},[57,3735,3736],{"class":63},"-- Actually :set returns nil; fix: return self at end of set\n",[57,3738,3739],{"class":59,"line":2430},[57,3740,83],{"emptyLinePlaceholder":82},[57,3742,3743,3745,3747,3749,3751,3754,3756,3758,3761,3763],{"class":59,"line":2456},[57,3744,2640],{"class":89},[57,3746,3152],{"class":93},[57,3748,2646],{"class":89},[57,3750,1455],{"class":176},[57,3752,3753],{"class":93},"(om) ",[57,3755,2655],{"class":89},[57,3757,2658],{"class":176},[57,3759,3760],{"class":93},"(k, v) ",[57,3762,139],{"class":89},[57,3764,3765],{"class":63},"  -- c 3 \u002F a 1 \u002F b 2 (insertion order!)\n",[57,3767,3768,3770,3772,3774,3777],{"class":59,"line":2472},[57,3769,186],{"class":176},[57,3771,803],{"class":93},[57,3773,606],{"class":89},[57,3775,3776],{"class":93},"om)  ",[57,3778,3779],{"class":63},"-- 3 (custom __len)\n",[40,3781,3783,3784],{"id":3782},"weak-tables-__gc","Weak Tables & ",[28,3785,679],{},[45,3787,3788,4085,4091],{"language":47},[49,3789,3791],{"className":51,"code":3790,"language":47,"meta":53,"style":53},"-- Weak tables: entries don't block GC of keys and\u002For values\n-- __mode = \"v\": weak values (value can be collected)\n-- __mode = \"k\": weak keys (key can be collected)\n-- __mode = \"kv\": both\n\n-- Cache with weak values: auto-evicts entries when the value is GC'd\nlocal function weak_cache()\n  return setmetatable({}, {__mode = \"v\"})\nend\n\nlocal cache = weak_cache()\nlocal obj = {id = 1}\ncache[\"item1\"] = obj\nobj = nil              -- only reference was in the cache\ncollectgarbage()      -- force GC\nprint(cache[\"item1\"])  -- nil (value collected, entry removed)\n\n-- __gc on tables (Lua 5.4+): runs when the table is collected\n-- Useful for tables that wrap resources (not just userdata)\nlocal function managed_table()\n  local t = setmetatable({}, {\n    __gc = function(self)\n      print(\"table collected, cleanup:\", self.name)\n      -- close file handles, release C resources, etc.\n    end,\n  })\n  return t\nend\n\ndo\n  local t = managed_table()\n  t.name = \"resource1\"\n  -- t goes out of scope → eligible for GC\nend\ncollectgarbage()  -- prints \"table collected, cleanup: resource1\"\n",[28,3792,3793,3798,3803,3808,3813,3817,3822,3833,3849,3853,3857,3869,3887,3902,3914,3925,3940,3944,3949,3954,3965,3977,3988,4008,4013,4019,4023,4030,4034,4038,4042,4054,4066,4071,4075],{"__ignoreMap":53},[57,3794,3795],{"class":59,"line":60},[57,3796,3797],{"class":63},"-- Weak tables: entries don't block GC of keys and\u002For values\n",[57,3799,3800],{"class":59,"line":67},[57,3801,3802],{"class":63},"-- __mode = \"v\": weak values (value can be collected)\n",[57,3804,3805],{"class":59,"line":73},[57,3806,3807],{"class":63},"-- __mode = \"k\": weak keys (key can be collected)\n",[57,3809,3810],{"class":59,"line":79},[57,3811,3812],{"class":63},"-- __mode = \"kv\": both\n",[57,3814,3815],{"class":59,"line":86},[57,3816,83],{"emptyLinePlaceholder":82},[57,3818,3819],{"class":59,"line":103},[57,3820,3821],{"class":63},"-- Cache with weak values: auto-evicts entries when the value is GC'd\n",[57,3823,3824,3826,3828,3831],{"class":59,"line":116},[57,3825,90],{"class":89},[57,3827,126],{"class":89},[57,3829,3830],{"class":119}," weak_cache",[57,3832,3431],{"class":93},[57,3834,3835,3837,3839,3842,3844,3847],{"class":59,"line":145},[57,3836,1503],{"class":89},[57,3838,788],{"class":176},[57,3840,3841],{"class":93},"({}, {__mode ",[57,3843,97],{"class":89},[57,3845,3846],{"class":135}," \"v\"",[57,3848,923],{"class":93},[57,3850,3851],{"class":59,"line":167},[57,3852,1513],{"class":89},[57,3854,3855],{"class":59,"line":173},[57,3856,83],{"emptyLinePlaceholder":82},[57,3858,3859,3861,3863,3865,3867],{"class":59,"line":183},[57,3860,90],{"class":89},[57,3862,2899],{"class":93},[57,3864,97],{"class":89},[57,3866,3830],{"class":176},[57,3868,3431],{"class":93},[57,3870,3871,3873,3876,3878,3881,3883,3885],{"class":59,"line":195},[57,3872,90],{"class":89},[57,3874,3875],{"class":93}," obj ",[57,3877,97],{"class":89},[57,3879,3880],{"class":93}," {id ",[57,3882,97],{"class":89},[57,3884,1761],{"class":176},[57,3886,170],{"class":93},[57,3888,3889,3892,3895,3897,3899],{"class":59,"line":212},[57,3890,3891],{"class":93},"cache[",[57,3893,3894],{"class":135},"\"item1\"",[57,3896,3629],{"class":93},[57,3898,97],{"class":89},[57,3900,3901],{"class":93}," obj\n",[57,3903,3904,3907,3909,3911],{"class":59,"line":886},[57,3905,3906],{"class":93},"obj ",[57,3908,97],{"class":89},[57,3910,2939],{"class":176},[57,3912,3913],{"class":63},"              -- only reference was in the cache\n",[57,3915,3916,3919,3922],{"class":59,"line":903},[57,3917,3918],{"class":176},"collectgarbage",[57,3920,3921],{"class":93},"()      ",[57,3923,3924],{"class":63},"-- force GC\n",[57,3926,3927,3929,3932,3934,3937],{"class":59,"line":912},[57,3928,186],{"class":176},[57,3930,3931],{"class":93},"(cache[",[57,3933,3894],{"class":135},[57,3935,3936],{"class":93},"])  ",[57,3938,3939],{"class":63},"-- nil (value collected, entry removed)\n",[57,3941,3942],{"class":59,"line":920},[57,3943,83],{"emptyLinePlaceholder":82},[57,3945,3946],{"class":59,"line":926},[57,3947,3948],{"class":63},"-- __gc on tables (Lua 5.4+): runs when the table is collected\n",[57,3950,3951],{"class":59,"line":2198},[57,3952,3953],{"class":63},"-- Useful for tables that wrap resources (not just userdata)\n",[57,3955,3956,3958,3960,3963],{"class":59,"line":2242},[57,3957,90],{"class":89},[57,3959,126],{"class":89},[57,3961,3962],{"class":119}," managed_table",[57,3964,3431],{"class":93},[57,3966,3967,3969,3971,3973,3975],{"class":59,"line":2306},[57,3968,1350],{"class":89},[57,3970,94],{"class":93},[57,3972,97],{"class":89},[57,3974,788],{"class":176},[57,3976,840],{"class":93},[57,3978,3979,3982,3984,3986],{"class":59,"line":2365},[57,3980,3981],{"class":119},"    __gc",[57,3983,123],{"class":89},[57,3985,126],{"class":89},[57,3987,3410],{"class":93},[57,3989,3990,3992,3994,3997,3999,4001,4003,4006],{"class":59,"line":2370},[57,3991,1648],{"class":176},[57,3993,803],{"class":93},[57,3995,3996],{"class":135},"\"table collected, cleanup:\"",[57,3998,31],{"class":93},[57,4000,894],{"class":176},[57,4002,1874],{"class":93},[57,4004,4005],{"class":119},"name",[57,4007,1432],{"class":93},[57,4009,4010],{"class":59,"line":2376},[57,4011,4012],{"class":63},"      -- close file handles, release C resources, etc.\n",[57,4014,4015,4017],{"class":59,"line":2382},[57,4016,1437],{"class":89},[57,4018,142],{"class":93},[57,4020,4021],{"class":59,"line":2388},[57,4022,1723],{"class":93},[57,4024,4025,4027],{"class":59,"line":2394},[57,4026,1503],{"class":89},[57,4028,4029],{"class":93}," t\n",[57,4031,4032],{"class":59,"line":2400},[57,4033,1513],{"class":89},[57,4035,4036],{"class":59,"line":2405},[57,4037,83],{"emptyLinePlaceholder":82},[57,4039,4040],{"class":59,"line":2430},[57,4041,3162],{"class":89},[57,4043,4044,4046,4048,4050,4052],{"class":59,"line":2456},[57,4045,1350],{"class":89},[57,4047,94],{"class":93},[57,4049,97],{"class":89},[57,4051,3962],{"class":176},[57,4053,3431],{"class":93},[57,4055,4056,4059,4061,4063],{"class":59,"line":2472},[57,4057,4058],{"class":93},"  t.",[57,4060,4005],{"class":119},[57,4062,123],{"class":89},[57,4064,4065],{"class":135}," \"resource1\"\n",[57,4067,4068],{"class":59,"line":2488},[57,4069,4070],{"class":63},"  -- t goes out of scope → eligible for GC\n",[57,4072,4073],{"class":59,"line":2506},[57,4074,1513],{"class":89},[57,4076,4077,4079,4082],{"class":59,"line":2521},[57,4078,3918],{"class":176},[57,4080,4081],{"class":93},"()  ",[57,4083,4084],{"class":63},"-- prints \"table collected, cleanup: resource1\"\n",[40,4086,4088,4090],{"id":4087},"__close-resource-management-lua-54",[28,4089,692],{}," — Resource Management (Lua 5.4+)",[45,4092,4093,4406,4415],{"language":47},[49,4094,4096],{"className":51,"code":4095,"language":47,"meta":53,"style":53},"-- \u003Cclose> attribute: when the local goes out of scope, __close is called\n-- Works like Go's defer \u002F Python's with — deterministic cleanup\n\nlocal function open_resource(path)\n  local res = {path = path, data = \"loaded\"}\n  local mt = {\n    __close = function(self)\n      print(\"closing:\", self.path)\n      -- release file handle, network connection, etc.\n    end,\n    __index = function(t, k) return t.data:match(k) end,\n  }\n  return setmetatable(res, mt)\nend\n\ndo\n  local r \u003Cclose> = open_resource(\"config.txt\")\n  -- use r\n  print(r.data)  -- \"loaded\"\n  -- when this block ends (even on error), __close is called\nend\n-- prints: \"closing: config.txt\"\n\n-- Built-in: file handles support \u003Cclose> in Lua 5.4\ndo\n  local f \u003Cclose> = io.open(\"data.txt\", \"r\")\n  if not f then error(\"can't open\") end\n  local content = f:read(\"*a\")\n  -- f:close() called automatically at block end (even on error\u002Fexception)\nend\n",[28,4097,4098,4103,4108,4112,4124,4148,4158,4169,4189,4194,4200,4231,4235,4244,4248,4252,4256,4277,4282,4297,4302,4306,4311,4315,4320,4324,4350,4373,4397,4402],{"__ignoreMap":53},[57,4099,4100],{"class":59,"line":60},[57,4101,4102],{"class":63},"-- \u003Cclose> attribute: when the local goes out of scope, __close is called\n",[57,4104,4105],{"class":59,"line":67},[57,4106,4107],{"class":63},"-- Works like Go's defer \u002F Python's with — deterministic cleanup\n",[57,4109,4110],{"class":59,"line":73},[57,4111,83],{"emptyLinePlaceholder":82},[57,4113,4114,4116,4118,4121],{"class":59,"line":79},[57,4115,90],{"class":89},[57,4117,126],{"class":89},[57,4119,4120],{"class":119}," open_resource",[57,4122,4123],{"class":93},"(path)\n",[57,4125,4126,4128,4131,4133,4136,4138,4141,4143,4146],{"class":59,"line":86},[57,4127,1350],{"class":89},[57,4129,4130],{"class":93}," res ",[57,4132,97],{"class":89},[57,4134,4135],{"class":93}," {path ",[57,4137,97],{"class":89},[57,4139,4140],{"class":93}," path, data ",[57,4142,97],{"class":89},[57,4144,4145],{"class":135}," \"loaded\"",[57,4147,170],{"class":93},[57,4149,4150,4152,4154,4156],{"class":59,"line":103},[57,4151,1350],{"class":89},[57,4153,108],{"class":93},[57,4155,97],{"class":89},[57,4157,113],{"class":93},[57,4159,4160,4163,4165,4167],{"class":59,"line":116},[57,4161,4162],{"class":119},"    __close",[57,4164,123],{"class":89},[57,4166,126],{"class":89},[57,4168,3410],{"class":93},[57,4170,4171,4173,4175,4178,4180,4182,4184,4187],{"class":59,"line":145},[57,4172,1648],{"class":176},[57,4174,803],{"class":93},[57,4176,4177],{"class":135},"\"closing:\"",[57,4179,31],{"class":93},[57,4181,894],{"class":176},[57,4183,1874],{"class":93},[57,4185,4186],{"class":119},"path",[57,4188,1432],{"class":93},[57,4190,4191],{"class":59,"line":167},[57,4192,4193],{"class":63},"      -- release file handle, network connection, etc.\n",[57,4195,4196,4198],{"class":59,"line":173},[57,4197,1437],{"class":89},[57,4199,142],{"class":93},[57,4201,4202,4204,4206,4208,4211,4213,4216,4219,4221,4224,4227,4229],{"class":59,"line":183},[57,4203,1376],{"class":119},[57,4205,123],{"class":89},[57,4207,126],{"class":89},[57,4209,4210],{"class":93},"(t, k) ",[57,4212,132],{"class":89},[57,4214,4215],{"class":93}," t.",[57,4217,4218],{"class":119},"data",[57,4220,809],{"class":93},[57,4222,4223],{"class":176},"match",[57,4225,4226],{"class":93},"(k) ",[57,4228,139],{"class":89},[57,4230,142],{"class":93},[57,4232,4233],{"class":59,"line":195},[57,4234,1498],{"class":93},[57,4236,4237,4239,4241],{"class":59,"line":212},[57,4238,1503],{"class":89},[57,4240,788],{"class":176},[57,4242,4243],{"class":93},"(res, mt)\n",[57,4245,4246],{"class":59,"line":886},[57,4247,1513],{"class":89},[57,4249,4250],{"class":59,"line":903},[57,4251,83],{"emptyLinePlaceholder":82},[57,4253,4254],{"class":59,"line":912},[57,4255,3162],{"class":89},[57,4257,4258,4260,4263,4266,4268,4270,4272,4275],{"class":59,"line":920},[57,4259,1350],{"class":89},[57,4261,4262],{"class":93}," r ",[57,4264,4265],{"class":89},"\u003Cclose>",[57,4267,123],{"class":89},[57,4269,4120],{"class":176},[57,4271,803],{"class":93},[57,4273,4274],{"class":135},"\"config.txt\"",[57,4276,1432],{"class":93},[57,4278,4279],{"class":59,"line":926},[57,4280,4281],{"class":63},"  -- use r\n",[57,4283,4284,4287,4290,4292,4294],{"class":59,"line":2198},[57,4285,4286],{"class":176},"  print",[57,4288,4289],{"class":93},"(r.",[57,4291,4218],{"class":119},[57,4293,206],{"class":93},[57,4295,4296],{"class":63},"-- \"loaded\"\n",[57,4298,4299],{"class":59,"line":2242},[57,4300,4301],{"class":63},"  -- when this block ends (even on error), __close is called\n",[57,4303,4304],{"class":59,"line":2306},[57,4305,1513],{"class":89},[57,4307,4308],{"class":59,"line":2365},[57,4309,4310],{"class":63},"-- prints: \"closing: config.txt\"\n",[57,4312,4313],{"class":59,"line":2370},[57,4314,83],{"emptyLinePlaceholder":82},[57,4316,4317],{"class":59,"line":2376},[57,4318,4319],{"class":63},"-- Built-in: file handles support \u003Cclose> in Lua 5.4\n",[57,4321,4322],{"class":59,"line":2382},[57,4323,3162],{"class":89},[57,4325,4326,4328,4331,4333,4335,4338,4340,4343,4345,4348],{"class":59,"line":2388},[57,4327,1350],{"class":89},[57,4329,4330],{"class":93}," f ",[57,4332,4265],{"class":89},[57,4334,123],{"class":89},[57,4336,4337],{"class":176}," io.open",[57,4339,803],{"class":93},[57,4341,4342],{"class":135},"\"data.txt\"",[57,4344,31],{"class":93},[57,4346,4347],{"class":135},"\"r\"",[57,4349,1432],{"class":93},[57,4351,4352,4354,4357,4359,4361,4364,4366,4369,4371],{"class":59,"line":2394},[57,4353,2038],{"class":89},[57,4355,4356],{"class":89}," not",[57,4358,4330],{"class":93},[57,4360,2052],{"class":89},[57,4362,4363],{"class":176}," error",[57,4365,803],{"class":93},[57,4367,4368],{"class":135},"\"can't open\"",[57,4370,1857],{"class":93},[57,4372,1513],{"class":89},[57,4374,4375,4377,4380,4382,4385,4387,4390,4392,4395],{"class":59,"line":2400},[57,4376,1350],{"class":89},[57,4378,4379],{"class":93}," content ",[57,4381,97],{"class":89},[57,4383,4384],{"class":119}," f",[57,4386,809],{"class":93},[57,4388,4389],{"class":176},"read",[57,4391,803],{"class":93},[57,4393,4394],{"class":135},"\"*a\"",[57,4396,1432],{"class":93},[57,4398,4399],{"class":59,"line":2405},[57,4400,4401],{"class":63},"  -- f:close() called automatically at block end (even on error\u002Fexception)\n",[57,4403,4404],{"class":59,"line":2430},[57,4405,1513],{"class":89},[40,4407,4409,4410,4412,4413],{"id":4408},"oop-class-system-with-__index-and-__call","OOP: Class System with ",[28,4411,30],{}," and ",[28,4414,304],{},[45,4416,4417,4771,4778],{"language":47},[49,4418,4420],{"className":51,"code":4419,"language":47,"meta":53,"style":53},"-- Production class system: constructor via __call (Class(args) creates instance)\nlocal function make_class(parent)\n  local class = setmetatable({}, {\n    __index = parent,        -- class inherits methods from parent class\n    __call = function(_, ...)  -- Class(args) → Class.new(args)\n      return class.new(...)\n    end,\n  })\n  class.__index = class     -- instances inherit from class\n  return class\nend\n\nlocal Animal = make_class()\nfunction Animal.new(name)\n  return setmetatable({name = name}, Animal)\nend\nfunction Animal:speak() return self.name .. \" makes a sound\" end\n\nlocal Dog = make_class(Animal)\nfunction Dog.new(name, breed)\n  local self = Animal.new(name)  -- call parent constructor\n  self.breed = breed\n  return setmetatable(self, Dog)  -- override metatable to Dog\nend\nfunction Dog:speak() return self.name .. \" barks\" end  -- override\n\nlocal d = Dog(\"Rex\", \"Lab\")  -- __call: Dog(\"Rex\", \"Lab\") → Dog.new(...)\nprint(d:speak())  -- \"Rex barks\"\n",[28,4421,4422,4427,4439,4452,4465,4479,4495,4501,4505,4520,4527,4531,4535,4548,4561,4575,4579,4608,4612,4626,4640,4659,4673,4689,4693,4723,4727,4753],{"__ignoreMap":53},[57,4423,4424],{"class":59,"line":60},[57,4425,4426],{"class":63},"-- Production class system: constructor via __call (Class(args) creates instance)\n",[57,4428,4429,4431,4433,4436],{"class":59,"line":67},[57,4430,90],{"class":89},[57,4432,126],{"class":89},[57,4434,4435],{"class":119}," make_class",[57,4437,4438],{"class":93},"(parent)\n",[57,4440,4441,4443,4446,4448,4450],{"class":59,"line":73},[57,4442,1350],{"class":89},[57,4444,4445],{"class":93}," class ",[57,4447,97],{"class":89},[57,4449,788],{"class":176},[57,4451,840],{"class":93},[57,4453,4454,4457,4459,4462],{"class":59,"line":79},[57,4455,4456],{"class":93},"    __index ",[57,4458,97],{"class":89},[57,4460,4461],{"class":93}," parent,        ",[57,4463,4464],{"class":63},"-- class inherits methods from parent class\n",[57,4466,4467,4469,4471,4473,4476],{"class":59,"line":86},[57,4468,2918],{"class":119},[57,4470,123],{"class":89},[57,4472,126],{"class":89},[57,4474,4475],{"class":93},"(_, ...)  ",[57,4477,4478],{"class":63},"-- Class(args) → Class.new(args)\n",[57,4480,4481,4483,4486,4488,4490,4493],{"class":59,"line":103},[57,4482,1665],{"class":89},[57,4484,4485],{"class":93}," class.",[57,4487,1877],{"class":176},[57,4489,803],{"class":93},[57,4491,4492],{"class":176},"...",[57,4494,1432],{"class":93},[57,4496,4497,4499],{"class":59,"line":116},[57,4498,1437],{"class":89},[57,4500,142],{"class":93},[57,4502,4503],{"class":59,"line":145},[57,4504,1723],{"class":93},[57,4506,4507,4510,4512,4514,4517],{"class":59,"line":167},[57,4508,4509],{"class":93},"  class.",[57,4511,30],{"class":119},[57,4513,123],{"class":89},[57,4515,4516],{"class":93}," class     ",[57,4518,4519],{"class":63},"-- instances inherit from class\n",[57,4521,4522,4524],{"class":59,"line":173},[57,4523,1503],{"class":89},[57,4525,4526],{"class":93}," class\n",[57,4528,4529],{"class":59,"line":183},[57,4530,1513],{"class":89},[57,4532,4533],{"class":59,"line":195},[57,4534,83],{"emptyLinePlaceholder":82},[57,4536,4537,4539,4542,4544,4546],{"class":59,"line":212},[57,4538,90],{"class":89},[57,4540,4541],{"class":93}," Animal ",[57,4543,97],{"class":89},[57,4545,4435],{"class":176},[57,4547,3431],{"class":93},[57,4549,4550,4552,4555,4557,4559],{"class":59,"line":886},[57,4551,1868],{"class":89},[57,4553,4554],{"class":119}," Animal",[57,4556,1874],{"class":93},[57,4558,1877],{"class":119},[57,4560,1600],{"class":93},[57,4562,4563,4565,4567,4570,4572],{"class":59,"line":903},[57,4564,1503],{"class":89},[57,4566,788],{"class":176},[57,4568,4569],{"class":93},"({name ",[57,4571,97],{"class":89},[57,4573,4574],{"class":93}," name}, Animal)\n",[57,4576,4577],{"class":59,"line":912},[57,4578,1513],{"class":89},[57,4580,4581,4583,4585,4587,4590,4592,4594,4596,4598,4600,4603,4606],{"class":59,"line":920},[57,4582,1868],{"class":89},[57,4584,4554],{"class":119},[57,4586,809],{"class":93},[57,4588,4589],{"class":119},"speak",[57,4591,970],{"class":93},[57,4593,132],{"class":89},[57,4595,3457],{"class":176},[57,4597,1874],{"class":93},[57,4599,4005],{"class":119},[57,4601,4602],{"class":89}," ..",[57,4604,4605],{"class":135}," \" makes a sound\" ",[57,4607,1513],{"class":89},[57,4609,4610],{"class":59,"line":926},[57,4611,83],{"emptyLinePlaceholder":82},[57,4613,4614,4616,4619,4621,4623],{"class":59,"line":2198},[57,4615,90],{"class":89},[57,4617,4618],{"class":93}," Dog ",[57,4620,97],{"class":89},[57,4622,4435],{"class":176},[57,4624,4625],{"class":93},"(Animal)\n",[57,4627,4628,4630,4633,4635,4637],{"class":59,"line":2242},[57,4629,1868],{"class":89},[57,4631,4632],{"class":119}," Dog",[57,4634,1874],{"class":93},[57,4636,1877],{"class":119},[57,4638,4639],{"class":93},"(name, breed)\n",[57,4641,4642,4644,4646,4648,4651,4653,4656],{"class":59,"line":2306},[57,4643,1350],{"class":89},[57,4645,3457],{"class":176},[57,4647,123],{"class":89},[57,4649,4650],{"class":93}," Animal.",[57,4652,1877],{"class":176},[57,4654,4655],{"class":93},"(name)  ",[57,4657,4658],{"class":63},"-- call parent constructor\n",[57,4660,4661,4663,4665,4668,4670],{"class":59,"line":2365},[57,4662,3646],{"class":176},[57,4664,1874],{"class":93},[57,4666,4667],{"class":119},"breed",[57,4669,123],{"class":89},[57,4671,4672],{"class":93}," breed\n",[57,4674,4675,4677,4679,4681,4683,4686],{"class":59,"line":2370},[57,4676,1503],{"class":89},[57,4678,788],{"class":176},[57,4680,803],{"class":93},[57,4682,894],{"class":176},[57,4684,4685],{"class":93},", Dog)  ",[57,4687,4688],{"class":63},"-- override metatable to Dog\n",[57,4690,4691],{"class":59,"line":2376},[57,4692,1513],{"class":89},[57,4694,4695,4697,4699,4701,4703,4705,4707,4709,4711,4713,4715,4718,4720],{"class":59,"line":2382},[57,4696,1868],{"class":89},[57,4698,4632],{"class":119},[57,4700,809],{"class":93},[57,4702,4589],{"class":119},[57,4704,970],{"class":93},[57,4706,132],{"class":89},[57,4708,3457],{"class":176},[57,4710,1874],{"class":93},[57,4712,4005],{"class":119},[57,4714,4602],{"class":89},[57,4716,4717],{"class":135}," \" barks\" ",[57,4719,139],{"class":89},[57,4721,4722],{"class":63},"  -- override\n",[57,4724,4725],{"class":59,"line":2388},[57,4726,83],{"emptyLinePlaceholder":82},[57,4728,4729,4731,4734,4736,4738,4740,4743,4745,4748,4750],{"class":59,"line":2394},[57,4730,90],{"class":89},[57,4732,4733],{"class":93}," d ",[57,4735,97],{"class":89},[57,4737,4632],{"class":176},[57,4739,803],{"class":93},[57,4741,4742],{"class":135},"\"Rex\"",[57,4744,31],{"class":93},[57,4746,4747],{"class":135},"\"Lab\"",[57,4749,206],{"class":93},[57,4751,4752],{"class":63},"-- __call: Dog(\"Rex\", \"Lab\") → Dog.new(...)\n",[57,4754,4755,4757,4759,4762,4764,4766,4768],{"class":59,"line":2400},[57,4756,186],{"class":176},[57,4758,803],{"class":93},[57,4760,4761],{"class":119},"d",[57,4763,809],{"class":93},[57,4765,4589],{"class":176},[57,4767,814],{"class":93},[57,4769,4770],{"class":63},"-- \"Rex barks\"\n",[40,4772,4774,4775,4777],{"id":4773},"anti-pattern-__index-recursion","Anti-Pattern: ",[28,4776,30],{}," Recursion",[45,4779,4780,5012,5016,5025,5036,5048],{"language":47},[49,4781,4783],{"className":51,"code":4782,"language":47,"meta":53,"style":53},"-- BAD: __index function that accesses self without rawget → infinite recursion\nlocal t = setmetatable({}, {\n  __index = function(self, key)\n    return self[key] or \"default\"  -- self[key] calls __index again → INFINITE LOOP\n  end,\n})\n\n-- GOOD: use rawget to check without triggering __index\nlocal t2 = setmetatable({}, {\n  __index = function(self, key)\n    local v = rawget(self, key)\n    if v ~= nil then return v end\n    return \"default\"\n  end,\n})\n\n-- EVEN BETTER: if __index is just a default table, use table form (no recursion risk)\nlocal defaults = {timeout = 5000, retries = 3}\nlocal t3 = setmetatable({timeout = 1000}, {__index = defaults})\nprint(t3.timeout)  -- 1000 (from t3 itself)\nprint(t3.retries)  -- 3 (from defaults via __index table lookup)\n",[28,4784,4785,4790,4802,4812,4828,4834,4838,4842,4847,4860,4870,4887,4905,4912,4918,4922,4926,4931,4957,4983,4998],{"__ignoreMap":53},[57,4786,4787],{"class":59,"line":60},[57,4788,4789],{"class":63},"-- BAD: __index function that accesses self without rawget → infinite recursion\n",[57,4791,4792,4794,4796,4798,4800],{"class":59,"line":67},[57,4793,90],{"class":89},[57,4795,94],{"class":93},[57,4797,97],{"class":89},[57,4799,788],{"class":176},[57,4801,840],{"class":93},[57,4803,4804,4806,4808,4810],{"class":59,"line":73},[57,4805,148],{"class":119},[57,4807,123],{"class":89},[57,4809,126],{"class":89},[57,4811,851],{"class":93},[57,4813,4814,4816,4818,4820,4822,4825],{"class":59,"line":79},[57,4815,906],{"class":89},[57,4817,3457],{"class":176},[57,4819,3489],{"class":93},[57,4821,1897],{"class":89},[57,4823,4824],{"class":135}," \"default\"  ",[57,4826,4827],{"class":63},"-- self[key] calls __index again → INFINITE LOOP\n",[57,4829,4830,4832],{"class":59,"line":86},[57,4831,915],{"class":89},[57,4833,142],{"class":93},[57,4835,4836],{"class":59,"line":103},[57,4837,923],{"class":93},[57,4839,4840],{"class":59,"line":116},[57,4841,83],{"emptyLinePlaceholder":82},[57,4843,4844],{"class":59,"line":145},[57,4845,4846],{"class":63},"-- GOOD: use rawget to check without triggering __index\n",[57,4848,4849,4851,4854,4856,4858],{"class":59,"line":167},[57,4850,90],{"class":89},[57,4852,4853],{"class":93}," t2 ",[57,4855,97],{"class":89},[57,4857,788],{"class":176},[57,4859,840],{"class":93},[57,4861,4862,4864,4866,4868],{"class":59,"line":173},[57,4863,148],{"class":119},[57,4865,123],{"class":89},[57,4867,126],{"class":89},[57,4869,851],{"class":93},[57,4871,4872,4874,4876,4878,4881,4883,4885],{"class":59,"line":183},[57,4873,869],{"class":89},[57,4875,1638],{"class":93},[57,4877,97],{"class":89},[57,4879,4880],{"class":176}," rawget",[57,4882,803],{"class":93},[57,4884,894],{"class":176},[57,4886,864],{"class":93},[57,4888,4889,4891,4893,4895,4897,4899,4901,4903],{"class":59,"line":195},[57,4890,3470],{"class":89},[57,4892,1638],{"class":93},[57,4894,2936],{"class":89},[57,4896,2939],{"class":176},[57,4898,2942],{"class":89},[57,4900,2055],{"class":89},[57,4902,1638],{"class":93},[57,4904,1513],{"class":89},[57,4906,4907,4909],{"class":59,"line":212},[57,4908,906],{"class":89},[57,4910,4911],{"class":135}," \"default\"\n",[57,4913,4914,4916],{"class":59,"line":886},[57,4915,915],{"class":89},[57,4917,142],{"class":93},[57,4919,4920],{"class":59,"line":903},[57,4921,923],{"class":93},[57,4923,4924],{"class":59,"line":912},[57,4925,83],{"emptyLinePlaceholder":82},[57,4927,4928],{"class":59,"line":920},[57,4929,4930],{"class":63},"-- EVEN BETTER: if __index is just a default table, use table form (no recursion risk)\n",[57,4932,4933,4935,4938,4940,4943,4945,4948,4951,4953,4955],{"class":59,"line":926},[57,4934,90],{"class":89},[57,4936,4937],{"class":93}," defaults ",[57,4939,97],{"class":89},[57,4941,4942],{"class":93}," {timeout ",[57,4944,97],{"class":89},[57,4946,4947],{"class":176}," 5000",[57,4949,4950],{"class":93},", retries ",[57,4952,97],{"class":89},[57,4954,2497],{"class":176},[57,4956,170],{"class":93},[57,4958,4959,4961,4964,4966,4968,4971,4973,4976,4978,4980],{"class":59,"line":2198},[57,4960,90],{"class":89},[57,4962,4963],{"class":93}," t3 ",[57,4965,97],{"class":89},[57,4967,788],{"class":176},[57,4969,4970],{"class":93},"({timeout ",[57,4972,97],{"class":89},[57,4974,4975],{"class":176}," 1000",[57,4977,1028],{"class":93},[57,4979,97],{"class":89},[57,4981,4982],{"class":93}," defaults})\n",[57,4984,4985,4987,4990,4993,4995],{"class":59,"line":2242},[57,4986,186],{"class":176},[57,4988,4989],{"class":93},"(t3.",[57,4991,4992],{"class":119},"timeout",[57,4994,206],{"class":93},[57,4996,4997],{"class":63},"-- 1000 (from t3 itself)\n",[57,4999,5000,5002,5004,5007,5009],{"class":59,"line":2306},[57,5001,186],{"class":176},[57,5003,4989],{"class":93},[57,5005,5006],{"class":119},"retries",[57,5008,206],{"class":93},[57,5010,5011],{"class":63},"-- 3 (from defaults via __index table lookup)\n",[40,5013,5015],{"id":5014},"tips-tricks","💡 Tips & Tricks",[15,5017,5018,5024],{},[19,5019,5020,5021,5023],{},"Use ",[28,5022,30],{}," as a table for method dispatch, not a function",": Table lookup is faster — no function call per access.",[15,5026,5027,5032,5033,5035],{},[19,5028,5029,5031],{},[28,5030,611],{}," on userdata can return arbitrary values",": For C-allocated objects, ",[28,5034,611],{}," can return a meaningful size (e.g., buffer length).",[15,5037,5038,5047],{},[19,5039,5040,5041,5043,5044,5046],{},"Combine ",[28,5042,30],{}," + ",[28,5045,285],{}," for property objects",": Computed properties with validation.",[45,5049,5050],{"language":47},[49,5051,5053],{"className":51,"code":5052,"language":47,"meta":53,"style":53},"local function property_object(spec)\n  local obj = setmetatable({}, {\n    __index = function(self, k)\n      local prop = spec[k]\n      if not prop then return nil end\n      if prop.get then return prop.get(self) end\n      return rawget(self, \"_\" .. k)\n    end,\n    __newindex = function(self, k, v)\n      local prop = spec[k]\n      if not prop then rawset(self, k, v); return end  -- unknown: store directly\n      if prop.validate then prop.validate(v) end  -- type\u002Frange check\n      if prop.set then prop.set(self, v); return end\n      rawset(self, \"_\" .. k, v)  -- store in _k backing field\n    end,\n  })\n  return obj\nend\n\nlocal person = property_object {\n  age = {\n    validate = function(v)\n      assert(type(v) == \"number\" and v >= 0 and v \u003C= 150, \"invalid age\")\n    end,\n  },\n  display_name = {\n    get = function(self) return rawget(self, \"_name\") or \"anonymous\" end,\n    set = function(self, v) rawset(self, \"_name\", v or \"anonymous\") end,\n  },\n}\n\nperson.age = 30       -- OK\n-- person.age = -1     -- ERROR: invalid age\nperson.display_name = \"Alice\"\nprint(person.display_name)  -- \"Alice\"\nprint(rawget(person, \"_name\"))  -- \"Alice\" (backing field)\n",[28,5054,5055,5067,5079,5089,5101,5117,5143,5163,5169,5179,5189,5216,5238,5263,5284,5290,5294,5300,5304,5308,5321,5330,5342,5383,5389,5394,5403,5438,5475,5479,5483,5487,5503,5508,5520,5534],{"__ignoreMap":53},[57,5056,5057,5059,5061,5064],{"class":59,"line":60},[57,5058,90],{"class":89},[57,5060,126],{"class":89},[57,5062,5063],{"class":119}," property_object",[57,5065,5066],{"class":93},"(spec)\n",[57,5068,5069,5071,5073,5075,5077],{"class":59,"line":67},[57,5070,1350],{"class":89},[57,5072,3875],{"class":93},[57,5074,97],{"class":89},[57,5076,788],{"class":176},[57,5078,840],{"class":93},[57,5080,5081,5083,5085,5087],{"class":59,"line":73},[57,5082,1376],{"class":119},[57,5084,123],{"class":89},[57,5086,126],{"class":89},[57,5088,1630],{"class":93},[57,5090,5091,5093,5096,5098],{"class":59,"line":79},[57,5092,1635],{"class":89},[57,5094,5095],{"class":93}," prop ",[57,5097,97],{"class":89},[57,5099,5100],{"class":93}," spec[k]\n",[57,5102,5103,5105,5107,5109,5111,5113,5115],{"class":59,"line":86},[57,5104,2930],{"class":89},[57,5106,4356],{"class":89},[57,5108,5095],{"class":93},[57,5110,2052],{"class":89},[57,5112,2055],{"class":89},[57,5114,2939],{"class":176},[57,5116,2239],{"class":89},[57,5118,5119,5121,5124,5127,5129,5131,5133,5135,5137,5139,5141],{"class":59,"line":103},[57,5120,2930],{"class":89},[57,5122,5123],{"class":93}," prop.",[57,5125,5126],{"class":119},"get",[57,5128,2942],{"class":89},[57,5130,2055],{"class":89},[57,5132,5123],{"class":93},[57,5134,5126],{"class":176},[57,5136,803],{"class":93},[57,5138,894],{"class":176},[57,5140,1857],{"class":93},[57,5142,1513],{"class":89},[57,5144,5145,5147,5149,5151,5153,5155,5158,5160],{"class":59,"line":116},[57,5146,1665],{"class":89},[57,5148,4880],{"class":176},[57,5150,803],{"class":93},[57,5152,894],{"class":176},[57,5154,31],{"class":93},[57,5156,5157],{"class":135},"\"_\" ",[57,5159,589],{"class":89},[57,5161,5162],{"class":93}," k)\n",[57,5164,5165,5167],{"class":59,"line":145},[57,5166,1437],{"class":89},[57,5168,142],{"class":93},[57,5170,5171,5173,5175,5177],{"class":59,"line":167},[57,5172,1401],{"class":119},[57,5174,123],{"class":89},[57,5176,126],{"class":89},[57,5178,1408],{"class":93},[57,5180,5181,5183,5185,5187],{"class":59,"line":173},[57,5182,1635],{"class":89},[57,5184,5095],{"class":93},[57,5186,97],{"class":89},[57,5188,5100],{"class":93},[57,5190,5191,5193,5195,5197,5199,5202,5204,5206,5209,5211,5213],{"class":59,"line":183},[57,5192,2930],{"class":89},[57,5194,4356],{"class":89},[57,5196,5095],{"class":93},[57,5198,2052],{"class":89},[57,5200,5201],{"class":176}," rawset",[57,5203,803],{"class":93},[57,5205,894],{"class":176},[57,5207,5208],{"class":93},", k, v); ",[57,5210,132],{"class":89},[57,5212,2725],{"class":89},[57,5214,5215],{"class":63},"  -- unknown: store directly\n",[57,5217,5218,5220,5222,5225,5227,5229,5231,5233,5235],{"class":59,"line":195},[57,5219,2930],{"class":89},[57,5221,5123],{"class":93},[57,5223,5224],{"class":119},"validate",[57,5226,2942],{"class":89},[57,5228,5123],{"class":93},[57,5230,5224],{"class":176},[57,5232,1834],{"class":93},[57,5234,139],{"class":89},[57,5236,5237],{"class":63},"  -- type\u002Frange check\n",[57,5239,5240,5242,5244,5246,5248,5250,5252,5254,5256,5259,5261],{"class":59,"line":212},[57,5241,2930],{"class":89},[57,5243,5123],{"class":93},[57,5245,3578],{"class":119},[57,5247,2942],{"class":89},[57,5249,5123],{"class":93},[57,5251,3578],{"class":176},[57,5253,803],{"class":93},[57,5255,894],{"class":176},[57,5257,5258],{"class":93},", v); ",[57,5260,132],{"class":89},[57,5262,2239],{"class":89},[57,5264,5265,5268,5270,5272,5274,5276,5278,5281],{"class":59,"line":886},[57,5266,5267],{"class":176},"      rawset",[57,5269,803],{"class":93},[57,5271,894],{"class":176},[57,5273,31],{"class":93},[57,5275,5157],{"class":135},[57,5277,589],{"class":89},[57,5279,5280],{"class":93}," k, v)  ",[57,5282,5283],{"class":63},"-- store in _k backing field\n",[57,5285,5286,5288],{"class":59,"line":903},[57,5287,1437],{"class":89},[57,5289,142],{"class":93},[57,5291,5292],{"class":59,"line":912},[57,5293,1723],{"class":93},[57,5295,5296,5298],{"class":59,"line":920},[57,5297,1503],{"class":89},[57,5299,3901],{"class":93},[57,5301,5302],{"class":59,"line":926},[57,5303,1513],{"class":89},[57,5305,5306],{"class":59,"line":2198},[57,5307,83],{"emptyLinePlaceholder":82},[57,5309,5310,5312,5315,5317,5319],{"class":59,"line":2242},[57,5311,90],{"class":89},[57,5313,5314],{"class":93}," person ",[57,5316,97],{"class":89},[57,5318,5063],{"class":176},[57,5320,113],{"class":93},[57,5322,5323,5326,5328],{"class":59,"line":2306},[57,5324,5325],{"class":93},"  age ",[57,5327,97],{"class":89},[57,5329,113],{"class":93},[57,5331,5332,5335,5337,5339],{"class":59,"line":2365},[57,5333,5334],{"class":119},"    validate",[57,5336,123],{"class":89},[57,5338,126],{"class":89},[57,5340,5341],{"class":93},"(v)\n",[57,5343,5344,5347,5349,5352,5354,5356,5358,5360,5362,5365,5367,5369,5371,5373,5376,5378,5381],{"class":59,"line":2370},[57,5345,5346],{"class":176},"      assert",[57,5348,803],{"class":93},[57,5350,5351],{"class":176},"type",[57,5353,1834],{"class":93},[57,5355,539],{"class":89},[57,5357,2049],{"class":135},[57,5359,3215],{"class":89},[57,5361,1638],{"class":93},[57,5363,5364],{"class":89},">=",[57,5366,1900],{"class":176},[57,5368,2226],{"class":89},[57,5370,1638],{"class":93},[57,5372,572],{"class":89},[57,5374,5375],{"class":176}," 150",[57,5377,31],{"class":93},[57,5379,5380],{"class":135},"\"invalid age\"",[57,5382,1432],{"class":93},[57,5384,5385,5387],{"class":59,"line":2376},[57,5386,1437],{"class":89},[57,5388,142],{"class":93},[57,5390,5391],{"class":59,"line":2382},[57,5392,5393],{"class":93},"  },\n",[57,5395,5396,5399,5401],{"class":59,"line":2388},[57,5397,5398],{"class":93},"  display_name ",[57,5400,97],{"class":89},[57,5402,113],{"class":93},[57,5404,5405,5408,5410,5412,5414,5416,5418,5420,5422,5424,5427,5429,5431,5434,5436],{"class":59,"line":2394},[57,5406,5407],{"class":119},"    get",[57,5409,123],{"class":89},[57,5411,126],{"class":89},[57,5413,129],{"class":93},[57,5415,132],{"class":89},[57,5417,4880],{"class":176},[57,5419,803],{"class":93},[57,5421,894],{"class":176},[57,5423,31],{"class":93},[57,5425,5426],{"class":135},"\"_name\"",[57,5428,1857],{"class":93},[57,5430,1897],{"class":89},[57,5432,5433],{"class":135}," \"anonymous\" ",[57,5435,139],{"class":89},[57,5437,142],{"class":93},[57,5439,5440,5443,5445,5447,5450,5453,5455,5457,5459,5461,5464,5466,5469,5471,5473],{"class":59,"line":2400},[57,5441,5442],{"class":119},"    set",[57,5444,123],{"class":89},[57,5446,126],{"class":89},[57,5448,5449],{"class":93},"(self, v) ",[57,5451,5452],{"class":176},"rawset",[57,5454,803],{"class":93},[57,5456,894],{"class":176},[57,5458,31],{"class":93},[57,5460,5426],{"class":135},[57,5462,5463],{"class":93},", v ",[57,5465,1897],{"class":89},[57,5467,5468],{"class":135}," \"anonymous\"",[57,5470,1857],{"class":93},[57,5472,139],{"class":89},[57,5474,142],{"class":93},[57,5476,5477],{"class":59,"line":2405},[57,5478,5393],{"class":93},[57,5480,5481],{"class":59,"line":2430},[57,5482,170],{"class":93},[57,5484,5485],{"class":59,"line":2456},[57,5486,83],{"emptyLinePlaceholder":82},[57,5488,5489,5492,5495,5497,5500],{"class":59,"line":2472},[57,5490,5491],{"class":93},"person.",[57,5493,5494],{"class":119},"age",[57,5496,123],{"class":89},[57,5498,5499],{"class":176}," 30",[57,5501,5502],{"class":63},"       -- OK\n",[57,5504,5505],{"class":59,"line":2488},[57,5506,5507],{"class":63},"-- person.age = -1     -- ERROR: invalid age\n",[57,5509,5510,5512,5515,5517],{"class":59,"line":2506},[57,5511,5491],{"class":93},[57,5513,5514],{"class":119},"display_name",[57,5516,123],{"class":89},[57,5518,5519],{"class":135}," \"Alice\"\n",[57,5521,5522,5524,5527,5529,5531],{"class":59,"line":2521},[57,5523,186],{"class":176},[57,5525,5526],{"class":93},"(person.",[57,5528,5514],{"class":119},[57,5530,206],{"class":93},[57,5532,5533],{"class":63},"-- \"Alice\"\n",[57,5535,5536,5538,5540,5542,5545,5547,5549],{"class":59,"line":2548},[57,5537,186],{"class":176},[57,5539,803],{"class":93},[57,5541,215],{"class":176},[57,5543,5544],{"class":93},"(person, ",[57,5546,5426],{"class":135},[57,5548,2542],{"class":93},[57,5550,5551],{"class":63},"-- \"Alice\" (backing field)\n",[40,5553,5555],{"id":5554},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[15,5557,5558,5565],{},[19,5559,5560,367,5562,5564],{},[28,5561,30],{},[28,5563,285],{}," only trigger on MISSING keys",": Existing keys bypass metamethods.",[45,5567,5568],{"language":47},[49,5569,5571],{"className":51,"code":5570,"language":47,"meta":53,"style":53},"local t = setmetatable({x = 1}, {\n  __index = function() return \"default\" end,\n  __newindex = function(self, k, v) print(\"new:\", k, v); rawset(self, k, v) end,\n})\nprint(t.x)   -- 1 (x exists → __index skipped)\nprint(t.y)   -- \"default\" (y missing → __index called)\nt.x = 2      -- no print (x exists → __newindex skipped, direct assignment)\nt.z = 3      -- \"new: z 3\" (z missing → __newindex called)\n",[28,5572,5573,5592,5610,5643,5647,5661,5674,5689],{"__ignoreMap":53},[57,5574,5575,5577,5579,5581,5583,5585,5587,5589],{"class":59,"line":60},[57,5576,90],{"class":89},[57,5578,94],{"class":93},[57,5580,97],{"class":89},[57,5582,788],{"class":176},[57,5584,1889],{"class":93},[57,5586,97],{"class":89},[57,5588,1761],{"class":176},[57,5590,5591],{"class":93},"}, {\n",[57,5593,5594,5596,5598,5600,5602,5604,5606,5608],{"class":59,"line":67},[57,5595,148],{"class":119},[57,5597,123],{"class":89},[57,5599,126],{"class":89},[57,5601,970],{"class":93},[57,5603,132],{"class":89},[57,5605,160],{"class":135},[57,5607,139],{"class":89},[57,5609,142],{"class":93},[57,5611,5612,5614,5616,5618,5621,5623,5625,5628,5630,5632,5634,5636,5639,5641],{"class":59,"line":73},[57,5613,1234],{"class":119},[57,5615,123],{"class":89},[57,5617,126],{"class":89},[57,5619,5620],{"class":93},"(self, k, v) ",[57,5622,186],{"class":176},[57,5624,803],{"class":93},[57,5626,5627],{"class":135},"\"new:\"",[57,5629,5208],{"class":93},[57,5631,5452],{"class":176},[57,5633,803],{"class":93},[57,5635,894],{"class":176},[57,5637,5638],{"class":93},", k, v) ",[57,5640,139],{"class":89},[57,5642,142],{"class":93},[57,5644,5645],{"class":59,"line":79},[57,5646,923],{"class":93},[57,5648,5649,5651,5653,5655,5658],{"class":59,"line":86},[57,5650,186],{"class":176},[57,5652,200],{"class":93},[57,5654,1290],{"class":119},[57,5656,5657],{"class":93},")   ",[57,5659,5660],{"class":63},"-- 1 (x exists → __index skipped)\n",[57,5662,5663,5665,5667,5669,5671],{"class":59,"line":103},[57,5664,186],{"class":176},[57,5666,200],{"class":93},[57,5668,1854],{"class":119},[57,5670,5657],{"class":93},[57,5672,5673],{"class":63},"-- \"default\" (y missing → __index called)\n",[57,5675,5676,5679,5681,5683,5686],{"class":59,"line":116},[57,5677,5678],{"class":93},"t.",[57,5680,1290],{"class":119},[57,5682,123],{"class":89},[57,5684,5685],{"class":176}," 2",[57,5687,5688],{"class":63},"      -- no print (x exists → __newindex skipped, direct assignment)\n",[57,5690,5691,5693,5696,5698,5700],{"class":59,"line":145},[57,5692,5678],{"class":93},[57,5694,5695],{"class":119},"z",[57,5697,123],{"class":89},[57,5699,2497],{"class":176},[57,5701,5702],{"class":63},"      -- \"new: z 3\" (z missing → __newindex called)\n",[15,5704,5705,5713,5714,5717,5718,5720],{},[19,5706,5707,5709,5710,5712],{},[28,5708,5452],{}," inside ",[28,5711,285],{}," is mandatory",": Using ",[28,5715,5716],{},"self[key] = value"," would call ",[28,5719,285],{}," again → infinite recursion.",[15,5722,5723,5726,5727,5729],{},[19,5724,5725],{},"Metatables are per-instance, not per-class",": Two tables with the same metatable share metamethods, but ",[28,5728,177],{}," creates a per-table reference. Don't accidentally share mutable metatables across instances unless intended.",[45,5731,5732],{"language":47},[49,5733,5735],{"className":51,"code":5734,"language":47,"meta":53,"style":53},"-- DANGER: shared metatable with mutable state\nlocal mt = {count = 0}  -- state in metatable — shared across ALL instances!\nmt.__index = mt\nlocal a = setmetatable({}, mt)\nlocal b = setmetatable({}, mt)\nmt.count = mt.count + 1  -- which instance's count? The shared one — a and b both see it\nprint(a.count)  -- 1 (from the shared mt)\nprint(b.count)  -- 1 (same)\n",[28,5736,5737,5742,5763,5775,5788,5801,5822,5835],{"__ignoreMap":53},[57,5738,5739],{"class":59,"line":60},[57,5740,5741],{"class":63},"-- DANGER: shared metatable with mutable state\n",[57,5743,5744,5746,5748,5750,5753,5755,5757,5760],{"class":59,"line":67},[57,5745,90],{"class":89},[57,5747,108],{"class":93},[57,5749,97],{"class":89},[57,5751,5752],{"class":93}," {count ",[57,5754,97],{"class":89},[57,5756,1900],{"class":176},[57,5758,5759],{"class":93},"}  ",[57,5761,5762],{"class":63},"-- state in metatable — shared across ALL instances!\n",[57,5764,5765,5768,5770,5772],{"class":59,"line":73},[57,5766,5767],{"class":93},"mt.",[57,5769,30],{"class":119},[57,5771,123],{"class":89},[57,5773,5774],{"class":93}," mt\n",[57,5776,5777,5779,5782,5784,5786],{"class":59,"line":79},[57,5778,90],{"class":89},[57,5780,5781],{"class":93}," a ",[57,5783,97],{"class":89},[57,5785,788],{"class":176},[57,5787,2998],{"class":93},[57,5789,5790,5792,5795,5797,5799],{"class":59,"line":86},[57,5791,90],{"class":89},[57,5793,5794],{"class":93}," b ",[57,5796,97],{"class":89},[57,5798,788],{"class":176},[57,5800,2998],{"class":93},[57,5802,5803,5805,5808,5810,5813,5815,5817,5819],{"class":59,"line":103},[57,5804,5767],{"class":93},[57,5806,5807],{"class":119},"count",[57,5809,123],{"class":89},[57,5811,5812],{"class":93}," mt.",[57,5814,5807],{"class":119},[57,5816,1957],{"class":89},[57,5818,1761],{"class":176},[57,5820,5821],{"class":63},"  -- which instance's count? The shared one — a and b both see it\n",[57,5823,5824,5826,5828,5830,5832],{"class":59,"line":116},[57,5825,186],{"class":176},[57,5827,1952],{"class":93},[57,5829,5807],{"class":119},[57,5831,206],{"class":93},[57,5833,5834],{"class":63},"-- 1 (from the shared mt)\n",[57,5836,5837,5839,5842,5844,5846],{"class":59,"line":145},[57,5838,186],{"class":176},[57,5840,5841],{"class":93},"(b.",[57,5843,5807],{"class":119},[57,5845,206],{"class":93},[57,5847,5848],{"class":63},"-- 1 (same)\n",[15,5850,5851,5856,5857,5859,5860,5863,5864,1874],{},[19,5852,5853,5855],{},[28,5854,679],{}," on tables requires Lua 5.4+",": In 5.3 and earlier, ",[28,5858,679],{}," only works on userdata, not regular tables. Use ",[28,5861,5862],{},"newproxy(true)"," in 5.1\u002FLuaJIT for table ",[28,5865,679],{},[15,5867,5868,5874,5875,5877],{},[19,5869,5870,5871,5873],{},"String comparison ",[28,5872,560],{}," doesn't work",": You can't override ",[28,5876,555],{}," for strings — only table and userdata operands trigger comparison metamethods.",[40,5879,5881],{"id":5880},"spot-the-bug","🧠 Spot the Bug",[45,5883,5884,5944],{"language":47},[49,5885,5887],{"className":51,"code":5886,"language":47,"meta":53,"style":53},"local proxy = setmetatable({}, {\n  __index = function(self, key)\n    return self[key] or \"default\"\n  end,\n})\nprint(proxy.missing)\n",[28,5888,5889,5901,5911,5923,5929,5933],{"__ignoreMap":53},[57,5890,5891,5893,5895,5897,5899],{"class":59,"line":60},[57,5892,90],{"class":89},[57,5894,1353],{"class":93},[57,5896,97],{"class":89},[57,5898,788],{"class":176},[57,5900,840],{"class":93},[57,5902,5903,5905,5907,5909],{"class":59,"line":67},[57,5904,148],{"class":119},[57,5906,123],{"class":89},[57,5908,126],{"class":89},[57,5910,851],{"class":93},[57,5912,5913,5915,5917,5919,5921],{"class":59,"line":73},[57,5914,906],{"class":89},[57,5916,3457],{"class":176},[57,5918,3489],{"class":93},[57,5920,1897],{"class":89},[57,5922,4911],{"class":135},[57,5924,5925,5927],{"class":59,"line":79},[57,5926,915],{"class":89},[57,5928,142],{"class":93},[57,5930,5931],{"class":59,"line":86},[57,5932,923],{"class":93},[57,5934,5935,5937,5940,5942],{"class":59,"line":103},[57,5936,186],{"class":176},[57,5938,5939],{"class":93},"(proxy.",[57,5941,1178],{"class":119},[57,5943,1432],{"class":93},[5945,5946,5947,5951,5957,5979,5987,6023,6029],"details",{},[5948,5949,5950],"summary",{},"Answer",[15,5952,5953,5956],{},[19,5954,5955],{},"Stack overflow"," (infinite recursion).",[15,5958,5959,5962,5963,5965,5966,5962,5969,5972,5973,5975,5976,5978],{},[28,5960,5961],{},"proxy.missing"," → ",[28,5964,30],{}," called → ",[28,5967,5968],{},"self[key]",[28,5970,5971],{},"self[\"missing\"]"," → key not in self → ",[28,5974,30],{}," called again → ",[28,5977,5971],{}," → ... infinite loop until C stack overflows.",[15,5980,5981,5982,5984,5985,809],{},"Fix: use ",[28,5983,215],{}," to check without triggering ",[28,5986,30],{},[49,5988,5990],{"className":51,"code":5989,"language":47,"meta":53,"style":53},"__index = function(self, key)\n  return rawget(self, key) or \"default\"\nend\n",[28,5991,5992,6002,6019],{"__ignoreMap":53},[57,5993,5994,5996,5998,6000],{"class":59,"line":60},[57,5995,30],{"class":119},[57,5997,123],{"class":89},[57,5999,126],{"class":89},[57,6001,851],{"class":93},[57,6003,6004,6006,6008,6010,6012,6015,6017],{"class":59,"line":67},[57,6005,1503],{"class":89},[57,6007,4880],{"class":176},[57,6009,803],{"class":93},[57,6011,894],{"class":176},[57,6013,6014],{"class":93},", key) ",[57,6016,1897],{"class":89},[57,6018,4911],{"class":135},[57,6020,6021],{"class":59,"line":73},[57,6022,1513],{"class":89},[15,6024,6025,6026,6028],{},"Or use ",[28,6027,30],{}," as a table (no recursion risk):",[49,6030,6032],{"className":51,"code":6031,"language":47,"meta":53,"style":53},"__index = {default = \"value\"}\n",[28,6033,6034],{"__ignoreMap":53},[57,6035,6036,6039,6041,6044,6046,6049],{"class":59,"line":60},[57,6037,6038],{"class":93},"__index ",[57,6040,97],{"class":89},[57,6042,6043],{"class":93}," {default ",[57,6045,97],{"class":89},[57,6047,6048],{"class":135}," \"value\"",[57,6050,170],{"class":93},[6052,6053,6054],"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 .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}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":53,"searchDepth":67,"depth":67,"links":6056},[6057,6058,6059,6061,6066,6067,6069,6071,6073,6075,6076],{"id":42,"depth":67,"text":43},{"id":229,"depth":67,"text":230},{"id":717,"depth":67,"text":6060},"__index — Fallback Lookup",{"id":1187,"depth":67,"text":6062,"children":6063},"__newindex — Intercept Assignment",[6064,6065],{"id":1315,"depth":73,"text":1316},{"id":1580,"depth":73,"text":1581},{"id":1784,"depth":67,"text":1785},{"id":2865,"depth":67,"text":6068},"__call — Callable Tables",{"id":3347,"depth":67,"text":6070},"__pairs \u002F __len — Custom Iteration & Length",{"id":3782,"depth":67,"text":6072},"Weak Tables & __gc",{"id":4087,"depth":67,"text":6074},"__close — Resource Management (Lua 5.4+)",{"id":5554,"depth":67,"text":5555},{"id":5880,"depth":67,"text":5881},"[object Object]","md",{},"\u002Flua\u002F07-metatables-and-metamethods",{"title":5,"description":6082},{"Deep-dive into Lua's metaprogramming":6083},"every metamethod (__index, __newindex, __call, __add, __eq, __lt, __pairs, __len, __gc, __close), operator overloading for math types, OOP via __index chains, read-only and logging proxy tables, weak references for caches, and __gc\u002F__close for resource management.","lua\u002F07-metatables-and-metamethods","mt_BulCsxBCDOZbfRA8JrUoHWsMwtWeS88D7968XKwU",1789924650329]