LuaCompiler.cs 51 KB

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