LuaCompiler.cs 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286
  1. using Lua.Internal;
  2. using Lua.CodeAnalysis.Syntax;
  3. using Lua.CodeAnalysis.Syntax.Nodes;
  4. using Lua.Runtime;
  5. namespace Lua.CodeAnalysis.Compilation;
  6. public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bool>
  7. {
  8. public static readonly LuaCompiler Default = new();
  9. public Chunk Compile(string source, string? chunkName = null)
  10. {
  11. return Compile(LuaSyntaxTree.Parse(source, chunkName), chunkName);
  12. }
  13. /// <summary>
  14. /// Returns a compiled chunk of the syntax tree.
  15. /// </summary>
  16. public Chunk Compile(LuaSyntaxTree syntaxTree, string? chunkName = null)
  17. {
  18. using var context = FunctionCompilationContext.Create(null);
  19. // set global enviroment upvalue
  20. context.AddUpValue(new()
  21. {
  22. Name = "_ENV".AsMemory(),
  23. Id = 0,
  24. Index = -1,
  25. IsInRegister = false,
  26. });
  27. context.ChunkName = chunkName;
  28. syntaxTree.Accept(this, context.Scope);
  29. return context.ToChunk();
  30. }
  31. // Syntax Tree
  32. public bool VisitSyntaxTree(LuaSyntaxTree node, ScopeCompilationContext context)
  33. {
  34. foreach (var childNode in node.Nodes)
  35. {
  36. childNode.Accept(this, context);
  37. }
  38. return true;
  39. }
  40. // Literals
  41. public bool VisitNilLiteralNode(NilLiteralNode node, ScopeCompilationContext context)
  42. {
  43. context.PushInstruction(Instruction.LoadNil(context.StackPosition, 1), node.Position, true);
  44. return true;
  45. }
  46. public bool VisitBooleanLiteralNode(BooleanLiteralNode node, ScopeCompilationContext context)
  47. {
  48. context.PushInstruction(Instruction.LoadBool(context.StackPosition, (ushort)(node.Value ? 1 : 0), 0), node.Position, true);
  49. return true;
  50. }
  51. public bool VisitNumericLiteralNode(NumericLiteralNode node, ScopeCompilationContext context)
  52. {
  53. var index = context.Function.GetConstantIndex(node.Value);
  54. context.PushInstruction(Instruction.LoadK(context.StackPosition, index), node.Position, true);
  55. return true;
  56. }
  57. public bool VisitStringLiteralNode(StringLiteralNode node, ScopeCompilationContext context)
  58. {
  59. string? str;
  60. if (node.IsShortLiteral)
  61. {
  62. if (!StringHelper.TryFromStringLiteral(node.Text.Span, out str))
  63. {
  64. throw new LuaParseException(context.Function.ChunkName, node.Position, $"invalid escape sequence near '{node.Text}'");
  65. }
  66. }
  67. else
  68. {
  69. str = node.Text.ToString();
  70. }
  71. var index = context.Function.GetConstantIndex(str);
  72. context.PushInstruction(Instruction.LoadK(context.StackPosition, index), node.Position, true);
  73. return true;
  74. }
  75. // identifier
  76. public bool VisitIdentifierNode(IdentifierNode node, ScopeCompilationContext context)
  77. {
  78. GetOrLoadIdentifier(node.Name, context, node.Position, false);
  79. return true;
  80. }
  81. // vararg
  82. public bool VisitVariableArgumentsExpressionNode(VariableArgumentsExpressionNode node, ScopeCompilationContext context)
  83. {
  84. CompileVariableArgumentsExpression(node, context, 1);
  85. return true;
  86. }
  87. void CompileVariableArgumentsExpression(VariableArgumentsExpressionNode node, ScopeCompilationContext context, int resultCount)
  88. {
  89. context.PushInstruction(Instruction.VarArg(context.StackPosition, (ushort)(resultCount == -1 ? 0 : resultCount + 1)), node.Position, true);
  90. }
  91. // Unary/Binary expression
  92. public bool VisitUnaryExpressionNode(UnaryExpressionNode node, ScopeCompilationContext context)
  93. {
  94. var b = context.StackPosition;
  95. node.Node.Accept(this, context);
  96. switch (node.Operator)
  97. {
  98. case UnaryOperator.Negate:
  99. context.PushInstruction(Instruction.Unm(b, b), node.Position);
  100. break;
  101. case UnaryOperator.Not:
  102. context.PushInstruction(Instruction.Not(b, b), node.Position);
  103. break;
  104. case UnaryOperator.Length:
  105. context.PushInstruction(Instruction.Len(b, b), node.Position);
  106. break;
  107. }
  108. return true;
  109. }
  110. public bool VisitBinaryExpressionNode(BinaryExpressionNode node, ScopeCompilationContext context)
  111. {
  112. var r = context.StackPosition;
  113. if (node.OperatorType is BinaryOperator.And or BinaryOperator.Or)
  114. {
  115. byte a;
  116. if (node.LeftNode is IdentifierNode leftIdentifier)
  117. {
  118. a = GetOrLoadIdentifier(leftIdentifier.Name, context, leftIdentifier.Position, true);
  119. }
  120. else
  121. {
  122. node.LeftNode.Accept(this, context);
  123. a = context.StackTopPosition;
  124. }
  125. context.PushInstruction(Instruction.Test(a, (byte)(node.OperatorType is BinaryOperator.And ? 0 : 1)), node.Position);
  126. var testJmpIndex = context.Function.Instructions.Length;
  127. context.PushInstruction(Instruction.Jmp(0, 0), node.Position);
  128. context.StackPosition = r;
  129. node.RightNode.Accept(this, context);
  130. context.Function.Instructions[testJmpIndex].SBx = context.Function.Instructions.Length - testJmpIndex - 1;
  131. }
  132. else
  133. {
  134. var b = (ushort)GetRKIndex(node.LeftNode, context);
  135. var c = (ushort)GetRKIndex(node.RightNode, context);
  136. switch (node.OperatorType)
  137. {
  138. case BinaryOperator.Addition:
  139. context.PushInstruction(Instruction.Add(r, b, c), node.Position);
  140. break;
  141. case BinaryOperator.Subtraction:
  142. context.PushInstruction(Instruction.Sub(r, b, c), node.Position);
  143. break;
  144. case BinaryOperator.Multiplication:
  145. context.PushInstruction(Instruction.Mul(r, b, c), node.Position);
  146. break;
  147. case BinaryOperator.Division:
  148. context.PushInstruction(Instruction.Div(r, b, c), node.Position);
  149. break;
  150. case BinaryOperator.Modulo:
  151. context.PushInstruction(Instruction.Mod(r, b, c), node.Position);
  152. break;
  153. case BinaryOperator.Exponentiation:
  154. context.PushInstruction(Instruction.Pow(r, b, c), node.Position);
  155. break;
  156. case BinaryOperator.Equality:
  157. context.PushInstruction(Instruction.Eq(1, b, c), node.Position);
  158. context.PushInstruction(Instruction.LoadBool(r, 1, 1), node.Position);
  159. context.PushInstruction(Instruction.LoadBool(r, 0, 0), node.Position);
  160. break;
  161. case BinaryOperator.Inequality:
  162. context.PushInstruction(Instruction.Eq(0, b, c), node.Position);
  163. context.PushInstruction(Instruction.LoadBool(r, 1, 1), node.Position);
  164. context.PushInstruction(Instruction.LoadBool(r, 0, 0), node.Position);
  165. break;
  166. case BinaryOperator.GreaterThan:
  167. context.PushInstruction(Instruction.Lt(1, c, b), node.Position);
  168. context.PushInstruction(Instruction.LoadBool(r, 1, 1), node.Position);
  169. context.PushInstruction(Instruction.LoadBool(r, 0, 0), node.Position);
  170. break;
  171. case BinaryOperator.GreaterThanOrEqual:
  172. context.PushInstruction(Instruction.Le(1, c, b), node.Position);
  173. context.PushInstruction(Instruction.LoadBool(r, 1, 1), node.Position);
  174. context.PushInstruction(Instruction.LoadBool(r, 0, 0), node.Position);
  175. break;
  176. case BinaryOperator.LessThan:
  177. context.PushInstruction(Instruction.Lt(1, b, c), node.Position);
  178. context.PushInstruction(Instruction.LoadBool(r, 1, 1), node.Position);
  179. context.PushInstruction(Instruction.LoadBool(r, 0, 0), node.Position);
  180. break;
  181. case BinaryOperator.LessThanOrEqual:
  182. context.PushInstruction(Instruction.Le(1, b, c), node.Position);
  183. context.PushInstruction(Instruction.LoadBool(r, 1, 1), node.Position);
  184. context.PushInstruction(Instruction.LoadBool(r, 0, 0), node.Position);
  185. break;
  186. case BinaryOperator.Concat:
  187. context.PushInstruction(Instruction.Concat(r, b, c), node.Position);
  188. break;
  189. }
  190. context.StackPosition = (byte)(r + 1);
  191. }
  192. return true;
  193. }
  194. public bool VisitGroupedExpressionNode(GroupedExpressionNode node, ScopeCompilationContext context)
  195. {
  196. return node.Expression.Accept(this, context);
  197. }
  198. // table
  199. public bool VisitTableConstructorExpressionNode(TableConstructorExpressionNode node, ScopeCompilationContext context)
  200. {
  201. var tableRegisterIndex = context.StackPosition;
  202. var newTableInstructionIndex = context.Function.Instructions.Length;
  203. context.PushInstruction(Instruction.NewTable(tableRegisterIndex, 0, 0), node.Position, true);
  204. var currentArrayChunkSize = 0;
  205. ushort hashMapSize = 0;
  206. ushort arrayBlock = 1;
  207. ListTableConstructorField? lastField = null;
  208. if (node.Fields.LastOrDefault() is ListTableConstructorField t)
  209. {
  210. lastField = t;
  211. }
  212. foreach (var group in node.Fields.GroupConsecutiveBy(x => x.GetType()))
  213. {
  214. foreach (var field in group)
  215. {
  216. var p = context.StackPosition;
  217. switch (field)
  218. {
  219. case ListTableConstructorField listItem:
  220. context.StackPosition = (byte)(p + currentArrayChunkSize - 50 * (arrayBlock - 1));
  221. // For the last element, we need to take into account variable arguments and multiple return values.
  222. if (listItem == lastField)
  223. {
  224. switch (listItem.Expression)
  225. {
  226. case CallFunctionExpressionNode call:
  227. CompileCallFunctionExpression(call, context, false, -1);
  228. break;
  229. case CallTableMethodExpressionNode method:
  230. CompileTableMethod(method, context, false, -1);
  231. break;
  232. case VariableArgumentsExpressionNode varArg:
  233. CompileVariableArgumentsExpression(varArg, context, -1);
  234. break;
  235. default:
  236. listItem.Expression.Accept(this, context);
  237. break;
  238. }
  239. context.PushInstruction(Instruction.SetList(tableRegisterIndex, 0, arrayBlock), listItem.Position);
  240. currentArrayChunkSize = 0;
  241. }
  242. else
  243. {
  244. listItem.Expression.Accept(this, context);
  245. currentArrayChunkSize++;
  246. if (currentArrayChunkSize == 50)
  247. {
  248. context.PushInstruction(Instruction.SetList(tableRegisterIndex, 50, arrayBlock), listItem.Position);
  249. currentArrayChunkSize = 0;
  250. arrayBlock++;
  251. }
  252. }
  253. break;
  254. case RecordTableConstructorField recordItem:
  255. recordItem.ValueExpression.Accept(this, context);
  256. var keyConstIndex = context.Function.GetConstantIndex(recordItem.Key) + 256;
  257. context.PushInstruction(Instruction.SetTable(tableRegisterIndex, (ushort)keyConstIndex, p), recordItem.Position);
  258. hashMapSize++;
  259. break;
  260. case GeneralTableConstructorField generalItem:
  261. var keyIndex = context.StackPosition;
  262. generalItem.KeyExpression.Accept(this, context);
  263. var valueIndex = context.StackPosition;
  264. generalItem.ValueExpression.Accept(this, context);
  265. context.PushInstruction(Instruction.SetTable(tableRegisterIndex, keyIndex, valueIndex), generalItem.Position);
  266. hashMapSize++;
  267. break;
  268. default:
  269. throw new NotSupportedException();
  270. }
  271. context.StackPosition = p;
  272. }
  273. if (currentArrayChunkSize > 0)
  274. {
  275. context.PushInstruction(Instruction.SetList(tableRegisterIndex, (ushort)currentArrayChunkSize, arrayBlock), node.Position);
  276. currentArrayChunkSize = 0;
  277. arrayBlock = 1;
  278. }
  279. }
  280. context.Function.Instructions[newTableInstructionIndex].B = (ushort)(currentArrayChunkSize + (arrayBlock - 1) * 50);
  281. context.Function.Instructions[newTableInstructionIndex].C = hashMapSize;
  282. return true;
  283. }
  284. public bool VisitTableIndexerAccessExpressionNode(TableIndexerAccessExpressionNode node, ScopeCompilationContext context)
  285. {
  286. // load table
  287. var tablePosition = context.StackPosition;
  288. node.TableNode.Accept(this, context);
  289. // load key
  290. var keyPosition = (ushort)GetRKIndex(node.KeyNode, context);
  291. // push interuction
  292. context.PushInstruction(Instruction.GetTable(tablePosition, tablePosition, keyPosition), node.Position);
  293. context.StackPosition = (byte)(tablePosition + 1);
  294. return true;
  295. }
  296. public bool VisitTableMemberAccessExpressionNode(TableMemberAccessExpressionNode node, ScopeCompilationContext context)
  297. {
  298. // load table
  299. var tablePosition = context.StackPosition;
  300. node.TableNode.Accept(this, context);
  301. // load key
  302. var keyIndex = context.Function.GetConstantIndex(node.MemberName) + 256;
  303. // push interuction
  304. context.PushInstruction(Instruction.GetTable(tablePosition, tablePosition, (ushort)keyIndex), node.Position);
  305. context.StackPosition = (byte)(tablePosition + 1);
  306. return true;
  307. }
  308. public bool VisitCallTableMethodExpressionNode(CallTableMethodExpressionNode node, ScopeCompilationContext context)
  309. {
  310. CompileTableMethod(node, context, false, 1);
  311. return true;
  312. }
  313. public bool VisitCallTableMethodStatementNode(CallTableMethodStatementNode node, ScopeCompilationContext context)
  314. {
  315. CompileTableMethod(node.Expression, context, false, 0);
  316. return true;
  317. }
  318. void CompileTableMethod(CallTableMethodExpressionNode node, ScopeCompilationContext context, bool isTailCall, int resultCount)
  319. {
  320. // load table
  321. var tablePosition = context.StackPosition;
  322. node.TableNode.Accept(this, context);
  323. // load key
  324. var keyIndex = context.Function.GetConstantIndex(node.MethodName) + 256;
  325. // get closure
  326. context.PushInstruction(Instruction.Self(tablePosition, tablePosition, (ushort)keyIndex), node.Position);
  327. context.StackPosition = (byte)(tablePosition + 2);
  328. // load arguments
  329. var b = node.ArgumentNodes.Length + 2;
  330. if (node.ArgumentNodes.Length > 0 && !IsFixedNumberOfReturnValues(node.ArgumentNodes[^1]))
  331. {
  332. b = 0;
  333. }
  334. CompileExpressionList(node, node.ArgumentNodes, b - 2, context);
  335. // push call interuction
  336. if (isTailCall)
  337. {
  338. context.PushInstruction(Instruction.TailCall(tablePosition, (ushort)b, 0), node.Position);
  339. context.StackPosition = tablePosition;
  340. }
  341. else
  342. {
  343. context.PushInstruction(Instruction.Call(tablePosition, (ushort)b, (ushort)(resultCount < 0 ? 0 : resultCount + 1)), node.Position);
  344. context.StackPosition = (byte)(tablePosition + resultCount);
  345. }
  346. }
  347. // return
  348. public bool VisitReturnStatementNode(ReturnStatementNode node, ScopeCompilationContext context)
  349. {
  350. ushort b;
  351. // tail call
  352. if (node.Nodes.Length == 1)
  353. {
  354. var lastNode = node.Nodes[^1];
  355. if (lastNode is CallFunctionExpressionNode call)
  356. {
  357. CompileCallFunctionExpression(call, context, true, -1);
  358. return true;
  359. }
  360. else if (lastNode is CallTableMethodExpressionNode callMethod)
  361. {
  362. CompileTableMethod(callMethod, context, true, -1);
  363. return true;
  364. }
  365. }
  366. b = node.Nodes.Length > 0 && !IsFixedNumberOfReturnValues(node.Nodes[^1])
  367. ? (ushort)0
  368. : (ushort)(node.Nodes.Length + 1);
  369. var a = context.StackPosition;
  370. CompileExpressionList(node, node.Nodes, b - 1, context);
  371. context.PushInstruction(Instruction.Return(a, b), node.Position);
  372. return true;
  373. }
  374. // assignment
  375. public bool VisitLocalAssignmentStatementNode(LocalAssignmentStatementNode node, ScopeCompilationContext context)
  376. {
  377. var startPosition = context.StackPosition;
  378. CompileExpressionList(node, node.RightNodes, node.LeftNodes.Length, context);
  379. for (int i = 0; i < node.Identifiers.Length; i++)
  380. {
  381. context.StackPosition = (byte)(startPosition + i + 1);
  382. var identifier = node.Identifiers[i];
  383. if (context.TryGetLocalVariableInThisScope(identifier.Name, out var variable))
  384. {
  385. // assign local variable
  386. context.PushInstruction(Instruction.Move(variable.RegisterIndex, (ushort)(context.StackPosition - 1)), node.Position, true);
  387. }
  388. else
  389. {
  390. // register local variable
  391. context.AddLocalVariable(identifier.Name, new()
  392. {
  393. RegisterIndex = (byte)(context.StackPosition - 1),
  394. });
  395. }
  396. }
  397. return true;
  398. }
  399. public bool VisitAssignmentStatementNode(AssignmentStatementNode node, ScopeCompilationContext context)
  400. {
  401. var startPosition = context.StackPosition;
  402. CompileExpressionList(node, node.RightNodes, node.LeftNodes.Length, context);
  403. for (int i = 0; i < node.LeftNodes.Length; i++)
  404. {
  405. context.StackPosition = (byte)(startPosition + i + 1);
  406. var leftNode = node.LeftNodes[i];
  407. switch (leftNode)
  408. {
  409. case IdentifierNode identifier:
  410. {
  411. if (context.TryGetLocalVariable(identifier.Name, out var variable))
  412. {
  413. // assign local variable
  414. context.PushInstruction(Instruction.Move(variable.RegisterIndex, (ushort)(context.StackPosition - 1)), node.Position, true);
  415. }
  416. else if (context.Function.TryGetUpValue(identifier.Name, out var upValue))
  417. {
  418. // assign upvalue
  419. context.PushInstruction(Instruction.SetUpVal((byte)(context.StackPosition - 1), (ushort)upValue.Id), node.Position);
  420. }
  421. else if (context.TryGetLocalVariable("_ENV".AsMemory(), out variable))
  422. {
  423. // assign env element
  424. var index = context.Function.GetConstantIndex(identifier.Name.ToString()) + 256;
  425. context.PushInstruction(Instruction.SetTable(variable.RegisterIndex, (ushort)index, (ushort)(context.StackPosition - 1)), node.Position);
  426. }
  427. else
  428. {
  429. // assign global variable
  430. var index = context.Function.GetConstantIndex(identifier.Name.ToString()) + 256;
  431. context.PushInstruction(Instruction.SetTabUp(0, (ushort)index, (ushort)(context.StackPosition - 1)), node.Position);
  432. }
  433. }
  434. break;
  435. case TableIndexerAccessExpressionNode tableIndexer:
  436. {
  437. var valueIndex = context.StackPosition - 1;
  438. tableIndexer.TableNode.Accept(this, context);
  439. var tableIndex = context.StackPosition - 1;
  440. tableIndexer.KeyNode.Accept(this, context);
  441. var keyIndex = context.StackPosition - 1;
  442. context.PushInstruction(Instruction.SetTable((byte)tableIndex, (ushort)keyIndex, (ushort)valueIndex), node.Position);
  443. }
  444. break;
  445. case TableMemberAccessExpressionNode tableMember:
  446. {
  447. var valueIndex = context.StackPosition - 1;
  448. tableMember.TableNode.Accept(this, context);
  449. var tableIndex = context.StackPosition - 1;
  450. var keyIndex = context.Function.GetConstantIndex(tableMember.MemberName) + 256;
  451. context.PushInstruction(Instruction.SetTable((byte)tableIndex, (ushort)keyIndex, (ushort)valueIndex), node.Position);
  452. }
  453. break;
  454. default:
  455. throw new LuaParseException(default, default, "An error occurred while parsing the code"); // TODO: add message
  456. }
  457. }
  458. context.StackPosition = startPosition;
  459. return true;
  460. }
  461. // function call
  462. public bool VisitCallFunctionStatementNode(CallFunctionStatementNode node, ScopeCompilationContext context)
  463. {
  464. CompileCallFunctionExpression(node.Expression, context, false, 0);
  465. return true;
  466. }
  467. public bool VisitCallFunctionExpressionNode(CallFunctionExpressionNode node, ScopeCompilationContext context)
  468. {
  469. CompileCallFunctionExpression(node, context, false, 1);
  470. return true;
  471. }
  472. void CompileCallFunctionExpression(CallFunctionExpressionNode node, ScopeCompilationContext context, bool isTailCall, int resultCount)
  473. {
  474. // get closure
  475. var r = context.StackPosition;
  476. node.FunctionNode.Accept(this, context);
  477. // load arguments
  478. var b = node.ArgumentNodes.Length + 1;
  479. if (node.ArgumentNodes.Length > 0 && !IsFixedNumberOfReturnValues(node.ArgumentNodes[^1]))
  480. {
  481. b = 0;
  482. }
  483. CompileExpressionList(node, node.ArgumentNodes, b - 1, context);
  484. // push call interuction
  485. if (isTailCall)
  486. {
  487. context.PushInstruction(Instruction.TailCall(r, (ushort)b, 0), node.Position);
  488. context.StackPosition = r;
  489. }
  490. else
  491. {
  492. context.PushInstruction(Instruction.Call(r, (ushort)b, (ushort)(resultCount == -1 ? 0 : resultCount + 1)), node.Position);
  493. context.StackPosition = (byte)(r + resultCount);
  494. }
  495. }
  496. // function declaration
  497. public bool VisitFunctionDeclarationExpressionNode(FunctionDeclarationExpressionNode node, ScopeCompilationContext context)
  498. {
  499. var funcIndex = CompileFunctionProto(ReadOnlyMemory<char>.Empty, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length, node.HasVariableArguments, false);
  500. // push closure instruction
  501. context.PushInstruction(Instruction.Closure(context.StackPosition, funcIndex), node.Position, true);
  502. return true;
  503. }
  504. public bool VisitLocalFunctionDeclarationStatementNode(LocalFunctionDeclarationStatementNode node, ScopeCompilationContext context)
  505. {
  506. // assign local variable
  507. context.AddLocalVariable(node.Name, new()
  508. {
  509. RegisterIndex = context.StackPosition,
  510. });
  511. // compile function
  512. var funcIndex = CompileFunctionProto(node.Name, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length, node.HasVariableArguments, false);
  513. // push closure instruction
  514. context.PushInstruction(Instruction.Closure(context.StackPosition, funcIndex), node.Position, true);
  515. return true;
  516. }
  517. public bool VisitFunctionDeclarationStatementNode(FunctionDeclarationStatementNode node, ScopeCompilationContext context)
  518. {
  519. var funcIndex = CompileFunctionProto(node.Name, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length, node.HasVariableArguments, false);
  520. // add closure
  521. var index = context.Function.GetConstantIndex(node.Name.ToString());
  522. // push closure instruction
  523. context.PushInstruction(Instruction.Closure(context.StackPosition, funcIndex), node.Position, true);
  524. if (context.TryGetLocalVariableInThisScope(node.Name, out var variable))
  525. {
  526. // assign local variable
  527. context.PushInstruction(Instruction.Move(variable.RegisterIndex, (ushort)(context.StackPosition - 1)), node.Position, true);
  528. }
  529. else
  530. {
  531. // assign global variable
  532. context.PushInstruction(Instruction.SetTabUp(0, (ushort)(index + 256), (ushort)(context.StackPosition - 1)), node.Position);
  533. }
  534. return true;
  535. }
  536. public bool VisitTableMethodDeclarationStatementNode(TableMethodDeclarationStatementNode node, ScopeCompilationContext context)
  537. {
  538. var funcIdentifier = node.MemberPath[^1];
  539. var funcIndex = CompileFunctionProto(funcIdentifier.Name, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length + 1, node.HasVariableArguments, node.HasSelfParameter);
  540. // add closure
  541. var index = context.Function.GetConstantIndex(funcIdentifier.Name.ToString());
  542. var r = context.StackPosition;
  543. // assign global variable
  544. var first = node.MemberPath[0];
  545. var tableIndex = GetOrLoadIdentifier(first.Name, context, first.Position, true);
  546. for (int i = 1; i < node.MemberPath.Length - 1; i++)
  547. {
  548. var member = node.MemberPath[i];
  549. var constant = context.Function.GetConstantIndex(member.Name.ToString());
  550. context.PushInstruction(Instruction.GetTable(context.StackPosition, tableIndex, (ushort)(constant + 256)), member.Position, true);
  551. tableIndex = context.StackTopPosition;
  552. }
  553. // push closure instruction
  554. var closureIndex = context.StackPosition;
  555. context.PushInstruction(Instruction.Closure(closureIndex, funcIndex), node.Position, true);
  556. // set table
  557. context.PushInstruction(Instruction.SetTable(tableIndex, (ushort)(index + 256), closureIndex), funcIdentifier.Position);
  558. context.StackPosition = r;
  559. return true;
  560. }
  561. int CompileFunctionProto(ReadOnlyMemory<char> functionName, ScopeCompilationContext context, IdentifierNode[] parameters, SyntaxNode[] statements, int parameterCount, bool hasVarArg, bool hasSelfParameter)
  562. {
  563. using var funcContext = context.CreateChildFunction();
  564. funcContext.ChunkName = functionName.ToString();
  565. funcContext.ParameterCount = parameterCount;
  566. funcContext.HasVariableArguments = hasVarArg;
  567. if (hasSelfParameter)
  568. {
  569. funcContext.Scope.AddLocalVariable("self".AsMemory(), new()
  570. {
  571. RegisterIndex = 0,
  572. });
  573. funcContext.Scope.StackPosition++;
  574. }
  575. // add arguments
  576. for (int i = 0; i < parameters.Length; i++)
  577. {
  578. var parameter = parameters[i];
  579. funcContext.Scope.AddLocalVariable(parameter.Name, new()
  580. {
  581. RegisterIndex = (byte)(i + (hasSelfParameter ? 1 : 0)),
  582. });
  583. funcContext.Scope.StackPosition++;
  584. }
  585. foreach (var statement in statements)
  586. {
  587. statement.Accept(this, funcContext.Scope);
  588. }
  589. // compile function
  590. var chunk = funcContext.ToChunk();
  591. int index;
  592. if (functionName.Length == 0)
  593. {
  594. // anonymous function
  595. context.Function.AddFunctionProto(chunk, out index);
  596. }
  597. else
  598. {
  599. context.Function.AddOrSetFunctionProto(functionName, chunk, out index);
  600. }
  601. return index;
  602. }
  603. // control statements
  604. public bool VisitDoStatementNode(DoStatementNode node, ScopeCompilationContext context)
  605. {
  606. using var scopeContext = context.CreateChildScope();
  607. foreach (var childNode in node.StatementNodes)
  608. {
  609. childNode.Accept(this, scopeContext);
  610. }
  611. scopeContext.TryPushCloseUpValue(scopeContext.StackTopPosition, node.Position);
  612. return true;
  613. }
  614. public bool VisitBreakStatementNode(BreakStatementNode node, ScopeCompilationContext context)
  615. {
  616. context.Function.AddUnresolvedBreak(new()
  617. {
  618. Index = context.Function.Instructions.Length
  619. }, node.Position);
  620. context.PushInstruction(Instruction.Jmp(0, 0), node.Position);
  621. return true;
  622. }
  623. public bool VisitIfStatementNode(IfStatementNode node, ScopeCompilationContext context)
  624. {
  625. using var endJumpIndexList = new PooledList<int>(8);
  626. var hasElse = node.ElseNodes.Length > 0;
  627. // if
  628. using (var scopeContext = context.CreateChildScope())
  629. {
  630. CompileConditionNode(node.IfNode.ConditionNode, scopeContext, true);
  631. var ifPosition = scopeContext.Function.Instructions.Length;
  632. scopeContext.PushInstruction(Instruction.Jmp(0, 0), node.Position);
  633. foreach (var childNode in node.IfNode.ThenNodes)
  634. {
  635. childNode.Accept(this, scopeContext);
  636. }
  637. if (hasElse)
  638. {
  639. endJumpIndexList.Add(scopeContext.Function.Instructions.Length);
  640. var a = scopeContext.HasCapturedLocalVariables ? scopeContext.StackPosition : (byte)0;
  641. scopeContext.PushInstruction(Instruction.Jmp(a, 0), node.Position, true);
  642. }
  643. else
  644. {
  645. scopeContext.TryPushCloseUpValue(scopeContext.StackPosition, node.Position);
  646. }
  647. scopeContext.Function.Instructions[ifPosition].SBx = scopeContext.Function.Instructions.Length - 1 - ifPosition;
  648. }
  649. // elseif
  650. foreach (var elseIf in node.ElseIfNodes)
  651. {
  652. using var scopeContext = context.CreateChildScope();
  653. CompileConditionNode(elseIf.ConditionNode, scopeContext, true);
  654. var elseifPosition = scopeContext.Function.Instructions.Length;
  655. scopeContext.PushInstruction(Instruction.Jmp(0, 0), node.Position);
  656. foreach (var childNode in elseIf.ThenNodes)
  657. {
  658. childNode.Accept(this, scopeContext);
  659. }
  660. // skip if node doesn't have else statements
  661. if (hasElse)
  662. {
  663. endJumpIndexList.Add(scopeContext.Function.Instructions.Length);
  664. var a = scopeContext.HasCapturedLocalVariables ? scopeContext.StackPosition : (byte)0;
  665. scopeContext.PushInstruction(Instruction.Jmp(a, 0), node.Position);
  666. }
  667. else
  668. {
  669. scopeContext.TryPushCloseUpValue(scopeContext.StackPosition, node.Position);
  670. }
  671. scopeContext.Function.Instructions[elseifPosition].SBx = scopeContext.Function.Instructions.Length - 1 - elseifPosition;
  672. }
  673. // else nodes
  674. using (var scopeContext = context.CreateChildScope())
  675. {
  676. foreach (var childNode in node.ElseNodes)
  677. {
  678. childNode.Accept(this, scopeContext);
  679. }
  680. scopeContext.TryPushCloseUpValue(scopeContext.StackPosition, node.Position);
  681. }
  682. // set JMP sBx
  683. foreach (var index in endJumpIndexList.AsSpan())
  684. {
  685. context.Function.Instructions[index].SBx = context.Function.Instructions.Length - 1 - index;
  686. }
  687. return true;
  688. }
  689. public bool VisitRepeatStatementNode(RepeatStatementNode node, ScopeCompilationContext context)
  690. {
  691. var startIndex = context.Function.Instructions.Length;
  692. context.Function.LoopLevel++;
  693. using var scopeContext = context.CreateChildScope();
  694. foreach (var childNode in node.Nodes)
  695. {
  696. childNode.Accept(this, scopeContext);
  697. }
  698. CompileConditionNode(node.ConditionNode, scopeContext, true);
  699. var a = scopeContext.HasCapturedLocalVariables ? scopeContext.StackPosition : (byte)0;
  700. scopeContext.PushInstruction(Instruction.Jmp(a, startIndex - scopeContext.Function.Instructions.Length - 1), node.Position);
  701. scopeContext.TryPushCloseUpValue(scopeContext.StackPosition, node.Position);
  702. context.Function.LoopLevel--;
  703. // resolve break statements inside repeat block
  704. context.Function.ResolveAllBreaks(a, context.Function.Instructions.Length - 1, scopeContext);
  705. return true;
  706. }
  707. public bool VisitWhileStatementNode(WhileStatementNode node, ScopeCompilationContext context)
  708. {
  709. var conditionIndex = context.Function.Instructions.Length;
  710. context.PushInstruction(Instruction.Jmp(0, 0), node.Position);
  711. context.Function.LoopLevel++;
  712. using var scopeContext = context.CreateChildScope();
  713. foreach (var childNode in node.Nodes)
  714. {
  715. childNode.Accept(this, scopeContext);
  716. }
  717. context.Function.LoopLevel--;
  718. // set JMP sBx
  719. scopeContext.Function.Instructions[conditionIndex].SBx = scopeContext.Function.Instructions.Length - 1 - conditionIndex;
  720. CompileConditionNode(node.ConditionNode, scopeContext, false);
  721. var a = scopeContext.HasCapturedLocalVariables ? scopeContext.StackPosition : (byte)0;
  722. scopeContext.PushInstruction(Instruction.Jmp(a, conditionIndex - context.Function.Instructions.Length), node.Position);
  723. scopeContext.TryPushCloseUpValue(scopeContext.StackPosition, node.Position);
  724. // resolve break statements inside while block
  725. context.Function.ResolveAllBreaks(scopeContext.StackPosition, context.Function.Instructions.Length - 1, scopeContext);
  726. return true;
  727. }
  728. public bool VisitNumericForStatementNode(NumericForStatementNode node, ScopeCompilationContext context)
  729. {
  730. var startPosition = context.StackPosition;
  731. node.InitNode.Accept(this, context);
  732. node.LimitNode.Accept(this, context);
  733. if (node.StepNode != null)
  734. {
  735. node.StepNode.Accept(this, context);
  736. }
  737. else
  738. {
  739. var index = context.Function.GetConstantIndex(1);
  740. context.PushInstruction(Instruction.LoadK(context.StackPosition, index), node.Position, true);
  741. }
  742. var prepIndex = context.Function.Instructions.Length;
  743. context.PushInstruction(Instruction.ForPrep(startPosition, 0), node.Position, true);
  744. // compile statements
  745. context.Function.LoopLevel++;
  746. using var scopeContext = context.CreateChildScope();
  747. {
  748. // add local variable
  749. scopeContext.AddLocalVariable(node.VariableName, new()
  750. {
  751. RegisterIndex = startPosition
  752. });
  753. foreach (var childNode in node.StatementNodes)
  754. {
  755. childNode.Accept(this, scopeContext);
  756. }
  757. scopeContext.TryPushCloseUpValue((byte)(startPosition + 1), node.Position);
  758. }
  759. context.Function.LoopLevel--;
  760. // set ForPrep
  761. context.Function.Instructions[prepIndex].SBx = context.Function.Instructions.Length - prepIndex - 1;
  762. // push ForLoop
  763. context.PushInstruction(Instruction.ForLoop(startPosition, prepIndex - context.Function.Instructions.Length), node.Position);
  764. context.Function.ResolveAllBreaks((byte)(startPosition + 1), context.Function.Instructions.Length - 1, scopeContext);
  765. context.StackPosition = startPosition;
  766. return true;
  767. }
  768. public bool VisitGenericForStatementNode(GenericForStatementNode node, ScopeCompilationContext context)
  769. {
  770. // get iterator
  771. var startPosition = context.StackPosition;
  772. CompileExpressionList(node, node.ExpressionNodes, 3, context);
  773. // jump to TFORCALL
  774. var startJumpIndex = context.Function.Instructions.Length;
  775. context.PushInstruction(Instruction.Jmp(0, 0), node.Position);
  776. // compile statements
  777. context.Function.LoopLevel++;
  778. using var scopeContext = context.CreateChildScope();
  779. {
  780. scopeContext.StackPosition = (byte)(startPosition + 3 + node.Names.Length);
  781. // add local variables
  782. for (int i = 0; i < node.Names.Length; i++)
  783. {
  784. var name = node.Names[i];
  785. scopeContext.AddLocalVariable(name.Name, new()
  786. {
  787. RegisterIndex = (byte)(startPosition + 3 + i)
  788. });
  789. }
  790. foreach (var childNode in node.StatementNodes)
  791. {
  792. childNode.Accept(this, scopeContext);
  793. }
  794. scopeContext.TryPushCloseUpValue(scopeContext.StackPosition, node.Position);
  795. }
  796. context.Function.LoopLevel--;
  797. // set jump
  798. context.Function.Instructions[startJumpIndex].SBx = context.Function.Instructions.Length - startJumpIndex - 1;
  799. // push OP_TFORCALL and OP_TFORLOOP
  800. context.PushInstruction(Instruction.TForCall(startPosition, (ushort)node.Names.Length), node.Position);
  801. context.PushInstruction(Instruction.TForLoop((byte)(startPosition + 2), startJumpIndex - context.Function.Instructions.Length), node.Position);
  802. context.Function.ResolveAllBreaks((byte)(startPosition + 1), context.Function.Instructions.Length - 1, scopeContext);
  803. context.StackPosition = startPosition;
  804. return true;
  805. }
  806. public bool VisitLabelStatementNode(LabelStatementNode node, ScopeCompilationContext context)
  807. {
  808. var desc = new LabelDescription()
  809. {
  810. Name = node.Name,
  811. Index = context.Function.Instructions.Length,
  812. RegisterIndex = context.StackPosition
  813. };
  814. context.AddLabel(desc);
  815. context.Function.ResolveGoto(desc);
  816. return true;
  817. }
  818. public bool VisitGotoStatementNode(GotoStatementNode node, ScopeCompilationContext context)
  819. {
  820. if (context.TryGetLabel(node.Name, out var description))
  821. {
  822. context.PushInstruction(Instruction.Jmp(description.RegisterIndex, description.Index - context.Function.Instructions.Length - 1), node.Position);
  823. }
  824. else
  825. {
  826. context.Function.AddUnresolvedGoto(new()
  827. {
  828. Name = node.Name,
  829. JumpInstructionIndex = context.Function.Instructions.Length
  830. });
  831. // add uninitialized jmp instruction
  832. context.PushInstruction(Instruction.Jmp(0, 0), node.Position);
  833. }
  834. return true;
  835. }
  836. static byte GetOrLoadIdentifier(ReadOnlyMemory<char> name, ScopeCompilationContext context, SourcePosition sourcePosition, bool dontLoadLocalVariable)
  837. {
  838. var p = context.StackPosition;
  839. if (context.TryGetLocalVariable(name, out var variable))
  840. {
  841. if (dontLoadLocalVariable)
  842. {
  843. return variable.RegisterIndex;
  844. }
  845. else if (p == variable.RegisterIndex)
  846. {
  847. context.StackPosition++;
  848. return p;
  849. }
  850. else
  851. {
  852. context.PushInstruction(Instruction.Move(p, variable.RegisterIndex), sourcePosition, true);
  853. return p;
  854. }
  855. }
  856. else if (context.Function.TryGetUpValue(name, out var upValue))
  857. {
  858. context.PushInstruction(Instruction.GetUpVal(p, (ushort)upValue.Id), sourcePosition, true);
  859. return p;
  860. }
  861. else if (context.TryGetLocalVariable("_ENV".AsMemory(), out variable))
  862. {
  863. var keyStringIndex = context.Function.GetConstantIndex(name.ToString()) + 256;
  864. context.PushInstruction(Instruction.GetTable(p, variable.RegisterIndex, (ushort)keyStringIndex), sourcePosition, true);
  865. return p;
  866. }
  867. else
  868. {
  869. context.Function.TryGetUpValue("_ENV".AsMemory(), out upValue);
  870. var index = context.Function.GetConstantIndex(name.ToString()) + 256;
  871. context.PushInstruction(Instruction.GetTabUp(p, (ushort)upValue.Id, (ushort)index), sourcePosition, true);
  872. return p;
  873. }
  874. }
  875. uint GetRKIndex(ExpressionNode node, ScopeCompilationContext context)
  876. {
  877. if (node is IdentifierNode identifier)
  878. {
  879. return GetOrLoadIdentifier(identifier.Name, context, identifier.Position, true);
  880. }
  881. else if (TryGetConstant(node, context, out var constant))
  882. {
  883. return context.Function.GetConstantIndex(constant) + 256;
  884. }
  885. else
  886. {
  887. node.Accept(this, context);
  888. return context.StackTopPosition;
  889. }
  890. }
  891. static bool TryGetConstant(ExpressionNode node, ScopeCompilationContext context, out LuaValue value)
  892. {
  893. switch (node)
  894. {
  895. case NilLiteralNode:
  896. value = LuaValue.Nil;
  897. return true;
  898. case BooleanLiteralNode booleanLiteral:
  899. value = booleanLiteral.Value;
  900. return true;
  901. case NumericLiteralNode numericLiteral:
  902. value = numericLiteral.Value;
  903. return true;
  904. case StringLiteralNode stringLiteral:
  905. if (stringLiteral.IsShortLiteral)
  906. {
  907. if (!StringHelper.TryFromStringLiteral(stringLiteral.Text.Span, out var str))
  908. {
  909. throw new LuaParseException(context.Function.ChunkName, stringLiteral.Position, $"invalid escape sequence near '{stringLiteral.Text}'");
  910. }
  911. value = str;
  912. }
  913. else
  914. {
  915. value = stringLiteral.Text.ToString();
  916. }
  917. return true;
  918. case UnaryExpressionNode unaryExpression:
  919. if (TryGetConstant(unaryExpression.Node, context, out var unaryNodeValue))
  920. {
  921. switch (unaryExpression.Operator)
  922. {
  923. case UnaryOperator.Negate:
  924. if (unaryNodeValue.TryRead<double>(out var d1))
  925. {
  926. value = -d1;
  927. return true;
  928. }
  929. break;
  930. case UnaryOperator.Not:
  931. if (unaryNodeValue.TryRead<bool>(out var b))
  932. {
  933. value = !b;
  934. return true;
  935. }
  936. break;
  937. }
  938. }
  939. break;
  940. case BinaryExpressionNode binaryExpression:
  941. if (TryGetConstant(binaryExpression.LeftNode, context, out var leftValue) &&
  942. TryGetConstant(binaryExpression.RightNode, context, out var rightValue))
  943. {
  944. switch (binaryExpression.OperatorType)
  945. {
  946. case BinaryOperator.Addition:
  947. {
  948. if (leftValue.TryRead<double>(out var d1) && rightValue.TryRead<double>(out var d2))
  949. {
  950. value = d1 + d2;
  951. return true;
  952. }
  953. }
  954. break;
  955. case BinaryOperator.Subtraction:
  956. {
  957. if (leftValue.TryRead<double>(out var d1) && rightValue.TryRead<double>(out var d2))
  958. {
  959. value = d1 - d2;
  960. return true;
  961. }
  962. }
  963. break;
  964. case BinaryOperator.Multiplication:
  965. {
  966. if (leftValue.TryRead<double>(out var d1) && rightValue.TryRead<double>(out var d2))
  967. {
  968. value = d1 * d2;
  969. return true;
  970. }
  971. }
  972. break;
  973. case BinaryOperator.Division:
  974. {
  975. if (leftValue.TryRead<double>(out var d1) && rightValue.TryRead<double>(out var d2) && d2 != 0)
  976. {
  977. value = d1 / d2;
  978. return true;
  979. }
  980. }
  981. break;
  982. }
  983. }
  984. break;
  985. }
  986. value = default;
  987. return false;
  988. }
  989. static bool IsFixedNumberOfReturnValues(ExpressionNode node)
  990. {
  991. return node is not (CallFunctionExpressionNode or CallTableMethodExpressionNode or VariableArgumentsExpressionNode);
  992. }
  993. /// <summary>
  994. /// Compiles a conditional boolean branch: if true (or false), the next instruction added is skipped.
  995. /// </summary>
  996. /// <param name="node">Condition node</param>
  997. /// <param name="context">Context</param>
  998. /// <param name="falseIsSkip">If true, generates an instruction sequence that skips the next instruction if the condition is false.</param>
  999. void CompileConditionNode(ExpressionNode node, ScopeCompilationContext context, bool falseIsSkip)
  1000. {
  1001. if (node is BinaryExpressionNode binaryExpression)
  1002. {
  1003. switch (binaryExpression.OperatorType)
  1004. {
  1005. case BinaryOperator.Equality:
  1006. {
  1007. var b = (ushort)GetRKIndex(binaryExpression.LeftNode, context);
  1008. var c = (ushort)GetRKIndex(binaryExpression.RightNode, context);
  1009. context.PushInstruction(Instruction.Eq(falseIsSkip ? (byte)0 : (byte)1, b, c), node.Position);
  1010. return;
  1011. }
  1012. case BinaryOperator.Inequality:
  1013. {
  1014. var b = (ushort)GetRKIndex(binaryExpression.LeftNode, context);
  1015. var c = (ushort)GetRKIndex(binaryExpression.RightNode, context);
  1016. context.PushInstruction(Instruction.Eq(falseIsSkip ? (byte)1 : (byte)0, b, c), node.Position);
  1017. return;
  1018. }
  1019. case BinaryOperator.LessThan:
  1020. {
  1021. var b = (ushort)GetRKIndex(binaryExpression.LeftNode, context);
  1022. var c = (ushort)GetRKIndex(binaryExpression.RightNode, context);
  1023. context.PushInstruction(Instruction.Lt(falseIsSkip ? (byte)0 : (byte)1, b, c), node.Position);
  1024. return;
  1025. }
  1026. case BinaryOperator.LessThanOrEqual:
  1027. {
  1028. var b = (ushort)GetRKIndex(binaryExpression.LeftNode, context);
  1029. var c = (ushort)GetRKIndex(binaryExpression.RightNode, context);
  1030. context.PushInstruction(Instruction.Le(falseIsSkip ? (byte)0 : (byte)1, b, c), node.Position);
  1031. return;
  1032. }
  1033. case BinaryOperator.GreaterThan:
  1034. {
  1035. var b = (ushort)GetRKIndex(binaryExpression.LeftNode, context);
  1036. var c = (ushort)GetRKIndex(binaryExpression.RightNode, context);
  1037. context.PushInstruction(Instruction.Lt(falseIsSkip ? (byte)0 : (byte)1, c, b), node.Position);
  1038. return;
  1039. }
  1040. case BinaryOperator.GreaterThanOrEqual:
  1041. {
  1042. var b = (ushort)GetRKIndex(binaryExpression.LeftNode, context);
  1043. var c = (ushort)GetRKIndex(binaryExpression.RightNode, context);
  1044. context.PushInstruction(Instruction.Le(falseIsSkip ? (byte)0 : (byte)1, c, b), node.Position);
  1045. return;
  1046. }
  1047. }
  1048. }
  1049. node.Accept(this, context);
  1050. context.PushInstruction(Instruction.Test((byte)(context.StackPosition - 1), falseIsSkip ? (byte)0 : (byte)1), node.Position);
  1051. }
  1052. void CompileExpressionList(SyntaxNode rootNode, ExpressionNode[] expressions, int minimumCount, ScopeCompilationContext context)
  1053. {
  1054. var isLastFunction = false;
  1055. for (int i = 0; i < expressions.Length; i++)
  1056. {
  1057. var expression = expressions[i];
  1058. var isLast = i == expressions.Length - 1;
  1059. var resultCount = isLast ? (minimumCount == -1 ? -1 : minimumCount - i) : 1;
  1060. if (expression is CallFunctionExpressionNode call)
  1061. {
  1062. CompileCallFunctionExpression(call, context, false, resultCount);
  1063. isLastFunction = isLast;
  1064. }
  1065. else if (expression is CallTableMethodExpressionNode method)
  1066. {
  1067. CompileTableMethod(method, context, false, resultCount);
  1068. isLastFunction = isLast;
  1069. }
  1070. else if (expression is VariableArgumentsExpressionNode varArg)
  1071. {
  1072. CompileVariableArgumentsExpression(varArg, context, resultCount);
  1073. isLastFunction = isLast;
  1074. }
  1075. else if (TryGetConstant(expression, context, out var constant))
  1076. {
  1077. var index = context.Function.GetConstantIndex(constant);
  1078. context.PushInstruction(Instruction.LoadK(context.StackPosition, index), expression.Position, true);
  1079. isLastFunction = false;
  1080. }
  1081. else
  1082. {
  1083. expression.Accept(this, context);
  1084. isLastFunction = false;
  1085. }
  1086. }
  1087. // fill space with nil
  1088. var varCount = minimumCount - expressions.Length;
  1089. if (varCount > 0 && !isLastFunction)
  1090. {
  1091. context.PushInstruction(Instruction.LoadNil(context.StackPosition, (ushort)varCount), rootNode.Position);
  1092. context.StackPosition = (byte)(context.StackPosition + varCount);
  1093. }
  1094. }
  1095. }