index.js 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870
  1. 'use strict';
  2. var TokenType = require('./const').TokenType;
  3. var Scanner = require('./scanner');
  4. var List = require('../utils/list');
  5. var needPositions;
  6. var filename;
  7. var scanner;
  8. var SCOPE_ATRULE_EXPRESSION = 1;
  9. var SCOPE_SELECTOR = 2;
  10. var SCOPE_VALUE = 3;
  11. var specialFunctions = {};
  12. specialFunctions[SCOPE_ATRULE_EXPRESSION] = {
  13. url: getUri
  14. };
  15. specialFunctions[SCOPE_SELECTOR] = {
  16. url: getUri,
  17. not: getNotFunction
  18. };
  19. specialFunctions[SCOPE_VALUE] = {
  20. url: getUri,
  21. expression: getOldIEExpression,
  22. var: getVarFunction
  23. };
  24. var initialContext = {
  25. stylesheet: getStylesheet,
  26. atrule: getAtrule,
  27. atruleExpression: getAtruleExpression,
  28. ruleset: getRuleset,
  29. selector: getSelector,
  30. simpleSelector: getSimpleSelector,
  31. block: getBlock,
  32. declaration: getDeclaration,
  33. value: getValue
  34. };
  35. var blockMode = {
  36. 'declaration': true,
  37. 'property': true
  38. };
  39. function parseError(message) {
  40. var error = new Error(message);
  41. var offset = 0;
  42. var line = 1;
  43. var column = 1;
  44. var lines;
  45. if (scanner.token !== null) {
  46. offset = scanner.token.offset;
  47. line = scanner.token.line;
  48. column = scanner.token.column;
  49. } else if (scanner.prevToken !== null) {
  50. lines = scanner.prevToken.value.trimRight();
  51. offset = scanner.prevToken.offset + lines.length;
  52. lines = lines.split(/\n|\r\n?|\f/);
  53. line = scanner.prevToken.line + lines.length - 1;
  54. column = lines.length > 1
  55. ? lines[lines.length - 1].length + 1
  56. : scanner.prevToken.column + lines[lines.length - 1].length;
  57. }
  58. error.name = 'CssSyntaxError';
  59. error.parseError = {
  60. offset: offset,
  61. line: line,
  62. column: column
  63. };
  64. throw error;
  65. }
  66. function eat(tokenType) {
  67. if (scanner.token !== null && scanner.token.type === tokenType) {
  68. scanner.next();
  69. return true;
  70. }
  71. parseError(tokenType + ' is expected');
  72. }
  73. function expectIdentifier(name, eat) {
  74. if (scanner.token !== null) {
  75. if (scanner.token.type === TokenType.Identifier &&
  76. scanner.token.value.toLowerCase() === name) {
  77. if (eat) {
  78. scanner.next();
  79. }
  80. return true;
  81. }
  82. }
  83. parseError('Identifier `' + name + '` is expected');
  84. }
  85. function expectAny(what) {
  86. if (scanner.token !== null) {
  87. for (var i = 1, type = scanner.token.type; i < arguments.length; i++) {
  88. if (type === arguments[i]) {
  89. return true;
  90. }
  91. }
  92. }
  93. parseError(what + ' is expected');
  94. }
  95. function getInfo() {
  96. if (needPositions && scanner.token) {
  97. return {
  98. source: filename,
  99. offset: scanner.token.offset,
  100. line: scanner.token.line,
  101. column: scanner.token.column
  102. };
  103. }
  104. return null;
  105. }
  106. function removeTrailingSpaces(list) {
  107. while (list.tail) {
  108. if (list.tail.data.type === 'Space') {
  109. list.remove(list.tail);
  110. } else {
  111. break;
  112. }
  113. }
  114. }
  115. function getStylesheet(nested) {
  116. var child = null;
  117. var node = {
  118. type: 'StyleSheet',
  119. info: getInfo(),
  120. rules: new List()
  121. };
  122. scan:
  123. while (scanner.token !== null) {
  124. switch (scanner.token.type) {
  125. case TokenType.Space:
  126. scanner.next();
  127. child = null;
  128. break;
  129. case TokenType.Comment:
  130. // ignore comments except exclamation comments on top level
  131. if (nested || scanner.token.value.charAt(2) !== '!') {
  132. scanner.next();
  133. child = null;
  134. } else {
  135. child = getComment();
  136. }
  137. break;
  138. case TokenType.Unknown:
  139. child = getUnknown();
  140. break;
  141. case TokenType.CommercialAt:
  142. child = getAtrule();
  143. break;
  144. case TokenType.RightCurlyBracket:
  145. if (!nested) {
  146. parseError('Unexpected right curly brace');
  147. }
  148. break scan;
  149. default:
  150. child = getRuleset();
  151. }
  152. if (child !== null) {
  153. node.rules.insert(List.createItem(child));
  154. }
  155. }
  156. return node;
  157. }
  158. // '//' ...
  159. // TODO: remove it as wrong thing
  160. function getUnknown() {
  161. var info = getInfo();
  162. var value = scanner.token.value;
  163. eat(TokenType.Unknown);
  164. return {
  165. type: 'Unknown',
  166. info: info,
  167. value: value
  168. };
  169. }
  170. function isBlockAtrule() {
  171. for (var offset = 1, cursor; cursor = scanner.lookup(offset); offset++) {
  172. var type = cursor.type;
  173. if (type === TokenType.RightCurlyBracket) {
  174. return true;
  175. }
  176. if (type === TokenType.LeftCurlyBracket ||
  177. type === TokenType.CommercialAt) {
  178. return false;
  179. }
  180. }
  181. return true;
  182. }
  183. function getAtruleExpression() {
  184. var child = null;
  185. var node = {
  186. type: 'AtruleExpression',
  187. info: getInfo(),
  188. sequence: new List()
  189. };
  190. scan:
  191. while (scanner.token !== null) {
  192. switch (scanner.token.type) {
  193. case TokenType.Semicolon:
  194. break scan;
  195. case TokenType.LeftCurlyBracket:
  196. break scan;
  197. case TokenType.Space:
  198. if (node.sequence.isEmpty()) {
  199. scanner.next(); // ignore spaces in beginning
  200. child = null;
  201. } else {
  202. child = getS();
  203. }
  204. break;
  205. case TokenType.Comment: // ignore comments
  206. scanner.next();
  207. child = null;
  208. break;
  209. case TokenType.Comma:
  210. child = getOperator();
  211. break;
  212. case TokenType.Colon:
  213. child = getPseudo();
  214. break;
  215. case TokenType.LeftParenthesis:
  216. child = getBraces(SCOPE_ATRULE_EXPRESSION);
  217. break;
  218. default:
  219. child = getAny(SCOPE_ATRULE_EXPRESSION);
  220. }
  221. if (child !== null) {
  222. node.sequence.insert(List.createItem(child));
  223. }
  224. }
  225. removeTrailingSpaces(node.sequence);
  226. return node;
  227. }
  228. function getAtrule() {
  229. eat(TokenType.CommercialAt);
  230. var node = {
  231. type: 'Atrule',
  232. info: getInfo(),
  233. name: readIdent(false),
  234. expression: getAtruleExpression(),
  235. block: null
  236. };
  237. if (scanner.token !== null) {
  238. switch (scanner.token.type) {
  239. case TokenType.Semicolon:
  240. scanner.next(); // {
  241. break;
  242. case TokenType.LeftCurlyBracket:
  243. scanner.next(); // {
  244. if (isBlockAtrule()) {
  245. node.block = getBlock();
  246. } else {
  247. node.block = getStylesheet(true);
  248. }
  249. eat(TokenType.RightCurlyBracket);
  250. break;
  251. default:
  252. parseError('Unexpected input');
  253. }
  254. }
  255. return node;
  256. }
  257. function getRuleset() {
  258. return {
  259. type: 'Ruleset',
  260. info: getInfo(),
  261. selector: getSelector(),
  262. block: getBlockWithBrackets()
  263. };
  264. }
  265. function getSelector() {
  266. var isBadSelector = false;
  267. var lastComma = true;
  268. var node = {
  269. type: 'Selector',
  270. info: getInfo(),
  271. selectors: new List()
  272. };
  273. scan:
  274. while (scanner.token !== null) {
  275. switch (scanner.token.type) {
  276. case TokenType.LeftCurlyBracket:
  277. break scan;
  278. case TokenType.Comma:
  279. if (lastComma) {
  280. isBadSelector = true;
  281. }
  282. lastComma = true;
  283. scanner.next();
  284. break;
  285. default:
  286. if (!lastComma) {
  287. isBadSelector = true;
  288. }
  289. lastComma = false;
  290. node.selectors.insert(List.createItem(getSimpleSelector()));
  291. if (node.selectors.tail.data.sequence.isEmpty()) {
  292. isBadSelector = true;
  293. }
  294. }
  295. }
  296. if (lastComma) {
  297. isBadSelector = true;
  298. // parseError('Unexpected trailing comma');
  299. }
  300. if (isBadSelector) {
  301. node.selectors = new List();
  302. }
  303. return node;
  304. }
  305. function getSimpleSelector(nested) {
  306. var child = null;
  307. var combinator = null;
  308. var node = {
  309. type: 'SimpleSelector',
  310. info: getInfo(),
  311. sequence: new List()
  312. };
  313. scan:
  314. while (scanner.token !== null) {
  315. switch (scanner.token.type) {
  316. case TokenType.Comma:
  317. break scan;
  318. case TokenType.LeftCurlyBracket:
  319. if (nested) {
  320. parseError('Unexpected input');
  321. }
  322. break scan;
  323. case TokenType.RightParenthesis:
  324. if (!nested) {
  325. parseError('Unexpected input');
  326. }
  327. break scan;
  328. case TokenType.Comment:
  329. scanner.next();
  330. child = null;
  331. break;
  332. case TokenType.Space:
  333. child = null;
  334. if (!combinator && node.sequence.head) {
  335. combinator = getCombinator();
  336. } else {
  337. scanner.next();
  338. }
  339. break;
  340. case TokenType.PlusSign:
  341. case TokenType.GreaterThanSign:
  342. case TokenType.Tilde:
  343. case TokenType.Solidus:
  344. if (combinator && combinator.name !== ' ') {
  345. parseError('Unexpected combinator');
  346. }
  347. child = null;
  348. combinator = getCombinator();
  349. break;
  350. case TokenType.FullStop:
  351. child = getClass();
  352. break;
  353. case TokenType.LeftSquareBracket:
  354. child = getAttribute();
  355. break;
  356. case TokenType.NumberSign:
  357. child = getShash();
  358. break;
  359. case TokenType.Colon:
  360. child = getPseudo();
  361. break;
  362. case TokenType.LowLine:
  363. case TokenType.Identifier:
  364. case TokenType.Asterisk:
  365. child = getNamespacedIdentifier(false);
  366. break;
  367. case TokenType.HyphenMinus:
  368. case TokenType.DecimalNumber:
  369. child = tryGetPercentage() || getNamespacedIdentifier(false);
  370. break;
  371. default:
  372. parseError('Unexpected input');
  373. }
  374. if (child !== null) {
  375. if (combinator !== null) {
  376. node.sequence.insert(List.createItem(combinator));
  377. combinator = null;
  378. }
  379. node.sequence.insert(List.createItem(child));
  380. }
  381. }
  382. if (combinator && combinator.name !== ' ') {
  383. parseError('Unexpected combinator');
  384. }
  385. return node;
  386. }
  387. function getDeclarations() {
  388. var child = null;
  389. var declarations = new List();
  390. scan:
  391. while (scanner.token !== null) {
  392. switch (scanner.token.type) {
  393. case TokenType.RightCurlyBracket:
  394. break scan;
  395. case TokenType.Space:
  396. case TokenType.Comment:
  397. scanner.next();
  398. child = null;
  399. break;
  400. case TokenType.Semicolon: // ;
  401. scanner.next();
  402. child = null;
  403. break;
  404. default:
  405. child = getDeclaration();
  406. }
  407. if (child !== null) {
  408. declarations.insert(List.createItem(child));
  409. }
  410. }
  411. return declarations;
  412. }
  413. function getBlockWithBrackets() {
  414. var info = getInfo();
  415. var node;
  416. eat(TokenType.LeftCurlyBracket);
  417. node = {
  418. type: 'Block',
  419. info: info,
  420. declarations: getDeclarations()
  421. };
  422. eat(TokenType.RightCurlyBracket);
  423. return node;
  424. }
  425. function getBlock() {
  426. return {
  427. type: 'Block',
  428. info: getInfo(),
  429. declarations: getDeclarations()
  430. };
  431. }
  432. function getDeclaration(nested) {
  433. var info = getInfo();
  434. var property = getProperty();
  435. var value;
  436. eat(TokenType.Colon);
  437. // check it's a filter
  438. if (/filter$/.test(property.name.toLowerCase()) && checkProgid()) {
  439. value = getFilterValue();
  440. } else {
  441. value = getValue(nested);
  442. }
  443. return {
  444. type: 'Declaration',
  445. info: info,
  446. property: property,
  447. value: value
  448. };
  449. }
  450. function getProperty() {
  451. var name = '';
  452. var node = {
  453. type: 'Property',
  454. info: getInfo(),
  455. name: null
  456. };
  457. for (; scanner.token !== null; scanner.next()) {
  458. var type = scanner.token.type;
  459. if (type !== TokenType.Solidus &&
  460. type !== TokenType.Asterisk &&
  461. type !== TokenType.DollarSign) {
  462. break;
  463. }
  464. name += scanner.token.value;
  465. }
  466. node.name = name + readIdent(true);
  467. readSC();
  468. return node;
  469. }
  470. function getValue(nested) {
  471. var child = null;
  472. var node = {
  473. type: 'Value',
  474. info: getInfo(),
  475. important: false,
  476. sequence: new List()
  477. };
  478. readSC();
  479. scan:
  480. while (scanner.token !== null) {
  481. switch (scanner.token.type) {
  482. case TokenType.RightCurlyBracket:
  483. case TokenType.Semicolon:
  484. break scan;
  485. case TokenType.RightParenthesis:
  486. if (!nested) {
  487. parseError('Unexpected input');
  488. }
  489. break scan;
  490. case TokenType.Space:
  491. child = getS();
  492. break;
  493. case TokenType.Comment: // ignore comments
  494. scanner.next();
  495. child = null;
  496. break;
  497. case TokenType.NumberSign:
  498. child = getVhash();
  499. break;
  500. case TokenType.Solidus:
  501. case TokenType.Comma:
  502. child = getOperator();
  503. break;
  504. case TokenType.LeftParenthesis:
  505. case TokenType.LeftSquareBracket:
  506. child = getBraces(SCOPE_VALUE);
  507. break;
  508. case TokenType.ExclamationMark:
  509. node.important = getImportant();
  510. child = null;
  511. break;
  512. default:
  513. // check for unicode range: U+0F00, U+0F00-0FFF, u+0F00??
  514. if (scanner.token.type === TokenType.Identifier) {
  515. var prefix = scanner.token.value;
  516. if (prefix === 'U' || prefix === 'u') {
  517. if (scanner.lookupType(1, TokenType.PlusSign)) {
  518. scanner.next(); // U or u
  519. scanner.next(); // +
  520. child = {
  521. type: 'Identifier',
  522. info: getInfo(), // FIXME: wrong position
  523. name: prefix + '+' + readUnicodeRange(true)
  524. };
  525. break;
  526. }
  527. }
  528. }
  529. child = getAny(SCOPE_VALUE);
  530. }
  531. if (child !== null) {
  532. node.sequence.insert(List.createItem(child));
  533. }
  534. }
  535. removeTrailingSpaces(node.sequence);
  536. return node;
  537. }
  538. // any = string | percentage | dimension | number | uri | functionExpression | funktion | unary | operator | ident
  539. function getAny(scope) {
  540. switch (scanner.token.type) {
  541. case TokenType.String:
  542. return getString();
  543. case TokenType.LowLine:
  544. case TokenType.Identifier:
  545. break;
  546. case TokenType.FullStop:
  547. case TokenType.DecimalNumber:
  548. case TokenType.HyphenMinus:
  549. case TokenType.PlusSign:
  550. var number = tryGetNumber();
  551. if (number !== null) {
  552. if (scanner.token !== null) {
  553. if (scanner.token.type === TokenType.PercentSign) {
  554. return getPercentage(number);
  555. } else if (scanner.token.type === TokenType.Identifier) {
  556. return getDimension(number.value);
  557. }
  558. }
  559. return number;
  560. }
  561. if (scanner.token.type === TokenType.HyphenMinus) {
  562. var next = scanner.lookup(1);
  563. if (next && (next.type === TokenType.Identifier || next.type === TokenType.HyphenMinus)) {
  564. break;
  565. }
  566. }
  567. if (scanner.token.type === TokenType.HyphenMinus ||
  568. scanner.token.type === TokenType.PlusSign) {
  569. return getOperator();
  570. }
  571. parseError('Unexpected input');
  572. default:
  573. parseError('Unexpected input');
  574. }
  575. var ident = getIdentifier(false);
  576. if (scanner.token !== null && scanner.token.type === TokenType.LeftParenthesis) {
  577. return getFunction(scope, ident);
  578. }
  579. return ident;
  580. }
  581. function readAttrselector() {
  582. expectAny('Attribute selector (=, ~=, ^=, $=, *=, |=)',
  583. TokenType.EqualsSign, // =
  584. TokenType.Tilde, // ~=
  585. TokenType.CircumflexAccent, // ^=
  586. TokenType.DollarSign, // $=
  587. TokenType.Asterisk, // *=
  588. TokenType.VerticalLine // |=
  589. );
  590. var name;
  591. if (scanner.token.type === TokenType.EqualsSign) {
  592. name = '=';
  593. scanner.next();
  594. } else {
  595. name = scanner.token.value + '=';
  596. scanner.next();
  597. eat(TokenType.EqualsSign);
  598. }
  599. return name;
  600. }
  601. // '[' S* attrib_name ']'
  602. // '[' S* attrib_name S* attrib_match S* [ IDENT | STRING ] S* attrib_flags? S* ']'
  603. function getAttribute() {
  604. var node = {
  605. type: 'Attribute',
  606. info: getInfo(),
  607. name: null,
  608. operator: null,
  609. value: null,
  610. flags: null
  611. };
  612. eat(TokenType.LeftSquareBracket);
  613. readSC();
  614. node.name = getNamespacedIdentifier(true);
  615. readSC();
  616. if (scanner.token !== null && scanner.token.type !== TokenType.RightSquareBracket) {
  617. // avoid case `[name i]`
  618. if (scanner.token.type !== TokenType.Identifier) {
  619. node.operator = readAttrselector();
  620. readSC();
  621. if (scanner.token !== null && scanner.token.type === TokenType.String) {
  622. node.value = getString();
  623. } else {
  624. node.value = getIdentifier(false);
  625. }
  626. readSC();
  627. }
  628. // attribute flags
  629. if (scanner.token !== null && scanner.token.type === TokenType.Identifier) {
  630. node.flags = scanner.token.value;
  631. scanner.next();
  632. readSC();
  633. }
  634. }
  635. eat(TokenType.RightSquareBracket);
  636. return node;
  637. }
  638. function getBraces(scope) {
  639. var close;
  640. var child = null;
  641. var node = {
  642. type: 'Braces',
  643. info: getInfo(),
  644. open: scanner.token.value,
  645. close: null,
  646. sequence: new List()
  647. };
  648. if (scanner.token.type === TokenType.LeftParenthesis) {
  649. close = TokenType.RightParenthesis;
  650. } else {
  651. close = TokenType.RightSquareBracket;
  652. }
  653. // left brace
  654. scanner.next();
  655. readSC();
  656. scan:
  657. while (scanner.token !== null) {
  658. switch (scanner.token.type) {
  659. case close:
  660. node.close = scanner.token.value;
  661. break scan;
  662. case TokenType.Space:
  663. child = getS();
  664. break;
  665. case TokenType.Comment:
  666. scanner.next();
  667. child = null;
  668. break;
  669. case TokenType.NumberSign: // ??
  670. child = getVhash();
  671. break;
  672. case TokenType.LeftParenthesis:
  673. case TokenType.LeftSquareBracket:
  674. child = getBraces(scope);
  675. break;
  676. case TokenType.Solidus:
  677. case TokenType.Asterisk:
  678. case TokenType.Comma:
  679. case TokenType.Colon:
  680. child = getOperator();
  681. break;
  682. default:
  683. child = getAny(scope);
  684. }
  685. if (child !== null) {
  686. node.sequence.insert(List.createItem(child));
  687. }
  688. }
  689. removeTrailingSpaces(node.sequence);
  690. // right brace
  691. eat(close);
  692. return node;
  693. }
  694. // '.' ident
  695. function getClass() {
  696. var info = getInfo();
  697. eat(TokenType.FullStop);
  698. return {
  699. type: 'Class',
  700. info: info,
  701. name: readIdent(false)
  702. };
  703. }
  704. // '#' ident
  705. function getShash() {
  706. var info = getInfo();
  707. eat(TokenType.NumberSign);
  708. return {
  709. type: 'Id',
  710. info: info,
  711. name: readIdent(false)
  712. };
  713. }
  714. // + | > | ~ | /deep/
  715. function getCombinator() {
  716. var info = getInfo();
  717. var combinator;
  718. switch (scanner.token.type) {
  719. case TokenType.Space:
  720. combinator = ' ';
  721. scanner.next();
  722. break;
  723. case TokenType.PlusSign:
  724. case TokenType.GreaterThanSign:
  725. case TokenType.Tilde:
  726. combinator = scanner.token.value;
  727. scanner.next();
  728. break;
  729. case TokenType.Solidus:
  730. combinator = '/deep/';
  731. scanner.next();
  732. expectIdentifier('deep', true);
  733. eat(TokenType.Solidus);
  734. break;
  735. default:
  736. parseError('Combinator (+, >, ~, /deep/) is expected');
  737. }
  738. return {
  739. type: 'Combinator',
  740. info: info,
  741. name: combinator
  742. };
  743. }
  744. // '/*' .* '*/'
  745. function getComment() {
  746. var info = getInfo();
  747. var value = scanner.token.value;
  748. var len = value.length;
  749. if (len > 4 && value.charAt(len - 2) === '*' && value.charAt(len - 1) === '/') {
  750. len -= 2;
  751. }
  752. scanner.next();
  753. return {
  754. type: 'Comment',
  755. info: info,
  756. value: value.substring(2, len)
  757. };
  758. }
  759. // special reader for units to avoid adjoined IE hacks (i.e. '1px\9')
  760. function readUnit() {
  761. if (scanner.token !== null && scanner.token.type === TokenType.Identifier) {
  762. var unit = scanner.token.value;
  763. var backSlashPos = unit.indexOf('\\');
  764. // no backslash in unit name
  765. if (backSlashPos === -1) {
  766. scanner.next();
  767. return unit;
  768. }
  769. // patch token
  770. scanner.token.value = unit.substr(backSlashPos);
  771. scanner.token.offset += backSlashPos;
  772. scanner.token.column += backSlashPos;
  773. // return unit w/o backslash part
  774. return unit.substr(0, backSlashPos);
  775. }
  776. parseError('Identifier is expected');
  777. }
  778. // number ident
  779. function getDimension(number) {
  780. return {
  781. type: 'Dimension',
  782. info: getInfo(),
  783. value: number || readNumber(),
  784. unit: readUnit()
  785. };
  786. }
  787. // number "%"
  788. function tryGetPercentage() {
  789. var number = tryGetNumber();
  790. if (number && scanner.token !== null && scanner.token.type === TokenType.PercentSign) {
  791. return getPercentage(number);
  792. }
  793. return null;
  794. }
  795. function getPercentage(number) {
  796. var info;
  797. if (!number) {
  798. info = getInfo();
  799. number = readNumber();
  800. } else {
  801. info = number.info;
  802. number = number.value;
  803. }
  804. eat(TokenType.PercentSign);
  805. return {
  806. type: 'Percentage',
  807. info: info,
  808. value: number
  809. };
  810. }
  811. // ident '(' functionBody ')' |
  812. // not '(' <simpleSelector>* ')'
  813. function getFunction(scope, ident) {
  814. var defaultArguments = getFunctionArguments;
  815. if (!ident) {
  816. ident = getIdentifier(false);
  817. }
  818. // parse special functions
  819. var name = ident.name.toLowerCase();
  820. if (specialFunctions.hasOwnProperty(scope)) {
  821. if (specialFunctions[scope].hasOwnProperty(name)) {
  822. return specialFunctions[scope][name](scope, ident);
  823. }
  824. }
  825. return getFunctionInternal(defaultArguments, scope, ident);
  826. }
  827. function getFunctionInternal(functionArgumentsReader, scope, ident) {
  828. var args;
  829. eat(TokenType.LeftParenthesis);
  830. args = functionArgumentsReader(scope);
  831. eat(TokenType.RightParenthesis);
  832. return {
  833. type: scope === SCOPE_SELECTOR ? 'FunctionalPseudo' : 'Function',
  834. info: ident.info,
  835. name: ident.name,
  836. arguments: args
  837. };
  838. }
  839. function getFunctionArguments(scope) {
  840. var args = new List();
  841. var argument = null;
  842. var child = null;
  843. readSC();
  844. scan:
  845. while (scanner.token !== null) {
  846. switch (scanner.token.type) {
  847. case TokenType.RightParenthesis:
  848. break scan;
  849. case TokenType.Space:
  850. child = getS();
  851. break;
  852. case TokenType.Comment: // ignore comments
  853. scanner.next();
  854. child = null;
  855. break;
  856. case TokenType.NumberSign: // TODO: not sure it should be here
  857. child = getVhash();
  858. break;
  859. case TokenType.LeftParenthesis:
  860. case TokenType.LeftSquareBracket:
  861. child = getBraces(scope);
  862. break;
  863. case TokenType.Comma:
  864. if (argument) {
  865. removeTrailingSpaces(argument.sequence);
  866. } else {
  867. args.insert(List.createItem({
  868. type: 'Argument',
  869. sequence: new List()
  870. }));
  871. }
  872. scanner.next();
  873. readSC();
  874. argument = null;
  875. child = null;
  876. break;
  877. case TokenType.Solidus:
  878. case TokenType.Asterisk:
  879. case TokenType.Colon:
  880. case TokenType.EqualsSign:
  881. child = getOperator();
  882. break;
  883. default:
  884. child = getAny(scope);
  885. }
  886. if (argument === null) {
  887. argument = {
  888. type: 'Argument',
  889. sequence: new List()
  890. };
  891. args.insert(List.createItem(argument));
  892. }
  893. if (child !== null) {
  894. argument.sequence.insert(List.createItem(child));
  895. }
  896. }
  897. if (argument !== null) {
  898. removeTrailingSpaces(argument.sequence);
  899. }
  900. return args;
  901. }
  902. function getVarFunction(scope, ident) {
  903. return getFunctionInternal(getVarFunctionArguments, scope, ident);
  904. }
  905. function getNotFunctionArguments() {
  906. var args = new List();
  907. var wasSelector = false;
  908. scan:
  909. while (scanner.token !== null) {
  910. switch (scanner.token.type) {
  911. case TokenType.RightParenthesis:
  912. if (!wasSelector) {
  913. parseError('Simple selector is expected');
  914. }
  915. break scan;
  916. case TokenType.Comma:
  917. if (!wasSelector) {
  918. parseError('Simple selector is expected');
  919. }
  920. wasSelector = false;
  921. scanner.next();
  922. break;
  923. default:
  924. wasSelector = true;
  925. args.insert(List.createItem(getSimpleSelector(true)));
  926. }
  927. }
  928. return args;
  929. }
  930. function getNotFunction(scope, ident) {
  931. var args;
  932. eat(TokenType.LeftParenthesis);
  933. args = getNotFunctionArguments(scope);
  934. eat(TokenType.RightParenthesis);
  935. return {
  936. type: 'Negation',
  937. info: ident.info,
  938. // name: ident.name, // TODO: add name?
  939. sequence: args // FIXME: -> arguments?
  940. };
  941. }
  942. // var '(' ident (',' <declaration-value>)? ')'
  943. function getVarFunctionArguments() { // TODO: special type Variable?
  944. var args = new List();
  945. readSC();
  946. args.insert(List.createItem({
  947. type: 'Argument',
  948. sequence: new List([getIdentifier(true)])
  949. }));
  950. readSC();
  951. if (scanner.token !== null && scanner.token.type === TokenType.Comma) {
  952. eat(TokenType.Comma);
  953. readSC();
  954. args.insert(List.createItem({
  955. type: 'Argument',
  956. sequence: new List([getValue(true)])
  957. }));
  958. readSC();
  959. }
  960. return args;
  961. }
  962. // url '(' ws* (string | raw) ws* ')'
  963. function getUri(scope, ident) {
  964. var node = {
  965. type: 'Url',
  966. info: ident.info,
  967. // name: ident.name,
  968. value: null
  969. };
  970. eat(TokenType.LeftParenthesis); // (
  971. readSC();
  972. if (scanner.token.type === TokenType.String) {
  973. node.value = getString();
  974. readSC();
  975. } else {
  976. var rawInfo = getInfo();
  977. var raw = '';
  978. for (; scanner.token !== null; scanner.next()) {
  979. var type = scanner.token.type;
  980. if (type === TokenType.Space ||
  981. type === TokenType.LeftParenthesis ||
  982. type === TokenType.RightParenthesis) {
  983. break;
  984. }
  985. raw += scanner.token.value;
  986. }
  987. node.value = {
  988. type: 'Raw',
  989. info: rawInfo,
  990. value: raw
  991. };
  992. readSC();
  993. }
  994. eat(TokenType.RightParenthesis); // )
  995. return node;
  996. }
  997. // expression '(' raw ')'
  998. function getOldIEExpression(scope, ident) {
  999. var balance = 0;
  1000. var raw = '';
  1001. eat(TokenType.LeftParenthesis);
  1002. for (; scanner.token !== null; scanner.next()) {
  1003. if (scanner.token.type === TokenType.RightParenthesis) {
  1004. if (balance === 0) {
  1005. break;
  1006. }
  1007. balance--;
  1008. } else if (scanner.token.type === TokenType.LeftParenthesis) {
  1009. balance++;
  1010. }
  1011. raw += scanner.token.value;
  1012. }
  1013. eat(TokenType.RightParenthesis);
  1014. return {
  1015. type: 'Function',
  1016. info: ident.info,
  1017. name: ident.name,
  1018. arguments: new List([{
  1019. type: 'Argument',
  1020. sequence: new List([{
  1021. type: 'Raw',
  1022. value: raw
  1023. }])
  1024. }])
  1025. };
  1026. }
  1027. function readUnicodeRange(tryNext) {
  1028. var hex = '';
  1029. for (; scanner.token !== null; scanner.next()) {
  1030. if (scanner.token.type !== TokenType.DecimalNumber &&
  1031. scanner.token.type !== TokenType.Identifier) {
  1032. break;
  1033. }
  1034. hex += scanner.token.value;
  1035. }
  1036. if (!/^[0-9a-f]{1,6}$/i.test(hex)) {
  1037. parseError('Unexpected input');
  1038. }
  1039. // U+abc???
  1040. if (tryNext) {
  1041. for (; hex.length < 6 && scanner.token !== null; scanner.next()) {
  1042. if (scanner.token.type !== TokenType.QuestionMark) {
  1043. break;
  1044. }
  1045. hex += scanner.token.value;
  1046. tryNext = false;
  1047. }
  1048. }
  1049. // U+aaa-bbb
  1050. if (tryNext) {
  1051. if (scanner.token !== null && scanner.token.type === TokenType.HyphenMinus) {
  1052. scanner.next();
  1053. var next = readUnicodeRange(false);
  1054. if (!next) {
  1055. parseError('Unexpected input');
  1056. }
  1057. hex += '-' + next;
  1058. }
  1059. }
  1060. return hex;
  1061. }
  1062. function readIdent(varAllowed) {
  1063. var name = '';
  1064. // optional first -
  1065. if (scanner.token !== null && scanner.token.type === TokenType.HyphenMinus) {
  1066. name = '-';
  1067. scanner.next();
  1068. if (varAllowed && scanner.token !== null && scanner.token.type === TokenType.HyphenMinus) {
  1069. name = '--';
  1070. scanner.next();
  1071. }
  1072. }
  1073. expectAny('Identifier',
  1074. TokenType.LowLine,
  1075. TokenType.Identifier
  1076. );
  1077. if (scanner.token !== null) {
  1078. name += scanner.token.value;
  1079. scanner.next();
  1080. for (; scanner.token !== null; scanner.next()) {
  1081. var type = scanner.token.type;
  1082. if (type !== TokenType.LowLine &&
  1083. type !== TokenType.Identifier &&
  1084. type !== TokenType.DecimalNumber &&
  1085. type !== TokenType.HyphenMinus) {
  1086. break;
  1087. }
  1088. name += scanner.token.value;
  1089. }
  1090. }
  1091. return name;
  1092. }
  1093. function getNamespacedIdentifier(checkColon) {
  1094. if (scanner.token === null) {
  1095. parseError('Unexpected end of input');
  1096. }
  1097. var info = getInfo();
  1098. var name;
  1099. if (scanner.token.type === TokenType.Asterisk) {
  1100. checkColon = false;
  1101. name = '*';
  1102. scanner.next();
  1103. } else {
  1104. name = readIdent(false);
  1105. }
  1106. if (scanner.token !== null) {
  1107. if (scanner.token.type === TokenType.VerticalLine &&
  1108. scanner.lookupType(1, TokenType.EqualsSign) === false) {
  1109. name += '|';
  1110. if (scanner.next() !== null) {
  1111. if (scanner.token.type === TokenType.HyphenMinus ||
  1112. scanner.token.type === TokenType.Identifier ||
  1113. scanner.token.type === TokenType.LowLine) {
  1114. name += readIdent(false);
  1115. } else if (scanner.token.type === TokenType.Asterisk) {
  1116. checkColon = false;
  1117. name += '*';
  1118. scanner.next();
  1119. }
  1120. }
  1121. }
  1122. }
  1123. if (checkColon && scanner.token !== null && scanner.token.type === TokenType.Colon) {
  1124. scanner.next();
  1125. name += ':' + readIdent(false);
  1126. }
  1127. return {
  1128. type: 'Identifier',
  1129. info: info,
  1130. name: name
  1131. };
  1132. }
  1133. function getIdentifier(varAllowed) {
  1134. return {
  1135. type: 'Identifier',
  1136. info: getInfo(),
  1137. name: readIdent(varAllowed)
  1138. };
  1139. }
  1140. // ! ws* important
  1141. function getImportant() { // TODO?
  1142. // var info = getInfo();
  1143. eat(TokenType.ExclamationMark);
  1144. readSC();
  1145. // return {
  1146. // type: 'Identifier',
  1147. // info: info,
  1148. // name: readIdent(false)
  1149. // };
  1150. expectIdentifier('important');
  1151. readIdent(false);
  1152. // should return identifier in future for original source restoring as is
  1153. // returns true for now since it's fit to optimizer purposes
  1154. return true;
  1155. }
  1156. // odd | even | number? n
  1157. function getNth() {
  1158. expectAny('Number, odd or even',
  1159. TokenType.Identifier,
  1160. TokenType.DecimalNumber
  1161. );
  1162. var info = getInfo();
  1163. var value = scanner.token.value;
  1164. var cmpValue;
  1165. if (scanner.token.type === TokenType.DecimalNumber) {
  1166. var next = scanner.lookup(1);
  1167. if (next !== null &&
  1168. next.type === TokenType.Identifier &&
  1169. next.value.toLowerCase() === 'n') {
  1170. value += next.value;
  1171. scanner.next();
  1172. }
  1173. } else {
  1174. var cmpValue = value.toLowerCase();
  1175. if (cmpValue !== 'odd' && cmpValue !== 'even' && cmpValue !== 'n') {
  1176. parseError('Unexpected identifier');
  1177. }
  1178. }
  1179. scanner.next();
  1180. return {
  1181. type: 'Nth',
  1182. info: info,
  1183. value: value
  1184. };
  1185. }
  1186. function getNthSelector() {
  1187. var info = getInfo();
  1188. var sequence = new List();
  1189. var node;
  1190. var child = null;
  1191. eat(TokenType.Colon);
  1192. expectIdentifier('nth', false);
  1193. node = {
  1194. type: 'FunctionalPseudo',
  1195. info: info,
  1196. name: readIdent(false),
  1197. arguments: new List([{
  1198. type: 'Argument',
  1199. sequence: sequence
  1200. }])
  1201. };
  1202. eat(TokenType.LeftParenthesis);
  1203. scan:
  1204. while (scanner.token !== null) {
  1205. switch (scanner.token.type) {
  1206. case TokenType.RightParenthesis:
  1207. break scan;
  1208. case TokenType.Space:
  1209. case TokenType.Comment:
  1210. scanner.next();
  1211. child = null;
  1212. break;
  1213. case TokenType.HyphenMinus:
  1214. case TokenType.PlusSign:
  1215. child = getOperator();
  1216. break;
  1217. default:
  1218. child = getNth();
  1219. }
  1220. if (child !== null) {
  1221. sequence.insert(List.createItem(child));
  1222. }
  1223. }
  1224. eat(TokenType.RightParenthesis);
  1225. return node;
  1226. }
  1227. function readNumber() {
  1228. var wasDigits = false;
  1229. var number = '';
  1230. var offset = 0;
  1231. if (scanner.lookupType(offset, TokenType.HyphenMinus)) {
  1232. number = '-';
  1233. offset++;
  1234. }
  1235. if (scanner.lookupType(offset, TokenType.DecimalNumber)) {
  1236. wasDigits = true;
  1237. number += scanner.lookup(offset).value;
  1238. offset++;
  1239. }
  1240. if (scanner.lookupType(offset, TokenType.FullStop)) {
  1241. number += '.';
  1242. offset++;
  1243. }
  1244. if (scanner.lookupType(offset, TokenType.DecimalNumber)) {
  1245. wasDigits = true;
  1246. number += scanner.lookup(offset).value;
  1247. offset++;
  1248. }
  1249. if (wasDigits) {
  1250. while (offset--) {
  1251. scanner.next();
  1252. }
  1253. return number;
  1254. }
  1255. return null;
  1256. }
  1257. function tryGetNumber() {
  1258. var info = getInfo();
  1259. var number = readNumber();
  1260. if (number !== null) {
  1261. return {
  1262. type: 'Number',
  1263. info: info,
  1264. value: number
  1265. };
  1266. }
  1267. return null;
  1268. }
  1269. // '/' | '*' | ',' | ':' | '=' | '+' | '-'
  1270. // TODO: remove '=' since it's wrong operator, but theat as operator
  1271. // to make old things like `filter: alpha(opacity=0)` works
  1272. function getOperator() {
  1273. var node = {
  1274. type: 'Operator',
  1275. info: getInfo(),
  1276. value: scanner.token.value
  1277. };
  1278. scanner.next();
  1279. return node;
  1280. }
  1281. function getFilterValue() { // TODO
  1282. var progid;
  1283. var node = {
  1284. type: 'Value',
  1285. info: getInfo(),
  1286. important: false,
  1287. sequence: new List()
  1288. };
  1289. while (progid = checkProgid()) {
  1290. node.sequence.insert(List.createItem(getProgid(progid)));
  1291. }
  1292. readSC(node);
  1293. if (scanner.token !== null && scanner.token.type === TokenType.ExclamationMark) {
  1294. node.important = getImportant();
  1295. }
  1296. return node;
  1297. }
  1298. // 'progid:' ws* 'DXImageTransform.Microsoft.' ident ws* '(' .* ')'
  1299. function checkProgid() {
  1300. function checkSC(offset) {
  1301. for (var cursor; cursor = scanner.lookup(offset); offset++) {
  1302. if (cursor.type !== TokenType.Space &&
  1303. cursor.type !== TokenType.Comment) {
  1304. break;
  1305. }
  1306. }
  1307. return offset;
  1308. }
  1309. var offset = checkSC(0);
  1310. if (scanner.lookup(offset + 1) === null ||
  1311. scanner.lookup(offset + 0).value.toLowerCase() !== 'progid' ||
  1312. scanner.lookup(offset + 1).type !== TokenType.Colon) {
  1313. return false; // fail
  1314. }
  1315. offset += 2;
  1316. offset = checkSC(offset);
  1317. if (scanner.lookup(offset + 5) === null ||
  1318. scanner.lookup(offset + 0).value.toLowerCase() !== 'dximagetransform' ||
  1319. scanner.lookup(offset + 1).type !== TokenType.FullStop ||
  1320. scanner.lookup(offset + 2).value.toLowerCase() !== 'microsoft' ||
  1321. scanner.lookup(offset + 3).type !== TokenType.FullStop ||
  1322. scanner.lookup(offset + 4).type !== TokenType.Identifier) {
  1323. return false; // fail
  1324. }
  1325. offset += 5;
  1326. offset = checkSC(offset);
  1327. if (scanner.lookupType(offset, TokenType.LeftParenthesis) === false) {
  1328. return false; // fail
  1329. }
  1330. for (var cursor; cursor = scanner.lookup(offset); offset++) {
  1331. if (cursor.type === TokenType.RightParenthesis) {
  1332. return cursor;
  1333. }
  1334. }
  1335. return false;
  1336. }
  1337. function getProgid(progidEnd) {
  1338. var value = '';
  1339. var node = {
  1340. type: 'Progid',
  1341. info: getInfo(),
  1342. value: null
  1343. };
  1344. if (!progidEnd) {
  1345. progidEnd = checkProgid();
  1346. }
  1347. if (!progidEnd) {
  1348. parseError('progid is expected');
  1349. }
  1350. readSC(node);
  1351. var rawInfo = getInfo();
  1352. for (; scanner.token && scanner.token !== progidEnd; scanner.next()) {
  1353. value += scanner.token.value;
  1354. }
  1355. eat(TokenType.RightParenthesis);
  1356. value += ')';
  1357. node.value = {
  1358. type: 'Raw',
  1359. info: rawInfo,
  1360. value: value
  1361. };
  1362. readSC(node);
  1363. return node;
  1364. }
  1365. // <pseudo-element> | <nth-selector> | <pseudo-class>
  1366. function getPseudo() {
  1367. var next = scanner.lookup(1);
  1368. if (next === null) {
  1369. scanner.next();
  1370. parseError('Colon or identifier is expected');
  1371. }
  1372. if (next.type === TokenType.Colon) {
  1373. return getPseudoElement();
  1374. }
  1375. if (next.type === TokenType.Identifier &&
  1376. next.value.toLowerCase() === 'nth') {
  1377. return getNthSelector();
  1378. }
  1379. return getPseudoClass();
  1380. }
  1381. // :: ident
  1382. function getPseudoElement() {
  1383. var info = getInfo();
  1384. eat(TokenType.Colon);
  1385. eat(TokenType.Colon);
  1386. return {
  1387. type: 'PseudoElement',
  1388. info: info,
  1389. name: readIdent(false)
  1390. };
  1391. }
  1392. // : ( ident | function )
  1393. function getPseudoClass() {
  1394. var info = getInfo();
  1395. var ident = eat(TokenType.Colon) && getIdentifier(false);
  1396. if (scanner.token !== null && scanner.token.type === TokenType.LeftParenthesis) {
  1397. return getFunction(SCOPE_SELECTOR, ident);
  1398. }
  1399. return {
  1400. type: 'PseudoClass',
  1401. info: info,
  1402. name: ident.name
  1403. };
  1404. }
  1405. // ws
  1406. function getS() {
  1407. var node = {
  1408. type: 'Space'
  1409. // value: scanner.token.value
  1410. };
  1411. scanner.next();
  1412. return node;
  1413. }
  1414. function readSC() {
  1415. // var nodes = [];
  1416. scan:
  1417. while (scanner.token !== null) {
  1418. switch (scanner.token.type) {
  1419. case TokenType.Space:
  1420. scanner.next();
  1421. // nodes.push(getS());
  1422. break;
  1423. case TokenType.Comment:
  1424. scanner.next();
  1425. // nodes.push(getComment());
  1426. break;
  1427. default:
  1428. break scan;
  1429. }
  1430. }
  1431. return null;
  1432. // return nodes.length ? new List(nodes) : null;
  1433. }
  1434. // node: String
  1435. function getString() {
  1436. var node = {
  1437. type: 'String',
  1438. info: getInfo(),
  1439. value: scanner.token.value
  1440. };
  1441. scanner.next();
  1442. return node;
  1443. }
  1444. // # ident
  1445. function getVhash() {
  1446. var info = getInfo();
  1447. var value;
  1448. eat(TokenType.NumberSign);
  1449. expectAny('Number or identifier',
  1450. TokenType.DecimalNumber,
  1451. TokenType.Identifier
  1452. );
  1453. value = scanner.token.value;
  1454. if (scanner.token.type === TokenType.DecimalNumber &&
  1455. scanner.lookupType(1, TokenType.Identifier)) {
  1456. scanner.next();
  1457. value += scanner.token.value;
  1458. }
  1459. scanner.next();
  1460. return {
  1461. type: 'Hash',
  1462. info: info,
  1463. value: value
  1464. };
  1465. }
  1466. module.exports = function parse(source, options) {
  1467. var ast;
  1468. if (!options || typeof options !== 'object') {
  1469. options = {};
  1470. }
  1471. var context = options.context || 'stylesheet';
  1472. needPositions = Boolean(options.positions);
  1473. filename = options.filename || '<unknown>';
  1474. if (!initialContext.hasOwnProperty(context)) {
  1475. throw new Error('Unknown context `' + context + '`');
  1476. }
  1477. scanner = new Scanner(source, blockMode.hasOwnProperty(context), options.line, options.column);
  1478. scanner.next();
  1479. ast = initialContext[context]();
  1480. scanner = null;
  1481. // console.log(JSON.stringify(ast, null, 4));
  1482. return ast;
  1483. };