Parser.cs 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013
  1. using System.Diagnostics.CodeAnalysis;
  2. using System.Runtime.CompilerServices;
  3. using Lua.Internal;
  4. using Lua.CodeAnalysis.Syntax.Nodes;
  5. using System.Globalization;
  6. namespace Lua.CodeAnalysis.Syntax;
  7. public ref struct Parser
  8. {
  9. public string? ChunkName { get; init; }
  10. PooledList<SyntaxToken> tokens;
  11. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  12. public void Add(SyntaxToken token) => tokens.Add(token);
  13. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  14. public void Dispose()
  15. {
  16. tokens.Dispose();
  17. }
  18. public LuaSyntaxTree Parse()
  19. {
  20. using var root = new PooledList<SyntaxNode>(64);
  21. var enumerator = new SyntaxTokenEnumerator(tokens.AsSpan());
  22. while (enumerator.MoveNext())
  23. {
  24. if (enumerator.Current.Type is SyntaxTokenType.EndOfLine or SyntaxTokenType.SemiColon) continue;
  25. var node = ParseStatement(ref enumerator);
  26. root.Add(node);
  27. }
  28. var tree = new LuaSyntaxTree(root.AsSpan().ToArray());
  29. Dispose();
  30. return tree;
  31. }
  32. StatementNode ParseStatement(ref SyntaxTokenEnumerator enumerator)
  33. {
  34. switch (enumerator.Current.Type)
  35. {
  36. case SyntaxTokenType.Identifier:
  37. {
  38. var firstExpression = ParseExpression(ref enumerator, OperatorPrecedence.NonOperator);
  39. switch (firstExpression)
  40. {
  41. case CallFunctionExpressionNode callFunctionExpression:
  42. return new CallFunctionStatementNode(callFunctionExpression);
  43. case CallTableMethodExpressionNode callTableMethodExpression:
  44. return new CallTableMethodStatementNode(callTableMethodExpression);
  45. default:
  46. if (enumerator.GetNext(true).Type is SyntaxTokenType.Comma or SyntaxTokenType.Assignment)
  47. {
  48. // skip ','
  49. MoveNextWithValidation(ref enumerator);
  50. enumerator.SkipEoL();
  51. return ParseAssignmentStatement(firstExpression, ref enumerator);
  52. }
  53. break;
  54. }
  55. }
  56. break;
  57. case SyntaxTokenType.Return:
  58. return ParseReturnStatement(ref enumerator);
  59. case SyntaxTokenType.Do:
  60. return ParseDoStatement(ref enumerator);
  61. case SyntaxTokenType.Goto:
  62. return ParseGotoStatement(ref enumerator);
  63. case SyntaxTokenType.Label:
  64. return new LabelStatementNode(enumerator.Current.Text, enumerator.Current.Position);
  65. case SyntaxTokenType.If:
  66. return ParseIfStatement(ref enumerator);
  67. case SyntaxTokenType.While:
  68. return ParseWhileStatement(ref enumerator);
  69. case SyntaxTokenType.Repeat:
  70. return ParseRepeatStatement(ref enumerator);
  71. case SyntaxTokenType.For:
  72. {
  73. // skip 'for' keyword
  74. var forToken = enumerator.Current;
  75. MoveNextWithValidation(ref enumerator);
  76. enumerator.SkipEoL();
  77. if (enumerator.GetNext(true).Type is SyntaxTokenType.Assignment)
  78. {
  79. return ParseNumericForStatement(ref enumerator, forToken);
  80. }
  81. else
  82. {
  83. return ParseGenericForStatement(ref enumerator, forToken);
  84. }
  85. }
  86. case SyntaxTokenType.Break:
  87. return new BreakStatementNode(enumerator.Current.Position);
  88. case SyntaxTokenType.Local:
  89. {
  90. // skip 'local' keyword
  91. CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.Local, out var localToken);
  92. // local function
  93. if (enumerator.Current.Type is SyntaxTokenType.Function)
  94. {
  95. // skip 'function' keyword
  96. CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.Function, out var functionToken);
  97. enumerator.SkipEoL();
  98. return ParseLocalFunctionDeclarationStatement(ref enumerator, functionToken);
  99. }
  100. CheckCurrent(ref enumerator, SyntaxTokenType.Identifier);
  101. var nextType = enumerator.GetNext().Type;
  102. if (nextType is SyntaxTokenType.Comma or SyntaxTokenType.Assignment)
  103. {
  104. return ParseLocalAssignmentStatement(ref enumerator, localToken);
  105. }
  106. else if (nextType is SyntaxTokenType.EndOfLine or SyntaxTokenType.SemiColon)
  107. {
  108. return new LocalAssignmentStatementNode([new IdentifierNode(enumerator.Current.Text, enumerator.Current.Position)], [], localToken.Position);
  109. }
  110. }
  111. break;
  112. case SyntaxTokenType.Function:
  113. {
  114. // skip 'function' keyword
  115. CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.Function, out var functionToken);
  116. enumerator.SkipEoL();
  117. if (enumerator.GetNext(true).Type is SyntaxTokenType.Dot or SyntaxTokenType.Colon)
  118. {
  119. return ParseTableMethodDeclarationStatement(ref enumerator, functionToken);
  120. }
  121. else
  122. {
  123. return ParseFunctionDeclarationStatement(ref enumerator, functionToken);
  124. }
  125. }
  126. }
  127. LuaParseException.UnexpectedToken(ChunkName, enumerator.Current.Position, enumerator.Current);
  128. return default!;
  129. }
  130. ReturnStatementNode ParseReturnStatement(ref SyntaxTokenEnumerator enumerator)
  131. {
  132. // skip 'return' keyword
  133. CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.Return, out var returnToken);
  134. // parse parameters
  135. var expressions = ParseExpressionList(ref enumerator);
  136. return new ReturnStatementNode(expressions, returnToken.Position);
  137. }
  138. DoStatementNode ParseDoStatement(ref SyntaxTokenEnumerator enumerator)
  139. {
  140. // check 'do' keyword
  141. CheckCurrent(ref enumerator, SyntaxTokenType.Do);
  142. var doToken = enumerator.Current;
  143. // parse statements
  144. var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End);
  145. return new DoStatementNode(statements, doToken.Position);
  146. }
  147. GotoStatementNode ParseGotoStatement(ref SyntaxTokenEnumerator enumerator)
  148. {
  149. // skip 'goto' keyword
  150. CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.Goto, out var gotoToken);
  151. CheckCurrent(ref enumerator, SyntaxTokenType.Identifier);
  152. return new GotoStatementNode(enumerator.Current.Text, gotoToken.Position);
  153. }
  154. AssignmentStatementNode ParseAssignmentStatement(ExpressionNode firstExpression, ref SyntaxTokenEnumerator enumerator)
  155. {
  156. // parse leftNodes
  157. using var leftNodes = new PooledList<SyntaxNode>(8);
  158. leftNodes.Add(firstExpression);
  159. while (enumerator.Current.Type == SyntaxTokenType.Comma)
  160. {
  161. // skip ','
  162. MoveNextWithValidation(ref enumerator);
  163. enumerator.SkipEoL();
  164. // parse identifier
  165. CheckCurrent(ref enumerator, SyntaxTokenType.Identifier);
  166. leftNodes.Add(ParseExpression(ref enumerator, OperatorPrecedence.NonOperator));
  167. MoveNextWithValidation(ref enumerator);
  168. enumerator.SkipEoL();
  169. }
  170. // skip '='
  171. if (enumerator.Current.Type is not SyntaxTokenType.Assignment)
  172. {
  173. enumerator.MovePrevious();
  174. return new AssignmentStatementNode(leftNodes.AsSpan().ToArray(), [], firstExpression.Position);
  175. }
  176. MoveNextWithValidation(ref enumerator);
  177. // parse expressions
  178. var expressions = ParseExpressionList(ref enumerator);
  179. return new AssignmentStatementNode(leftNodes.AsSpan().ToArray(), expressions, firstExpression.Position);
  180. }
  181. LocalAssignmentStatementNode ParseLocalAssignmentStatement(ref SyntaxTokenEnumerator enumerator, SyntaxToken localToken)
  182. {
  183. // parse identifiers
  184. var identifiers = ParseIdentifierList(ref enumerator);
  185. // skip '='
  186. if (enumerator.Current.Type is not SyntaxTokenType.Assignment)
  187. {
  188. enumerator.MovePrevious();
  189. return new LocalAssignmentStatementNode(identifiers, [], localToken.Position);
  190. }
  191. MoveNextWithValidation(ref enumerator);
  192. // parse expressions
  193. var expressions = ParseExpressionList(ref enumerator);
  194. return new LocalAssignmentStatementNode(identifiers, expressions, localToken.Position);
  195. }
  196. IfStatementNode ParseIfStatement(ref SyntaxTokenEnumerator enumerator)
  197. {
  198. // skip 'if' keyword
  199. CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.If, out var ifToken);
  200. enumerator.SkipEoL();
  201. // parse condition
  202. var condition = ParseExpression(ref enumerator, GetPrecedence(enumerator.Current.Type));
  203. MoveNextWithValidation(ref enumerator);
  204. enumerator.SkipEoL();
  205. // skip 'then' keyword
  206. CheckCurrent(ref enumerator, SyntaxTokenType.Then);
  207. using var builder = new PooledList<StatementNode>(64);
  208. using var elseIfBuilder = new PooledList<IfStatementNode.ConditionAndThenNodes>(64);
  209. IfStatementNode.ConditionAndThenNodes ifNodes = default!;
  210. StatementNode[] elseNodes = [];
  211. // if = 0, elseif = 1, else = 2
  212. var state = 0;
  213. // parse statements
  214. while (true)
  215. {
  216. if (!enumerator.MoveNext())
  217. {
  218. LuaParseException.ExpectedToken(ChunkName, enumerator.Current.Position, SyntaxTokenType.End);
  219. }
  220. var tokenType = enumerator.Current.Type;
  221. if (tokenType is SyntaxTokenType.EndOfLine or SyntaxTokenType.SemiColon)
  222. {
  223. continue;
  224. }
  225. if (tokenType is SyntaxTokenType.ElseIf or SyntaxTokenType.Else or SyntaxTokenType.End)
  226. {
  227. switch (state)
  228. {
  229. case 0:
  230. ifNodes = new()
  231. {
  232. ConditionNode = condition,
  233. ThenNodes = builder.AsSpan().ToArray(),
  234. };
  235. builder.Clear();
  236. break;
  237. case 1:
  238. elseIfBuilder.Add(new()
  239. {
  240. ConditionNode = condition,
  241. ThenNodes = builder.AsSpan().ToArray(),
  242. });
  243. builder.Clear();
  244. break;
  245. case 2:
  246. elseNodes = builder.AsSpan().ToArray();
  247. break;
  248. }
  249. if (tokenType is SyntaxTokenType.ElseIf)
  250. {
  251. // skip 'elseif' keywords
  252. MoveNextWithValidation(ref enumerator);
  253. enumerator.SkipEoL();
  254. // parse condition
  255. condition = ParseExpression(ref enumerator, GetPrecedence(enumerator.Current.Type));
  256. MoveNextWithValidation(ref enumerator);
  257. enumerator.SkipEoL();
  258. // skip 'then' keyword
  259. CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.Then, out _);
  260. enumerator.SkipEoL();
  261. // set elseif state
  262. state = 1;
  263. }
  264. else if (tokenType is SyntaxTokenType.Else)
  265. {
  266. // skip 'else' keywords
  267. MoveNextWithValidation(ref enumerator);
  268. enumerator.SkipEoL();
  269. // set else state
  270. state = 2;
  271. }
  272. else if (tokenType is SyntaxTokenType.End)
  273. {
  274. goto RETURN;
  275. }
  276. }
  277. var node = ParseStatement(ref enumerator);
  278. builder.Add(node);
  279. }
  280. RETURN:
  281. return new IfStatementNode(ifNodes, elseIfBuilder.AsSpan().ToArray(), elseNodes, ifToken.Position);
  282. }
  283. WhileStatementNode ParseWhileStatement(ref SyntaxTokenEnumerator enumerator)
  284. {
  285. // skip 'while' keyword
  286. CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.While, out var whileToken);
  287. enumerator.SkipEoL();
  288. // parse condition
  289. var condition = ParseExpression(ref enumerator, GetPrecedence(enumerator.Current.Type));
  290. MoveNextWithValidation(ref enumerator);
  291. enumerator.SkipEoL();
  292. // skip 'do' keyword
  293. CheckCurrent(ref enumerator, SyntaxTokenType.Do);
  294. // parse statements
  295. var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End);
  296. return new WhileStatementNode(condition, statements, whileToken.Position);
  297. }
  298. RepeatStatementNode ParseRepeatStatement(ref SyntaxTokenEnumerator enumerator)
  299. {
  300. // skip 'repeat' keyword
  301. CheckCurrent(ref enumerator, SyntaxTokenType.Repeat);
  302. var repeatToken = enumerator.Current;
  303. // parse statements
  304. var statements = ParseStatementList(ref enumerator, SyntaxTokenType.Until);
  305. // skip 'until keyword'
  306. CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.Until, out _);
  307. enumerator.SkipEoL();
  308. // parse condition
  309. var condition = ParseExpression(ref enumerator, GetPrecedence(enumerator.Current.Type));
  310. return new RepeatStatementNode(condition, statements, repeatToken.Position);
  311. }
  312. NumericForStatementNode ParseNumericForStatement(ref SyntaxTokenEnumerator enumerator, SyntaxToken forToken)
  313. {
  314. // parse variable name
  315. CheckCurrent(ref enumerator, SyntaxTokenType.Identifier);
  316. var varName = enumerator.Current.Text;
  317. MoveNextWithValidation(ref enumerator);
  318. enumerator.SkipEoL();
  319. // skip '='
  320. CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.Assignment, out _);
  321. enumerator.SkipEoL();
  322. // parse initial value
  323. var initialValueNode = ParseExpression(ref enumerator, OperatorPrecedence.NonOperator);
  324. MoveNextWithValidation(ref enumerator);
  325. enumerator.SkipEoL();
  326. // skip ','
  327. CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.Comma, out _);
  328. enumerator.SkipEoL();
  329. // parse limit
  330. var limitNode = ParseExpression(ref enumerator, OperatorPrecedence.NonOperator);
  331. MoveNextWithValidation(ref enumerator);
  332. enumerator.SkipEoL();
  333. // parse stepNode
  334. ExpressionNode? stepNode = null;
  335. if (enumerator.Current.Type is SyntaxTokenType.Comma)
  336. {
  337. // skip ','
  338. enumerator.MoveNext();
  339. // parse step
  340. stepNode = ParseExpression(ref enumerator, OperatorPrecedence.NonOperator);
  341. MoveNextWithValidation(ref enumerator);
  342. enumerator.SkipEoL();
  343. }
  344. // skip 'do' keyword
  345. CheckCurrent(ref enumerator, SyntaxTokenType.Do);
  346. // parse statements
  347. var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End);
  348. return new NumericForStatementNode(varName, initialValueNode, limitNode, stepNode, statements, forToken.Position);
  349. }
  350. GenericForStatementNode ParseGenericForStatement(ref SyntaxTokenEnumerator enumerator, SyntaxToken forToken)
  351. {
  352. var identifiers = ParseIdentifierList(ref enumerator);
  353. enumerator.SkipEoL();
  354. // skip 'in' keyword
  355. CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.In, out _);
  356. enumerator.SkipEoL();
  357. var expression = ParseExpression(ref enumerator, OperatorPrecedence.NonOperator);
  358. MoveNextWithValidation(ref enumerator);
  359. enumerator.SkipEoL();
  360. // skip 'do' keyword
  361. CheckCurrent(ref enumerator, SyntaxTokenType.Do);
  362. // parse statements
  363. var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End);
  364. return new GenericForStatementNode(identifiers, expression, statements, forToken.Position);
  365. }
  366. FunctionDeclarationStatementNode ParseFunctionDeclarationStatement(ref SyntaxTokenEnumerator enumerator, SyntaxToken functionToken)
  367. {
  368. var (Name, Identifiers, Statements, HasVariableArgments) = ParseFunctionDeclarationCore(ref enumerator, false);
  369. return new FunctionDeclarationStatementNode(Name, Identifiers, Statements, HasVariableArgments, functionToken.Position);
  370. }
  371. LocalFunctionDeclarationStatementNode ParseLocalFunctionDeclarationStatement(ref SyntaxTokenEnumerator enumerator, SyntaxToken functionToken)
  372. {
  373. var (Name, Identifiers, Statements, HasVariableArgments) = ParseFunctionDeclarationCore(ref enumerator, false);
  374. return new LocalFunctionDeclarationStatementNode(Name, Identifiers, Statements, HasVariableArgments, functionToken.Position);
  375. }
  376. (ReadOnlyMemory<char> Name, IdentifierNode[] Identifiers, StatementNode[] Statements, bool HasVariableArgments) ParseFunctionDeclarationCore(ref SyntaxTokenEnumerator enumerator, bool isAnonymous)
  377. {
  378. ReadOnlyMemory<char> name;
  379. if (isAnonymous)
  380. {
  381. name = ReadOnlyMemory<char>.Empty;
  382. }
  383. else
  384. {
  385. // parse function name
  386. CheckCurrent(ref enumerator, SyntaxTokenType.Identifier);
  387. name = enumerator.Current.Text;
  388. MoveNextWithValidation(ref enumerator);
  389. enumerator.SkipEoL();
  390. }
  391. // skip '('
  392. CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.LParen, out _);
  393. enumerator.SkipEoL();
  394. // parse parameters
  395. var identifiers = enumerator.Current.Type is SyntaxTokenType.Identifier
  396. ? ParseIdentifierList(ref enumerator)
  397. : [];
  398. // check variable arguments
  399. var hasVarArg = enumerator.Current.Type is SyntaxTokenType.VarArg;
  400. if (hasVarArg) enumerator.MoveNext();
  401. // skip ')'
  402. CheckCurrent(ref enumerator, SyntaxTokenType.RParen);
  403. // parse statements
  404. var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End);
  405. return (name, identifiers, statements, hasVarArg);
  406. }
  407. TableMethodDeclarationStatementNode ParseTableMethodDeclarationStatement(ref SyntaxTokenEnumerator enumerator, SyntaxToken functionToken)
  408. {
  409. using var names = new PooledList<IdentifierNode>(32);
  410. var hasSelfParameter = false;
  411. while (true)
  412. {
  413. CheckCurrent(ref enumerator, SyntaxTokenType.Identifier);
  414. names.Add(new IdentifierNode(enumerator.Current.Text, enumerator.Current.Position));
  415. MoveNextWithValidation(ref enumerator);
  416. enumerator.SkipEoL();
  417. if (enumerator.Current.Type is SyntaxTokenType.Dot or SyntaxTokenType.Colon)
  418. {
  419. if (hasSelfParameter)
  420. {
  421. LuaParseException.UnexpectedToken(ChunkName, enumerator.Current.Position, enumerator.Current);
  422. }
  423. hasSelfParameter = enumerator.Current.Type is SyntaxTokenType.Colon;
  424. MoveNextWithValidation(ref enumerator);
  425. enumerator.SkipEoL();
  426. }
  427. else if (enumerator.Current.Type is SyntaxTokenType.LParen)
  428. {
  429. // skip '('
  430. MoveNextWithValidation(ref enumerator);
  431. enumerator.SkipEoL();
  432. break;
  433. }
  434. }
  435. // parse parameters
  436. var identifiers = enumerator.Current.Type is SyntaxTokenType.Identifier
  437. ? ParseIdentifierList(ref enumerator)
  438. : [];
  439. // check variable arguments
  440. var hasVarArg = enumerator.Current.Type is SyntaxTokenType.VarArg;
  441. if (hasVarArg) enumerator.MoveNext();
  442. // skip ')'
  443. CheckCurrent(ref enumerator, SyntaxTokenType.RParen);
  444. // parse statements
  445. var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End);
  446. return new TableMethodDeclarationStatementNode(names.AsSpan().ToArray(), identifiers, statements, hasVarArg, hasSelfParameter, functionToken.Position);
  447. }
  448. bool TryParseExpression(ref SyntaxTokenEnumerator enumerator, OperatorPrecedence precedence, [NotNullWhen(true)] out ExpressionNode? result)
  449. {
  450. result = enumerator.Current.Type switch
  451. {
  452. SyntaxTokenType.Identifier => enumerator.GetNext(true).Type switch
  453. {
  454. SyntaxTokenType.LParen or SyntaxTokenType.String or SyntaxTokenType.RawString => ParseCallFunctionExpression(ref enumerator, null),
  455. SyntaxTokenType.LSquare or SyntaxTokenType.Dot or SyntaxTokenType.Colon => ParseTableAccessExpression(ref enumerator, null),
  456. _ => new IdentifierNode(enumerator.Current.Text, enumerator.Current.Position),
  457. },
  458. SyntaxTokenType.Number => new NumericLiteralNode(ConvertTextToNumber(enumerator.Current.Text.Span), enumerator.Current.Position),
  459. SyntaxTokenType.String => new StringLiteralNode(enumerator.Current.Text, true, enumerator.Current.Position),
  460. SyntaxTokenType.RawString => new StringLiteralNode(enumerator.Current.Text, false, enumerator.Current.Position),
  461. SyntaxTokenType.True => new BooleanLiteralNode(true, enumerator.Current.Position),
  462. SyntaxTokenType.False => new BooleanLiteralNode(false, enumerator.Current.Position),
  463. SyntaxTokenType.Nil => new NilLiteralNode(enumerator.Current.Position),
  464. SyntaxTokenType.VarArg => new VariableArgumentsExpressionNode(enumerator.Current.Position),
  465. SyntaxTokenType.Subtraction => ParseMinusNumber(ref enumerator),
  466. SyntaxTokenType.Not or SyntaxTokenType.Length => ParseUnaryExpression(ref enumerator, enumerator.Current),
  467. SyntaxTokenType.LParen => ParseGroupedExpression(ref enumerator),
  468. SyntaxTokenType.LCurly => ParseTableConstructorExpression(ref enumerator),
  469. SyntaxTokenType.Function => ParseFunctionDeclarationExpression(ref enumerator),
  470. _ => null,
  471. };
  472. if (result == null) return false;
  473. // nested table access & function call
  474. RECURSIVE:
  475. enumerator.SkipEoL();
  476. var nextType = enumerator.GetNext().Type;
  477. if (nextType is SyntaxTokenType.LSquare or SyntaxTokenType.Dot or SyntaxTokenType.Colon)
  478. {
  479. MoveNextWithValidation(ref enumerator);
  480. result = ParseTableAccessExpression(ref enumerator, result);
  481. goto RECURSIVE;
  482. }
  483. else if (nextType is SyntaxTokenType.LParen or SyntaxTokenType.String or SyntaxTokenType.RawString or SyntaxTokenType.LCurly)
  484. {
  485. MoveNextWithValidation(ref enumerator);
  486. result = ParseCallFunctionExpression(ref enumerator, result);
  487. goto RECURSIVE;
  488. }
  489. // binary expression
  490. while (true)
  491. {
  492. var opPrecedence = GetPrecedence(enumerator.GetNext().Type);
  493. if (precedence >= opPrecedence) break;
  494. MoveNextWithValidation(ref enumerator);
  495. result = ParseBinaryExpression(ref enumerator, opPrecedence, result);
  496. enumerator.SkipEoL();
  497. }
  498. return true;
  499. }
  500. ExpressionNode ParseExpression(ref SyntaxTokenEnumerator enumerator, OperatorPrecedence precedence)
  501. {
  502. if (!TryParseExpression(ref enumerator, precedence, out var result))
  503. {
  504. throw new LuaParseException(ChunkName, enumerator.Current.Position, "Unexpected token <{enumerator.Current.Type}>");
  505. }
  506. return result;
  507. }
  508. ExpressionNode ParseMinusNumber(ref SyntaxTokenEnumerator enumerator)
  509. {
  510. var token = enumerator.Current;
  511. if (enumerator.GetNext(true).Type is SyntaxTokenType.Number)
  512. {
  513. enumerator.MoveNext();
  514. enumerator.SkipEoL();
  515. return new NumericLiteralNode(-ConvertTextToNumber(enumerator.Current.Text.Span), token.Position);
  516. }
  517. else
  518. {
  519. return ParseUnaryExpression(ref enumerator, token);
  520. }
  521. }
  522. UnaryExpressionNode ParseUnaryExpression(ref SyntaxTokenEnumerator enumerator, SyntaxToken operatorToken)
  523. {
  524. var operatorType = enumerator.Current.Type switch
  525. {
  526. SyntaxTokenType.Subtraction => UnaryOperator.Negate,
  527. SyntaxTokenType.Not => UnaryOperator.Not,
  528. SyntaxTokenType.Length => UnaryOperator.Length,
  529. _ => throw new LuaParseException(ChunkName, operatorToken.Position, $"unexpected symbol near '{enumerator.Current.Text}'"),
  530. };
  531. MoveNextWithValidation(ref enumerator);
  532. var right = ParseExpression(ref enumerator, OperatorPrecedence.Unary);
  533. return new UnaryExpressionNode(operatorType, right, operatorToken.Position);
  534. }
  535. BinaryExpressionNode ParseBinaryExpression(ref SyntaxTokenEnumerator enumerator, OperatorPrecedence precedence, ExpressionNode left)
  536. {
  537. var operatorToken = enumerator.Current;
  538. var operatorType = operatorToken.Type switch
  539. {
  540. SyntaxTokenType.Addition => BinaryOperator.Addition,
  541. SyntaxTokenType.Subtraction => BinaryOperator.Subtraction,
  542. SyntaxTokenType.Multiplication => BinaryOperator.Multiplication,
  543. SyntaxTokenType.Division => BinaryOperator.Division,
  544. SyntaxTokenType.Modulo => BinaryOperator.Modulo,
  545. SyntaxTokenType.Exponentiation => BinaryOperator.Exponentiation,
  546. SyntaxTokenType.Equality => BinaryOperator.Equality,
  547. SyntaxTokenType.Inequality => BinaryOperator.Inequality,
  548. SyntaxTokenType.LessThan => BinaryOperator.LessThan,
  549. SyntaxTokenType.LessThanOrEqual => BinaryOperator.LessThanOrEqual,
  550. SyntaxTokenType.GreaterThan => BinaryOperator.GreaterThan,
  551. SyntaxTokenType.GreaterThanOrEqual => BinaryOperator.GreaterThanOrEqual,
  552. SyntaxTokenType.And => BinaryOperator.And,
  553. SyntaxTokenType.Or => BinaryOperator.Or,
  554. SyntaxTokenType.Concat => BinaryOperator.Concat,
  555. _ => throw new LuaParseException(ChunkName, enumerator.Current.Position, $"unexpected symbol near '{enumerator.Current.Text}'"),
  556. };
  557. enumerator.SkipEoL();
  558. MoveNextWithValidation(ref enumerator);
  559. enumerator.SkipEoL();
  560. var right = ParseExpression(ref enumerator, precedence);
  561. return new BinaryExpressionNode(operatorType, left, right, operatorToken.Position);
  562. }
  563. TableConstructorExpressionNode ParseTableConstructorExpression(ref SyntaxTokenEnumerator enumerator)
  564. {
  565. CheckCurrent(ref enumerator, SyntaxTokenType.LCurly);
  566. var startToken = enumerator.Current;
  567. using var items = new PooledList<TableConstructorField>(16);
  568. while (enumerator.MoveNext())
  569. {
  570. var currentToken = enumerator.Current;
  571. switch (currentToken.Type)
  572. {
  573. case SyntaxTokenType.RCurly:
  574. goto RETURN;
  575. case SyntaxTokenType.EndOfLine:
  576. case SyntaxTokenType.SemiColon:
  577. case SyntaxTokenType.Comma:
  578. continue;
  579. case SyntaxTokenType.LSquare:
  580. // general style ([key] = value)
  581. enumerator.MoveNext();
  582. var keyExpression = ParseExpression(ref enumerator, OperatorPrecedence.NonOperator);
  583. enumerator.MoveNext();
  584. // skip '] ='
  585. CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.RSquare, out _);
  586. CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.Assignment, out _);
  587. var valueExpression = ParseExpression(ref enumerator, OperatorPrecedence.NonOperator);
  588. items.Add(new GeneralTableConstructorField(keyExpression, valueExpression, currentToken.Position));
  589. break;
  590. case SyntaxTokenType.Identifier when enumerator.GetNext(true).Type is SyntaxTokenType.Assignment:
  591. // record style (key = value)
  592. var name = enumerator.Current.Text;
  593. // skip key and '='
  594. enumerator.MoveNext();
  595. enumerator.MoveNext();
  596. var expression = ParseExpression(ref enumerator, OperatorPrecedence.NonOperator);
  597. items.Add(new RecordTableConstructorField(name.ToString(), expression, currentToken.Position));
  598. break;
  599. default:
  600. // list style
  601. items.Add(new ListTableConstructorField(ParseExpression(ref enumerator, OperatorPrecedence.NonOperator), currentToken.Position));
  602. break;
  603. }
  604. }
  605. RETURN:
  606. return new TableConstructorExpressionNode(items.AsSpan().ToArray(), startToken.Position);
  607. }
  608. ExpressionNode ParseTableAccessExpression(ref SyntaxTokenEnumerator enumerator, ExpressionNode? parentTable)
  609. {
  610. IdentifierNode? identifier = null;
  611. if (parentTable == null)
  612. {
  613. // parse identifier
  614. CheckCurrent(ref enumerator, SyntaxTokenType.Identifier);
  615. identifier = new IdentifierNode(enumerator.Current.Text, enumerator.Current.Position);
  616. MoveNextWithValidation(ref enumerator);
  617. enumerator.SkipEoL();
  618. }
  619. ExpressionNode result;
  620. var current = enumerator.Current;
  621. if (current.Type is SyntaxTokenType.LSquare)
  622. {
  623. // indexer access -- table[key]
  624. // skip '['
  625. MoveNextWithValidation(ref enumerator);
  626. enumerator.SkipEoL();
  627. // parse key expression
  628. var keyExpression = ParseExpression(ref enumerator, OperatorPrecedence.NonOperator);
  629. MoveNextWithValidation(ref enumerator);
  630. enumerator.SkipEoL();
  631. // check ']'
  632. CheckCurrent(ref enumerator, SyntaxTokenType.RSquare);
  633. result = new TableIndexerAccessExpressionNode(identifier ?? parentTable!, keyExpression, current.Position);
  634. }
  635. else if (current.Type is SyntaxTokenType.Dot)
  636. {
  637. // member access -- table.key
  638. // skip '.'
  639. MoveNextWithValidation(ref enumerator);
  640. enumerator.SkipEoL();
  641. // parse identifier
  642. CheckCurrent(ref enumerator, SyntaxTokenType.Identifier);
  643. var key = enumerator.Current.Text.ToString();
  644. result = new TableMemberAccessExpressionNode(identifier ?? parentTable!, key, current.Position);
  645. }
  646. else if (current.Type is SyntaxTokenType.Colon)
  647. {
  648. // self method call -- table:method(arg0, arg1, ...)
  649. // skip ':'
  650. MoveNextWithValidation(ref enumerator);
  651. enumerator.SkipEoL();
  652. // parse identifier
  653. CheckCurrent(ref enumerator, SyntaxTokenType.Identifier);
  654. var methodName = enumerator.Current.Text;
  655. MoveNextWithValidation(ref enumerator);
  656. enumerator.SkipEoL();
  657. // parse arguments
  658. var arguments = ParseCallFunctionArguments(ref enumerator);
  659. result = new CallTableMethodExpressionNode(identifier ?? parentTable!, methodName.ToString(), arguments, current.Position);
  660. }
  661. else
  662. {
  663. LuaParseException.SyntaxError(ChunkName, current.Position, current);
  664. return null!; // dummy
  665. }
  666. return result;
  667. }
  668. GroupedExpressionNode ParseGroupedExpression(ref SyntaxTokenEnumerator enumerator)
  669. {
  670. // skip '('
  671. CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.LParen, out var lParen);
  672. enumerator.SkipEoL();
  673. var expression = ParseExpression(ref enumerator, GetPrecedence(enumerator.Current.Type));
  674. MoveNextWithValidation(ref enumerator);
  675. // check ')'
  676. CheckCurrent(ref enumerator, SyntaxTokenType.RParen);
  677. return new GroupedExpressionNode(expression, lParen.Position);
  678. }
  679. ExpressionNode ParseCallFunctionExpression(ref SyntaxTokenEnumerator enumerator, ExpressionNode? function)
  680. {
  681. // parse name
  682. if (function == null)
  683. {
  684. CheckCurrent(ref enumerator, SyntaxTokenType.Identifier);
  685. function = new IdentifierNode(enumerator.Current.Text, enumerator.Current.Position);
  686. enumerator.MoveNext();
  687. enumerator.SkipEoL();
  688. }
  689. // parse parameters
  690. var parameters = ParseCallFunctionArguments(ref enumerator);
  691. return new CallFunctionExpressionNode(function, parameters);
  692. }
  693. FunctionDeclarationExpressionNode ParseFunctionDeclarationExpression(ref SyntaxTokenEnumerator enumerator)
  694. {
  695. // skip 'function' keyword
  696. CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.Function, out var functionToken);
  697. enumerator.SkipEoL();
  698. var (_, Identifiers, Statements, HasVariableArgments) = ParseFunctionDeclarationCore(ref enumerator, true);
  699. return new FunctionDeclarationExpressionNode(Identifiers, Statements, HasVariableArgments, functionToken.Position);
  700. }
  701. ExpressionNode[] ParseCallFunctionArguments(ref SyntaxTokenEnumerator enumerator)
  702. {
  703. if (enumerator.Current.Type is SyntaxTokenType.String)
  704. {
  705. return [new StringLiteralNode(enumerator.Current.Text, true, enumerator.Current.Position)];
  706. }
  707. else if (enumerator.Current.Type is SyntaxTokenType.RawString)
  708. {
  709. return [new StringLiteralNode(enumerator.Current.Text, false, enumerator.Current.Position)];
  710. }
  711. else if (enumerator.Current.Type is SyntaxTokenType.LCurly)
  712. {
  713. return [ParseTableConstructorExpression(ref enumerator)];
  714. }
  715. // check and skip '('
  716. CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.LParen, out _);
  717. ExpressionNode[] arguments;
  718. if (enumerator.Current.Type is SyntaxTokenType.RParen)
  719. {
  720. // parameterless
  721. arguments = [];
  722. }
  723. else
  724. {
  725. // parse arguments
  726. arguments = ParseExpressionList(ref enumerator);
  727. enumerator.SkipEoL();
  728. MoveNextWithValidation(ref enumerator);
  729. enumerator.SkipEoL();
  730. // check ')'
  731. CheckCurrent(ref enumerator, SyntaxTokenType.RParen);
  732. }
  733. return arguments;
  734. }
  735. ExpressionNode[] ParseExpressionList(ref SyntaxTokenEnumerator enumerator)
  736. {
  737. using var builder = new PooledList<ExpressionNode>(8);
  738. while (true)
  739. {
  740. if (!TryParseExpression(ref enumerator, OperatorPrecedence.NonOperator, out var expression))
  741. {
  742. enumerator.MovePrevious();
  743. break;
  744. }
  745. builder.Add(expression);
  746. enumerator.SkipEoL();
  747. if (enumerator.GetNext().Type != SyntaxTokenType.Comma) break;
  748. MoveNextWithValidation(ref enumerator);
  749. enumerator.SkipEoL();
  750. if (!enumerator.MoveNext()) break;
  751. }
  752. return builder.AsSpan().ToArray();
  753. }
  754. IdentifierNode[] ParseIdentifierList(ref SyntaxTokenEnumerator enumerator)
  755. {
  756. using var buffer = new PooledList<IdentifierNode>(8);
  757. while (true)
  758. {
  759. if (enumerator.Current.Type != SyntaxTokenType.Identifier) break;
  760. var identifier = new IdentifierNode(enumerator.Current.Text, enumerator.Current.Position);
  761. buffer.Add(identifier);
  762. MoveNextWithValidation(ref enumerator);
  763. enumerator.SkipEoL();
  764. if (enumerator.Current.Type != SyntaxTokenType.Comma) break;
  765. MoveNextWithValidation(ref enumerator);
  766. enumerator.SkipEoL();
  767. }
  768. return buffer.AsSpan().ToArray();
  769. }
  770. StatementNode[] ParseStatementList(ref SyntaxTokenEnumerator enumerator, SyntaxTokenType endToken)
  771. {
  772. using var statements = new PooledList<StatementNode>(64);
  773. // parse statements
  774. while (enumerator.MoveNext())
  775. {
  776. if (enumerator.Current.Type == endToken) break;
  777. if (enumerator.Current.Type is SyntaxTokenType.EndOfLine or SyntaxTokenType.SemiColon) continue;
  778. var node = ParseStatement(ref enumerator);
  779. statements.Add(node);
  780. }
  781. return statements.AsSpan().ToArray();
  782. }
  783. void CheckCurrentAndSkip(ref SyntaxTokenEnumerator enumerator, SyntaxTokenType expectedToken, out SyntaxToken token)
  784. {
  785. CheckCurrent(ref enumerator, expectedToken);
  786. token = enumerator.Current;
  787. MoveNextWithValidation(ref enumerator);
  788. }
  789. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  790. void CheckCurrent(ref SyntaxTokenEnumerator enumerator, SyntaxTokenType expectedToken)
  791. {
  792. if (enumerator.Current.Type != expectedToken)
  793. {
  794. LuaParseException.ExpectedToken(ChunkName, enumerator.Current.Position, expectedToken);
  795. }
  796. }
  797. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  798. void MoveNextWithValidation(ref SyntaxTokenEnumerator enumerator)
  799. {
  800. if (!enumerator.MoveNext()) LuaParseException.SyntaxError(ChunkName, enumerator.Current.Position, enumerator.Current);
  801. }
  802. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  803. static OperatorPrecedence GetPrecedence(SyntaxTokenType type)
  804. {
  805. return type switch
  806. {
  807. SyntaxTokenType.Addition or SyntaxTokenType.Subtraction => OperatorPrecedence.Addition,
  808. SyntaxTokenType.Multiplication or SyntaxTokenType.Division or SyntaxTokenType.Modulo => OperatorPrecedence.Multiplication,
  809. SyntaxTokenType.Equality or SyntaxTokenType.Inequality or SyntaxTokenType.LessThan or SyntaxTokenType.LessThanOrEqual or SyntaxTokenType.GreaterThan or SyntaxTokenType.GreaterThanOrEqual => OperatorPrecedence.Relational,
  810. SyntaxTokenType.Concat => OperatorPrecedence.Concat,
  811. SyntaxTokenType.Exponentiation => OperatorPrecedence.Exponentiation,
  812. SyntaxTokenType.And => OperatorPrecedence.And,
  813. SyntaxTokenType.Or => OperatorPrecedence.Or,
  814. _ => OperatorPrecedence.NonOperator,
  815. };
  816. }
  817. static double ConvertTextToNumber(ReadOnlySpan<char> text)
  818. {
  819. if (text.Length > 2 && text[0] is '0' && text[1] is 'x' or 'X')
  820. {
  821. return HexConverter.ToDouble(text);
  822. }
  823. else
  824. {
  825. return double.Parse(text);
  826. }
  827. }
  828. }