parser.js 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294
  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. , node
  428. , brace;
  429. // Real property
  430. if (fromProperty && ':' == this.lookahead(i + 1).type
  431. && (this.lookahead(i + 1).space || 'indent' == this.lookahead(i + 2).type))
  432. return false;
  433. // Assume selector when an ident is
  434. // followed by a selector
  435. while ('ident' == this.lookahead(i).type
  436. && ('newline' == this.lookahead(i + 1).type
  437. || ',' == this.lookahead(i + 1).type)) i += 2;
  438. while (this.isSelectorToken(i)
  439. || ',' == this.lookahead(i).type) {
  440. if ('selector' == this.lookahead(i).type)
  441. return true;
  442. if ('&' == this.lookahead(i + 1).type)
  443. return true;
  444. // Hash values inside properties
  445. if (
  446. i > 1 &&
  447. 'ident' === this.lookahead(i - 1).type &&
  448. '.' === this.lookahead(i).type &&
  449. 'ident' === this.lookahead(i + 1).type
  450. ) {
  451. while ((node = this.lookahead(i + 2))) {
  452. if ([
  453. 'indent',
  454. 'outdent',
  455. '{',
  456. ';',
  457. 'eos',
  458. 'selector',
  459. 'media',
  460. 'if',
  461. 'atrule',
  462. ')',
  463. '}',
  464. 'unit',
  465. '[',
  466. 'for',
  467. 'function'
  468. ].indexOf(node.type) !== -1) {
  469. if (node.type === '[') {
  470. while ((node = this.lookahead(i + 3)) && node.type !== ']') {
  471. if (~['.', 'unit'].indexOf(node.type)) {
  472. return false;
  473. }
  474. i += 1
  475. }
  476. } else {
  477. if (this.isPseudoSelector(i + 2)) {
  478. return true;
  479. }
  480. if (node.type === ')' && this.lookahead(i + 3) && this.lookahead(i + 3).type === '}') {
  481. break;
  482. }
  483. return [
  484. 'outdent',
  485. ';',
  486. 'eos',
  487. 'media',
  488. 'if',
  489. 'atrule',
  490. ')',
  491. '}',
  492. 'unit',
  493. 'for',
  494. 'function'
  495. ].indexOf(node.type) === -1;
  496. }
  497. }
  498. i += 1
  499. }
  500. return true;
  501. }
  502. if ('.' == this.lookahead(i).type && 'ident' == this.lookahead(i + 1).type) {
  503. return true;
  504. }
  505. if ('*' == this.lookahead(i).type && 'newline' == this.lookahead(i + 1).type)
  506. return true;
  507. // Pseudo-elements
  508. if (':' == this.lookahead(i).type
  509. && ':' == this.lookahead(i + 1).type)
  510. return true;
  511. // #a after an ident and newline
  512. if ('color' == this.lookahead(i).type
  513. && 'newline' == this.lookahead(i - 1).type)
  514. return true;
  515. if (this.looksLikeAttributeSelector(i))
  516. return true;
  517. if (('=' == this.lookahead(i).type || 'function' == this.lookahead(i).type)
  518. && '{' == this.lookahead(i + 1).type)
  519. return false;
  520. // Hash values inside properties
  521. if (':' == this.lookahead(i).type
  522. && !this.isPseudoSelector(i + 1)
  523. && this.lineContains('.'))
  524. return false;
  525. // the ':' token within braces signifies
  526. // a selector. ex: "foo{bar:'baz'}"
  527. if ('{' == this.lookahead(i).type) brace = true;
  528. else if ('}' == this.lookahead(i).type) brace = false;
  529. if (brace && ':' == this.lookahead(i).type) return true;
  530. // '{' preceded by a space is considered a selector.
  531. // for example "foo{bar}{baz}" may be a property,
  532. // however "foo{bar} {baz}" is a selector
  533. if ('space' == this.lookahead(i).type
  534. && '{' == this.lookahead(i + 1).type)
  535. return true;
  536. // Assume pseudo selectors are NOT properties
  537. // as 'td:th-child(1)' may look like a property
  538. // and function call to the parser otherwise
  539. if (':' == this.lookahead(i++).type
  540. && !this.lookahead(i-1).space
  541. && this.isPseudoSelector(i))
  542. return true;
  543. // Trailing space
  544. if ('space' == this.lookahead(i).type
  545. && 'newline' == this.lookahead(i + 1).type
  546. && '{' == this.lookahead(i + 2).type)
  547. return true;
  548. if (',' == this.lookahead(i).type
  549. && 'newline' == this.lookahead(i + 1).type)
  550. return true;
  551. }
  552. // Trailing comma
  553. if (',' == this.lookahead(i).type
  554. && 'newline' == this.lookahead(i + 1).type)
  555. return true;
  556. // Trailing brace
  557. if ('{' == this.lookahead(i).type
  558. && 'newline' == this.lookahead(i + 1).type)
  559. return true;
  560. // css-style mode, false on ; }
  561. if (this.css) {
  562. if (';' == this.lookahead(i).type ||
  563. '}' == this.lookahead(i - 1).type)
  564. return false;
  565. }
  566. // Trailing separators
  567. while (!~[
  568. 'indent'
  569. , 'outdent'
  570. , 'newline'
  571. , 'for'
  572. , 'if'
  573. , ';'
  574. , '}'
  575. , 'eos'].indexOf(this.lookahead(i).type))
  576. ++i;
  577. if ('indent' == this.lookahead(i).type)
  578. return true;
  579. },
  580. /**
  581. * Check if the following sequence of tokens
  582. * forms an attribute selector.
  583. */
  584. looksLikeAttributeSelector: function(n) {
  585. var type = this.lookahead(n).type;
  586. if ('=' == type && this.bracketed) return true;
  587. return ('ident' == type || 'string' == type)
  588. && ']' == this.lookahead(n + 1).type
  589. && ('newline' == this.lookahead(n + 2).type || this.isSelectorToken(n + 2))
  590. && !this.lineContains(':')
  591. && !this.lineContains('=');
  592. },
  593. /**
  594. * Check if the following sequence of tokens
  595. * forms a keyframe block.
  596. */
  597. looksLikeKeyframe: function() {
  598. var i = 2
  599. , type;
  600. switch (this.lookahead(i).type) {
  601. case '{':
  602. case 'indent':
  603. case ',':
  604. return true;
  605. case 'newline':
  606. while ('unit' == this.lookahead(++i).type
  607. || 'newline' == this.lookahead(i).type) ;
  608. type = this.lookahead(i).type;
  609. return 'indent' == type || '{' == type;
  610. }
  611. },
  612. /**
  613. * Check if the current state supports selectors.
  614. */
  615. stateAllowsSelector: function() {
  616. switch (this.currentState()) {
  617. case 'root':
  618. case 'atblock':
  619. case 'selector':
  620. case 'conditional':
  621. case 'function':
  622. case 'atrule':
  623. case 'for':
  624. return true;
  625. }
  626. },
  627. /**
  628. * Try to assign @block to the node.
  629. *
  630. * @param {Expression} expr
  631. * @private
  632. */
  633. assignAtblock: function(expr) {
  634. try {
  635. expr.push(this.atblock(expr));
  636. } catch(err) {
  637. this.error('invalid right-hand side operand in assignment, got {peek}');
  638. }
  639. },
  640. /**
  641. * statement
  642. * | statement 'if' expression
  643. * | statement 'unless' expression
  644. */
  645. statement: function() {
  646. var stmt = this.stmt()
  647. , state = this.prevState
  648. , block
  649. , op;
  650. // special-case statements since it
  651. // is not an expression. We could
  652. // implement postfix conditionals at
  653. // the expression level, however they
  654. // would then fail to enclose properties
  655. if (this.allowPostfix) {
  656. this.allowPostfix = false;
  657. state = 'expression';
  658. }
  659. switch (state) {
  660. case 'assignment':
  661. case 'expression':
  662. case 'function arguments':
  663. while (op =
  664. this.accept('if')
  665. || this.accept('unless')
  666. || this.accept('for')) {
  667. switch (op.type) {
  668. case 'if':
  669. case 'unless':
  670. stmt = new nodes.If(this.expression(), stmt);
  671. stmt.postfix = true;
  672. stmt.negate = 'unless' == op.type;
  673. this.accept(';');
  674. break;
  675. case 'for':
  676. var key
  677. , val = this.id().name;
  678. if (this.accept(',')) key = this.id().name;
  679. this.expect('in');
  680. var each = new nodes.Each(val, key, this.expression());
  681. block = new nodes.Block(this.parent, each);
  682. block.push(stmt);
  683. each.block = block;
  684. stmt = each;
  685. }
  686. }
  687. }
  688. return stmt;
  689. },
  690. /**
  691. * ident
  692. * | selector
  693. * | literal
  694. * | charset
  695. * | namespace
  696. * | import
  697. * | require
  698. * | media
  699. * | atrule
  700. * | scope
  701. * | keyframes
  702. * | mozdocument
  703. * | for
  704. * | if
  705. * | unless
  706. * | comment
  707. * | expression
  708. * | 'return' expression
  709. */
  710. stmt: function() {
  711. var tok = this.peek(), selector;
  712. switch (tok.type) {
  713. case 'keyframes':
  714. return this.keyframes();
  715. case '-moz-document':
  716. return this.mozdocument();
  717. case 'comment':
  718. case 'selector':
  719. case 'literal':
  720. case 'charset':
  721. case 'namespace':
  722. case 'import':
  723. case 'require':
  724. case 'extend':
  725. case 'media':
  726. case 'atrule':
  727. case 'ident':
  728. case 'scope':
  729. case 'supports':
  730. case 'unless':
  731. case 'function':
  732. case 'for':
  733. case 'if':
  734. return this[tok.type]();
  735. case 'return':
  736. return this.return();
  737. case '{':
  738. return this.property();
  739. default:
  740. // Contextual selectors
  741. if (this.stateAllowsSelector()) {
  742. switch (tok.type) {
  743. case 'color':
  744. case '~':
  745. case '>':
  746. case '<':
  747. case ':':
  748. case '&':
  749. case '&&':
  750. case '[':
  751. case '.':
  752. case '/':
  753. selector = this.selector();
  754. selector.column = tok.column;
  755. selector.lineno = tok.lineno;
  756. return selector;
  757. // relative reference
  758. case '..':
  759. if ('/' == this.lookahead(2).type)
  760. return this.selector();
  761. case '+':
  762. return 'function' == this.lookahead(2).type
  763. ? this.functionCall()
  764. : this.selector();
  765. case '*':
  766. return this.property();
  767. // keyframe blocks (10%, 20% { ... })
  768. case 'unit':
  769. if (this.looksLikeKeyframe()) {
  770. selector = this.selector();
  771. selector.column = tok.column;
  772. selector.lineno = tok.lineno;
  773. return selector;
  774. }
  775. case '-':
  776. if ('{' == this.lookahead(2).type)
  777. return this.property();
  778. }
  779. }
  780. // Expression fallback
  781. var expr = this.expression();
  782. if (expr.isEmpty) this.error('unexpected {peek}');
  783. return expr;
  784. }
  785. },
  786. /**
  787. * indent (!outdent)+ outdent
  788. */
  789. block: function(node, scope) {
  790. var delim
  791. , stmt
  792. , next
  793. , block = this.parent = new nodes.Block(this.parent, node);
  794. if (false === scope) block.scope = false;
  795. this.accept('newline');
  796. // css-style
  797. if (this.accept('{')) {
  798. this.css++;
  799. delim = '}';
  800. this.skipWhitespace();
  801. } else {
  802. delim = 'outdent';
  803. this.expect('indent');
  804. }
  805. while (delim != this.peek().type) {
  806. // css-style
  807. if (this.css) {
  808. if (this.accept('newline') || this.accept('indent')) continue;
  809. stmt = this.statement();
  810. this.accept(';');
  811. this.skipWhitespace();
  812. } else {
  813. if (this.accept('newline')) continue;
  814. // skip useless indents and comments
  815. next = this.lookahead(2).type;
  816. if ('indent' == this.peek().type
  817. && ~['outdent', 'newline', 'comment'].indexOf(next)) {
  818. this.skip(['indent', 'outdent']);
  819. continue;
  820. }
  821. if ('eos' == this.peek().type) return block;
  822. stmt = this.statement();
  823. this.accept(';');
  824. }
  825. if (!stmt) this.error('unexpected token {peek} in block');
  826. block.push(stmt);
  827. }
  828. // css-style
  829. if (this.css) {
  830. this.skipWhitespace();
  831. this.expect('}');
  832. this.skipSpaces();
  833. this.css--;
  834. } else {
  835. this.expect('outdent');
  836. }
  837. this.parent = block.parent;
  838. return block;
  839. },
  840. /**
  841. * comment space*
  842. */
  843. comment: function(){
  844. var node = this.next().val;
  845. this.skipSpaces();
  846. return node;
  847. },
  848. /**
  849. * for val (',' key) in expr
  850. */
  851. for: function() {
  852. this.expect('for');
  853. var key
  854. , val = this.id().name;
  855. if (this.accept(',')) key = this.id().name;
  856. this.expect('in');
  857. this.state.push('for');
  858. this.cond = true;
  859. var each = new nodes.Each(val, key, this.expression());
  860. this.cond = false;
  861. each.block = this.block(each, false);
  862. this.state.pop();
  863. return each;
  864. },
  865. /**
  866. * return expression
  867. */
  868. return: function() {
  869. this.expect('return');
  870. var expr = this.expression();
  871. return expr.isEmpty
  872. ? new nodes.Return
  873. : new nodes.Return(expr);
  874. },
  875. /**
  876. * unless expression block
  877. */
  878. unless: function() {
  879. this.expect('unless');
  880. this.state.push('conditional');
  881. this.cond = true;
  882. var node = new nodes.If(this.expression(), true);
  883. this.cond = false;
  884. node.block = this.block(node, false);
  885. this.state.pop();
  886. return node;
  887. },
  888. /**
  889. * if expression block (else block)?
  890. */
  891. if: function() {
  892. var token = this.expect('if');
  893. this.state.push('conditional');
  894. this.cond = true;
  895. var node = new nodes.If(this.expression())
  896. , cond
  897. , block
  898. , item;
  899. node.column = token.column;
  900. this.cond = false;
  901. node.block = this.block(node, false);
  902. this.skip(['newline', 'comment']);
  903. while (this.accept('else')) {
  904. token = this.accept('if');
  905. if (token) {
  906. this.cond = true;
  907. cond = this.expression();
  908. this.cond = false;
  909. block = this.block(node, false);
  910. item = new nodes.If(cond, block);
  911. item.column = token.column;
  912. node.elses.push(item);
  913. } else {
  914. node.elses.push(this.block(node, false));
  915. break;
  916. }
  917. this.skip(['newline', 'comment']);
  918. }
  919. this.state.pop();
  920. return node;
  921. },
  922. /**
  923. * @block
  924. *
  925. * @param {Expression} [node]
  926. */
  927. atblock: function(node){
  928. if (!node) this.expect('atblock');
  929. node = new nodes.Atblock;
  930. this.state.push('atblock');
  931. node.block = this.block(node, false);
  932. this.state.pop();
  933. return node;
  934. },
  935. /**
  936. * atrule selector? block?
  937. */
  938. atrule: function(){
  939. var type = this.expect('atrule').val
  940. , node = new nodes.Atrule(type)
  941. , tok;
  942. this.skipSpacesAndComments();
  943. node.segments = this.selectorParts();
  944. this.skipSpacesAndComments();
  945. tok = this.peek().type;
  946. if ('indent' == tok || '{' == tok || ('newline' == tok
  947. && '{' == this.lookahead(2).type)) {
  948. this.state.push('atrule');
  949. node.block = this.block(node);
  950. this.state.pop();
  951. }
  952. return node;
  953. },
  954. /**
  955. * scope
  956. */
  957. scope: function(){
  958. this.expect('scope');
  959. var selector = this.selectorParts()
  960. .map(function(selector) { return selector.val; })
  961. .join('');
  962. this.selectorScope = selector.trim();
  963. return nodes.null;
  964. },
  965. /**
  966. * supports
  967. */
  968. supports: function(){
  969. this.expect('supports');
  970. var node = new nodes.Supports(this.supportsCondition());
  971. this.state.push('atrule');
  972. node.block = this.block(node);
  973. this.state.pop();
  974. return node;
  975. },
  976. /**
  977. * supports negation
  978. * | supports op
  979. * | expression
  980. */
  981. supportsCondition: function(){
  982. var node = this.supportsNegation()
  983. || this.supportsOp();
  984. if (!node) {
  985. this.cond = true;
  986. node = this.expression();
  987. this.cond = false;
  988. }
  989. return node;
  990. },
  991. /**
  992. * 'not' supports feature
  993. */
  994. supportsNegation: function(){
  995. if (this.accept('not')) {
  996. var node = new nodes.Expression;
  997. node.push(new nodes.Literal('not'));
  998. node.push(this.supportsFeature());
  999. return node;
  1000. }
  1001. },
  1002. /**
  1003. * supports feature (('and' | 'or') supports feature)+
  1004. */
  1005. supportsOp: function(){
  1006. var feature = this.supportsFeature()
  1007. , op
  1008. , expr;
  1009. if (feature) {
  1010. expr = new nodes.Expression;
  1011. expr.push(feature);
  1012. while (op = this.accept('&&') || this.accept('||')) {
  1013. expr.push(new nodes.Literal('&&' == op.val ? 'and' : 'or'));
  1014. expr.push(this.supportsFeature());
  1015. }
  1016. return expr;
  1017. }
  1018. },
  1019. /**
  1020. * ('(' supports condition ')')
  1021. * | feature
  1022. */
  1023. supportsFeature: function(){
  1024. this.skipSpacesAndComments();
  1025. if ('(' == this.peek().type) {
  1026. var la = this.lookahead(2).type;
  1027. if ('ident' == la || '{' == la) {
  1028. return this.feature();
  1029. } else {
  1030. this.expect('(');
  1031. var node = new nodes.Expression;
  1032. node.push(new nodes.Literal('('));
  1033. node.push(this.supportsCondition());
  1034. this.expect(')')
  1035. node.push(new nodes.Literal(')'));
  1036. this.skipSpacesAndComments();
  1037. return node;
  1038. }
  1039. }
  1040. },
  1041. /**
  1042. * extend
  1043. */
  1044. extend: function(){
  1045. var tok = this.expect('extend')
  1046. , selectors = []
  1047. , sel
  1048. , node
  1049. , arr;
  1050. do {
  1051. arr = this.selectorParts();
  1052. if (!arr.length) continue;
  1053. sel = new nodes.Selector(arr);
  1054. selectors.push(sel);
  1055. if ('!' !== this.peek().type) continue;
  1056. tok = this.lookahead(2);
  1057. if ('ident' !== tok.type || 'optional' !== tok.val.name) continue;
  1058. this.skip(['!', 'ident']);
  1059. sel.optional = true;
  1060. } while(this.accept(','));
  1061. node = new nodes.Extend(selectors);
  1062. node.lineno = tok.lineno;
  1063. node.column = tok.column;
  1064. return node;
  1065. },
  1066. /**
  1067. * media queries
  1068. */
  1069. media: function() {
  1070. this.expect('media');
  1071. this.state.push('atrule');
  1072. var media = new nodes.Media(this.queries());
  1073. media.block = this.block(media);
  1074. this.state.pop();
  1075. return media;
  1076. },
  1077. /**
  1078. * query (',' query)*
  1079. */
  1080. queries: function() {
  1081. var queries = new nodes.QueryList
  1082. , skip = ['comment', 'newline', 'space'];
  1083. do {
  1084. this.skip(skip);
  1085. queries.push(this.query());
  1086. this.skip(skip);
  1087. } while (this.accept(','));
  1088. return queries;
  1089. },
  1090. /**
  1091. * expression
  1092. * | (ident | 'not')? ident ('and' feature)*
  1093. * | feature ('and' feature)*
  1094. */
  1095. query: function() {
  1096. var query = new nodes.Query
  1097. , expr
  1098. , pred
  1099. , id;
  1100. // hash values support
  1101. if ('ident' == this.peek().type
  1102. && ('.' == this.lookahead(2).type
  1103. || '[' == this.lookahead(2).type)) {
  1104. this.cond = true;
  1105. expr = this.expression();
  1106. this.cond = false;
  1107. query.push(new nodes.Feature(expr.nodes));
  1108. return query;
  1109. }
  1110. if (pred = this.accept('ident') || this.accept('not')) {
  1111. pred = new nodes.Literal(pred.val.string || pred.val);
  1112. this.skipSpacesAndComments();
  1113. if (id = this.accept('ident')) {
  1114. query.type = id.val;
  1115. query.predicate = pred;
  1116. } else {
  1117. query.type = pred;
  1118. }
  1119. this.skipSpacesAndComments();
  1120. if (!this.accept('&&')) return query;
  1121. }
  1122. do {
  1123. query.push(this.feature());
  1124. } while (this.accept('&&'));
  1125. return query;
  1126. },
  1127. /**
  1128. * '(' ident ( ':'? expression )? ')'
  1129. */
  1130. feature: function() {
  1131. this.skipSpacesAndComments();
  1132. this.expect('(');
  1133. this.skipSpacesAndComments();
  1134. var node = new nodes.Feature(this.interpolate());
  1135. this.skipSpacesAndComments();
  1136. this.accept(':')
  1137. this.skipSpacesAndComments();
  1138. this.inProperty = true;
  1139. node.expr = this.list();
  1140. this.inProperty = false;
  1141. this.skipSpacesAndComments();
  1142. this.expect(')');
  1143. this.skipSpacesAndComments();
  1144. return node;
  1145. },
  1146. /**
  1147. * @-moz-document call (',' call)* block
  1148. */
  1149. mozdocument: function(){
  1150. this.expect('-moz-document');
  1151. var mozdocument = new nodes.Atrule('-moz-document')
  1152. , calls = [];
  1153. do {
  1154. this.skipSpacesAndComments();
  1155. calls.push(this.functionCall());
  1156. this.skipSpacesAndComments();
  1157. } while (this.accept(','));
  1158. mozdocument.segments = [new nodes.Literal(calls.join(', '))];
  1159. this.state.push('atrule');
  1160. mozdocument.block = this.block(mozdocument, false);
  1161. this.state.pop();
  1162. return mozdocument;
  1163. },
  1164. /**
  1165. * import expression
  1166. */
  1167. import: function() {
  1168. this.expect('import');
  1169. this.allowPostfix = true;
  1170. return new nodes.Import(this.expression(), false);
  1171. },
  1172. /**
  1173. * require expression
  1174. */
  1175. require: function() {
  1176. this.expect('require');
  1177. this.allowPostfix = true;
  1178. return new nodes.Import(this.expression(), true);
  1179. },
  1180. /**
  1181. * charset string
  1182. */
  1183. charset: function() {
  1184. this.expect('charset');
  1185. var str = this.expect('string').val;
  1186. this.allowPostfix = true;
  1187. return new nodes.Charset(str);
  1188. },
  1189. /**
  1190. * namespace ident? (string | url)
  1191. */
  1192. namespace: function() {
  1193. var str
  1194. , prefix;
  1195. this.expect('namespace');
  1196. this.skipSpacesAndComments();
  1197. if (prefix = this.accept('ident')) {
  1198. prefix = prefix.val;
  1199. }
  1200. this.skipSpacesAndComments();
  1201. str = this.accept('string') || this.url();
  1202. this.allowPostfix = true;
  1203. return new nodes.Namespace(str, prefix);
  1204. },
  1205. /**
  1206. * keyframes name block
  1207. */
  1208. keyframes: function() {
  1209. var tok = this.expect('keyframes')
  1210. , keyframes;
  1211. this.skipSpacesAndComments();
  1212. keyframes = new nodes.Keyframes(this.selectorParts(), tok.val);
  1213. keyframes.column = tok.column;
  1214. this.skipSpacesAndComments();
  1215. // block
  1216. this.state.push('atrule');
  1217. keyframes.block = this.block(keyframes);
  1218. this.state.pop();
  1219. return keyframes;
  1220. },
  1221. /**
  1222. * literal
  1223. */
  1224. literal: function() {
  1225. return this.expect('literal').val;
  1226. },
  1227. /**
  1228. * ident space?
  1229. */
  1230. id: function() {
  1231. var tok = this.expect('ident');
  1232. this.accept('space');
  1233. return tok.val;
  1234. },
  1235. /**
  1236. * ident
  1237. * | assignment
  1238. * | property
  1239. * | selector
  1240. */
  1241. ident: function() {
  1242. var i = 2
  1243. , la = this.lookahead(i).type;
  1244. while ('space' == la) la = this.lookahead(++i).type;
  1245. switch (la) {
  1246. // Assignment
  1247. case '=':
  1248. case '?=':
  1249. case '-=':
  1250. case '+=':
  1251. case '*=':
  1252. case '/=':
  1253. case '%=':
  1254. return this.assignment();
  1255. // Member
  1256. case '.':
  1257. if ('space' == this.lookahead(i - 1).type) return this.selector();
  1258. if (this._ident == this.peek()) return this.id();
  1259. while ('=' != this.lookahead(++i).type
  1260. && !~['[', ',', 'newline', 'indent', 'eos'].indexOf(this.lookahead(i).type)) ;
  1261. if ('=' == this.lookahead(i).type) {
  1262. this._ident = this.peek();
  1263. return this.expression();
  1264. } else if (this.looksLikeSelector() && this.stateAllowsSelector()) {
  1265. return this.selector();
  1266. }
  1267. // Assignment []=
  1268. case '[':
  1269. if (this._ident == this.peek()) return this.id();
  1270. while (']' != this.lookahead(i++).type
  1271. && 'selector' != this.lookahead(i).type
  1272. && 'eos' != this.lookahead(i).type) ;
  1273. if ('=' == this.lookahead(i).type) {
  1274. this._ident = this.peek();
  1275. return this.expression();
  1276. } else if (this.looksLikeSelector() && this.stateAllowsSelector()) {
  1277. return this.selector();
  1278. }
  1279. // Operation
  1280. case '-':
  1281. case '+':
  1282. case '/':
  1283. case '*':
  1284. case '%':
  1285. case '**':
  1286. case '&&':
  1287. case '||':
  1288. case '>':
  1289. case '<':
  1290. case '>=':
  1291. case '<=':
  1292. case '!=':
  1293. case '==':
  1294. case '?':
  1295. case 'in':
  1296. case 'is a':
  1297. case 'is defined':
  1298. // Prevent cyclic .ident, return literal
  1299. if (this._ident == this.peek()) {
  1300. return this.id();
  1301. } else {
  1302. this._ident = this.peek();
  1303. switch (this.currentState()) {
  1304. // unary op or selector in property / for
  1305. case 'for':
  1306. case 'selector':
  1307. return this.property();
  1308. // Part of a selector
  1309. case 'root':
  1310. case 'atblock':
  1311. case 'atrule':
  1312. return '[' == la
  1313. ? this.subscript()
  1314. : this.selector();
  1315. case 'function':
  1316. case 'conditional':
  1317. return this.looksLikeSelector()
  1318. ? this.selector()
  1319. : this.expression();
  1320. // Do not disrupt the ident when an operand
  1321. default:
  1322. return this.operand
  1323. ? this.id()
  1324. : this.expression();
  1325. }
  1326. }
  1327. // Selector or property
  1328. default:
  1329. switch (this.currentState()) {
  1330. case 'root':
  1331. return this.selector();
  1332. case 'for':
  1333. case 'selector':
  1334. case 'function':
  1335. case 'conditional':
  1336. case 'atblock':
  1337. case 'atrule':
  1338. return this.property();
  1339. default:
  1340. var id = this.id();
  1341. if ('interpolation' == this.previousState()) id.mixin = true;
  1342. return id;
  1343. }
  1344. }
  1345. },
  1346. /**
  1347. * '*'? (ident | '{' expression '}')+
  1348. */
  1349. interpolate: function() {
  1350. var node
  1351. , segs = []
  1352. , star;
  1353. star = this.accept('*');
  1354. if (star) segs.push(new nodes.Literal('*'));
  1355. while (true) {
  1356. if (this.accept('{')) {
  1357. this.state.push('interpolation');
  1358. segs.push(this.expression());
  1359. this.expect('}');
  1360. this.state.pop();
  1361. } else if (node = this.accept('-')){
  1362. segs.push(new nodes.Literal('-'));
  1363. } else if (node = this.accept('ident')){
  1364. segs.push(node.val);
  1365. } else {
  1366. break;
  1367. }
  1368. }
  1369. if (!segs.length) this.expect('ident');
  1370. return segs;
  1371. },
  1372. /**
  1373. * property ':'? expression
  1374. * | ident
  1375. */
  1376. property: function() {
  1377. if (this.looksLikeSelector(true)) return this.selector();
  1378. // property
  1379. var ident = this.interpolate()
  1380. , prop = new nodes.Property(ident)
  1381. , ret = prop;
  1382. // optional ':'
  1383. this.accept('space');
  1384. if (this.accept(':')) this.accept('space');
  1385. this.state.push('property');
  1386. this.inProperty = true;
  1387. prop.expr = this.list();
  1388. if (prop.expr.isEmpty) ret = ident[0];
  1389. this.inProperty = false;
  1390. this.allowPostfix = true;
  1391. this.state.pop();
  1392. // optional ';'
  1393. this.accept(';');
  1394. return ret;
  1395. },
  1396. /**
  1397. * selector ',' selector
  1398. * | selector newline selector
  1399. * | selector block
  1400. */
  1401. selector: function() {
  1402. var arr
  1403. , group = new nodes.Group
  1404. , scope = this.selectorScope
  1405. , isRoot = 'root' == this.currentState()
  1406. , selector;
  1407. do {
  1408. // Clobber newline after ,
  1409. this.accept('newline');
  1410. arr = this.selectorParts();
  1411. // Push the selector
  1412. if (isRoot && scope) arr.unshift(new nodes.Literal(scope + ' '));
  1413. if (arr.length) {
  1414. selector = new nodes.Selector(arr);
  1415. selector.lineno = arr[0].lineno;
  1416. selector.column = arr[0].column;
  1417. group.push(selector);
  1418. }
  1419. } while (this.accept(',') || this.accept('newline'));
  1420. if ('selector-parts' == this.currentState()) return group.nodes;
  1421. this.state.push('selector');
  1422. group.block = this.block(group);
  1423. this.state.pop();
  1424. return group;
  1425. },
  1426. selectorParts: function(){
  1427. var tok
  1428. , arr = [];
  1429. // Selector candidates,
  1430. // stitched together to
  1431. // form a selector.
  1432. while (tok = this.selectorToken()) {
  1433. debug.selector('%s', tok);
  1434. // Selector component
  1435. switch (tok.type) {
  1436. case '{':
  1437. this.skipSpaces();
  1438. var expr = this.expression();
  1439. this.skipSpaces();
  1440. this.expect('}');
  1441. arr.push(expr);
  1442. break;
  1443. case this.prefix && '.':
  1444. var literal = new nodes.Literal(tok.val + this.prefix);
  1445. literal.prefixed = true;
  1446. arr.push(literal);
  1447. break;
  1448. case 'comment':
  1449. // ignore comments
  1450. break;
  1451. case 'color':
  1452. case 'unit':
  1453. arr.push(new nodes.Literal(tok.val.raw));
  1454. break;
  1455. case 'space':
  1456. arr.push(new nodes.Literal(' '));
  1457. break;
  1458. case 'function':
  1459. arr.push(new nodes.Literal(tok.val.name + '('));
  1460. break;
  1461. case 'ident':
  1462. arr.push(new nodes.Literal(tok.val.name || tok.val.string));
  1463. break;
  1464. default:
  1465. arr.push(new nodes.Literal(tok.val));
  1466. if (tok.space) arr.push(new nodes.Literal(' '));
  1467. }
  1468. }
  1469. return arr;
  1470. },
  1471. /**
  1472. * ident ('=' | '?=') expression
  1473. */
  1474. assignment: function() {
  1475. var
  1476. op,
  1477. node,
  1478. ident = this.id(),
  1479. name = ident.name;
  1480. if (op =
  1481. this.accept('=')
  1482. || this.accept('?=')
  1483. || this.accept('+=')
  1484. || this.accept('-=')
  1485. || this.accept('*=')
  1486. || this.accept('/=')
  1487. || this.accept('%=')) {
  1488. this.state.push('assignment');
  1489. var expr = this.list();
  1490. // @block support
  1491. if (expr.isEmpty) this.assignAtblock(expr);
  1492. node = new nodes.Ident(name, expr);
  1493. node.lineno = ident.lineno;
  1494. node.column = ident.column;
  1495. this.state.pop();
  1496. switch (op.type) {
  1497. case '?=':
  1498. var defined = new nodes.BinOp('is defined', node)
  1499. , lookup = new nodes.Expression;
  1500. lookup.push(new nodes.Ident(name));
  1501. node = new nodes.Ternary(defined, lookup, node);
  1502. break;
  1503. case '+=':
  1504. case '-=':
  1505. case '*=':
  1506. case '/=':
  1507. case '%=':
  1508. node.val = new nodes.BinOp(op.type[0], new nodes.Ident(name), expr);
  1509. break;
  1510. }
  1511. }
  1512. return node;
  1513. },
  1514. /**
  1515. * definition
  1516. * | call
  1517. */
  1518. function: function() {
  1519. var parens = 1
  1520. , i = 2
  1521. , tok;
  1522. // Lookahead and determine if we are dealing
  1523. // with a function call or definition. Here
  1524. // we pair parens to prevent false negatives
  1525. out:
  1526. while (tok = this.lookahead(i++)) {
  1527. switch (tok.type) {
  1528. case 'function':
  1529. case '(':
  1530. ++parens;
  1531. break;
  1532. case ')':
  1533. if (!--parens) break out;
  1534. break;
  1535. case 'eos':
  1536. this.error('failed to find closing paren ")"');
  1537. }
  1538. }
  1539. // Definition or call
  1540. switch (this.currentState()) {
  1541. case 'expression':
  1542. return this.functionCall();
  1543. default:
  1544. return this.looksLikeFunctionDefinition(i)
  1545. ? this.functionDefinition()
  1546. : this.expression();
  1547. }
  1548. },
  1549. /**
  1550. * url '(' (expression | urlchars)+ ')'
  1551. */
  1552. url: function() {
  1553. this.expect('function');
  1554. this.state.push('function arguments');
  1555. var args = this.args();
  1556. this.expect(')');
  1557. this.state.pop();
  1558. return new nodes.Call('url', args);
  1559. },
  1560. /**
  1561. * '+'? ident '(' expression ')' block?
  1562. */
  1563. functionCall: function() {
  1564. var withBlock = this.accept('+');
  1565. if ('url' == this.peek().val.name) return this.url();
  1566. var tok = this.expect('function').val;
  1567. var name = tok.name;
  1568. this.state.push('function arguments');
  1569. this.parens++;
  1570. var args = this.args();
  1571. this.expect(')');
  1572. this.parens--;
  1573. this.state.pop();
  1574. var call = new nodes.Call(name, args);
  1575. call.column = tok.column;
  1576. call.lineno = tok.lineno;
  1577. if (withBlock) {
  1578. this.state.push('function');
  1579. call.block = this.block(call);
  1580. this.state.pop();
  1581. }
  1582. return call;
  1583. },
  1584. /**
  1585. * ident '(' params ')' block
  1586. */
  1587. functionDefinition: function() {
  1588. var
  1589. tok = this.expect('function'),
  1590. name = tok.val.name;
  1591. // params
  1592. this.state.push('function params');
  1593. this.skipWhitespace();
  1594. var params = this.params();
  1595. this.skipWhitespace();
  1596. this.expect(')');
  1597. this.state.pop();
  1598. // Body
  1599. this.state.push('function');
  1600. var fn = new nodes.Function(name, params);
  1601. fn.column = tok.column;
  1602. fn.lineno = tok.lineno;
  1603. fn.block = this.block(fn);
  1604. this.state.pop();
  1605. return new nodes.Ident(name, fn);
  1606. },
  1607. /**
  1608. * ident
  1609. * | ident '...'
  1610. * | ident '=' expression
  1611. * | ident ',' ident
  1612. */
  1613. params: function() {
  1614. var tok
  1615. , node
  1616. , params = new nodes.Params;
  1617. while (tok = this.accept('ident')) {
  1618. this.accept('space');
  1619. params.push(node = tok.val);
  1620. if (this.accept('...')) {
  1621. node.rest = true;
  1622. } else if (this.accept('=')) {
  1623. node.val = this.expression();
  1624. }
  1625. this.skipWhitespace();
  1626. this.accept(',');
  1627. this.skipWhitespace();
  1628. }
  1629. return params;
  1630. },
  1631. /**
  1632. * (ident ':')? expression (',' (ident ':')? expression)*
  1633. */
  1634. args: function() {
  1635. var args = new nodes.Arguments
  1636. , keyword;
  1637. do {
  1638. // keyword
  1639. if ('ident' == this.peek().type && ':' == this.lookahead(2).type) {
  1640. keyword = this.next().val.string;
  1641. this.expect(':');
  1642. args.map[keyword] = this.expression();
  1643. // arg
  1644. } else {
  1645. args.push(this.expression());
  1646. }
  1647. } while (this.accept(','));
  1648. return args;
  1649. },
  1650. /**
  1651. * expression (',' expression)*
  1652. */
  1653. list: function() {
  1654. var node = this.expression();
  1655. while (this.accept(',')) {
  1656. if (node.isList) {
  1657. list.push(this.expression());
  1658. } else {
  1659. var list = new nodes.Expression(true);
  1660. list.push(node);
  1661. list.push(this.expression());
  1662. node = list;
  1663. }
  1664. }
  1665. return node;
  1666. },
  1667. /**
  1668. * negation+
  1669. */
  1670. expression: function() {
  1671. var node
  1672. , expr = new nodes.Expression;
  1673. this.state.push('expression');
  1674. while (node = this.negation()) {
  1675. if (!node) this.error('unexpected token {peek} in expression');
  1676. expr.push(node);
  1677. }
  1678. this.state.pop();
  1679. if (expr.nodes.length) {
  1680. expr.lineno = expr.nodes[0].lineno;
  1681. expr.column = expr.nodes[0].column;
  1682. }
  1683. return expr;
  1684. },
  1685. /**
  1686. * 'not' ternary
  1687. * | ternary
  1688. */
  1689. negation: function() {
  1690. if (this.accept('not')) {
  1691. return new nodes.UnaryOp('!', this.negation());
  1692. }
  1693. return this.ternary();
  1694. },
  1695. /**
  1696. * logical ('?' expression ':' expression)?
  1697. */
  1698. ternary: function() {
  1699. var node = this.logical();
  1700. if (this.accept('?')) {
  1701. var trueExpr = this.expression();
  1702. this.expect(':');
  1703. var falseExpr = this.expression();
  1704. node = new nodes.Ternary(node, trueExpr, falseExpr);
  1705. }
  1706. return node;
  1707. },
  1708. /**
  1709. * typecheck (('&&' | '||') typecheck)*
  1710. */
  1711. logical: function() {
  1712. var op
  1713. , node = this.typecheck();
  1714. while (op = this.accept('&&') || this.accept('||')) {
  1715. node = new nodes.BinOp(op.type, node, this.typecheck());
  1716. }
  1717. return node;
  1718. },
  1719. /**
  1720. * equality ('is a' equality)*
  1721. */
  1722. typecheck: function() {
  1723. var op
  1724. , node = this.equality();
  1725. while (op = this.accept('is a')) {
  1726. this.operand = true;
  1727. if (!node) this.error('illegal unary "' + op + '", missing left-hand operand');
  1728. node = new nodes.BinOp(op.type, node, this.equality());
  1729. this.operand = false;
  1730. }
  1731. return node;
  1732. },
  1733. /**
  1734. * in (('==' | '!=') in)*
  1735. */
  1736. equality: function() {
  1737. var op
  1738. , node = this.in();
  1739. while (op = this.accept('==') || this.accept('!=')) {
  1740. this.operand = true;
  1741. if (!node) this.error('illegal unary "' + op + '", missing left-hand operand');
  1742. node = new nodes.BinOp(op.type, node, this.in());
  1743. this.operand = false;
  1744. }
  1745. return node;
  1746. },
  1747. /**
  1748. * relational ('in' relational)*
  1749. */
  1750. in: function() {
  1751. var node = this.relational();
  1752. while (this.accept('in')) {
  1753. this.operand = true;
  1754. if (!node) this.error('illegal unary "in", missing left-hand operand');
  1755. node = new nodes.BinOp('in', node, this.relational());
  1756. this.operand = false;
  1757. }
  1758. return node;
  1759. },
  1760. /**
  1761. * range (('>=' | '<=' | '>' | '<') range)*
  1762. */
  1763. relational: function() {
  1764. var op
  1765. , node = this.range();
  1766. while (op =
  1767. this.accept('>=')
  1768. || this.accept('<=')
  1769. || this.accept('<')
  1770. || this.accept('>')
  1771. ) {
  1772. this.operand = true;
  1773. if (!node) this.error('illegal unary "' + op + '", missing left-hand operand');
  1774. node = new nodes.BinOp(op.type, node, this.range());
  1775. this.operand = false;
  1776. }
  1777. return node;
  1778. },
  1779. /**
  1780. * additive (('..' | '...') additive)*
  1781. */
  1782. range: function() {
  1783. var op
  1784. , node = this.additive();
  1785. if (op = this.accept('...') || this.accept('..')) {
  1786. this.operand = true;
  1787. if (!node) this.error('illegal unary "' + op + '", missing left-hand operand');
  1788. node = new nodes.BinOp(op.val, node, this.additive());
  1789. this.operand = false;
  1790. }
  1791. return node;
  1792. },
  1793. /**
  1794. * multiplicative (('+' | '-') multiplicative)*
  1795. */
  1796. additive: function() {
  1797. var op
  1798. , node = this.multiplicative();
  1799. while (op = this.accept('+') || this.accept('-')) {
  1800. this.operand = true;
  1801. node = new nodes.BinOp(op.type, node, this.multiplicative());
  1802. this.operand = false;
  1803. }
  1804. return node;
  1805. },
  1806. /**
  1807. * defined (('**' | '*' | '/' | '%') defined)*
  1808. */
  1809. multiplicative: function() {
  1810. var op
  1811. , node = this.defined();
  1812. while (op =
  1813. this.accept('**')
  1814. || this.accept('*')
  1815. || this.accept('/')
  1816. || this.accept('%')) {
  1817. this.operand = true;
  1818. if ('/' == op && this.inProperty && !this.parens) {
  1819. this.stash.push(new Token('literal', new nodes.Literal('/')));
  1820. this.operand = false;
  1821. return node;
  1822. } else {
  1823. if (!node) this.error('illegal unary "' + op + '", missing left-hand operand');
  1824. node = new nodes.BinOp(op.type, node, this.defined());
  1825. this.operand = false;
  1826. }
  1827. }
  1828. return node;
  1829. },
  1830. /**
  1831. * unary 'is defined'
  1832. * | unary
  1833. */
  1834. defined: function() {
  1835. var node = this.unary();
  1836. if (this.accept('is defined')) {
  1837. if (!node) this.error('illegal unary "is defined", missing left-hand operand');
  1838. node = new nodes.BinOp('is defined', node);
  1839. }
  1840. return node;
  1841. },
  1842. /**
  1843. * ('!' | '~' | '+' | '-') unary
  1844. * | subscript
  1845. */
  1846. unary: function() {
  1847. var op
  1848. , node;
  1849. if (op =
  1850. this.accept('!')
  1851. || this.accept('~')
  1852. || this.accept('+')
  1853. || this.accept('-')) {
  1854. this.operand = true;
  1855. node = this.unary();
  1856. if (!node) this.error('illegal unary "' + op + '"');
  1857. node = new nodes.UnaryOp(op.type, node);
  1858. this.operand = false;
  1859. return node;
  1860. }
  1861. return this.subscript();
  1862. },
  1863. /**
  1864. * member ('[' expression ']')+ '='?
  1865. * | member
  1866. */
  1867. subscript: function() {
  1868. var node = this.member()
  1869. , id;
  1870. while (this.accept('[')) {
  1871. node = new nodes.BinOp('[]', node, this.expression());
  1872. this.expect(']');
  1873. }
  1874. // TODO: TernaryOp :)
  1875. if (this.accept('=')) {
  1876. node.op += '=';
  1877. node.val = this.list();
  1878. // @block support
  1879. if (node.val.isEmpty) this.assignAtblock(node.val);
  1880. }
  1881. return node;
  1882. },
  1883. /**
  1884. * primary ('.' id)+ '='?
  1885. * | primary
  1886. */
  1887. member: function() {
  1888. var node = this.primary();
  1889. if (node) {
  1890. while (this.accept('.')) {
  1891. var id = new nodes.Ident(this.expect('ident').val.string);
  1892. node = new nodes.Member(node, id);
  1893. }
  1894. this.skipSpaces();
  1895. if (this.accept('=')) {
  1896. node.val = this.list();
  1897. // @block support
  1898. if (node.val.isEmpty) this.assignAtblock(node.val);
  1899. }
  1900. }
  1901. return node;
  1902. },
  1903. /**
  1904. * '{' '}'
  1905. * | '{' pair (ws pair)* '}'
  1906. */
  1907. object: function(){
  1908. var obj = new nodes.Object
  1909. , id, val, comma, hash;
  1910. this.expect('{');
  1911. this.skipWhitespace();
  1912. while (!this.accept('}')) {
  1913. if (this.accept('comment')
  1914. || this.accept('newline')) continue;
  1915. if (!comma) this.accept(',');
  1916. id = this.accept('ident') || this.accept('string');
  1917. if (!id) {
  1918. this.error('expected "ident" or "string", got {peek}');
  1919. }
  1920. hash = id.val.hash;
  1921. this.skipSpacesAndComments();
  1922. this.expect(':');
  1923. val = this.expression();
  1924. obj.setValue(hash, val);
  1925. obj.setKey(hash, id.val);
  1926. comma = this.accept(',');
  1927. this.skipWhitespace();
  1928. }
  1929. return obj;
  1930. },
  1931. /**
  1932. * unit
  1933. * | null
  1934. * | color
  1935. * | string
  1936. * | ident
  1937. * | boolean
  1938. * | literal
  1939. * | object
  1940. * | atblock
  1941. * | atrule
  1942. * | '(' expression ')' '%'?
  1943. */
  1944. primary: function() {
  1945. var tok;
  1946. this.skipSpaces();
  1947. // Parenthesis
  1948. if (this.accept('(')) {
  1949. ++this.parens;
  1950. var expr = this.expression()
  1951. , paren = this.expect(')');
  1952. --this.parens;
  1953. if (this.accept('%')) expr.push(new nodes.Ident('%'));
  1954. tok = this.peek();
  1955. // (1 + 2)px, (1 + 2)em, etc.
  1956. if (!paren.space
  1957. && 'ident' == tok.type
  1958. && ~units.indexOf(tok.val.string)) {
  1959. expr.push(new nodes.Ident(tok.val.string));
  1960. this.next();
  1961. }
  1962. return expr;
  1963. }
  1964. tok = this.peek();
  1965. // Primitive
  1966. switch (tok.type) {
  1967. case 'null':
  1968. case 'unit':
  1969. case 'color':
  1970. case 'string':
  1971. case 'literal':
  1972. case 'boolean':
  1973. case 'comment':
  1974. return this.next().val;
  1975. case !this.cond && '{':
  1976. return this.object();
  1977. case 'atblock':
  1978. return this.atblock();
  1979. // property lookup
  1980. case 'atrule':
  1981. var id = new nodes.Ident(this.next().val);
  1982. id.property = true;
  1983. return id;
  1984. case 'ident':
  1985. return this.ident();
  1986. case 'function':
  1987. return tok.anonymous
  1988. ? this.functionDefinition()
  1989. : this.functionCall();
  1990. }
  1991. }
  1992. };