parser.js 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181
  1. /*!
  2. * Stylus - Parser
  3. * Copyright (c) Automattic <developer.wordpress.com>
  4. * MIT Licensed
  5. */
  6. /**
  7. * Module dependencies.
  8. */
  9. var Lexer = require('./lexer')
  10. , nodes = require('./nodes')
  11. , Token = require('./token')
  12. , units = require('./units')
  13. , errors = require('./errors')
  14. , cache = require('./cache');
  15. // debuggers
  16. var debug = {
  17. lexer: require('debug')('stylus:lexer')
  18. , selector: require('debug')('stylus:parser:selector')
  19. };
  20. /**
  21. * Selector composite tokens.
  22. */
  23. var selectorTokens = [
  24. 'ident'
  25. , 'string'
  26. , 'selector'
  27. , 'function'
  28. , 'comment'
  29. , 'boolean'
  30. , 'space'
  31. , 'color'
  32. , 'unit'
  33. , 'for'
  34. , 'in'
  35. , '['
  36. , ']'
  37. , '('
  38. , ')'
  39. , '+'
  40. , '-'
  41. , '*'
  42. , '*='
  43. , '<'
  44. , '>'
  45. , '='
  46. , ':'
  47. , '&'
  48. , '&&'
  49. , '~'
  50. , '{'
  51. , '}'
  52. , '.'
  53. , '..'
  54. , '/'
  55. ];
  56. /**
  57. * CSS pseudo-classes and pseudo-elements.
  58. * See http://dev.w3.org/csswg/selectors4/
  59. */
  60. var pseudoSelectors = [
  61. // Logical Combinations
  62. 'matches'
  63. , 'not'
  64. // Linguistic Pseudo-classes
  65. , 'dir'
  66. , 'lang'
  67. // Location Pseudo-classes
  68. , 'any-link'
  69. , 'link'
  70. , 'visited'
  71. , 'local-link'
  72. , 'target'
  73. , 'scope'
  74. // User Action Pseudo-classes
  75. , 'hover'
  76. , 'active'
  77. , 'focus'
  78. , 'drop'
  79. // Time-dimensional Pseudo-classes
  80. , 'current'
  81. , 'past'
  82. , 'future'
  83. // The Input Pseudo-classes
  84. , 'enabled'
  85. , 'disabled'
  86. , 'read-only'
  87. , 'read-write'
  88. , 'placeholder-shown'
  89. , 'checked'
  90. , 'indeterminate'
  91. , 'valid'
  92. , 'invalid'
  93. , 'in-range'
  94. , 'out-of-range'
  95. , 'required'
  96. , 'optional'
  97. , 'user-error'
  98. // Tree-Structural pseudo-classes
  99. , 'root'
  100. , 'empty'
  101. , 'blank'
  102. , 'nth-child'
  103. , 'nth-last-child'
  104. , 'first-child'
  105. , 'last-child'
  106. , 'only-child'
  107. , 'nth-of-type'
  108. , 'nth-last-of-type'
  109. , 'first-of-type'
  110. , 'last-of-type'
  111. , 'only-of-type'
  112. , 'nth-match'
  113. , 'nth-last-match'
  114. // Grid-Structural Selectors
  115. , 'nth-column'
  116. , 'nth-last-column'
  117. // Pseudo-elements
  118. , 'first-line'
  119. , 'first-letter'
  120. , 'before'
  121. , 'after'
  122. // Non-standard
  123. , 'selection'
  124. ];
  125. /**
  126. * Initialize a new `Parser` with the given `str` and `options`.
  127. *
  128. * @param {String} str
  129. * @param {Object} options
  130. * @api private
  131. */
  132. var Parser = module.exports = function Parser(str, options) {
  133. var self = this;
  134. options = options || {};
  135. Parser.cache = Parser.cache || Parser.getCache(options);
  136. this.hash = Parser.cache.key(str, options);
  137. this.lexer = {};
  138. if (!Parser.cache.has(this.hash)) {
  139. this.lexer = new Lexer(str, options);
  140. }
  141. this.prefix = options.prefix || '';
  142. this.root = options.root || new nodes.Root;
  143. this.state = ['root'];
  144. this.stash = [];
  145. this.parens = 0;
  146. this.css = 0;
  147. this.state.pop = function(){
  148. self.prevState = [].pop.call(this);
  149. };
  150. };
  151. /**
  152. * Get cache instance.
  153. *
  154. * @param {Object} options
  155. * @return {Object}
  156. * @api private
  157. */
  158. Parser.getCache = function(options) {
  159. return false === options.cache
  160. ? cache(false)
  161. : cache(options.cache || 'memory', options);
  162. };
  163. /**
  164. * Parser prototype.
  165. */
  166. Parser.prototype = {
  167. /**
  168. * Constructor.
  169. */
  170. constructor: Parser,
  171. /**
  172. * Return current state.
  173. *
  174. * @return {String}
  175. * @api private
  176. */
  177. currentState: function() {
  178. return this.state[this.state.length - 1];
  179. },
  180. /**
  181. * Return previous state.
  182. *
  183. * @return {String}
  184. * @api private
  185. */
  186. previousState: function() {
  187. return this.state[this.state.length - 2];
  188. },
  189. /**
  190. * Parse the input, then return the root node.
  191. *
  192. * @return {Node}
  193. * @api private
  194. */
  195. parse: function(){
  196. var block = this.parent = this.root;
  197. if (Parser.cache.has(this.hash)) {
  198. block = Parser.cache.get(this.hash);
  199. // normalize cached imports
  200. if ('block' == block.nodeName) block.constructor = nodes.Root;
  201. } else {
  202. while ('eos' != this.peek().type) {
  203. this.skipWhitespace();
  204. if ('eos' == this.peek().type) break;
  205. var stmt = this.statement();
  206. this.accept(';');
  207. if (!stmt) this.error('unexpected token {peek}, not allowed at the root level');
  208. block.push(stmt);
  209. }
  210. Parser.cache.set(this.hash, block);
  211. }
  212. return block;
  213. },
  214. /**
  215. * Throw an `Error` with the given `msg`.
  216. *
  217. * @param {String} msg
  218. * @api private
  219. */
  220. error: function(msg){
  221. var type = this.peek().type
  222. , val = undefined == this.peek().val
  223. ? ''
  224. : ' ' + this.peek().toString();
  225. if (val.trim() == type.trim()) val = '';
  226. throw new errors.ParseError(msg.replace('{peek}', '"' + type + val + '"'));
  227. },
  228. /**
  229. * Accept the given token `type`, and return it,
  230. * otherwise return `undefined`.
  231. *
  232. * @param {String} type
  233. * @return {Token}
  234. * @api private
  235. */
  236. accept: function(type){
  237. if (type == this.peek().type) {
  238. return this.next();
  239. }
  240. },
  241. /**
  242. * Expect token `type` and return it, throw otherwise.
  243. *
  244. * @param {String} type
  245. * @return {Token}
  246. * @api private
  247. */
  248. expect: function(type){
  249. if (type != this.peek().type) {
  250. this.error('expected "' + type + '", got {peek}');
  251. }
  252. return this.next();
  253. },
  254. /**
  255. * Get the next token.
  256. *
  257. * @return {Token}
  258. * @api private
  259. */
  260. next: function() {
  261. var tok = this.stash.length
  262. ? this.stash.pop()
  263. : this.lexer.next()
  264. , line = tok.lineno
  265. , column = tok.column || 1;
  266. if (tok.val && tok.val.nodeName) {
  267. tok.val.lineno = line;
  268. tok.val.column = column;
  269. }
  270. nodes.lineno = line;
  271. nodes.column = column;
  272. debug.lexer('%s %s', tok.type, tok.val || '');
  273. return tok;
  274. },
  275. /**
  276. * Peek with lookahead(1).
  277. *
  278. * @return {Token}
  279. * @api private
  280. */
  281. peek: function() {
  282. return this.lexer.peek();
  283. },
  284. /**
  285. * Lookahead `n` tokens.
  286. *
  287. * @param {Number} n
  288. * @return {Token}
  289. * @api private
  290. */
  291. lookahead: function(n){
  292. return this.lexer.lookahead(n);
  293. },
  294. /**
  295. * Check if the token at `n` is a valid selector token.
  296. *
  297. * @param {Number} n
  298. * @return {Boolean}
  299. * @api private
  300. */
  301. isSelectorToken: function(n) {
  302. var la = this.lookahead(n).type;
  303. switch (la) {
  304. case 'for':
  305. return this.bracketed;
  306. case '[':
  307. this.bracketed = true;
  308. return true;
  309. case ']':
  310. this.bracketed = false;
  311. return true;
  312. default:
  313. return ~selectorTokens.indexOf(la);
  314. }
  315. },
  316. /**
  317. * Check if the token at `n` is a pseudo selector.
  318. *
  319. * @param {Number} n
  320. * @return {Boolean}
  321. * @api private
  322. */
  323. isPseudoSelector: function(n){
  324. var val = this.lookahead(n).val;
  325. return val && ~pseudoSelectors.indexOf(val.name);
  326. },
  327. /**
  328. * Check if the current line contains `type`.
  329. *
  330. * @param {String} type
  331. * @return {Boolean}
  332. * @api private
  333. */
  334. lineContains: function(type){
  335. var i = 1
  336. , la;
  337. while (la = this.lookahead(i++)) {
  338. if (~['indent', 'outdent', 'newline', 'eos'].indexOf(la.type)) return;
  339. if (type == la.type) return true;
  340. }
  341. },
  342. /**
  343. * Valid selector tokens.
  344. */
  345. selectorToken: function() {
  346. if (this.isSelectorToken(1)) {
  347. if ('{' == this.peek().type) {
  348. // unclosed, must be a block
  349. if (!this.lineContains('}')) return;
  350. // check if ':' is within the braces.
  351. // though not required by Stylus, chances
  352. // are if someone is using {} they will
  353. // use CSS-style props, helping us with
  354. // the ambiguity in this case
  355. var i = 0
  356. , la;
  357. while (la = this.lookahead(++i)) {
  358. if ('}' == la.type) {
  359. // Check empty block.
  360. if (i == 2 || (i == 3 && this.lookahead(i - 1).type == 'space'))
  361. return;
  362. break;
  363. }
  364. if (':' == la.type) return;
  365. }
  366. }
  367. return this.next();
  368. }
  369. },
  370. /**
  371. * Skip the given `tokens`.
  372. *
  373. * @param {Array} tokens
  374. * @api private
  375. */
  376. skip: function(tokens) {
  377. while (~tokens.indexOf(this.peek().type))
  378. this.next();
  379. },
  380. /**
  381. * Consume whitespace.
  382. */
  383. skipWhitespace: function() {
  384. this.skip(['space', 'indent', 'outdent', 'newline']);
  385. },
  386. /**
  387. * Consume newlines.
  388. */
  389. skipNewlines: function() {
  390. while ('newline' == this.peek().type)
  391. this.next();
  392. },
  393. /**
  394. * Consume spaces.
  395. */
  396. skipSpaces: function() {
  397. while ('space' == this.peek().type)
  398. this.next();
  399. },
  400. /**
  401. * Consume spaces and comments.
  402. */
  403. skipSpacesAndComments: function() {
  404. while ('space' == this.peek().type
  405. || 'comment' == this.peek().type)
  406. this.next();
  407. },
  408. /**
  409. * Check if the following sequence of tokens
  410. * forms a function definition, ie trailing
  411. * `{` or indentation.
  412. */
  413. looksLikeFunctionDefinition: function(i) {
  414. return 'indent' == this.lookahead(i).type
  415. || '{' == this.lookahead(i).type;
  416. },
  417. /**
  418. * Check if the following sequence of tokens
  419. * forms a selector.
  420. *
  421. * @param {Boolean} [fromProperty]
  422. * @return {Boolean}
  423. * @api private
  424. */
  425. looksLikeSelector: function(fromProperty) {
  426. var i = 1
  427. , brace;
  428. // Real property
  429. if (fromProperty && ':' == this.lookahead(i + 1).type
  430. && (this.lookahead(i + 1).space || 'indent' == this.lookahead(i + 2).type))
  431. return false;
  432. // Assume selector when an ident is
  433. // followed by a selector
  434. while ('ident' == this.lookahead(i).type
  435. && ('newline' == this.lookahead(i + 1).type
  436. || ',' == this.lookahead(i + 1).type)) i += 2;
  437. while (this.isSelectorToken(i)
  438. || ',' == this.lookahead(i).type) {
  439. if ('selector' == this.lookahead(i).type)
  440. return true;
  441. if ('&' == this.lookahead(i + 1).type)
  442. return true;
  443. if ('.' == this.lookahead(i).type && 'ident' == this.lookahead(i + 1).type)
  444. return true;
  445. if ('*' == this.lookahead(i).type && 'newline' == this.lookahead(i + 1).type)
  446. return true;
  447. // Pseudo-elements
  448. if (':' == this.lookahead(i).type
  449. && ':' == this.lookahead(i + 1).type)
  450. return true;
  451. // #a after an ident and newline
  452. if ('color' == this.lookahead(i).type
  453. && 'newline' == this.lookahead(i - 1).type)
  454. return true;
  455. if (this.looksLikeAttributeSelector(i))
  456. return true;
  457. if (('=' == this.lookahead(i).type || 'function' == this.lookahead(i).type)
  458. && '{' == this.lookahead(i + 1).type)
  459. return false;
  460. // Hash values inside properties
  461. if (':' == this.lookahead(i).type
  462. && !this.isPseudoSelector(i + 1)
  463. && this.lineContains('.'))
  464. return false;
  465. // the ':' token within braces signifies
  466. // a selector. ex: "foo{bar:'baz'}"
  467. if ('{' == this.lookahead(i).type) brace = true;
  468. else if ('}' == this.lookahead(i).type) brace = false;
  469. if (brace && ':' == this.lookahead(i).type) return true;
  470. // '{' preceded by a space is considered a selector.
  471. // for example "foo{bar}{baz}" may be a property,
  472. // however "foo{bar} {baz}" is a selector
  473. if ('space' == this.lookahead(i).type
  474. && '{' == this.lookahead(i + 1).type)
  475. return true;
  476. // Assume pseudo selectors are NOT properties
  477. // as 'td:th-child(1)' may look like a property
  478. // and function call to the parser otherwise
  479. if (':' == this.lookahead(i++).type
  480. && !this.lookahead(i-1).space
  481. && this.isPseudoSelector(i))
  482. return true;
  483. // Trailing space
  484. if ('space' == this.lookahead(i).type
  485. && 'newline' == this.lookahead(i + 1).type
  486. && '{' == this.lookahead(i + 2).type)
  487. return true;
  488. if (',' == this.lookahead(i).type
  489. && 'newline' == this.lookahead(i + 1).type)
  490. return true;
  491. }
  492. // Trailing comma
  493. if (',' == this.lookahead(i).type
  494. && 'newline' == this.lookahead(i + 1).type)
  495. return true;
  496. // Trailing brace
  497. if ('{' == this.lookahead(i).type
  498. && 'newline' == this.lookahead(i + 1).type)
  499. return true;
  500. // css-style mode, false on ; }
  501. if (this.css) {
  502. if (';' == this.lookahead(i).type ||
  503. '}' == this.lookahead(i - 1).type)
  504. return false;
  505. }
  506. // Trailing separators
  507. while (!~[
  508. 'indent'
  509. , 'outdent'
  510. , 'newline'
  511. , 'for'
  512. , 'if'
  513. , ';'
  514. , '}'
  515. , 'eos'].indexOf(this.lookahead(i).type))
  516. ++i;
  517. if ('indent' == this.lookahead(i).type)
  518. return true;
  519. },
  520. /**
  521. * Check if the following sequence of tokens
  522. * forms an attribute selector.
  523. */
  524. looksLikeAttributeSelector: function(n) {
  525. var type = this.lookahead(n).type;
  526. if ('=' == type && this.bracketed) return true;
  527. return ('ident' == type || 'string' == type)
  528. && ']' == this.lookahead(n + 1).type
  529. && ('newline' == this.lookahead(n + 2).type || this.isSelectorToken(n + 2))
  530. && !this.lineContains(':')
  531. && !this.lineContains('=');
  532. },
  533. /**
  534. * Check if the following sequence of tokens
  535. * forms a keyframe block.
  536. */
  537. looksLikeKeyframe: function() {
  538. var i = 2
  539. , type;
  540. switch (this.lookahead(i).type) {
  541. case '{':
  542. case 'indent':
  543. case ',':
  544. return true;
  545. case 'newline':
  546. while ('unit' == this.lookahead(++i).type
  547. || 'newline' == this.lookahead(i).type) ;
  548. type = this.lookahead(i).type;
  549. return 'indent' == type || '{' == type;
  550. }
  551. },
  552. /**
  553. * Check if the current state supports selectors.
  554. */
  555. stateAllowsSelector: function() {
  556. switch (this.currentState()) {
  557. case 'root':
  558. case 'atblock':
  559. case 'selector':
  560. case 'conditional':
  561. case 'function':
  562. case 'atrule':
  563. case 'for':
  564. return true;
  565. }
  566. },
  567. /**
  568. * Try to assign @block to the node.
  569. *
  570. * @param {Expression} expr
  571. * @private
  572. */
  573. assignAtblock: function(expr) {
  574. try {
  575. expr.push(this.atblock(expr));
  576. } catch(err) {
  577. this.error('invalid right-hand side operand in assignment, got {peek}');
  578. }
  579. },
  580. /**
  581. * statement
  582. * | statement 'if' expression
  583. * | statement 'unless' expression
  584. */
  585. statement: function() {
  586. var stmt = this.stmt()
  587. , state = this.prevState
  588. , block
  589. , op;
  590. // special-case statements since it
  591. // is not an expression. We could
  592. // implement postfix conditionals at
  593. // the expression level, however they
  594. // would then fail to enclose properties
  595. if (this.allowPostfix) {
  596. this.allowPostfix = false;
  597. state = 'expression';
  598. }
  599. switch (state) {
  600. case 'assignment':
  601. case 'expression':
  602. case 'function arguments':
  603. while (op =
  604. this.accept('if')
  605. || this.accept('unless')
  606. || this.accept('for')) {
  607. switch (op.type) {
  608. case 'if':
  609. case 'unless':
  610. stmt = new nodes.If(this.expression(), stmt);
  611. stmt.postfix = true;
  612. stmt.negate = 'unless' == op.type;
  613. this.accept(';');
  614. break;
  615. case 'for':
  616. var key
  617. , val = this.id().name;
  618. if (this.accept(',')) key = this.id().name;
  619. this.expect('in');
  620. var each = new nodes.Each(val, key, this.expression());
  621. block = new nodes.Block(this.parent, each);
  622. block.push(stmt);
  623. each.block = block;
  624. stmt = each;
  625. }
  626. }
  627. }
  628. return stmt;
  629. },
  630. /**
  631. * ident
  632. * | selector
  633. * | literal
  634. * | charset
  635. * | namespace
  636. * | import
  637. * | require
  638. * | media
  639. * | atrule
  640. * | scope
  641. * | keyframes
  642. * | mozdocument
  643. * | for
  644. * | if
  645. * | unless
  646. * | comment
  647. * | expression
  648. * | 'return' expression
  649. */
  650. stmt: function() {
  651. var type = this.peek().type;
  652. switch (type) {
  653. case 'keyframes':
  654. return this.keyframes();
  655. case '-moz-document':
  656. return this.mozdocument();
  657. case 'comment':
  658. case 'selector':
  659. case 'literal':
  660. case 'charset':
  661. case 'namespace':
  662. case 'import':
  663. case 'require':
  664. case 'extend':
  665. case 'media':
  666. case 'atrule':
  667. case 'ident':
  668. case 'scope':
  669. case 'supports':
  670. case 'unless':
  671. case 'function':
  672. case 'for':
  673. case 'if':
  674. return this[type]();
  675. case 'return':
  676. return this.return();
  677. case '{':
  678. return this.property();
  679. default:
  680. // Contextual selectors
  681. if (this.stateAllowsSelector()) {
  682. switch (type) {
  683. case 'color':
  684. case '~':
  685. case '>':
  686. case '<':
  687. case ':':
  688. case '&':
  689. case '&&':
  690. case '[':
  691. case '.':
  692. case '/':
  693. return this.selector();
  694. // relative reference
  695. case '..':
  696. if ('/' == this.lookahead(2).type)
  697. return this.selector();
  698. case '+':
  699. return 'function' == this.lookahead(2).type
  700. ? this.functionCall()
  701. : this.selector();
  702. case '*':
  703. return this.property();
  704. // keyframe blocks (10%, 20% { ... })
  705. case 'unit':
  706. if (this.looksLikeKeyframe()) return this.selector();
  707. case '-':
  708. if ('{' == this.lookahead(2).type)
  709. return this.property();
  710. }
  711. }
  712. // Expression fallback
  713. var expr = this.expression();
  714. if (expr.isEmpty) this.error('unexpected {peek}');
  715. return expr;
  716. }
  717. },
  718. /**
  719. * indent (!outdent)+ outdent
  720. */
  721. block: function(node, scope) {
  722. var delim
  723. , stmt
  724. , next
  725. , block = this.parent = new nodes.Block(this.parent, node);
  726. if (false === scope) block.scope = false;
  727. this.accept('newline');
  728. // css-style
  729. if (this.accept('{')) {
  730. this.css++;
  731. delim = '}';
  732. this.skipWhitespace();
  733. } else {
  734. delim = 'outdent';
  735. this.expect('indent');
  736. }
  737. while (delim != this.peek().type) {
  738. // css-style
  739. if (this.css) {
  740. if (this.accept('newline') || this.accept('indent')) continue;
  741. stmt = this.statement();
  742. this.accept(';');
  743. this.skipWhitespace();
  744. } else {
  745. if (this.accept('newline')) continue;
  746. // skip useless indents and comments
  747. next = this.lookahead(2).type;
  748. if ('indent' == this.peek().type
  749. && ~['outdent', 'newline', 'comment'].indexOf(next)) {
  750. this.skip(['indent', 'outdent']);
  751. continue;
  752. }
  753. if ('eos' == this.peek().type) return block;
  754. stmt = this.statement();
  755. this.accept(';');
  756. }
  757. if (!stmt) this.error('unexpected token {peek} in block');
  758. block.push(stmt);
  759. }
  760. // css-style
  761. if (this.css) {
  762. this.skipWhitespace();
  763. this.expect('}');
  764. this.skipSpaces();
  765. this.css--;
  766. } else {
  767. this.expect('outdent');
  768. }
  769. this.parent = block.parent;
  770. return block;
  771. },
  772. /**
  773. * comment space*
  774. */
  775. comment: function(){
  776. var node = this.next().val;
  777. this.skipSpaces();
  778. return node;
  779. },
  780. /**
  781. * for val (',' key) in expr
  782. */
  783. for: function() {
  784. this.expect('for');
  785. var key
  786. , val = this.id().name;
  787. if (this.accept(',')) key = this.id().name;
  788. this.expect('in');
  789. this.state.push('for');
  790. this.cond = true;
  791. var each = new nodes.Each(val, key, this.expression());
  792. this.cond = false;
  793. each.block = this.block(each, false);
  794. this.state.pop();
  795. return each;
  796. },
  797. /**
  798. * return expression
  799. */
  800. return: function() {
  801. this.expect('return');
  802. var expr = this.expression();
  803. return expr.isEmpty
  804. ? new nodes.Return
  805. : new nodes.Return(expr);
  806. },
  807. /**
  808. * unless expression block
  809. */
  810. unless: function() {
  811. this.expect('unless');
  812. this.state.push('conditional');
  813. this.cond = true;
  814. var node = new nodes.If(this.expression(), true);
  815. this.cond = false;
  816. node.block = this.block(node, false);
  817. this.state.pop();
  818. return node;
  819. },
  820. /**
  821. * if expression block (else block)?
  822. */
  823. if: function() {
  824. this.expect('if');
  825. this.state.push('conditional');
  826. this.cond = true;
  827. var node = new nodes.If(this.expression())
  828. , cond
  829. , block;
  830. this.cond = false;
  831. node.block = this.block(node, false);
  832. this.skip(['newline', 'comment']);
  833. while (this.accept('else')) {
  834. if (this.accept('if')) {
  835. this.cond = true;
  836. cond = this.expression();
  837. this.cond = false;
  838. block = this.block(node, false);
  839. node.elses.push(new nodes.If(cond, block));
  840. } else {
  841. node.elses.push(this.block(node, false));
  842. break;
  843. }
  844. this.skip(['newline', 'comment']);
  845. }
  846. this.state.pop();
  847. return node;
  848. },
  849. /**
  850. * @block
  851. *
  852. * @param {Expression} [node]
  853. */
  854. atblock: function(node){
  855. if (!node) this.expect('atblock');
  856. node = new nodes.Atblock;
  857. this.state.push('atblock');
  858. node.block = this.block(node, false);
  859. this.state.pop();
  860. return node;
  861. },
  862. /**
  863. * atrule selector? block?
  864. */
  865. atrule: function(){
  866. var type = this.expect('atrule').val
  867. , node = new nodes.Atrule(type)
  868. , tok;
  869. this.skipSpacesAndComments();
  870. node.segments = this.selectorParts();
  871. this.skipSpacesAndComments();
  872. tok = this.peek().type;
  873. if ('indent' == tok || '{' == tok || ('newline' == tok
  874. && '{' == this.lookahead(2).type)) {
  875. this.state.push('atrule');
  876. node.block = this.block(node);
  877. this.state.pop();
  878. }
  879. return node;
  880. },
  881. /**
  882. * scope
  883. */
  884. scope: function(){
  885. this.expect('scope');
  886. var selector = this.selectorParts()
  887. .map(function(selector) { return selector.val; })
  888. .join('');
  889. this.selectorScope = selector.trim();
  890. return nodes.null;
  891. },
  892. /**
  893. * supports
  894. */
  895. supports: function(){
  896. this.expect('supports');
  897. var node = new nodes.Supports(this.supportsCondition());
  898. this.state.push('atrule');
  899. node.block = this.block(node);
  900. this.state.pop();
  901. return node;
  902. },
  903. /**
  904. * supports negation
  905. * | supports op
  906. * | expression
  907. */
  908. supportsCondition: function(){
  909. var node = this.supportsNegation()
  910. || this.supportsOp();
  911. if (!node) {
  912. this.cond = true;
  913. node = this.expression();
  914. this.cond = false;
  915. }
  916. return node;
  917. },
  918. /**
  919. * 'not' supports feature
  920. */
  921. supportsNegation: function(){
  922. if (this.accept('not')) {
  923. var node = new nodes.Expression;
  924. node.push(new nodes.Literal('not'));
  925. node.push(this.supportsFeature());
  926. return node;
  927. }
  928. },
  929. /**
  930. * supports feature (('and' | 'or') supports feature)+
  931. */
  932. supportsOp: function(){
  933. var feature = this.supportsFeature()
  934. , op
  935. , expr;
  936. if (feature) {
  937. expr = new nodes.Expression;
  938. expr.push(feature);
  939. while (op = this.accept('&&') || this.accept('||')) {
  940. expr.push(new nodes.Literal('&&' == op.val ? 'and' : 'or'));
  941. expr.push(this.supportsFeature());
  942. }
  943. return expr;
  944. }
  945. },
  946. /**
  947. * ('(' supports condition ')')
  948. * | feature
  949. */
  950. supportsFeature: function(){
  951. this.skipSpacesAndComments();
  952. if ('(' == this.peek().type) {
  953. var la = this.lookahead(2).type;
  954. if ('ident' == la || '{' == la) {
  955. return this.feature();
  956. } else {
  957. this.expect('(');
  958. var node = new nodes.Expression;
  959. node.push(new nodes.Literal('('));
  960. node.push(this.supportsCondition());
  961. this.expect(')')
  962. node.push(new nodes.Literal(')'));
  963. this.skipSpacesAndComments();
  964. return node;
  965. }
  966. }
  967. },
  968. /**
  969. * extend
  970. */
  971. extend: function(){
  972. var tok = this.expect('extend')
  973. , selectors = []
  974. , sel
  975. , node
  976. , arr;
  977. do {
  978. arr = this.selectorParts();
  979. if (!arr.length) continue;
  980. sel = new nodes.Selector(arr);
  981. selectors.push(sel);
  982. if ('!' !== this.peek().type) continue;
  983. tok = this.lookahead(2);
  984. if ('ident' !== tok.type || 'optional' !== tok.val.name) continue;
  985. this.skip(['!', 'ident']);
  986. sel.optional = true;
  987. } while(this.accept(','));
  988. node = new nodes.Extend(selectors);
  989. node.lineno = tok.lineno;
  990. node.column = tok.column;
  991. return node;
  992. },
  993. /**
  994. * media queries
  995. */
  996. media: function() {
  997. this.expect('media');
  998. this.state.push('atrule');
  999. var media = new nodes.Media(this.queries());
  1000. media.block = this.block(media);
  1001. this.state.pop();
  1002. return media;
  1003. },
  1004. /**
  1005. * query (',' query)*
  1006. */
  1007. queries: function() {
  1008. var queries = new nodes.QueryList
  1009. , skip = ['comment', 'newline', 'space'];
  1010. do {
  1011. this.skip(skip);
  1012. queries.push(this.query());
  1013. this.skip(skip);
  1014. } while (this.accept(','));
  1015. return queries;
  1016. },
  1017. /**
  1018. * expression
  1019. * | (ident | 'not')? ident ('and' feature)*
  1020. * | feature ('and' feature)*
  1021. */
  1022. query: function() {
  1023. var query = new nodes.Query
  1024. , expr
  1025. , pred
  1026. , id;
  1027. // hash values support
  1028. if ('ident' == this.peek().type
  1029. && ('.' == this.lookahead(2).type
  1030. || '[' == this.lookahead(2).type)) {
  1031. this.cond = true;
  1032. expr = this.expression();
  1033. this.cond = false;
  1034. query.push(new nodes.Feature(expr.nodes));
  1035. return query;
  1036. }
  1037. if (pred = this.accept('ident') || this.accept('not')) {
  1038. pred = new nodes.Literal(pred.val.string || pred.val);
  1039. this.skipSpacesAndComments();
  1040. if (id = this.accept('ident')) {
  1041. query.type = id.val;
  1042. query.predicate = pred;
  1043. } else {
  1044. query.type = pred;
  1045. }
  1046. this.skipSpacesAndComments();
  1047. if (!this.accept('&&')) return query;
  1048. }
  1049. do {
  1050. query.push(this.feature());
  1051. } while (this.accept('&&'));
  1052. return query;
  1053. },
  1054. /**
  1055. * '(' ident ( ':'? expression )? ')'
  1056. */
  1057. feature: function() {
  1058. this.skipSpacesAndComments();
  1059. this.expect('(');
  1060. this.skipSpacesAndComments();
  1061. var node = new nodes.Feature(this.interpolate());
  1062. this.skipSpacesAndComments();
  1063. this.accept(':')
  1064. this.skipSpacesAndComments();
  1065. this.inProperty = true;
  1066. node.expr = this.list();
  1067. this.inProperty = false;
  1068. this.skipSpacesAndComments();
  1069. this.expect(')');
  1070. this.skipSpacesAndComments();
  1071. return node;
  1072. },
  1073. /**
  1074. * @-moz-document call (',' call)* block
  1075. */
  1076. mozdocument: function(){
  1077. this.expect('-moz-document');
  1078. var mozdocument = new nodes.Atrule('-moz-document')
  1079. , calls = [];
  1080. do {
  1081. this.skipSpacesAndComments();
  1082. calls.push(this.functionCall());
  1083. this.skipSpacesAndComments();
  1084. } while (this.accept(','));
  1085. mozdocument.segments = [new nodes.Literal(calls.join(', '))];
  1086. this.state.push('atrule');
  1087. mozdocument.block = this.block(mozdocument, false);
  1088. this.state.pop();
  1089. return mozdocument;
  1090. },
  1091. /**
  1092. * import expression
  1093. */
  1094. import: function() {
  1095. this.expect('import');
  1096. this.allowPostfix = true;
  1097. return new nodes.Import(this.expression(), false);
  1098. },
  1099. /**
  1100. * require expression
  1101. */
  1102. require: function() {
  1103. this.expect('require');
  1104. this.allowPostfix = true;
  1105. return new nodes.Import(this.expression(), true);
  1106. },
  1107. /**
  1108. * charset string
  1109. */
  1110. charset: function() {
  1111. this.expect('charset');
  1112. var str = this.expect('string').val;
  1113. this.allowPostfix = true;
  1114. return new nodes.Charset(str);
  1115. },
  1116. /**
  1117. * namespace ident? (string | url)
  1118. */
  1119. namespace: function() {
  1120. var str
  1121. , prefix;
  1122. this.expect('namespace');
  1123. this.skipSpacesAndComments();
  1124. if (prefix = this.accept('ident')) {
  1125. prefix = prefix.val;
  1126. }
  1127. this.skipSpacesAndComments();
  1128. str = this.accept('string') || this.url();
  1129. this.allowPostfix = true;
  1130. return new nodes.Namespace(str, prefix);
  1131. },
  1132. /**
  1133. * keyframes name block
  1134. */
  1135. keyframes: function() {
  1136. var tok = this.expect('keyframes')
  1137. , keyframes;
  1138. this.skipSpacesAndComments();
  1139. keyframes = new nodes.Keyframes(this.selectorParts(), tok.val);
  1140. this.skipSpacesAndComments();
  1141. // block
  1142. this.state.push('atrule');
  1143. keyframes.block = this.block(keyframes);
  1144. this.state.pop();
  1145. return keyframes;
  1146. },
  1147. /**
  1148. * literal
  1149. */
  1150. literal: function() {
  1151. return this.expect('literal').val;
  1152. },
  1153. /**
  1154. * ident space?
  1155. */
  1156. id: function() {
  1157. var tok = this.expect('ident');
  1158. this.accept('space');
  1159. return tok.val;
  1160. },
  1161. /**
  1162. * ident
  1163. * | assignment
  1164. * | property
  1165. * | selector
  1166. */
  1167. ident: function() {
  1168. var i = 2
  1169. , la = this.lookahead(i).type;
  1170. while ('space' == la) la = this.lookahead(++i).type;
  1171. switch (la) {
  1172. // Assignment
  1173. case '=':
  1174. case '?=':
  1175. case '-=':
  1176. case '+=':
  1177. case '*=':
  1178. case '/=':
  1179. case '%=':
  1180. return this.assignment();
  1181. // Member
  1182. case '.':
  1183. if ('space' == this.lookahead(i - 1).type) return this.selector();
  1184. if (this._ident == this.peek()) return this.id();
  1185. while ('=' != this.lookahead(++i).type
  1186. && !~['[', ',', 'newline', 'indent', 'eos'].indexOf(this.lookahead(i).type)) ;
  1187. if ('=' == this.lookahead(i).type) {
  1188. this._ident = this.peek();
  1189. return this.expression();
  1190. } else if (this.looksLikeSelector() && this.stateAllowsSelector()) {
  1191. return this.selector();
  1192. }
  1193. // Assignment []=
  1194. case '[':
  1195. if (this._ident == this.peek()) return this.id();
  1196. while (']' != this.lookahead(i++).type
  1197. && 'selector' != this.lookahead(i).type
  1198. && 'eos' != this.lookahead(i).type) ;
  1199. if ('=' == this.lookahead(i).type) {
  1200. this._ident = this.peek();
  1201. return this.expression();
  1202. } else if (this.looksLikeSelector() && this.stateAllowsSelector()) {
  1203. return this.selector();
  1204. }
  1205. // Operation
  1206. case '-':
  1207. case '+':
  1208. case '/':
  1209. case '*':
  1210. case '%':
  1211. case '**':
  1212. case '&&':
  1213. case '||':
  1214. case '>':
  1215. case '<':
  1216. case '>=':
  1217. case '<=':
  1218. case '!=':
  1219. case '==':
  1220. case '?':
  1221. case 'in':
  1222. case 'is a':
  1223. case 'is defined':
  1224. // Prevent cyclic .ident, return literal
  1225. if (this._ident == this.peek()) {
  1226. return this.id();
  1227. } else {
  1228. this._ident = this.peek();
  1229. switch (this.currentState()) {
  1230. // unary op or selector in property / for
  1231. case 'for':
  1232. case 'selector':
  1233. return this.property();
  1234. // Part of a selector
  1235. case 'root':
  1236. case 'atblock':
  1237. case 'atrule':
  1238. return '[' == la
  1239. ? this.subscript()
  1240. : this.selector();
  1241. case 'function':
  1242. case 'conditional':
  1243. return this.looksLikeSelector()
  1244. ? this.selector()
  1245. : this.expression();
  1246. // Do not disrupt the ident when an operand
  1247. default:
  1248. return this.operand
  1249. ? this.id()
  1250. : this.expression();
  1251. }
  1252. }
  1253. // Selector or property
  1254. default:
  1255. switch (this.currentState()) {
  1256. case 'root':
  1257. return this.selector();
  1258. case 'for':
  1259. case 'selector':
  1260. case 'function':
  1261. case 'conditional':
  1262. case 'atblock':
  1263. case 'atrule':
  1264. return this.property();
  1265. default:
  1266. var id = this.id();
  1267. if ('interpolation' == this.previousState()) id.mixin = true;
  1268. return id;
  1269. }
  1270. }
  1271. },
  1272. /**
  1273. * '*'? (ident | '{' expression '}')+
  1274. */
  1275. interpolate: function() {
  1276. var node
  1277. , segs = []
  1278. , star;
  1279. star = this.accept('*');
  1280. if (star) segs.push(new nodes.Literal('*'));
  1281. while (true) {
  1282. if (this.accept('{')) {
  1283. this.state.push('interpolation');
  1284. segs.push(this.expression());
  1285. this.expect('}');
  1286. this.state.pop();
  1287. } else if (node = this.accept('-')){
  1288. segs.push(new nodes.Literal('-'));
  1289. } else if (node = this.accept('ident')){
  1290. segs.push(node.val);
  1291. } else {
  1292. break;
  1293. }
  1294. }
  1295. if (!segs.length) this.expect('ident');
  1296. return segs;
  1297. },
  1298. /**
  1299. * property ':'? expression
  1300. * | ident
  1301. */
  1302. property: function() {
  1303. if (this.looksLikeSelector(true)) return this.selector();
  1304. // property
  1305. var ident = this.interpolate()
  1306. , prop = new nodes.Property(ident)
  1307. , ret = prop;
  1308. // optional ':'
  1309. this.accept('space');
  1310. if (this.accept(':')) this.accept('space');
  1311. this.state.push('property');
  1312. this.inProperty = true;
  1313. prop.expr = this.list();
  1314. if (prop.expr.isEmpty) ret = ident[0];
  1315. this.inProperty = false;
  1316. this.allowPostfix = true;
  1317. this.state.pop();
  1318. // optional ';'
  1319. this.accept(';');
  1320. return ret;
  1321. },
  1322. /**
  1323. * selector ',' selector
  1324. * | selector newline selector
  1325. * | selector block
  1326. */
  1327. selector: function() {
  1328. var arr
  1329. , group = new nodes.Group
  1330. , scope = this.selectorScope
  1331. , isRoot = 'root' == this.currentState()
  1332. , selector;
  1333. do {
  1334. // Clobber newline after ,
  1335. this.accept('newline');
  1336. arr = this.selectorParts();
  1337. // Push the selector
  1338. if (isRoot && scope) arr.unshift(new nodes.Literal(scope + ' '));
  1339. if (arr.length) {
  1340. selector = new nodes.Selector(arr);
  1341. selector.lineno = arr[0].lineno;
  1342. selector.column = arr[0].column;
  1343. group.push(selector);
  1344. }
  1345. } while (this.accept(',') || this.accept('newline'));
  1346. if ('selector-parts' == this.currentState()) return group.nodes;
  1347. this.state.push('selector');
  1348. group.block = this.block(group);
  1349. this.state.pop();
  1350. return group;
  1351. },
  1352. selectorParts: function(){
  1353. var tok
  1354. , arr = [];
  1355. // Selector candidates,
  1356. // stitched together to
  1357. // form a selector.
  1358. while (tok = this.selectorToken()) {
  1359. debug.selector('%s', tok);
  1360. // Selector component
  1361. switch (tok.type) {
  1362. case '{':
  1363. this.skipSpaces();
  1364. var expr = this.expression();
  1365. this.skipSpaces();
  1366. this.expect('}');
  1367. arr.push(expr);
  1368. break;
  1369. case this.prefix && '.':
  1370. var literal = new nodes.Literal(tok.val + this.prefix);
  1371. literal.prefixed = true;
  1372. arr.push(literal);
  1373. break;
  1374. case 'comment':
  1375. // ignore comments
  1376. break;
  1377. case 'color':
  1378. case 'unit':
  1379. arr.push(new nodes.Literal(tok.val.raw));
  1380. break;
  1381. case 'space':
  1382. arr.push(new nodes.Literal(' '));
  1383. break;
  1384. case 'function':
  1385. arr.push(new nodes.Literal(tok.val.name + '('));
  1386. break;
  1387. case 'ident':
  1388. arr.push(new nodes.Literal(tok.val.name || tok.val.string));
  1389. break;
  1390. default:
  1391. arr.push(new nodes.Literal(tok.val));
  1392. if (tok.space) arr.push(new nodes.Literal(' '));
  1393. }
  1394. }
  1395. return arr;
  1396. },
  1397. /**
  1398. * ident ('=' | '?=') expression
  1399. */
  1400. assignment: function() {
  1401. var op
  1402. , node
  1403. , name = this.id().name;
  1404. if (op =
  1405. this.accept('=')
  1406. || this.accept('?=')
  1407. || this.accept('+=')
  1408. || this.accept('-=')
  1409. || this.accept('*=')
  1410. || this.accept('/=')
  1411. || this.accept('%=')) {
  1412. this.state.push('assignment');
  1413. var expr = this.list();
  1414. // @block support
  1415. if (expr.isEmpty) this.assignAtblock(expr);
  1416. node = new nodes.Ident(name, expr);
  1417. this.state.pop();
  1418. switch (op.type) {
  1419. case '?=':
  1420. var defined = new nodes.BinOp('is defined', node)
  1421. , lookup = new nodes.Expression;
  1422. lookup.push(new nodes.Ident(name));
  1423. node = new nodes.Ternary(defined, lookup, node);
  1424. break;
  1425. case '+=':
  1426. case '-=':
  1427. case '*=':
  1428. case '/=':
  1429. case '%=':
  1430. node.val = new nodes.BinOp(op.type[0], new nodes.Ident(name), expr);
  1431. break;
  1432. }
  1433. }
  1434. return node;
  1435. },
  1436. /**
  1437. * definition
  1438. * | call
  1439. */
  1440. function: function() {
  1441. var parens = 1
  1442. , i = 2
  1443. , tok;
  1444. // Lookahead and determine if we are dealing
  1445. // with a function call or definition. Here
  1446. // we pair parens to prevent false negatives
  1447. out:
  1448. while (tok = this.lookahead(i++)) {
  1449. switch (tok.type) {
  1450. case 'function':
  1451. case '(':
  1452. ++parens;
  1453. break;
  1454. case ')':
  1455. if (!--parens) break out;
  1456. break;
  1457. case 'eos':
  1458. this.error('failed to find closing paren ")"');
  1459. }
  1460. }
  1461. // Definition or call
  1462. switch (this.currentState()) {
  1463. case 'expression':
  1464. return this.functionCall();
  1465. default:
  1466. return this.looksLikeFunctionDefinition(i)
  1467. ? this.functionDefinition()
  1468. : this.expression();
  1469. }
  1470. },
  1471. /**
  1472. * url '(' (expression | urlchars)+ ')'
  1473. */
  1474. url: function() {
  1475. this.expect('function');
  1476. this.state.push('function arguments');
  1477. var args = this.args();
  1478. this.expect(')');
  1479. this.state.pop();
  1480. return new nodes.Call('url', args);
  1481. },
  1482. /**
  1483. * '+'? ident '(' expression ')' block?
  1484. */
  1485. functionCall: function() {
  1486. var withBlock = this.accept('+');
  1487. if ('url' == this.peek().val.name) return this.url();
  1488. var name = this.expect('function').val.name;
  1489. this.state.push('function arguments');
  1490. this.parens++;
  1491. var args = this.args();
  1492. this.expect(')');
  1493. this.parens--;
  1494. this.state.pop();
  1495. var call = new nodes.Call(name, args);
  1496. if (withBlock) {
  1497. this.state.push('function');
  1498. call.block = this.block(call);
  1499. this.state.pop();
  1500. }
  1501. return call;
  1502. },
  1503. /**
  1504. * ident '(' params ')' block
  1505. */
  1506. functionDefinition: function() {
  1507. var name = this.expect('function').val.name;
  1508. // params
  1509. this.state.push('function params');
  1510. this.skipWhitespace();
  1511. var params = this.params();
  1512. this.skipWhitespace();
  1513. this.expect(')');
  1514. this.state.pop();
  1515. // Body
  1516. this.state.push('function');
  1517. var fn = new nodes.Function(name, params);
  1518. fn.block = this.block(fn);
  1519. this.state.pop();
  1520. return new nodes.Ident(name, fn);
  1521. },
  1522. /**
  1523. * ident
  1524. * | ident '...'
  1525. * | ident '=' expression
  1526. * | ident ',' ident
  1527. */
  1528. params: function() {
  1529. var tok
  1530. , node
  1531. , params = new nodes.Params;
  1532. while (tok = this.accept('ident')) {
  1533. this.accept('space');
  1534. params.push(node = tok.val);
  1535. if (this.accept('...')) {
  1536. node.rest = true;
  1537. } else if (this.accept('=')) {
  1538. node.val = this.expression();
  1539. }
  1540. this.skipWhitespace();
  1541. this.accept(',');
  1542. this.skipWhitespace();
  1543. }
  1544. return params;
  1545. },
  1546. /**
  1547. * (ident ':')? expression (',' (ident ':')? expression)*
  1548. */
  1549. args: function() {
  1550. var args = new nodes.Arguments
  1551. , keyword;
  1552. do {
  1553. // keyword
  1554. if ('ident' == this.peek().type && ':' == this.lookahead(2).type) {
  1555. keyword = this.next().val.string;
  1556. this.expect(':');
  1557. args.map[keyword] = this.expression();
  1558. // arg
  1559. } else {
  1560. args.push(this.expression());
  1561. }
  1562. } while (this.accept(','));
  1563. return args;
  1564. },
  1565. /**
  1566. * expression (',' expression)*
  1567. */
  1568. list: function() {
  1569. var node = this.expression();
  1570. while (this.accept(',')) {
  1571. if (node.isList) {
  1572. list.push(this.expression());
  1573. } else {
  1574. var list = new nodes.Expression(true);
  1575. list.push(node);
  1576. list.push(this.expression());
  1577. node = list;
  1578. }
  1579. }
  1580. return node;
  1581. },
  1582. /**
  1583. * negation+
  1584. */
  1585. expression: function() {
  1586. var node
  1587. , expr = new nodes.Expression;
  1588. this.state.push('expression');
  1589. while (node = this.negation()) {
  1590. if (!node) this.error('unexpected token {peek} in expression');
  1591. expr.push(node);
  1592. }
  1593. this.state.pop();
  1594. if (expr.nodes.length) {
  1595. expr.lineno = expr.nodes[0].lineno;
  1596. expr.column = expr.nodes[0].column;
  1597. }
  1598. return expr;
  1599. },
  1600. /**
  1601. * 'not' ternary
  1602. * | ternary
  1603. */
  1604. negation: function() {
  1605. if (this.accept('not')) {
  1606. return new nodes.UnaryOp('!', this.negation());
  1607. }
  1608. return this.ternary();
  1609. },
  1610. /**
  1611. * logical ('?' expression ':' expression)?
  1612. */
  1613. ternary: function() {
  1614. var node = this.logical();
  1615. if (this.accept('?')) {
  1616. var trueExpr = this.expression();
  1617. this.expect(':');
  1618. var falseExpr = this.expression();
  1619. node = new nodes.Ternary(node, trueExpr, falseExpr);
  1620. }
  1621. return node;
  1622. },
  1623. /**
  1624. * typecheck (('&&' | '||') typecheck)*
  1625. */
  1626. logical: function() {
  1627. var op
  1628. , node = this.typecheck();
  1629. while (op = this.accept('&&') || this.accept('||')) {
  1630. node = new nodes.BinOp(op.type, node, this.typecheck());
  1631. }
  1632. return node;
  1633. },
  1634. /**
  1635. * equality ('is a' equality)*
  1636. */
  1637. typecheck: function() {
  1638. var op
  1639. , node = this.equality();
  1640. while (op = this.accept('is a')) {
  1641. this.operand = true;
  1642. if (!node) this.error('illegal unary "' + op + '", missing left-hand operand');
  1643. node = new nodes.BinOp(op.type, node, this.equality());
  1644. this.operand = false;
  1645. }
  1646. return node;
  1647. },
  1648. /**
  1649. * in (('==' | '!=') in)*
  1650. */
  1651. equality: function() {
  1652. var op
  1653. , node = this.in();
  1654. while (op = this.accept('==') || this.accept('!=')) {
  1655. this.operand = true;
  1656. if (!node) this.error('illegal unary "' + op + '", missing left-hand operand');
  1657. node = new nodes.BinOp(op.type, node, this.in());
  1658. this.operand = false;
  1659. }
  1660. return node;
  1661. },
  1662. /**
  1663. * relational ('in' relational)*
  1664. */
  1665. in: function() {
  1666. var node = this.relational();
  1667. while (this.accept('in')) {
  1668. this.operand = true;
  1669. if (!node) this.error('illegal unary "in", missing left-hand operand');
  1670. node = new nodes.BinOp('in', node, this.relational());
  1671. this.operand = false;
  1672. }
  1673. return node;
  1674. },
  1675. /**
  1676. * range (('>=' | '<=' | '>' | '<') range)*
  1677. */
  1678. relational: function() {
  1679. var op
  1680. , node = this.range();
  1681. while (op =
  1682. this.accept('>=')
  1683. || this.accept('<=')
  1684. || this.accept('<')
  1685. || this.accept('>')
  1686. ) {
  1687. this.operand = true;
  1688. if (!node) this.error('illegal unary "' + op + '", missing left-hand operand');
  1689. node = new nodes.BinOp(op.type, node, this.range());
  1690. this.operand = false;
  1691. }
  1692. return node;
  1693. },
  1694. /**
  1695. * additive (('..' | '...') additive)*
  1696. */
  1697. range: function() {
  1698. var op
  1699. , node = this.additive();
  1700. if (op = this.accept('...') || this.accept('..')) {
  1701. this.operand = true;
  1702. if (!node) this.error('illegal unary "' + op + '", missing left-hand operand');
  1703. node = new nodes.BinOp(op.val, node, this.additive());
  1704. this.operand = false;
  1705. }
  1706. return node;
  1707. },
  1708. /**
  1709. * multiplicative (('+' | '-') multiplicative)*
  1710. */
  1711. additive: function() {
  1712. var op
  1713. , node = this.multiplicative();
  1714. while (op = this.accept('+') || this.accept('-')) {
  1715. this.operand = true;
  1716. node = new nodes.BinOp(op.type, node, this.multiplicative());
  1717. this.operand = false;
  1718. }
  1719. return node;
  1720. },
  1721. /**
  1722. * defined (('**' | '*' | '/' | '%') defined)*
  1723. */
  1724. multiplicative: function() {
  1725. var op
  1726. , node = this.defined();
  1727. while (op =
  1728. this.accept('**')
  1729. || this.accept('*')
  1730. || this.accept('/')
  1731. || this.accept('%')) {
  1732. this.operand = true;
  1733. if ('/' == op && this.inProperty && !this.parens) {
  1734. this.stash.push(new Token('literal', new nodes.Literal('/')));
  1735. this.operand = false;
  1736. return node;
  1737. } else {
  1738. if (!node) this.error('illegal unary "' + op + '", missing left-hand operand');
  1739. node = new nodes.BinOp(op.type, node, this.defined());
  1740. this.operand = false;
  1741. }
  1742. }
  1743. return node;
  1744. },
  1745. /**
  1746. * unary 'is defined'
  1747. * | unary
  1748. */
  1749. defined: function() {
  1750. var node = this.unary();
  1751. if (this.accept('is defined')) {
  1752. if (!node) this.error('illegal unary "is defined", missing left-hand operand');
  1753. node = new nodes.BinOp('is defined', node);
  1754. }
  1755. return node;
  1756. },
  1757. /**
  1758. * ('!' | '~' | '+' | '-') unary
  1759. * | subscript
  1760. */
  1761. unary: function() {
  1762. var op
  1763. , node;
  1764. if (op =
  1765. this.accept('!')
  1766. || this.accept('~')
  1767. || this.accept('+')
  1768. || this.accept('-')) {
  1769. this.operand = true;
  1770. node = this.unary();
  1771. if (!node) this.error('illegal unary "' + op + '"');
  1772. node = new nodes.UnaryOp(op.type, node);
  1773. this.operand = false;
  1774. return node;
  1775. }
  1776. return this.subscript();
  1777. },
  1778. /**
  1779. * member ('[' expression ']')+ '='?
  1780. * | member
  1781. */
  1782. subscript: function() {
  1783. var node = this.member()
  1784. , id;
  1785. while (this.accept('[')) {
  1786. node = new nodes.BinOp('[]', node, this.expression());
  1787. this.expect(']');
  1788. }
  1789. // TODO: TernaryOp :)
  1790. if (this.accept('=')) {
  1791. node.op += '=';
  1792. node.val = this.list();
  1793. // @block support
  1794. if (node.val.isEmpty) this.assignAtblock(node.val);
  1795. }
  1796. return node;
  1797. },
  1798. /**
  1799. * primary ('.' id)+ '='?
  1800. * | primary
  1801. */
  1802. member: function() {
  1803. var node = this.primary();
  1804. if (node) {
  1805. while (this.accept('.')) {
  1806. var id = new nodes.Ident(this.expect('ident').val.string);
  1807. node = new nodes.Member(node, id);
  1808. }
  1809. this.skipSpaces();
  1810. if (this.accept('=')) {
  1811. node.val = this.list();
  1812. // @block support
  1813. if (node.val.isEmpty) this.assignAtblock(node.val);
  1814. }
  1815. }
  1816. return node;
  1817. },
  1818. /**
  1819. * '{' '}'
  1820. * | '{' pair (ws pair)* '}'
  1821. */
  1822. object: function(){
  1823. var obj = new nodes.Object
  1824. , id, val, comma;
  1825. this.expect('{');
  1826. this.skipWhitespace();
  1827. while (!this.accept('}')) {
  1828. if (this.accept('comment')
  1829. || this.accept('newline')) continue;
  1830. if (!comma) this.accept(',');
  1831. id = this.accept('ident') || this.accept('string');
  1832. if (!id) this.error('expected "ident" or "string", got {peek}');
  1833. id = id.val.hash;
  1834. this.skipSpacesAndComments();
  1835. this.expect(':');
  1836. val = this.expression();
  1837. obj.set(id, val);
  1838. comma = this.accept(',');
  1839. this.skipWhitespace();
  1840. }
  1841. return obj;
  1842. },
  1843. /**
  1844. * unit
  1845. * | null
  1846. * | color
  1847. * | string
  1848. * | ident
  1849. * | boolean
  1850. * | literal
  1851. * | object
  1852. * | atblock
  1853. * | atrule
  1854. * | '(' expression ')' '%'?
  1855. */
  1856. primary: function() {
  1857. var tok;
  1858. this.skipSpaces();
  1859. // Parenthesis
  1860. if (this.accept('(')) {
  1861. ++this.parens;
  1862. var expr = this.expression()
  1863. , paren = this.expect(')');
  1864. --this.parens;
  1865. if (this.accept('%')) expr.push(new nodes.Ident('%'));
  1866. tok = this.peek();
  1867. // (1 + 2)px, (1 + 2)em, etc.
  1868. if (!paren.space
  1869. && 'ident' == tok.type
  1870. && ~units.indexOf(tok.val.string)) {
  1871. expr.push(new nodes.Ident(tok.val.string));
  1872. this.next();
  1873. }
  1874. return expr;
  1875. }
  1876. tok = this.peek();
  1877. // Primitive
  1878. switch (tok.type) {
  1879. case 'null':
  1880. case 'unit':
  1881. case 'color':
  1882. case 'string':
  1883. case 'literal':
  1884. case 'boolean':
  1885. case 'comment':
  1886. return this.next().val;
  1887. case !this.cond && '{':
  1888. return this.object();
  1889. case 'atblock':
  1890. return this.atblock();
  1891. // property lookup
  1892. case 'atrule':
  1893. var id = new nodes.Ident(this.next().val);
  1894. id.property = true;
  1895. return id;
  1896. case 'ident':
  1897. return this.ident();
  1898. case 'function':
  1899. return tok.anonymous
  1900. ? this.functionDefinition()
  1901. : this.functionCall();
  1902. }
  1903. }
  1904. };