luaparse.js 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989
  1. define(function(require, exports, module) {
  2. /*global exports:true module:true require:true define:true global:true */
  3. (function (root, name, factory) {
  4. factory(exports)
  5. }(this, 'luaparse', function (exports) {
  6. 'use strict';
  7. exports.version = '0.1.4';
  8. var input, options, length;
  9. // Options can be set either globally on the parser object through
  10. // defaultOptions, or during the parse call.
  11. var defaultOptions = exports.defaultOptions = {
  12. // Explicitly tell the parser when the input ends.
  13. wait: false
  14. // Store comments as an array in the chunk object.
  15. , comments: true
  16. // Track identifier scopes by adding an isLocal attribute to each
  17. // identifier-node.
  18. , scope: false
  19. // Store location information on each syntax node as
  20. // `loc: { start: { line, column }, end: { line, column } }`.
  21. , locations: false
  22. // Store the start and end character locations on each syntax node as
  23. // `range: [start, end]`.
  24. , ranges: false
  25. };
  26. // The available tokens expressed as enum flags so they can be checked with
  27. // bitwise operations.
  28. var EOF = 1, StringLiteral = 2, Keyword = 4, Identifier = 8
  29. , NumericLiteral = 16, Punctuator = 32, BooleanLiteral = 64
  30. , NilLiteral = 128, VarargLiteral = 256;
  31. exports.tokenTypes = { EOF: EOF, StringLiteral: StringLiteral
  32. , Keyword: Keyword, Identifier: Identifier, NumericLiteral: NumericLiteral
  33. , Punctuator: Punctuator, BooleanLiteral: BooleanLiteral
  34. , NilLiteral: NilLiteral, VarargLiteral: VarargLiteral
  35. };
  36. // As this parser is a bit different from luas own, the error messages
  37. // will be different in some situations.
  38. var errors = exports.errors = {
  39. unexpected: 'Unexpected %1 \'%2\' near \'%3\''
  40. , expected: '\'%1\' expected near \'%2\''
  41. , expectedToken: '%1 expected near \'%2\''
  42. , unfinishedString: 'unfinished string near \'%1\''
  43. , malformedNumber: 'malformed number near \'%1\''
  44. };
  45. // ### Abstract Syntax Tree
  46. //
  47. // The default AST structure is inspired by the Mozilla Parser API but can
  48. // easily be customized by overriding these functions.
  49. var ast = exports.ast = {
  50. labelStatement: function(label) {
  51. return {
  52. type: 'LabelStatement'
  53. , label: label
  54. };
  55. }
  56. , breakStatement: function() {
  57. return {
  58. type: 'BreakStatement'
  59. };
  60. }
  61. , gotoStatement: function(label) {
  62. return {
  63. type: 'GotoStatement'
  64. , label: label
  65. };
  66. }
  67. , returnStatement: function(args) {
  68. return {
  69. type: 'ReturnStatement'
  70. , 'arguments': args
  71. };
  72. }
  73. , ifStatement: function(clauses) {
  74. return {
  75. type: 'IfStatement'
  76. , clauses: clauses
  77. };
  78. }
  79. , ifClause: function(condition, body) {
  80. return {
  81. type: 'IfClause'
  82. , condition: condition
  83. , body: body
  84. };
  85. }
  86. , elseifClause: function(condition, body) {
  87. return {
  88. type: 'ElseifClause'
  89. , condition: condition
  90. , body: body
  91. };
  92. }
  93. , elseClause: function(body) {
  94. return {
  95. type: 'ElseClause'
  96. , body: body
  97. };
  98. }
  99. , whileStatement: function(condition, body) {
  100. return {
  101. type: 'WhileStatement'
  102. , condition: condition
  103. , body: body
  104. };
  105. }
  106. , doStatement: function(body) {
  107. return {
  108. type: 'DoStatement'
  109. , body: body
  110. };
  111. }
  112. , repeatStatement: function(condition, body) {
  113. return {
  114. type: 'RepeatStatement'
  115. , condition: condition
  116. , body: body
  117. };
  118. }
  119. , localStatement: function(variables, init) {
  120. return {
  121. type: 'LocalStatement'
  122. , variables: variables
  123. , init: init
  124. };
  125. }
  126. , assignmentStatement: function(variables, init) {
  127. return {
  128. type: 'AssignmentStatement'
  129. , variables: variables
  130. , init: init
  131. };
  132. }
  133. , callStatement: function(expression) {
  134. return {
  135. type: 'CallStatement'
  136. , expression: expression
  137. };
  138. }
  139. , functionStatement: function(identifier, parameters, isLocal, body) {
  140. return {
  141. type: 'FunctionDeclaration'
  142. , identifier: identifier
  143. , isLocal: isLocal
  144. , parameters: parameters
  145. , body: body
  146. };
  147. }
  148. , forNumericStatement: function(variable, start, end, step, body) {
  149. return {
  150. type: 'ForNumericStatement'
  151. , variable: variable
  152. , start: start
  153. , end: end
  154. , step: step
  155. , body: body
  156. };
  157. }
  158. , forGenericStatement: function(variables, iterators, body) {
  159. return {
  160. type: 'ForGenericStatement'
  161. , variables: variables
  162. , iterators: iterators
  163. , body: body
  164. };
  165. }
  166. , chunk: function(body) {
  167. return {
  168. type: 'Chunk'
  169. , body: body
  170. };
  171. }
  172. , identifier: function(name) {
  173. return {
  174. type: 'Identifier'
  175. , name: name
  176. };
  177. }
  178. , literal: function(type, value, raw) {
  179. type = (type === StringLiteral) ? 'StringLiteral'
  180. : (type === NumericLiteral) ? 'NumericLiteral'
  181. : (type === BooleanLiteral) ? 'BooleanLiteral'
  182. : (type === NilLiteral) ? 'NilLiteral'
  183. : 'VarargLiteral';
  184. return {
  185. type: type
  186. , value: value
  187. , raw: raw
  188. };
  189. }
  190. , tableKey: function(key, value) {
  191. return {
  192. type: 'TableKey'
  193. , key: key
  194. , value: value
  195. };
  196. }
  197. , tableKeyString: function(key, value) {
  198. return {
  199. type: 'TableKeyString'
  200. , key: key
  201. , value: value
  202. };
  203. }
  204. , tableValue: function(value) {
  205. return {
  206. type: 'TableValue'
  207. , value: value
  208. };
  209. }
  210. , tableConstructorExpression: function(fields) {
  211. return {
  212. type: 'TableConstructorExpression'
  213. , fields: fields
  214. };
  215. }
  216. , binaryExpression: function(operator, left, right) {
  217. var type = ('and' === operator || 'or' === operator) ?
  218. 'LogicalExpression' :
  219. 'BinaryExpression';
  220. return {
  221. type: type
  222. , operator: operator
  223. , left: left
  224. , right: right
  225. };
  226. }
  227. , unaryExpression: function(operator, argument) {
  228. return {
  229. type: 'UnaryExpression'
  230. , operator: operator
  231. , argument: argument
  232. };
  233. }
  234. , memberExpression: function(base, indexer, identifier) {
  235. return {
  236. type: 'MemberExpression'
  237. , indexer: indexer
  238. , identifier: identifier
  239. , base: base
  240. };
  241. }
  242. , indexExpression: function(base, index) {
  243. return {
  244. type: 'IndexExpression'
  245. , base: base
  246. , index: index
  247. };
  248. }
  249. , callExpression: function(base, args) {
  250. return {
  251. type: 'CallExpression'
  252. , base: base
  253. , 'arguments': args
  254. };
  255. }
  256. , tableCallExpression: function(base, args) {
  257. return {
  258. type: 'TableCallExpression'
  259. , base: base
  260. , 'arguments': args
  261. };
  262. }
  263. , stringCallExpression: function(base, argument) {
  264. return {
  265. type: 'StringCallExpression'
  266. , base: base
  267. , argument: argument
  268. };
  269. }
  270. , comment: function(value, raw) {
  271. return {
  272. type: 'Comment'
  273. , value: value
  274. , raw: raw
  275. };
  276. }
  277. };
  278. // Wrap up the node object.
  279. function finishNode(node) {
  280. // Pop a `Marker` off the location-array and attach its location data.
  281. if (trackLocations) {
  282. var location = locations.pop();
  283. location.complete();
  284. if (options.locations) node.loc = location.loc;
  285. if (options.ranges) node.range = location.range;
  286. }
  287. return node;
  288. }
  289. // Helpers
  290. // -------
  291. var slice = Array.prototype.slice
  292. , toString = Object.prototype.toString
  293. , indexOf = function indexOf(array, element) {
  294. for (var i = 0, length = array.length; i < length; i++) {
  295. if (array[i] === element) return i;
  296. }
  297. return -1;
  298. };
  299. // Iterate through an array of objects and return the index of an object
  300. // with a matching property.
  301. function indexOfObject(array, property, element) {
  302. for (var i = 0, length = array.length; i < length; i++) {
  303. if (array[i][property] === element) return i;
  304. }
  305. return -1;
  306. }
  307. // A sprintf implementation using %index (beginning at 1) to input
  308. // arguments in the format string.
  309. //
  310. // Example:
  311. //
  312. // // Unexpected function in token
  313. // sprintf('Unexpected %2 in %1.', 'token', 'function');
  314. function sprintf(format) {
  315. var args = slice.call(arguments, 1);
  316. format = format.replace(/%(\d)/g, function (match, index) {
  317. return '' + args[index - 1] || '';
  318. });
  319. return format;
  320. }
  321. // Returns a new object with the properties from all objectes passed as
  322. // arguments. Last argument takes precedence.
  323. //
  324. // Example:
  325. //
  326. // this.options = extend(options, { output: false });
  327. function extend() {
  328. var args = slice.call(arguments)
  329. , dest = {}
  330. , src, prop;
  331. for (var i = 0, length = args.length; i < length; i++) {
  332. src = args[i];
  333. for (prop in src) if (src.hasOwnProperty(prop)) {
  334. dest[prop] = src[prop];
  335. }
  336. }
  337. return dest;
  338. }
  339. // ### Error functions
  340. // #### Raise an exception.
  341. //
  342. // Raise an exception by passing a token, a string format and its paramters.
  343. //
  344. // The passed tokens location will automatically be added to the error
  345. // message if it exists, if not it will default to the lexers current
  346. // position.
  347. //
  348. // Example:
  349. //
  350. // // [1:0] expected [ near (
  351. // raise(token, "expected %1 near %2", '[', token.value);
  352. function raise(token) {
  353. var message = sprintf.apply(null, slice.call(arguments, 1))
  354. , error, col;
  355. if ('undefined' !== typeof token.line) {
  356. col = token.range[0] - token.lineStart;
  357. error = new SyntaxError(sprintf('[%1:%2] %3', token.line, col, message));
  358. error.line = token.line;
  359. error.index = token.range[0];
  360. error.column = col;
  361. } else {
  362. col = index - lineStart + 1;
  363. error = new SyntaxError(sprintf('[%1:%2] %3', line, col, message));
  364. error.index = index;
  365. error.line = line;
  366. error.column = col;
  367. }
  368. throw error;
  369. }
  370. // #### Raise an unexpected token error.
  371. //
  372. // Example:
  373. //
  374. // // expected <name> near '0'
  375. // raiseUnexpectedToken('<name>', token);
  376. function raiseUnexpectedToken(type, token) {
  377. raise(token, errors.expectedToken, type, token.value);
  378. }
  379. // #### Raise a general unexpected error
  380. //
  381. // Usage should pass either a token object or a symbol string which was
  382. // expected. We can also specify a nearby token such as <eof>, this will
  383. // default to the currently active token.
  384. //
  385. // Example:
  386. //
  387. // // Unexpected symbol 'end' near '<eof>'
  388. // unexpected(token);
  389. //
  390. // If there's no token in the buffer it means we have reached <eof>.
  391. function unexpected(found, near) {
  392. if ('undefined' === typeof near) near = lookahead.value;
  393. if ('undefined' !== typeof found.type) {
  394. var type;
  395. switch (found.type) {
  396. case StringLiteral: type = 'string'; break;
  397. case Keyword: type = 'keyword'; break;
  398. case Identifier: type = 'identifier'; break;
  399. case NumericLiteral: type = 'number'; break;
  400. case Punctuator: type = 'symbol'; break;
  401. case BooleanLiteral: type = 'boolean'; break;
  402. case NilLiteral:
  403. return raise(found, errors.unexpected, 'symbol', 'nil', near);
  404. }
  405. return raise(found, errors.unexpected, type, found.value, near);
  406. }
  407. return raise(found, errors.unexpected, 'symbol', found, near);
  408. }
  409. // Lexer
  410. // -----
  411. //
  412. // The lexer, or the tokenizer reads the input string character by character
  413. // and derives a token left-right. To be as efficient as possible the lexer
  414. // prioritizes the common cases such as identifiers. It also works with
  415. // character codes instead of characters as string comparisons was the
  416. // biggest bottleneck of the parser.
  417. //
  418. // If `options.comments` is enabled, all comments encountered will be stored
  419. // in an array which later will be appended to the chunk object. If disabled,
  420. // they will simply be disregarded.
  421. //
  422. // When the lexer has derived a valid token, it will be returned as an object
  423. // containing its value and as well as its position in the input string (this
  424. // is always enabled to provide proper debug messages).
  425. //
  426. // `lex()` starts lexing and returns the following token in the stream.
  427. var index
  428. , token
  429. , previousToken
  430. , lookahead
  431. , comments
  432. , tokenStart
  433. , line
  434. , lineStart;
  435. exports.lex = lex;
  436. function lex() {
  437. skipWhiteSpace();
  438. // Skip comments beginning with --
  439. while (45 === input.charCodeAt(index) &&
  440. 45 === input.charCodeAt(index + 1)) {
  441. scanComment();
  442. skipWhiteSpace();
  443. }
  444. if (index >= length) return {
  445. type : EOF
  446. , value: '<eof>'
  447. , line: line
  448. , lineStart: lineStart
  449. , range: [index, index]
  450. };
  451. var charCode = input.charCodeAt(index)
  452. , next = input.charCodeAt(index + 1);
  453. // Memorize the range index where the token begins.
  454. tokenStart = index;
  455. if (isIdentifierStart(charCode)) return scanIdentifierOrKeyword();
  456. switch (charCode) {
  457. case 39: case 34: // '"
  458. return scanStringLiteral();
  459. // 0-9
  460. case 48: case 49: case 50: case 51: case 52: case 53:
  461. case 54: case 55: case 56: case 57:
  462. return scanNumericLiteral();
  463. case 46: // .
  464. // If the dot is followed by a digit it's a float.
  465. if (isDecDigit(next)) return scanNumericLiteral();
  466. if (46 === next) {
  467. if (46 === input.charCodeAt(index + 2)) return scanVarargLiteral();
  468. return scanPunctuator('..');
  469. }
  470. return scanPunctuator('.');
  471. case 61: // =
  472. if (61 === next) return scanPunctuator('==');
  473. return scanPunctuator('=');
  474. case 62: // >
  475. if (61 === next) return scanPunctuator('>=');
  476. return scanPunctuator('>');
  477. case 60: // <
  478. if (61 === next) return scanPunctuator('<=');
  479. return scanPunctuator('<');
  480. case 126: // ~
  481. if (61 === next) return scanPunctuator('~=');
  482. return raise({}, errors.expected, '=', '~');
  483. case 58: // :
  484. if (58 === next) return scanPunctuator('::');
  485. return scanPunctuator(':');
  486. case 91: // [
  487. // Check for a multiline string, they begin with [= or [[
  488. if (91 === next || 61 === next) return scanLongStringLiteral();
  489. return scanPunctuator('[');
  490. // \* / ^ % , { } ] ( ) ; # - +
  491. case 42: case 47: case 94: case 37: case 44: case 123: case 125:
  492. case 93: case 40: case 41: case 59: case 35: case 45: case 43:
  493. return scanPunctuator(input.charAt(index));
  494. }
  495. return unexpected(input.charAt(index));
  496. }
  497. // Whitespace has no semantic meaning in lua so simply skip ahead while
  498. // tracking the encounted newlines. Newlines are also tracked in all
  499. // token functions where multiline values are allowed.
  500. function skipWhiteSpace() {
  501. while (index < length) {
  502. var charCode = input.charCodeAt(index);
  503. if (isWhiteSpace(charCode)) {
  504. index++;
  505. } else if (isLineTerminator(charCode)) {
  506. line++;
  507. lineStart = ++index;
  508. } else {
  509. break;
  510. }
  511. }
  512. }
  513. // Identifiers, keywords, booleans and nil all look the same syntax wise. We
  514. // simply go through them one by one and defaulting to an identifier if no
  515. // previous case matched.
  516. function scanIdentifierOrKeyword() {
  517. var value, type;
  518. // Slicing the input string is prefered before string concatenation in a
  519. // loop for performance reasons.
  520. while (isIdentifierPart(input.charCodeAt(++index)));
  521. value = input.slice(tokenStart, index);
  522. // Decide on the token type and possibly cast the value.
  523. if (isKeyword(value)) {
  524. type = Keyword;
  525. } else if ('true' === value || 'false' === value) {
  526. type = BooleanLiteral;
  527. value = ('true' === value);
  528. } else if ('nil' === value) {
  529. type = NilLiteral;
  530. value = null;
  531. } else {
  532. type = Identifier;
  533. }
  534. return {
  535. type: type
  536. , value: value
  537. , line: line
  538. , lineStart: lineStart
  539. , range: [tokenStart, index]
  540. };
  541. }
  542. // Once a punctuator reaches this function it should already have been
  543. // validated so we simply return it as a token.
  544. function scanPunctuator(value) {
  545. index += value.length;
  546. return {
  547. type: Punctuator
  548. , value: value
  549. , line: line
  550. , lineStart: lineStart
  551. , range: [tokenStart, index]
  552. };
  553. }
  554. // A vararg literal consists of three dots.
  555. function scanVarargLiteral() {
  556. index += 3;
  557. return {
  558. type: VarargLiteral
  559. , value: '...'
  560. , line: line
  561. , lineStart: lineStart
  562. , range: [tokenStart, index]
  563. };
  564. }
  565. // Find the string literal by matching the delimiter marks used.
  566. function scanStringLiteral() {
  567. var delimiter = input.charCodeAt(index++)
  568. , stringStart = index
  569. , string = ''
  570. , charCode;
  571. while (index < length) {
  572. charCode = input.charCodeAt(index++);
  573. if (delimiter === charCode) break;
  574. if (92 === charCode) { // \
  575. string += input.slice(stringStart, index - 1) + readEscapeSequence();
  576. stringStart = index;
  577. }
  578. // EOF or `\n` terminates a string literal. If we haven't found the
  579. // ending delimiter by now, raise an exception.
  580. else if (index >= length || isLineTerminator(charCode)) {
  581. string += input.slice(stringStart, index - 1);
  582. raise({}, errors.unfinishedString, string + String.fromCharCode(charCode));
  583. }
  584. }
  585. string += input.slice(stringStart, index - 1);
  586. return {
  587. type: StringLiteral
  588. , value: string
  589. , line: line
  590. , lineStart: lineStart
  591. , range: [tokenStart, index]
  592. };
  593. }
  594. // Expect a multiline string literal and return it as a regular string
  595. // literal, if it doesn't validate into a valid multiline string, throw an
  596. // exception.
  597. function scanLongStringLiteral() {
  598. var string = readLongString();
  599. // Fail if it's not a multiline literal.
  600. if (false === string) raise(token, errors.expected, '[', token.value);
  601. return {
  602. type: StringLiteral
  603. , value: string
  604. , line: line
  605. , lineStart: lineStart
  606. , range: [tokenStart, index]
  607. };
  608. }
  609. // Numeric literals will be returned as floating-point numbers instead of
  610. // strings. The raw value should be retrieved from slicing the input string
  611. // later on in the process.
  612. //
  613. // If a hexadecimal number is encountered, it will be converted.
  614. function scanNumericLiteral() {
  615. var character = input.charAt(index)
  616. , next = input.charAt(index + 1);
  617. var value = ('0' === character && 'xX'.indexOf(next || null) >= 0) ?
  618. readHexLiteral() : readDecLiteral();
  619. return {
  620. type: NumericLiteral
  621. , value: value
  622. , line: line
  623. , lineStart: lineStart
  624. , range: [tokenStart, index]
  625. };
  626. }
  627. // Lua hexadecimals have an optional fraction part and an optional binary
  628. // exoponent part. These are not included in JavaScript so we will compute
  629. // all three parts separately and then sum them up at the end of the function
  630. // with the following algorithm.
  631. //
  632. // Digit := toDec(digit)
  633. // Fraction := toDec(fraction) / 16 ^ fractionCount
  634. // BinaryExp := 2 ^ binaryExp
  635. // Number := ( Digit + Fraction ) * BinaryExp
  636. function readHexLiteral() {
  637. var fraction = 0 // defaults to 0 as it gets summed
  638. , binaryExponent = 1 // defaults to 1 as it gets multiplied
  639. , binarySign = 1 // positive
  640. , digit, fractionStart, exponentStart, digitStart;
  641. digitStart = index += 2; // Skip 0x part
  642. // A minimum of one hex digit is required.
  643. if (!isHexDigit(input.charCodeAt(index)))
  644. raise({}, errors.malformedNumber, input.slice(tokenStart, index));
  645. while (isHexDigit(input.charCodeAt(index))) index++;
  646. // Convert the hexadecimal digit to base 10.
  647. digit = parseInt(input.slice(digitStart, index), 16);
  648. // Fraction part i optional.
  649. if ('.' === input.charAt(index)) {
  650. fractionStart = ++index;
  651. while (isHexDigit(input.charCodeAt(index))) index++;
  652. fraction = input.slice(fractionStart, index);
  653. // Empty fraction parts should default to 0, others should be converted
  654. // 0.x form so we can use summation at the end.
  655. fraction = (fractionStart === index) ? 0
  656. : parseInt(fraction, 16) / Math.pow(16, index - fractionStart);
  657. }
  658. // Binary exponents are optional
  659. if ('pP'.indexOf(input.charAt(index) || null) >= 0) {
  660. index++;
  661. // Sign part is optional and defaults to 1 (positive).
  662. if ('+-'.indexOf(input.charAt(index) || null) >= 0)
  663. binarySign = ('+' === input.charAt(index++)) ? 1 : -1;
  664. exponentStart = index;
  665. // The binary exponent sign requires a decimal digit.
  666. if (!isDecDigit(input.charCodeAt(index)))
  667. raise({}, errors.malformedNumber, input.slice(tokenStart, index));
  668. while (isDecDigit(input.charCodeAt(index))) index++;
  669. binaryExponent = input.slice(exponentStart, index);
  670. // Calculate the binary exponent of the number.
  671. binaryExponent = Math.pow(2, binaryExponent * binarySign);
  672. }
  673. return (digit + fraction) * binaryExponent;
  674. }
  675. // Decimal numbers are exactly the same in Lua and in JavaScript, because of
  676. // this we check where the token ends and then parse it with native
  677. // functions.
  678. function readDecLiteral() {
  679. while (isDecDigit(input.charCodeAt(index))) index++;
  680. // Fraction part is optional
  681. if ('.' === input.charAt(index)) {
  682. index++;
  683. // Fraction part defaults to 0
  684. while (isDecDigit(input.charCodeAt(index))) index++;
  685. }
  686. // Exponent part is optional.
  687. if ('eE'.indexOf(input.charAt(index) || null) >= 0) {
  688. index++;
  689. // Sign part is optional.
  690. if ('+-'.indexOf(input.charAt(index) || null) >= 0) index++;
  691. // An exponent is required to contain at least one decimal digit.
  692. if (!isDecDigit(input.charCodeAt(index)))
  693. raise({}, errors.malformedNumber, input.slice(tokenStart, index));
  694. while (isDecDigit(input.charCodeAt(index))) index++;
  695. }
  696. return parseFloat(input.slice(tokenStart, index));
  697. }
  698. // Translate escape sequences to the actual characters.
  699. function readEscapeSequence() {
  700. var sequenceStart = index;
  701. switch (input.charAt(index)) {
  702. // Lua allow the following escape sequences.
  703. // We don't escape the bell sequence.
  704. case 'n': index++; return '\n';
  705. case 'r': index++; return '\r';
  706. case 't': index++; return '\t';
  707. case 'v': index++; return '\x0B';
  708. case 'b': index++; return '\b';
  709. case 'f': index++; return '\f';
  710. // Skips the following span of white-space.
  711. case 'z': index++; skipWhiteSpace(); return '';
  712. // Byte representation should for now be returned as is.
  713. case 'x':
  714. // \xXX, where XX is a sequence of exactly two hexadecimal digits
  715. if (isHexDigit(input.charCodeAt(index + 1)) &&
  716. isHexDigit(input.charCodeAt(index + 2))) {
  717. index += 3;
  718. // Return it as is, without translating the byte.
  719. return '\\' + input.slice(sequenceStart, index);
  720. }
  721. return '\\' + input.charAt(index++);
  722. default:
  723. // \ddd, where ddd is a sequence of up to three decimal digits.
  724. if (isDecDigit(input.charCodeAt(index))) {
  725. while (isDecDigit(input.charCodeAt(++index)));
  726. return '\\' + input.slice(sequenceStart, index);
  727. }
  728. // Simply return the \ as is, it's not escaping any sequence.
  729. return input.charAt(index++);
  730. }
  731. }
  732. // Comments begin with -- after which it will be decided if they are
  733. // multiline comments or not.
  734. //
  735. // The multiline functionality works the exact same way as with string
  736. // literals so we reuse the functionality.
  737. function scanComment() {
  738. tokenStart = index;
  739. index += 2; // --
  740. var character = input.charAt(index)
  741. , content = ''
  742. , isLong = false
  743. , commentStart = index
  744. , lineStartComment = lineStart
  745. , lineComment = line;
  746. if ('[' === character) {
  747. content = readLongString();
  748. // This wasn't a multiline comment after all.
  749. if (false === content) content = character;
  750. else isLong = true;
  751. }
  752. // Scan until next line as long as it's not a multiline comment.
  753. if (!isLong) {
  754. while (index < length) {
  755. if (isLineTerminator(input.charCodeAt(index))) break;
  756. index++;
  757. }
  758. if (options.comments) content = input.slice(commentStart, index);
  759. }
  760. if (options.comments) {
  761. var node = ast.comment(content, input.slice(tokenStart, index));
  762. // `Marker`s depend on tokens available in the parser and as comments are
  763. // intercepted in the lexer all location data is set manually.
  764. if (options.locations) {
  765. node.loc = {
  766. start: { line: lineComment, column: tokenStart - lineStartComment }
  767. , end: { line: line, column: index - lineStart }
  768. };
  769. }
  770. if (options.ranges) {
  771. node.range = [tokenStart, index];
  772. }
  773. comments.push(node);
  774. }
  775. }
  776. // Read a multiline string by calculating the depth of `=` characters and
  777. // then appending until an equal depth is found.
  778. function readLongString() {
  779. var level = 0
  780. , content = ''
  781. , terminator = false
  782. , character, stringStart;
  783. index++; // [
  784. // Calculate the depth of the comment.
  785. while ('=' === input.charAt(index + level)) level++;
  786. // Exit, this is not a long string afterall.
  787. if ('[' !== input.charAt(index + level)) return false;
  788. index += level + 1;
  789. // If the first character is a newline, ignore it and begin on next line.
  790. if (isLineTerminator(input.charCodeAt(index))) {
  791. line++;
  792. lineStart = index++;
  793. }
  794. stringStart = index;
  795. while (index < length) {
  796. character = input.charAt(index++);
  797. // We have to keep track of newlines as `skipWhiteSpace()` does not get
  798. // to scan this part.
  799. if (isLineTerminator(character.charCodeAt(0))) {
  800. line++;
  801. lineStart = index;
  802. }
  803. // Once the delimiter is found, iterate through the depth count and see
  804. // if it matches.
  805. if (']' === character) {
  806. terminator = true;
  807. for (var i = 0; i < level; i++) {
  808. if ('=' !== input.charAt(index + i)) terminator = false;
  809. }
  810. if (']' !== input.charAt(index + level)) terminator = false;
  811. }
  812. // We reached the end of the multiline string. Get out now.
  813. if (terminator) break;
  814. }
  815. content += input.slice(stringStart, index - 1);
  816. index += level + 1;
  817. return content;
  818. }
  819. // ## Lex functions and helpers.
  820. // Read the next token.
  821. //
  822. // This is actually done by setting the current token to the lookahead and
  823. // reading in the new lookahead token.
  824. function next() {
  825. previousToken = token;
  826. token = lookahead;
  827. lookahead = lex();
  828. }
  829. // Consume a token if its value matches. Once consumed or not, return the
  830. // success of the operation.
  831. function consume(value) {
  832. if (value === token.value) {
  833. next();
  834. return true;
  835. }
  836. return false;
  837. }
  838. // Expect the next token value to match. If not, throw an exception.
  839. function expect(value) {
  840. if (value === token.value) next();
  841. else raise(token, errors.expected, value, token.value);
  842. }
  843. // ### Validation functions
  844. function isWhiteSpace(charCode) {
  845. return 9 === charCode || 32 === charCode || 0xB === charCode || 0xC === charCode;
  846. }
  847. function isLineTerminator(charCode) {
  848. return 10 === charCode || 13 === charCode;
  849. }
  850. function isDecDigit(charCode) {
  851. return charCode >= 48 && charCode <= 57;
  852. }
  853. function isHexDigit(charCode) {
  854. return (charCode >= 48 && charCode <= 57) || (charCode >= 97 && charCode <= 102) || (charCode >= 65 && charCode <= 70);
  855. }
  856. // From [Lua 5.2](http://www.lua.org/manual/5.2/manual.html#8.1) onwards
  857. // identifiers cannot use locale-dependet letters.
  858. function isIdentifierStart(charCode) {
  859. return (charCode >= 65 && charCode <= 90) || (charCode >= 97 && charCode <= 122) || 95 === charCode;
  860. }
  861. function isIdentifierPart(charCode) {
  862. return (charCode >= 65 && charCode <= 90) || (charCode >= 97 && charCode <= 122) || 95 === charCode || (charCode >= 48 && charCode <= 57);
  863. }
  864. // [3.1 Lexical Conventions](http://www.lua.org/manual/5.2/manual.html#3.1)
  865. //
  866. // `true`, `false` and `nil` will not be considered keywords, but literals.
  867. function isKeyword(id) {
  868. switch (id.length) {
  869. case 2:
  870. return 'do' === id || 'if' === id || 'in' === id || 'or' === id;
  871. case 3:
  872. return 'and' === id || 'end' === id || 'for' === id || 'not' === id;
  873. case 4:
  874. return 'else' === id || 'goto' === id || 'then' === id;
  875. case 5:
  876. return 'break' === id || 'local' === id || 'until' === id || 'while' === id;
  877. case 6:
  878. return 'elseif' === id || 'repeat' === id || 'return' === id;
  879. case 8:
  880. return 'function' === id;
  881. }
  882. return false;
  883. }
  884. function isUnary(token) {
  885. if (Punctuator === token.type) return '#-'.indexOf(token.value) >= 0;
  886. if (Keyword === token.type) return 'not' === token.value;
  887. return false;
  888. }
  889. // @TODO this needs to be rethought.
  890. function isCallExpression(expression) {
  891. switch (expression.type) {
  892. case 'CallExpression':
  893. case 'TableCallExpression':
  894. case 'StringCallExpression':
  895. return true;
  896. }
  897. return false;
  898. }
  899. // Check if the token syntactically closes a block.
  900. function isBlockFollow(token) {
  901. if (EOF === token.type) return true;
  902. if (Keyword !== token.type) return false;
  903. switch (token.value) {
  904. case 'else': case 'elseif':
  905. case 'end': case 'until':
  906. return true;
  907. default:
  908. return false;
  909. }
  910. }
  911. // Scope
  912. // -----
  913. // Store each block scope as a an array of identifier names. Each scope is
  914. // stored in an FILO-array.
  915. var scopes
  916. // The current scope index
  917. , scopeDepth
  918. // A list of all global identifier nodes.
  919. , globals;
  920. // Create a new scope inheriting all declarations from the previous scope.
  921. function createScope() {
  922. scopes.push(Array.apply(null, scopes[scopeDepth++]));
  923. }
  924. // Exit and remove the current scope.
  925. function exitScope() {
  926. scopes.pop();
  927. scopeDepth--;
  928. }
  929. // Add identifier name to the current scope if it doesnt already exist.
  930. function scopeIdentifierName(name) {
  931. if (-1 !== indexOf(scopes[scopeDepth], name)) return;
  932. scopes[scopeDepth].push(name);
  933. }
  934. // Add identifier to the current scope
  935. function scopeIdentifier(node) {
  936. scopeIdentifierName(node.name);
  937. attachScope(node, true);
  938. }
  939. // Attach scope information to node. If the node is global, store it in the
  940. // globals array so we can return the information to the user.
  941. function attachScope(node, isLocal) {
  942. if (!isLocal && -1 === indexOfObject(globals, 'name', node.name))
  943. globals.push(node);
  944. node.isLocal = isLocal;
  945. }
  946. // Is the identifier name available in this scope.
  947. function scopeHasName(name) {
  948. return (-1 !== indexOf(scopes[scopeDepth], name));
  949. }
  950. // Location tracking
  951. // -----------------
  952. //
  953. // Locations are stored in FILO-array as a `Marker` object consisting of both
  954. // `loc` and `range` data. Once a `Marker` is popped off the list an end
  955. // location is added and the data is attached to a syntax node.
  956. var locations = []
  957. , trackLocations;
  958. function createLocationMarker() {
  959. return new Marker(token);
  960. }
  961. function Marker(token) {
  962. if (options.locations) {
  963. this.loc = {
  964. start: {
  965. line: token.line
  966. , column: token.range[0] - token.lineStart
  967. }
  968. , end: {
  969. line: 0
  970. , column: 0
  971. }
  972. };
  973. }
  974. if (options.ranges) this.range = [token.range[0], 0];
  975. }
  976. // Complete the location data stored in the `Marker` by adding the location
  977. // of the *previous token* as an end location.
  978. Marker.prototype.complete = function() {
  979. if (options.locations) {
  980. this.loc.end.line = previousToken.line;
  981. this.loc.end.column = previousToken.range[1] - previousToken.lineStart;
  982. }
  983. if (options.ranges) {
  984. this.range[1] = previousToken.range[1];
  985. }
  986. };
  987. // Create a new `Marker` and add it to the FILO-array.
  988. function markLocation() {
  989. if (trackLocations) locations.push(createLocationMarker());
  990. }
  991. // Push an arbitrary `Marker` object onto the FILO-array.
  992. function pushLocation(marker) {
  993. if (trackLocations) locations.push(marker);
  994. }
  995. // Parse functions
  996. // ---------------
  997. // Chunk is the main program object. Syntactically it's the same as a block.
  998. //
  999. // chunk ::= block
  1000. function parseChunk() {
  1001. next();
  1002. markLocation();
  1003. var body = parseBlock();
  1004. if (EOF !== token.type) unexpected(token);
  1005. // If the body is empty no previousToken exists when finishNode runs.
  1006. if (trackLocations && !body.length) previousToken = token;
  1007. return finishNode(ast.chunk(body));
  1008. }
  1009. // A block contains a list of statements with an optional return statement
  1010. // as its last statement.
  1011. //
  1012. // block ::= {stat} [retstat]
  1013. function parseBlock(terminator) {
  1014. var block = []
  1015. , statement;
  1016. // Each block creates a new scope.
  1017. if (options.scope) createScope();
  1018. while (!isBlockFollow(token)) {
  1019. // Return has to be the last statement in a block.
  1020. if ('return' === token.value) {
  1021. block.push(parseStatement());
  1022. break;
  1023. }
  1024. statement = parseStatement();
  1025. // Statements are only added if they are returned, this allows us to
  1026. // ignore some statements, such as EmptyStatement.
  1027. if (statement) block.push(statement);
  1028. }
  1029. if (options.scope) exitScope();
  1030. // Doesn't really need an ast node
  1031. return block;
  1032. }
  1033. // There are two types of statements, simple and compound.
  1034. //
  1035. // statement ::= break | goto | do | while | repeat | return
  1036. // | if | for | function | local | label | assignment
  1037. // | functioncall | ';'
  1038. function parseStatement() {
  1039. markLocation();
  1040. if (Keyword === token.type) {
  1041. switch (token.value) {
  1042. case 'local': next(); return parseLocalStatement();
  1043. case 'if': next(); return parseIfStatement();
  1044. case 'return': next(); return parseReturnStatement();
  1045. case 'function': next();
  1046. var name = parseFunctionName();
  1047. return parseFunctionDeclaration(name);
  1048. case 'while': next(); return parseWhileStatement();
  1049. case 'for': next(); return parseForStatement();
  1050. case 'repeat': next(); return parseRepeatStatement();
  1051. case 'break': next(); return parseBreakStatement();
  1052. case 'do': next(); return parseDoStatement();
  1053. case 'goto': next(); return parseGotoStatement();
  1054. }
  1055. }
  1056. if (Punctuator === token.type) {
  1057. if (consume('::')) return parseLabelStatement();
  1058. }
  1059. // Assignments memorizes the location and pushes it manually for wrapper
  1060. // nodes. Additionally empty `;` statements should not mark a location.
  1061. if (trackLocations) locations.pop();
  1062. // When a `;` is encounted, simply eat it without storing it.
  1063. if (consume(';')) return;
  1064. return parseAssignmentOrCallStatement();
  1065. }
  1066. // ## Statements
  1067. // label ::= '::' Name '::'
  1068. function parseLabelStatement() {
  1069. var name = token.value
  1070. , label = parseIdentifier();
  1071. if (options.scope) {
  1072. scopeIdentifierName('::' + name + '::');
  1073. attachScope(label, true);
  1074. }
  1075. expect('::');
  1076. return finishNode(ast.labelStatement(label));
  1077. }
  1078. // break ::= 'break'
  1079. function parseBreakStatement() {
  1080. return finishNode(ast.breakStatement());
  1081. }
  1082. // goto ::= 'goto' Name
  1083. function parseGotoStatement() {
  1084. var name = token.value
  1085. , label = parseIdentifier();
  1086. if (options.scope) label.isLabel = scopeHasName('::' + name + '::');
  1087. return finishNode(ast.gotoStatement(label));
  1088. }
  1089. // do ::= 'do' block 'end'
  1090. function parseDoStatement() {
  1091. var body = parseBlock();
  1092. expect('end');
  1093. return finishNode(ast.doStatement(body));
  1094. }
  1095. // while ::= 'while' exp 'do' block 'end'
  1096. function parseWhileStatement() {
  1097. var condition = parseExpectedExpression();
  1098. expect('do');
  1099. var body = parseBlock();
  1100. expect('end');
  1101. return finishNode(ast.whileStatement(condition, body));
  1102. }
  1103. // repeat ::= 'repeat' block 'until' exp
  1104. function parseRepeatStatement() {
  1105. var body = parseBlock();
  1106. expect('until');
  1107. var condition = parseExpectedExpression();
  1108. return finishNode(ast.repeatStatement(condition, body));
  1109. }
  1110. // retstat ::= 'return' [exp {',' exp}] [';']
  1111. function parseReturnStatement() {
  1112. var expressions = [];
  1113. if ('end' !== token.value) {
  1114. var expression = parseExpression();
  1115. if (null != expression) expressions.push(expression);
  1116. while (consume(',')) {
  1117. expression = parseExpectedExpression();
  1118. expressions.push(expression);
  1119. }
  1120. consume(';'); // grammar tells us ; is optional here.
  1121. }
  1122. return finishNode(ast.returnStatement(expressions));
  1123. }
  1124. // if ::= 'if' exp 'then' block {elif} ['else' block] 'end'
  1125. // elif ::= 'elseif' exp 'then' block
  1126. function parseIfStatement() {
  1127. var clauses = []
  1128. , condition
  1129. , body
  1130. , marker;
  1131. // IfClauses begin at the same location as the parent IfStatement.
  1132. // It ends at the start of `end`, `else`, or `elseif`.
  1133. if (trackLocations) {
  1134. marker = locations[locations.length - 1];
  1135. locations.push(marker);
  1136. }
  1137. condition = parseExpectedExpression();
  1138. expect('then');
  1139. body = parseBlock();
  1140. clauses.push(finishNode(ast.ifClause(condition, body)));
  1141. if (trackLocations) marker = createLocationMarker();
  1142. while (consume('elseif')) {
  1143. pushLocation(marker);
  1144. condition = parseExpectedExpression();
  1145. expect('then');
  1146. body = parseBlock();
  1147. clauses.push(finishNode(ast.elseifClause(condition, body)));
  1148. if (trackLocations) marker = createLocationMarker();
  1149. }
  1150. if (consume('else')) {
  1151. // Include the `else` in the location of ElseClause.
  1152. if (trackLocations) {
  1153. marker = new Marker(previousToken);
  1154. locations.push(marker);
  1155. }
  1156. body = parseBlock();
  1157. clauses.push(finishNode(ast.elseClause(body)));
  1158. }
  1159. expect('end');
  1160. return finishNode(ast.ifStatement(clauses));
  1161. }
  1162. // There are two types of for statements, generic and numeric.
  1163. //
  1164. // for ::= Name '=' exp ',' exp [',' exp] 'do' block 'end'
  1165. // for ::= namelist 'in' explist 'do' block 'end'
  1166. // namelist ::= Name {',' Name}
  1167. // explist ::= exp {',' exp}
  1168. function parseForStatement() {
  1169. var variable = parseIdentifier()
  1170. , body;
  1171. // The start-identifier is local.
  1172. if (options.scope) scopeIdentifier(variable);
  1173. // If the first expression is followed by a `=` punctuator, this is a
  1174. // Numeric For Statement.
  1175. if (consume('=')) {
  1176. // Start expression
  1177. var start = parseExpectedExpression();
  1178. expect(',');
  1179. // End expression
  1180. var end = parseExpectedExpression();
  1181. // Optional step expression
  1182. var step = consume(',') ? parseExpectedExpression() : null;
  1183. expect('do');
  1184. body = parseBlock();
  1185. expect('end');
  1186. return finishNode(ast.forNumericStatement(variable, start, end, step, body));
  1187. }
  1188. // If not, it's a Generic For Statement
  1189. else {
  1190. // The namelist can contain one or more identifiers.
  1191. var variables = [variable];
  1192. while (consume(',')) {
  1193. variable = parseIdentifier();
  1194. // Each variable in the namelist is locally scoped.
  1195. if (options.scope) scopeIdentifier(variable);
  1196. variables.push(variable);
  1197. }
  1198. expect('in');
  1199. var iterators = [];
  1200. // One or more expressions in the explist.
  1201. do {
  1202. var expression = parseExpectedExpression();
  1203. iterators.push(expression);
  1204. } while (consume(','));
  1205. expect('do');
  1206. body = parseBlock();
  1207. expect('end');
  1208. return finishNode(ast.forGenericStatement(variables, iterators, body));
  1209. }
  1210. }
  1211. // Local statements can either be variable assignments or function
  1212. // definitions. If a function definition is found, it will be delegated to
  1213. // `parseFunctionDeclaration()` with the isLocal flag.
  1214. //
  1215. // This AST structure might change into a local assignment with a function
  1216. // child.
  1217. //
  1218. // local ::= 'local' 'function' Name funcdecl
  1219. // | 'local' Name {',' Name} ['=' exp {',' exp}
  1220. function parseLocalStatement() {
  1221. var name;
  1222. if (Identifier === token.type) {
  1223. var variables = []
  1224. , init = [];
  1225. do {
  1226. name = parseIdentifier();
  1227. variables.push(name);
  1228. } while (consume(','));
  1229. if (consume('=')) {
  1230. do {
  1231. var expression = parseExpectedExpression();
  1232. init.push(expression);
  1233. } while (consume(','));
  1234. }
  1235. // Declarations doesn't exist before the statement has been evaluated.
  1236. // Therefore assignments can't use their declarator. And the identifiers
  1237. // shouldn't be added to the scope until the statement is complete.
  1238. if (options.scope) {
  1239. for (var i = 0, l = variables.length; i < l; i++) {
  1240. scopeIdentifier(variables[i]);
  1241. }
  1242. }
  1243. return finishNode(ast.localStatement(variables, init));
  1244. }
  1245. if (consume('function')) {
  1246. name = parseIdentifier();
  1247. if (options.scope) scopeIdentifier(name);
  1248. // MemberExpressions are not allowed in local function statements.
  1249. return parseFunctionDeclaration(name, true);
  1250. } else {
  1251. raiseUnexpectedToken('<name>', token);
  1252. }
  1253. }
  1254. // assignment ::= varlist '=' explist
  1255. // varlist ::= prefixexp {',' prefixexp}
  1256. // explist ::= exp {',' exp}
  1257. //
  1258. // call ::= callexp
  1259. // callexp ::= prefixexp args | prefixexp ':' Name args
  1260. function parseAssignmentOrCallStatement() {
  1261. // Keep a reference to the previous token for better error messages in case
  1262. // of invalid statement
  1263. var previous = token
  1264. , expression, marker;
  1265. if (trackLocations) marker = createLocationMarker();
  1266. expression = parsePrefixExpression();
  1267. if (null == expression) return unexpected(token);
  1268. if (',='.indexOf(token.value) >= 0) {
  1269. var variables = [expression]
  1270. , init = []
  1271. , exp;
  1272. while (consume(',')) {
  1273. exp = parsePrefixExpression();
  1274. if (null == exp) raiseUnexpectedToken('<expression>', token);
  1275. variables.push(exp);
  1276. }
  1277. expect('=');
  1278. do {
  1279. exp = parseExpectedExpression();
  1280. init.push(exp);
  1281. } while (consume(','));
  1282. pushLocation(marker);
  1283. return finishNode(ast.assignmentStatement(variables, init));
  1284. }
  1285. if (isCallExpression(expression)) {
  1286. pushLocation(marker);
  1287. return finishNode(ast.callStatement(expression));
  1288. }
  1289. // The prefix expression was neither part of an assignment or a
  1290. // callstatement, however as it was valid it's been consumed, so raise
  1291. // the exception on the previous token to provide a helpful message.
  1292. return unexpected(previous);
  1293. }
  1294. // ### Non-statements
  1295. // Identifier ::= Name
  1296. function parseIdentifier() {
  1297. markLocation();
  1298. var identifier = token.value;
  1299. if (Identifier !== token.type) raiseUnexpectedToken('<name>', token);
  1300. next();
  1301. return finishNode(ast.identifier(identifier));
  1302. }
  1303. // Parse the functions parameters and body block. The name should already
  1304. // have been parsed and passed to this declaration function. By separating
  1305. // this we allow for anonymous functions in expressions.
  1306. //
  1307. // For local functions there's a boolean parameter which needs to be set
  1308. // when parsing the declaration.
  1309. //
  1310. // funcdecl ::= '(' [parlist] ')' block 'end'
  1311. // parlist ::= Name {',' Name} | [',' '...'] | '...'
  1312. function parseFunctionDeclaration(name, isLocal) {
  1313. var parameters = [];
  1314. expect('(');
  1315. // The declaration has arguments
  1316. if (!consume(')')) {
  1317. // Arguments are a comma separated list of identifiers, optionally ending
  1318. // with a vararg.
  1319. while (true) {
  1320. if (Identifier === token.type) {
  1321. var parameter = parseIdentifier();
  1322. // Function parameters are local.
  1323. if (options.scope) scopeIdentifier(parameter);
  1324. parameters.push(parameter);
  1325. if (consume(',')) continue;
  1326. else if (consume(')')) break;
  1327. }
  1328. // No arguments are allowed after a vararg.
  1329. else if (VarargLiteral === token.type) {
  1330. parameters.push(parsePrimaryExpression());
  1331. expect(')');
  1332. break;
  1333. } else {
  1334. raiseUnexpectedToken('<name> or \'...\'', token);
  1335. }
  1336. }
  1337. }
  1338. var body = parseBlock();
  1339. expect('end');
  1340. isLocal = isLocal || false;
  1341. return finishNode(ast.functionStatement(name, parameters, isLocal, body));
  1342. }
  1343. // Parse the function name as identifiers and member expressions.
  1344. //
  1345. // Name {'.' Name} [':' Name]
  1346. function parseFunctionName() {
  1347. var base, name, marker;
  1348. if (trackLocations) marker = createLocationMarker();
  1349. base = parseIdentifier();
  1350. if (options.scope) attachScope(base, false);
  1351. while (consume('.')) {
  1352. pushLocation(marker);
  1353. name = parseIdentifier();
  1354. if (options.scope) attachScope(name, false);
  1355. base = finishNode(ast.memberExpression(base, '.', name));
  1356. }
  1357. if (consume(':')) {
  1358. pushLocation(marker);
  1359. name = parseIdentifier();
  1360. if (options.scope) attachScope(name, false);
  1361. base = finishNode(ast.memberExpression(base, ':', name));
  1362. }
  1363. return base;
  1364. }
  1365. // tableconstructor ::= '{' [fieldlist] '}'
  1366. // fieldlist ::= field {fieldsep field} fieldsep
  1367. // field ::= '[' exp ']' '=' exp | Name = 'exp' | exp
  1368. //
  1369. // fieldsep ::= ',' | ';'
  1370. function parseTableConstructor() {
  1371. var fields = []
  1372. , key, value;
  1373. while (true) {
  1374. markLocation();
  1375. if (Punctuator === token.type && consume('[')) {
  1376. key = parseExpectedExpression();
  1377. expect(']');
  1378. expect('=');
  1379. value = parseExpectedExpression();
  1380. fields.push(finishNode(ast.tableKey(key, value)));
  1381. } else if (Identifier === token.type) {
  1382. key = parseExpectedExpression();
  1383. if (consume('=')) {
  1384. value = parseExpectedExpression();
  1385. fields.push(finishNode(ast.tableKeyString(key, value)));
  1386. } else {
  1387. fields.push(finishNode(ast.tableValue(key)));
  1388. }
  1389. } else {
  1390. if (null == (value = parseExpression())) {
  1391. locations.pop();
  1392. break;
  1393. }
  1394. fields.push(finishNode(ast.tableValue(value)));
  1395. }
  1396. if (',;'.indexOf(token.value) >= 0) {
  1397. next();
  1398. continue;
  1399. }
  1400. if ('}' === token.value) break;
  1401. }
  1402. expect('}');
  1403. return finishNode(ast.tableConstructorExpression(fields));
  1404. }
  1405. // Expression parser
  1406. // -----------------
  1407. //
  1408. // Expressions are evaluated and always return a value. If nothing is
  1409. // matched null will be returned.
  1410. //
  1411. // exp ::= (unop exp | primary | prefixexp ) { binop exp }
  1412. //
  1413. // primary ::= nil | false | true | Number | String | '...'
  1414. // | functiondef | tableconstructor
  1415. //
  1416. // prefixexp ::= (Name | '(' exp ')' ) { '[' exp ']'
  1417. // | '.' Name | ':' Name args | args }
  1418. //
  1419. function parseExpression() {
  1420. var expression = parseSubExpression(0);
  1421. return expression;
  1422. }
  1423. // Parse an expression expecting it to be valid.
  1424. function parseExpectedExpression() {
  1425. var expression = parseExpression();
  1426. if (null == expression) raiseUnexpectedToken('<expression>', token);
  1427. else return expression;
  1428. }
  1429. // Return the precedence priority of the operator.
  1430. //
  1431. // As unary `-` can't be distinguished from binary `-`, unary precedence
  1432. // isn't described in this table but in `parseSubExpression()` itself.
  1433. //
  1434. // As this function gets hit on every expression it's been optimized due to
  1435. // the expensive CompareICStub which took ~8% of the parse time.
  1436. function binaryPrecedence(operator) {
  1437. var charCode = operator.charCodeAt(0)
  1438. , length = operator.length;
  1439. if (1 === length) {
  1440. switch (charCode) {
  1441. case 94: return 10; // ^
  1442. case 42: case 47: case 37: return 7; // * / %
  1443. case 43: case 45: return 6; // + -
  1444. case 60: case 62: return 3; // < >
  1445. }
  1446. } else if (2 === length) {
  1447. switch (charCode) {
  1448. case 46: return 5; // ..
  1449. case 60: case 62: case 61: case 126: return 3; // <= >= == ~=
  1450. case 111: return 1; // or
  1451. }
  1452. } else if (97 === charCode && 'and' === operator) return 2;
  1453. return 0;
  1454. }
  1455. // Implement an operator-precedence parser to handle binary operator
  1456. // precedence.
  1457. //
  1458. // We use this algorithm because it's compact, it's fast and Lua core uses
  1459. // the same so we can be sure our expressions are parsed in the same manner
  1460. // without excessive amounts of tests.
  1461. //
  1462. // exp ::= (unop exp | primary | prefixexp ) { binop exp }
  1463. function parseSubExpression(minPrecedence) {
  1464. var operator = token.value
  1465. // The left-hand side in binary operations.
  1466. , expression, marker;
  1467. if (trackLocations) marker = createLocationMarker();
  1468. // UnaryExpression
  1469. if (isUnary(token)) {
  1470. markLocation();
  1471. next();
  1472. var argument = parseSubExpression(8);
  1473. if (argument == null) raiseUnexpectedToken('<expression>', token);
  1474. expression = finishNode(ast.unaryExpression(operator, argument));
  1475. }
  1476. if (null == expression) {
  1477. // PrimaryExpression
  1478. expression = parsePrimaryExpression();
  1479. // PrefixExpression
  1480. if (null == expression) {
  1481. expression = parsePrefixExpression();
  1482. }
  1483. }
  1484. // This is not a valid left hand expression.
  1485. if (null == expression) return null;
  1486. var precedence;
  1487. while (true) {
  1488. operator = token.value;
  1489. precedence = (Punctuator === token.type || Keyword === token.type) ?
  1490. binaryPrecedence(operator) : 0;
  1491. if (precedence === 0 || precedence <= minPrecedence) break;
  1492. // Right-hand precedence operators
  1493. if ('^' === operator || '..' === operator) precedence--;
  1494. next();
  1495. var right = parseSubExpression(precedence);
  1496. if (null == right) raiseUnexpectedToken('<expression>', token);
  1497. // Push in the marker created before the loop to wrap its entirety.
  1498. if (trackLocations) locations.push(marker);
  1499. expression = finishNode(ast.binaryExpression(operator, expression, right));
  1500. }
  1501. return expression;
  1502. }
  1503. // prefixexp ::= prefix {suffix}
  1504. // prefix ::= Name | '(' exp ')'
  1505. // suffix ::= '[' exp ']' | '.' Name | ':' Name args | args
  1506. //
  1507. // args ::= '(' [explist] ')' | tableconstructor | String
  1508. function parsePrefixExpression() {
  1509. var base, name, marker
  1510. // Keep track of the scope, if a parent is local so are the children.
  1511. , isLocal;
  1512. if (trackLocations) marker = createLocationMarker();
  1513. // The prefix
  1514. if (Identifier === token.type) {
  1515. name = token.value;
  1516. base = parseIdentifier();
  1517. // Set the parent scope.
  1518. if (options.scope) attachScope(base, isLocal = scopeHasName(name));
  1519. } else if (consume('(')) {
  1520. base = parseExpectedExpression();
  1521. expect(')');
  1522. if (options.scope) isLocal = base.isLocal;
  1523. } else {
  1524. return null;
  1525. }
  1526. // The suffix
  1527. var expression, identifier;
  1528. while (true) {
  1529. if (Punctuator === token.type) {
  1530. switch (token.value) {
  1531. case '[':
  1532. pushLocation(marker);
  1533. next();
  1534. expression = parseExpectedExpression();
  1535. base = finishNode(ast.indexExpression(base, expression));
  1536. expect(']');
  1537. break;
  1538. case '.':
  1539. pushLocation(marker);
  1540. next();
  1541. identifier = parseIdentifier();
  1542. // Inherit the scope
  1543. if (options.scope) attachScope(identifier, isLocal);
  1544. base = finishNode(ast.memberExpression(base, '.', identifier));
  1545. break;
  1546. case ':':
  1547. pushLocation(marker);
  1548. next();
  1549. identifier = parseIdentifier();
  1550. if (options.scope) attachScope(identifier, isLocal);
  1551. base = finishNode(ast.memberExpression(base, ':', identifier));
  1552. // Once a : is found, this has to be a CallExpression, otherwise
  1553. // throw an error.
  1554. pushLocation(marker);
  1555. base = parseCallExpression(base);
  1556. break;
  1557. case '(': case '{': // args
  1558. pushLocation(marker);
  1559. base = parseCallExpression(base);
  1560. break;
  1561. default:
  1562. return base;
  1563. }
  1564. } else if (StringLiteral === token.type) {
  1565. pushLocation(marker);
  1566. base = parseCallExpression(base);
  1567. } else {
  1568. break;
  1569. }
  1570. }
  1571. return base;
  1572. }
  1573. // args ::= '(' [explist] ')' | tableconstructor | String
  1574. function parseCallExpression(base) {
  1575. if (Punctuator === token.type) {
  1576. switch (token.value) {
  1577. case '(':
  1578. next();
  1579. // List of expressions
  1580. var expressions = [];
  1581. var expression = parseExpression();
  1582. if (null != expression) expressions.push(expression);
  1583. while (consume(',')) {
  1584. expression = parseExpectedExpression();
  1585. expressions.push(expression);
  1586. }
  1587. expect(')');
  1588. return finishNode(ast.callExpression(base, expressions));
  1589. case '{':
  1590. markLocation();
  1591. next();
  1592. var table = parseTableConstructor();
  1593. return finishNode(ast.tableCallExpression(base, table));
  1594. }
  1595. } else if (StringLiteral === token.type) {
  1596. return finishNode(ast.stringCallExpression(base, parsePrimaryExpression()));
  1597. }
  1598. raiseUnexpectedToken('function arguments', token);
  1599. }
  1600. // primary ::= String | Numeric | nil | true | false
  1601. // | functiondef | tableconstructor | '...'
  1602. function parsePrimaryExpression() {
  1603. var literals = StringLiteral | NumericLiteral | BooleanLiteral | NilLiteral | VarargLiteral
  1604. , value = token.value
  1605. , type = token.type
  1606. , marker;
  1607. if (trackLocations) marker = createLocationMarker();
  1608. if (type & literals) {
  1609. pushLocation(marker);
  1610. var raw = input.slice(token.range[0], token.range[1]);
  1611. next();
  1612. return finishNode(ast.literal(type, value, raw));
  1613. } else if (Keyword === type && 'function' === value) {
  1614. pushLocation(marker);
  1615. next();
  1616. return parseFunctionDeclaration(null);
  1617. } else if (consume('{')) {
  1618. pushLocation(marker);
  1619. return parseTableConstructor();
  1620. }
  1621. }
  1622. // Parser
  1623. // ------
  1624. // Export the main parser.
  1625. //
  1626. // - `wait` Hold parsing until end() is called. Defaults to false
  1627. // - `comments` Store comments. Defaults to true.
  1628. // - `scope` Track identifier scope. Defaults to false.
  1629. //
  1630. // Example:
  1631. //
  1632. // var parser = require('luaparser');
  1633. // parser.parse('i = 0');
  1634. exports.parse = parse;
  1635. function parse(_input, _options) {
  1636. if ('undefined' === typeof _options && 'object' === typeof _input) {
  1637. _options = _input;
  1638. _input = undefined;
  1639. }
  1640. if (!_options) _options = {};
  1641. input = _input || '';
  1642. options = extend(defaultOptions, _options);
  1643. // Rewind the lexer
  1644. index = 0;
  1645. line = 1;
  1646. lineStart = 0;
  1647. length = input.length;
  1648. // When tracking identifier scope, initialize with an empty scope.
  1649. scopes = [[]];
  1650. scopeDepth = 0;
  1651. globals = [];
  1652. locations = [];
  1653. if (options.comments) comments = [];
  1654. if (!options.wait) return end();
  1655. return exports;
  1656. }
  1657. // Write to the source code buffer without beginning the parse.
  1658. exports.write = write;
  1659. function write(_input) {
  1660. input += String(_input);
  1661. length = input.length;
  1662. return exports;
  1663. }
  1664. // Send an EOF and begin parsing.
  1665. exports.end = end;
  1666. function end(_input) {
  1667. if ('undefined' !== typeof _input) write(_input);
  1668. length = input.length;
  1669. trackLocations = options.locations || options.ranges;
  1670. // Initialize with a lookahead token.
  1671. lookahead = lex();
  1672. var chunk = parseChunk();
  1673. if (options.comments) chunk.comments = comments;
  1674. if (options.scope) chunk.globals = globals;
  1675. if (locations.length > 0)
  1676. throw new Error('Location tracking failed. This is most likely a bug in luaparse');
  1677. return chunk;
  1678. }
  1679. }));
  1680. /* vim: set sw=2 ts=2 et tw=79 : */
  1681. });