/cssed-0.4.0/libcroco/parser/cr-parser.c

# · C · 4469 lines · 3249 code · 592 blank · 628 comment · 788 complexity · f6f52a8ea2487ef05af5aa9724643aaa MD5 · raw file

Large files are truncated click here to view the full file

  1. /* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
  2. /*
  3. * This file is part of The Croco Library
  4. *
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of version 2.1 of the
  8. * GNU Lesser General Public
  9. * License as published by the Free Software Foundation.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  19. * USA
  20. *
  21. * Author: Dodji Seketeli
  22. * See COPYRIGHTS file for copyrights information.
  23. */
  24. /**
  25. *@file
  26. *The definition of the #CRParser class.
  27. */
  28. #include "string.h"
  29. #include "cr-parser.h"
  30. #include "cr-num.h"
  31. #include "cr-term.h"
  32. #include "cr-simple-sel.h"
  33. #include "cr-attr-sel.h"
  34. /*
  35. *Random notes:
  36. *CSS core syntax vs CSS level 2 syntax
  37. *=====================================
  38. *
  39. *One must keep in mind
  40. *that css UA must comply with two syntax.
  41. *
  42. *1/the specific syntax that defines the css language
  43. *for a given level of specificatin (e.g css2 syntax
  44. *defined in appendix D.1 of the css2 spec)
  45. *
  46. *2/the core (general) syntax that is there to allow
  47. *UAs to parse style sheets written in levels of CSS that
  48. *didn't exist at the time the UAs were created.
  49. *
  50. *the name of parsing functions (or methods) contained in this file
  51. *follows the following scheme: cr_parser_parse_<production_name> (...) ;
  52. *where <production_name> is the name
  53. *of a production of the css2 language.
  54. *When a given production is
  55. *defined by the css2 level grammar *and* by the
  56. *css core syntax, there will be two functions to parse that production:
  57. *one will parse the production defined by the css2 level grammar and the
  58. *other will parse the production defined by the css core grammar.
  59. *The css2 level grammar related parsing function will be called:
  60. *cr_parser_parse_<production_name> (...) ;
  61. *Then css core grammar related parsing function will be called:
  62. *cr_parser_parse_<production_name>_core (...) ;
  63. *
  64. *If a production is defined only by the css core grammar, then
  65. *it will be named:
  66. *cr_parser_parse_<production_name>_core (...) ;
  67. */
  68. /* moved _CRParserError struct to make it public and not opaque
  69. modified by Iago Rubio to cssed */
  70. /**
  71. *An abstraction of an error reported by by the
  72. *parsing routines.
  73. */
  74. enum CRParserState {
  75. READY_STATE = 0,
  76. TRY_PARSE_CHARSET_STATE,
  77. CHARSET_PARSED_STATE,
  78. TRY_PARSE_IMPORT_STATE,
  79. IMPORT_PARSED_STATE,
  80. TRY_PARSE_RULESET_STATE,
  81. RULESET_PARSED_STATE,
  82. TRY_PARSE_MEDIA_STATE,
  83. MEDIA_PARSED_STATE,
  84. TRY_PARSE_PAGE_STATE,
  85. PAGE_PARSED_STATE,
  86. TRY_PARSE_FONT_FACE_STATE,
  87. FONT_FACE_PARSED_STATE
  88. } ;
  89. /**
  90. *The private attributes of
  91. *#CRParser.
  92. */
  93. struct _CRParserPriv {
  94. /**
  95. *The tokenizer
  96. */
  97. CRTknzr *tknzr;
  98. /**
  99. *The sac handlers to call
  100. *to notify the parsing of
  101. *the css2 constructions.
  102. */
  103. CRDocHandler *sac_handler;
  104. /**
  105. *A stack of errors reported
  106. *by the parsing routines.
  107. *Contains instance of #CRParserError.
  108. *This pointer is the top of the stack.
  109. */
  110. GList *err_stack;
  111. enum CRParserState state;
  112. gboolean resolve_import;
  113. gboolean is_case_sensitive;
  114. gboolean use_core_grammar;
  115. };
  116. #define PRIVATE(obj) ((obj)->priv)
  117. #define CHARS_TAB_SIZE 12
  118. /**
  119. *return TRUE if the character is a number ([0-9]), FALSE otherwise
  120. *@param a_char the char to test.
  121. */
  122. #define IS_NUM(a_char) (((a_char) >= '0' && (a_char) <= '9')?TRUE:FALSE)
  123. /**
  124. *Checks if 'status' equals CR_OK. If not, goto the 'error' label.
  125. *
  126. *@param status the status (of type enum CRStatus) to test.
  127. *@param is_exception if set to FALSE, the final status returned
  128. *by the current function will be CR_PARSING_ERROR. If set to TRUE, the
  129. *current status will be the current value of the 'status' variable.
  130. *
  131. */
  132. #define CHECK_PARSING_STATUS(status, is_exception) \
  133. if ((status) != CR_OK) \
  134. { \
  135. if (is_exception == FALSE) \
  136. { \
  137. status = CR_PARSING_ERROR ; \
  138. } \
  139. goto error ; \
  140. }
  141. /**
  142. *same as CHECK_PARSING_STATUS() but this one pushes an error
  143. *on the parser error stack when an error arises.
  144. *@param a_this the current instance of #CRParser .
  145. *@param a_status the status to check. Is of type enum #CRStatus.
  146. *@param a_is_exception in case of error, if is TRUE, the status
  147. *is set to CR_PARSING_ERROR before goto error. If is false, the
  148. *real low level status is kept and will be returned by the
  149. *upper level function that called this macro. Usally,this must
  150. *be set to FALSE.
  151. *
  152. */
  153. #define CHECK_PARSING_STATUS_ERR(a_this, a_status, a_is_exception,\
  154. a_err_msg, a_err_status) \
  155. if ((a_status) != CR_OK) \
  156. { \
  157. if (a_is_exception == FALSE) a_status = CR_PARSING_ERROR ; \
  158. cr_parser_push_error (a_this, a_err_msg, a_err_status) ; \
  159. goto error ; \
  160. }
  161. /**
  162. *Peeks the next char from the input stream of the current parser
  163. *by invoking cr_tknzr_input_peek_char().
  164. *invokes CHECK_PARSING_STATUS on the status returned by
  165. *cr_tknzr_peek_char().
  166. *
  167. *@param a_this the current instance of #CRParser.
  168. *@param a_to_char a pointer to the char where to store the
  169. *char peeked.
  170. */
  171. #define PEEK_NEXT_CHAR(a_this, a_to_char) \
  172. {\
  173. enum CRStatus status ; \
  174. status = cr_tknzr_peek_char (PRIVATE (a_this)->tknzr, a_to_char) ; \
  175. CHECK_PARSING_STATUS (status, TRUE) \
  176. }
  177. /**
  178. *Reads the next char from the input stream of the current parser.
  179. *In case of error, jumps to the "error:" label located in the
  180. *function where this macro is called.
  181. *@param a_this the curent instance of #CRParser
  182. *@param to_char a pointer to the guint32 char where to store
  183. *the character read.
  184. */
  185. #define READ_NEXT_CHAR(a_this, a_to_char) \
  186. status = cr_tknzr_read_char (PRIVATE (a_this)->tknzr, a_to_char) ; \
  187. CHECK_PARSING_STATUS (status, TRUE)
  188. /**
  189. *Gets information about the current position in
  190. *the input of the parser.
  191. *In case of failure, this macro returns from the
  192. *calling function and
  193. *returns a status code of type enum #CRStatus.
  194. *@param a_this the current instance of #CRParser.
  195. *@param a_pos out parameter. A pointer to the position
  196. *inside the current parser input. Must
  197. */
  198. #define RECORD_INITIAL_POS(a_this, a_pos) \
  199. status = cr_tknzr_get_cur_pos (PRIVATE \
  200. (a_this)->tknzr, a_pos) ; \
  201. g_return_val_if_fail (status == CR_OK, status)
  202. /**
  203. *Gets the address of the current byte inside the
  204. *parser input.
  205. *@param parser the current instance of #CRParser.
  206. *@param addr out parameter a pointer (guchar*)
  207. *to where the address must be put.
  208. */
  209. #define RECORD_CUR_BYTE_ADDR(a_this, a_addr) \
  210. status = cr_tknzr_get_cur_byte_addr \
  211. (PRIVATE (a_this)->tknzr, a_addr) ; \
  212. CHECK_PARSING_STATUS (status, TRUE)
  213. /**
  214. *Peeks a byte from the topmost parser input at
  215. *a given offset from the current position.
  216. *If it fails, goto the "error:" label.
  217. *
  218. *@param a_parser the current instance of #CRParser.
  219. *@param a_offset the offset of the byte to peek, the
  220. *current byte having the offset '0'.
  221. *@param a_byte_ptr out parameter a pointer (guchar*) to
  222. *where the peeked char is to be stored.
  223. */
  224. #define PEEK_BYTE(a_parser, a_offset, a_byte_ptr) \
  225. status = cr_tknzr_peek_byte (PRIVATE (a_this)->tknzr, \
  226. a_offset, \
  227. a_byte_ptr) ; \
  228. CHECK_PARSING_STATUS (status, TRUE) ;
  229. #define BYTE(a_parser, a_offset, a_eof) \
  230. cr_tknzr_peek_byte2 (PRIVATE (a_this)->tknzr, a_offset, a_eof)
  231. /**
  232. *Reads a byte from the topmost parser input
  233. *steam.
  234. *If it fails, goto the "error" label.
  235. *@param a_this the current instance of #CRParser.
  236. *@param a_byte_ptr the guchar * where to put the read char.
  237. */
  238. #define READ_NEXT_BYTE(a_this, a_byte_ptr) \
  239. status = cr_tknzr_read_byte (PRIVATE (a_this)->tknzr, a_byte_ptr) ; \
  240. CHECK_PARSING_STATUS (status, TRUE) ;
  241. /**
  242. *Skips a given number of byte in the topmost
  243. *parser input. Don't update line and column number.
  244. *In case of error, jumps to the "error:" label
  245. *of the surrounding function.
  246. *@param a_parser the current instance of #CRParser.
  247. *@param a_nb_bytes the number of bytes to skip.
  248. */
  249. #define SKIP_BYTES(a_this, a_nb_bytes) \
  250. status = cr_tknzr_seek_index (PRIVATE (a_this)->tknzr, \
  251. CR_SEEK_CUR, a_nb_bytes) ; \
  252. CHECK_PARSING_STATUS (status, TRUE) ;
  253. /**
  254. *Skip utf8 encoded characters.
  255. *Updates line and column numbers.
  256. *@param a_parser the current instance of #CRParser.
  257. *@param a_nb_chars the number of chars to skip. Must be of
  258. *type glong.
  259. */
  260. #define SKIP_CHARS(a_parser, a_nb_chars) \
  261. { \
  262. glong nb_chars = a_nb_chars ; \
  263. status = cr_tknzr_consume_chars \
  264. (PRIVATE (a_parser)->tknzr,0, &nb_chars) ; \
  265. CHECK_PARSING_STATUS (status, TRUE) ; \
  266. }
  267. /**
  268. *Tests the condition and if it is false, sets
  269. *status to "CR_PARSING_ERROR" and goto the 'error'
  270. *label.
  271. *@param condition the condition to test.
  272. */
  273. #define ENSURE_PARSING_COND(condition) \
  274. if (! (condition)) {status = CR_PARSING_ERROR; goto error ;}
  275. #define ENSURE_PARSING_COND_ERR(a_this, a_condition, \
  276. a_err_msg, a_err_status) \
  277. if (! (a_condition)) \
  278. { \
  279. status = CR_PARSING_ERROR; \
  280. cr_parser_push_error (a_this, a_err_msg, a_err_status) ; \
  281. goto error ; \
  282. }
  283. #define GET_NEXT_TOKEN(a_this, a_token_ptr) \
  284. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, \
  285. a_token_ptr) ; \
  286. ENSURE_PARSING_COND (status == CR_OK) ;
  287. #ifdef WITH_UNICODE_ESCAPE_AND_RANGE
  288. static enum CRStatus cr_parser_parse_unicode_escape (CRParser * a_this,
  289. guint32 * a_unicode);
  290. static enum CRStatus cr_parser_parse_escape (CRParser * a_this,
  291. guint32 * a_esc_code);
  292. static enum CRStatus cr_parser_parse_unicode_range (CRParser * a_this,
  293. CRString ** a_inf,
  294. CRString ** a_sup);
  295. #endif
  296. static enum CRStatus cr_parser_parse_stylesheet_core (CRParser * a_this);
  297. static enum CRStatus cr_parser_parse_atrule_core (CRParser * a_this);
  298. static enum CRStatus cr_parser_parse_ruleset_core (CRParser * a_this);
  299. static enum CRStatus cr_parser_parse_selector_core (CRParser * a_this);
  300. static enum CRStatus cr_parser_parse_declaration_core (CRParser * a_this);
  301. static enum CRStatus cr_parser_parse_any_core (CRParser * a_this);
  302. static enum CRStatus cr_parser_parse_block_core (CRParser * a_this);
  303. static enum CRStatus cr_parser_parse_value_core (CRParser * a_this);
  304. static enum CRStatus cr_parser_parse_string (CRParser * a_this,
  305. CRString ** a_str);
  306. static enum CRStatus cr_parser_parse_ident (CRParser * a_this,
  307. CRString ** a_str);
  308. static enum CRStatus cr_parser_parse_uri (CRParser * a_this,
  309. CRString ** a_str);
  310. static enum CRStatus cr_parser_parse_function (CRParser * a_this,
  311. CRString ** a_func_name,
  312. CRTerm ** a_expr);
  313. static enum CRStatus cr_parser_parse_property (CRParser * a_this,
  314. CRString ** a_property);
  315. static enum CRStatus cr_parser_parse_attribute_selector (CRParser * a_this,
  316. CRAttrSel ** a_sel);
  317. static enum CRStatus cr_parser_parse_simple_selector (CRParser * a_this,
  318. CRSimpleSel ** a_sel);
  319. static enum CRStatus cr_parser_parse_simple_sels (CRParser * a_this,
  320. CRSimpleSel ** a_sel);
  321. static CRParserError *cr_parser_error_new (const guchar * a_msg,
  322. enum CRStatus);
  323. static void cr_parser_error_set_msg (CRParserError * a_this,
  324. const guchar * a_msg);
  325. static void cr_parser_error_dump (CRParserError * a_this);
  326. static void cr_parser_error_set_status (CRParserError * a_this,
  327. enum CRStatus a_status);
  328. static void cr_parser_error_set_pos (CRParserError * a_this,
  329. glong a_line,
  330. glong a_column, glong a_byte_num);
  331. static void
  332. cr_parser_error_destroy (CRParserError * a_this);
  333. static enum CRStatus cr_parser_push_error (CRParser * a_this,
  334. const guchar * a_msg,
  335. enum CRStatus a_status);
  336. static enum CRStatus cr_parser_dump_err_stack (CRParser * a_this,
  337. gboolean a_clear_errs);
  338. static enum CRStatus
  339. cr_parser_clear_errors (CRParser * a_this);
  340. /* **********************************************************
  341. Added by Iago Rubio to use it in cssed as syntax validator
  342. ********************************************************** */
  343. GList*
  344. cr_parser_get_error_stack(CRParser *a_this)
  345. {
  346. GList *stack ;
  347. stack = g_list_copy(PRIVATE(a_this)->err_stack);
  348. return stack;
  349. }
  350. gulong
  351. cr_parser_get_pos(CRParser *a_this)
  352. {
  353. return cr_tknzr_get_cur_line(PRIVATE(a_this)->tknzr);
  354. }
  355. /*****************************
  356. * end iago's changes
  357. *****************************/
  358. /*****************************
  359. *error managemet methods
  360. *****************************/
  361. /**
  362. *Constructor of #CRParserError class.
  363. *@param a_msg the brute error message.
  364. *@param a_status the error status.
  365. *@return the newly built instance of #CRParserError.
  366. */
  367. static CRParserError *
  368. cr_parser_error_new (const guchar * a_msg, enum CRStatus a_status)
  369. {
  370. CRParserError *result = NULL;
  371. result = g_try_malloc (sizeof (CRParserError));
  372. if (result == NULL) {
  373. cr_utils_trace_info ("Out of memory");
  374. return NULL;
  375. }
  376. memset (result, 0, sizeof (CRParserError));
  377. cr_parser_error_set_msg (result, a_msg);
  378. cr_parser_error_set_status (result, a_status);
  379. return result;
  380. }
  381. /**
  382. *Sets the message associated to this instance of #CRError.
  383. *@param a_this the current instance of #CRParserError.
  384. *@param a_msg the new message.
  385. */
  386. static void
  387. cr_parser_error_set_msg (CRParserError * a_this, const guchar * a_msg)
  388. {
  389. g_return_if_fail (a_this);
  390. if (a_this->msg) {
  391. g_free (a_this->msg);
  392. }
  393. a_this->msg = g_strdup (a_msg);
  394. }
  395. /**
  396. *Sets the error status.
  397. *@param a_this the current instance of #CRParserError.
  398. *@param a_status the new error status.
  399. *
  400. */
  401. static void
  402. cr_parser_error_set_status (CRParserError * a_this, enum CRStatus a_status)
  403. {
  404. g_return_if_fail (a_this);
  405. a_this->status = a_status;
  406. }
  407. /**
  408. *Sets the position of the parser error.
  409. *@param a_this the current instance of #CRParserError.
  410. *@param a_line the line number.
  411. *@param a_column the column number.
  412. *@param a_byte_num the byte number.
  413. */
  414. static void
  415. cr_parser_error_set_pos (CRParserError * a_this,
  416. glong a_line, glong a_column, glong a_byte_num)
  417. {
  418. g_return_if_fail (a_this);
  419. a_this->line = a_line;
  420. a_this->column = a_column;
  421. a_this->byte_num = a_byte_num;
  422. }
  423. static void
  424. cr_parser_error_dump (CRParserError * a_this)
  425. {
  426. g_return_if_fail (a_this);
  427. g_printerr ("parsing error: %ld:%ld:", a_this->line, a_this->column);
  428. g_printerr ("%s\n", a_this->msg);
  429. }
  430. /**
  431. *The destructor of #CRParserError.
  432. *@param a_this the current instance of #CRParserError.
  433. */
  434. static void
  435. cr_parser_error_destroy (CRParserError * a_this)
  436. {
  437. g_return_if_fail (a_this);
  438. if (a_this->msg) {
  439. g_free (a_this->msg);
  440. a_this->msg = NULL;
  441. }
  442. g_free (a_this);
  443. }
  444. /**
  445. *Pushes an error on the parser error stack.
  446. *@param a_this the current instance of #CRParser.
  447. *@param a_msg the error message.
  448. *@param a_status the error status.
  449. *@return CR_OK upon successfull completion, an error code otherwise.
  450. */
  451. static enum CRStatus
  452. cr_parser_push_error (CRParser * a_this,
  453. const guchar * a_msg, enum CRStatus a_status)
  454. {
  455. enum CRStatus status = CR_OK;
  456. CRParserError *error = NULL;
  457. CRInputPos pos;
  458. g_return_val_if_fail (a_this && PRIVATE (a_this)
  459. && a_msg, CR_BAD_PARAM_ERROR);
  460. error = cr_parser_error_new (a_msg, a_status);
  461. g_return_val_if_fail (error, CR_ERROR);
  462. RECORD_INITIAL_POS (a_this, &pos);
  463. cr_parser_error_set_pos
  464. (error, pos.line, pos.col, pos.next_byte_index - 1);
  465. PRIVATE (a_this)->err_stack =
  466. g_list_prepend (PRIVATE (a_this)->err_stack, error);
  467. if (PRIVATE (a_this)->err_stack == NULL)
  468. goto error;
  469. return CR_OK;
  470. error:
  471. if (error) {
  472. cr_parser_error_destroy (error);
  473. error = NULL;
  474. }
  475. return status;
  476. }
  477. /**
  478. *Dumps the error stack on stdout.
  479. *@param a_this the current instance of #CRParser.
  480. *@param a_clear_errs whether to clear the error stack
  481. *after the dump or not.
  482. *@return CR_OK upon successfull completion, an error code
  483. *otherwise.
  484. */
  485. static enum CRStatus
  486. cr_parser_dump_err_stack (CRParser * a_this, gboolean a_clear_errs)
  487. {
  488. GList *cur = NULL;
  489. g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
  490. if (PRIVATE (a_this)->err_stack == NULL)
  491. return CR_OK;
  492. for (cur = PRIVATE (a_this)->err_stack; cur; cur = cur->next) {
  493. cr_parser_error_dump ((CRParserError *) cur->data);
  494. }
  495. if (a_clear_errs == TRUE) {
  496. cr_parser_clear_errors (a_this);
  497. }
  498. return CR_OK;
  499. }
  500. /**
  501. *Clears all the errors contained in the parser error stack.
  502. *Frees all the errors, and the stack that contains'em.
  503. *@param a_this the current instance of #CRParser.
  504. */
  505. static enum CRStatus
  506. cr_parser_clear_errors (CRParser * a_this)
  507. {
  508. GList *cur = NULL;
  509. g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
  510. for (cur = PRIVATE (a_this)->err_stack; cur; cur = cur->next) {
  511. if (cur->data) {
  512. cr_parser_error_destroy ((CRParserError *)
  513. cur->data);
  514. }
  515. }
  516. if (PRIVATE (a_this)->err_stack) {
  517. g_list_free (PRIVATE (a_this)->err_stack);
  518. PRIVATE (a_this)->err_stack = NULL;
  519. }
  520. return CR_OK;
  521. }
  522. /**
  523. *Same as cr_parser_try_to_skip_spaces() but this one skips
  524. *spaces and comments.
  525. *
  526. *@param a_this the current instance of #CRParser.
  527. *@return CR_OK upon successfull completion, an error code otherwise.
  528. */
  529. enum CRStatus
  530. cr_parser_try_to_skip_spaces_and_comments (CRParser * a_this)
  531. {
  532. enum CRStatus status = CR_ERROR;
  533. CRToken *token = NULL;
  534. g_return_val_if_fail (a_this && PRIVATE (a_this)
  535. && PRIVATE (a_this)->tknzr, CR_BAD_PARAM_ERROR);
  536. do {
  537. if (token) {
  538. cr_token_destroy (token);
  539. token = NULL;
  540. }
  541. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
  542. &token);
  543. if (status != CR_OK)
  544. goto error;
  545. }
  546. while ((token != NULL)
  547. && (token->type == COMMENT_TK || token->type == S_TK));
  548. cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
  549. return status;
  550. error:
  551. if (token) {
  552. cr_token_destroy (token);
  553. token = NULL;
  554. }
  555. return status;
  556. }
  557. /***************************************
  558. *End of Parser input handling routines
  559. ***************************************/
  560. /*************************************
  561. *Non trivial terminal productions
  562. *parsing routines
  563. *************************************/
  564. /**
  565. *Parses a css stylesheet following the core css grammar.
  566. *This is mainly done for test purposes.
  567. *During the parsing, no callback is called. This is just
  568. *to validate that the stylesheet is well formed according to the
  569. *css core syntax.
  570. *stylesheet : [ CDO | CDC | S | statement ]*;
  571. *@param a_this the current instance of #CRParser.
  572. *@return CR_OK upon successfull completion, an error code otherwise.
  573. */
  574. static enum CRStatus
  575. cr_parser_parse_stylesheet_core (CRParser * a_this)
  576. {
  577. CRToken *token = NULL;
  578. CRInputPos init_pos;
  579. enum CRStatus status = CR_ERROR;
  580. g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
  581. RECORD_INITIAL_POS (a_this, &init_pos);
  582. continue_parsing:
  583. if (token) {
  584. cr_token_destroy (token);
  585. token = NULL;
  586. }
  587. cr_parser_try_to_skip_spaces_and_comments (a_this);
  588. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
  589. if (status == CR_END_OF_INPUT_ERROR) {
  590. status = CR_OK;
  591. goto done;
  592. } else if (status != CR_OK) {
  593. goto error;
  594. }
  595. switch (token->type) {
  596. case CDO_TK:
  597. case CDC_TK:
  598. goto continue_parsing;
  599. break;
  600. default:
  601. status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
  602. token);
  603. CHECK_PARSING_STATUS (status, TRUE);
  604. token = NULL;
  605. status = cr_parser_parse_statement_core (a_this);
  606. cr_parser_clear_errors (a_this);
  607. if (status == CR_OK) {
  608. goto continue_parsing;
  609. } else if (status == CR_END_OF_INPUT_ERROR) {
  610. goto done;
  611. } else {
  612. goto error;
  613. }
  614. }
  615. done:
  616. if (token) {
  617. cr_token_destroy (token);
  618. token = NULL;
  619. }
  620. cr_parser_clear_errors (a_this);
  621. return CR_OK;
  622. error:
  623. cr_parser_push_error
  624. (a_this, "could not recognize next production", CR_ERROR);
  625. //cr_parser_dump_err_stack (a_this, TRUE);
  626. if (token) {
  627. cr_token_destroy (token);
  628. token = NULL;
  629. }
  630. cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
  631. return status;
  632. }
  633. /**
  634. *Parses an at-rule as defined by the css core grammar
  635. *in chapter 4.1 in the css2 spec.
  636. *at-rule : ATKEYWORD S* any* [ block | ';' S* ];
  637. *@param a_this the current instance of #CRParser.
  638. *@return CR_OK upon successfull completion, an error code
  639. *otherwise.
  640. */
  641. static enum CRStatus
  642. cr_parser_parse_atrule_core (CRParser * a_this)
  643. {
  644. CRToken *token = NULL;
  645. CRInputPos init_pos;
  646. enum CRStatus status = CR_ERROR;
  647. g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
  648. RECORD_INITIAL_POS (a_this, &init_pos);
  649. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
  650. &token);
  651. ENSURE_PARSING_COND (status == CR_OK
  652. && token
  653. &&
  654. (token->type == ATKEYWORD_TK
  655. || token->type == IMPORT_SYM_TK
  656. || token->type == PAGE_SYM_TK
  657. || token->type == MEDIA_SYM_TK
  658. || token->type == FONT_FACE_SYM_TK
  659. || token->type == CHARSET_SYM_TK));
  660. cr_token_destroy (token);
  661. token = NULL;
  662. cr_parser_try_to_skip_spaces_and_comments (a_this);
  663. do {
  664. status = cr_parser_parse_any_core (a_this);
  665. } while (status == CR_OK);
  666. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
  667. &token);
  668. ENSURE_PARSING_COND (status == CR_OK && token);
  669. if (token->type == CBO_TK) {
  670. cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
  671. token);
  672. token = NULL;
  673. status = cr_parser_parse_block_core (a_this);
  674. CHECK_PARSING_STATUS (status,
  675. FALSE);
  676. goto done;
  677. } else if (token->type == SEMICOLON_TK) {
  678. goto done;
  679. } else {
  680. status = CR_PARSING_ERROR ;
  681. goto error;
  682. }
  683. done:
  684. if (token) {
  685. cr_token_destroy (token);
  686. token = NULL;
  687. }
  688. return CR_OK;
  689. error:
  690. if (token) {
  691. cr_token_destroy (token);
  692. token = NULL;
  693. }
  694. cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr,
  695. &init_pos);
  696. return status;
  697. }
  698. /**
  699. *Parses a ruleset as defined by the css core grammar in chapter
  700. *4.1 of the css2 spec.
  701. *ruleset ::= selector? '{' S* declaration? [ ';' S* declaration? ]* '}' S*;
  702. *@param a_this the current instance of #CRParser.
  703. *@return CR_OK upon successfull completion, an error code otherwise.
  704. */
  705. static enum CRStatus
  706. cr_parser_parse_ruleset_core (CRParser * a_this)
  707. {
  708. CRToken *token = NULL;
  709. CRInputPos init_pos;
  710. enum CRStatus status = CR_ERROR;
  711. g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
  712. RECORD_INITIAL_POS (a_this, &init_pos);
  713. status = cr_parser_parse_selector_core (a_this);
  714. ENSURE_PARSING_COND (status == CR_OK
  715. || status == CR_PARSING_ERROR
  716. || status == CR_END_OF_INPUT_ERROR);
  717. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
  718. ENSURE_PARSING_COND (status == CR_OK && token
  719. && token->type == CBO_TK);
  720. cr_token_destroy (token);
  721. token = NULL;
  722. cr_parser_try_to_skip_spaces_and_comments (a_this);
  723. status = cr_parser_parse_declaration_core (a_this);
  724. parse_declaration_list:
  725. if (token) {
  726. cr_token_destroy (token);
  727. token = NULL;
  728. }
  729. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
  730. ENSURE_PARSING_COND (status == CR_OK && token);
  731. if (token->type == CBC_TK) {
  732. goto done;
  733. }
  734. ENSURE_PARSING_COND (status == CR_OK
  735. && token && token->type == SEMICOLON_TK);
  736. cr_token_destroy (token);
  737. token = NULL;
  738. cr_parser_try_to_skip_spaces_and_comments (a_this);
  739. status = cr_parser_parse_declaration_core (a_this);
  740. cr_parser_clear_errors (a_this);
  741. ENSURE_PARSING_COND (status == CR_OK || status == CR_PARSING_ERROR);
  742. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
  743. ENSURE_PARSING_COND (status == CR_OK && token);
  744. if (token->type == CBC_TK) {
  745. cr_token_destroy (token);
  746. token = NULL;
  747. cr_parser_try_to_skip_spaces_and_comments (a_this);
  748. goto done;
  749. } else {
  750. status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
  751. token);
  752. token = NULL;
  753. goto parse_declaration_list;
  754. }
  755. done:
  756. if (token) {
  757. cr_token_destroy (token);
  758. token = NULL;
  759. }
  760. if (status == CR_OK) {
  761. return CR_OK;
  762. }
  763. error:
  764. if (token) {
  765. cr_token_destroy (token);
  766. token = NULL;
  767. }
  768. cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
  769. return status;
  770. }
  771. /**
  772. *Parses a "selector" as specified by the css core
  773. *grammar.
  774. *selector : any+;
  775. *@param a_this the current instance of #CRParser.
  776. *@return CR_OK upon successfull completion, an error code
  777. *otherwise.
  778. */
  779. static enum CRStatus
  780. cr_parser_parse_selector_core (CRParser * a_this)
  781. {
  782. CRToken *token = NULL;
  783. CRInputPos init_pos;
  784. enum CRStatus status = CR_ERROR;
  785. g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
  786. RECORD_INITIAL_POS (a_this, &init_pos);
  787. status = cr_parser_parse_any_core (a_this);
  788. CHECK_PARSING_STATUS (status, FALSE);
  789. do {
  790. status = cr_parser_parse_any_core (a_this);
  791. } while (status == CR_OK);
  792. return CR_OK;
  793. error:
  794. if (token) {
  795. cr_token_destroy (token);
  796. token = NULL;
  797. }
  798. cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
  799. return status;
  800. }
  801. /**
  802. *Parses a "block" as defined in the css core grammar
  803. *in chapter 4.1 of the css2 spec.
  804. *block ::= '{' S* [ any | block | ATKEYWORD S* | ';' ]* '}' S*;
  805. *@param a_this the current instance of #CRParser.
  806. *FIXME: code this function.
  807. */
  808. static enum CRStatus
  809. cr_parser_parse_block_core (CRParser * a_this)
  810. {
  811. CRToken *token = NULL;
  812. CRInputPos init_pos;
  813. enum CRStatus status = CR_ERROR;
  814. g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
  815. RECORD_INITIAL_POS (a_this, &init_pos);
  816. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
  817. ENSURE_PARSING_COND (status == CR_OK && token
  818. && token->type == CBO_TK);
  819. parse_block_content:
  820. if (token) {
  821. cr_token_destroy (token);
  822. token = NULL;
  823. }
  824. cr_parser_try_to_skip_spaces_and_comments (a_this);
  825. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
  826. ENSURE_PARSING_COND (status == CR_OK && token);
  827. if (token->type == CBC_TK) {
  828. cr_parser_try_to_skip_spaces_and_comments (a_this);
  829. goto done;
  830. } else if (token->type == SEMICOLON_TK) {
  831. goto parse_block_content;
  832. } else if (token->type == ATKEYWORD_TK) {
  833. cr_parser_try_to_skip_spaces_and_comments (a_this);
  834. goto parse_block_content;
  835. } else if (token->type == CBO_TK) {
  836. cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
  837. token = NULL;
  838. status = cr_parser_parse_block_core (a_this);
  839. CHECK_PARSING_STATUS (status, FALSE);
  840. goto parse_block_content;
  841. } else {
  842. cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token);
  843. token = NULL;
  844. status = cr_parser_parse_any_core (a_this);
  845. CHECK_PARSING_STATUS (status, FALSE);
  846. goto parse_block_content;
  847. }
  848. done:
  849. if (token) {
  850. cr_token_destroy (token);
  851. token = NULL;
  852. }
  853. if (status == CR_OK)
  854. return CR_OK;
  855. error:
  856. if (token) {
  857. cr_token_destroy (token);
  858. token = NULL;
  859. }
  860. cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
  861. return status;
  862. }
  863. static enum CRStatus
  864. cr_parser_parse_declaration_core (CRParser * a_this)
  865. {
  866. CRToken *token = NULL;
  867. CRInputPos init_pos;
  868. enum CRStatus status = CR_ERROR;
  869. CRString *prop = NULL;
  870. g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
  871. RECORD_INITIAL_POS (a_this, &init_pos);
  872. status = cr_parser_parse_property (a_this, &prop);
  873. CHECK_PARSING_STATUS (status, FALSE);
  874. cr_parser_clear_errors (a_this);
  875. ENSURE_PARSING_COND (status == CR_OK && prop);
  876. cr_string_destroy (prop);
  877. prop = NULL;
  878. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
  879. ENSURE_PARSING_COND (status == CR_OK
  880. && token
  881. && token->type == DELIM_TK
  882. && token->u.unichar == ':');
  883. cr_token_destroy (token);
  884. token = NULL;
  885. cr_parser_try_to_skip_spaces_and_comments (a_this);
  886. status = cr_parser_parse_value_core (a_this);
  887. CHECK_PARSING_STATUS (status, FALSE);
  888. return CR_OK;
  889. error:
  890. if (prop) {
  891. cr_string_destroy (prop);
  892. prop = NULL;
  893. }
  894. if (token) {
  895. cr_token_destroy (token);
  896. token = NULL;
  897. }
  898. cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
  899. return status;
  900. }
  901. /**
  902. *Parses a "value" production as defined by the css core grammar
  903. *in chapter 4.1.
  904. *value ::= [ any | block | ATKEYWORD S* ]+;
  905. *@param a_this the current instance of #CRParser.
  906. *@return CR_OK upon successfull completion, an error code otherwise.
  907. */
  908. static enum CRStatus
  909. cr_parser_parse_value_core (CRParser * a_this)
  910. {
  911. CRToken *token = NULL;
  912. CRInputPos init_pos;
  913. enum CRStatus status = CR_ERROR;
  914. glong ref = 0;
  915. g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
  916. RECORD_INITIAL_POS (a_this, &init_pos);
  917. continue_parsing:
  918. if (token) {
  919. cr_token_destroy (token);
  920. token = NULL;
  921. }
  922. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
  923. ENSURE_PARSING_COND (status == CR_OK && token);
  924. switch (token->type) {
  925. case CBO_TK:
  926. status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
  927. token);
  928. token = NULL;
  929. status = cr_parser_parse_block_core (a_this);
  930. CHECK_PARSING_STATUS (status, FALSE);
  931. ref++;
  932. goto continue_parsing;
  933. case ATKEYWORD_TK:
  934. cr_parser_try_to_skip_spaces_and_comments (a_this);
  935. ref++;
  936. goto continue_parsing;
  937. default:
  938. status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
  939. token);
  940. token = NULL;
  941. status = cr_parser_parse_any_core (a_this);
  942. if (status == CR_OK) {
  943. ref++;
  944. goto continue_parsing;
  945. } else if (status == CR_PARSING_ERROR) {
  946. status = CR_OK;
  947. goto done;
  948. } else {
  949. goto error;
  950. }
  951. }
  952. done:
  953. if (token) {
  954. cr_token_destroy (token);
  955. token = NULL;
  956. }
  957. if (status == CR_OK && ref)
  958. return CR_OK;
  959. error:
  960. if (token) {
  961. cr_token_destroy (token);
  962. token = NULL;
  963. }
  964. cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
  965. return status;
  966. }
  967. /**
  968. *Parses an "any" as defined by the css core grammar in the
  969. *css2 spec in chapter 4.1.
  970. *any ::= [ IDENT | NUMBER | PERCENTAGE | DIMENSION | STRING
  971. * | DELIM | URI | HASH | UNICODE-RANGE | INCLUDES
  972. * | FUNCTION | DASHMATCH | '(' any* ')' | '[' any* ']' ] S*;
  973. *
  974. *@param a_this the current instance of #CRParser.
  975. *@return CR_OK upon successfull completion, an error code otherwise.
  976. */
  977. static enum CRStatus
  978. cr_parser_parse_any_core (CRParser * a_this)
  979. {
  980. CRToken *token1 = NULL,
  981. *token2 = NULL;
  982. CRInputPos init_pos;
  983. enum CRStatus status = CR_ERROR;
  984. g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR);
  985. RECORD_INITIAL_POS (a_this, &init_pos);
  986. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token1);
  987. ENSURE_PARSING_COND (status == CR_OK && token1);
  988. switch (token1->type) {
  989. case IDENT_TK:
  990. case NUMBER_TK:
  991. case RGB_TK:
  992. case PERCENTAGE_TK:
  993. case DIMEN_TK:
  994. case EMS_TK:
  995. case EXS_TK:
  996. case LENGTH_TK:
  997. case ANGLE_TK:
  998. case FREQ_TK:
  999. case TIME_TK:
  1000. case STRING_TK:
  1001. case DELIM_TK:
  1002. case URI_TK:
  1003. case HASH_TK:
  1004. case UNICODERANGE_TK:
  1005. case INCLUDES_TK:
  1006. case DASHMATCH_TK:
  1007. case S_TK:
  1008. case COMMENT_TK:
  1009. case IMPORTANT_SYM_TK:
  1010. status = CR_OK;
  1011. break;
  1012. case FUNCTION_TK:
  1013. /*
  1014. *this case isn't specified by the spec but it
  1015. *does happen. So we have to handle it.
  1016. *We must consider function with parameters.
  1017. *We consider parameter as being an "any*" production.
  1018. */
  1019. do {
  1020. status = cr_parser_parse_any_core (a_this);
  1021. } while (status == CR_OK);
  1022. ENSURE_PARSING_COND (status == CR_PARSING_ERROR);
  1023. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
  1024. &token2);
  1025. ENSURE_PARSING_COND (status == CR_OK
  1026. && token2 && token2->type == PC_TK);
  1027. break;
  1028. case PO_TK:
  1029. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
  1030. &token2);
  1031. ENSURE_PARSING_COND (status == CR_OK && token2);
  1032. if (token2->type == PC_TK) {
  1033. cr_token_destroy (token2);
  1034. token2 = NULL;
  1035. goto done;
  1036. } else {
  1037. status = cr_tknzr_unget_token
  1038. (PRIVATE (a_this)->tknzr, token2);
  1039. token2 = NULL;
  1040. }
  1041. do {
  1042. status = cr_parser_parse_any_core (a_this);
  1043. } while (status == CR_OK);
  1044. ENSURE_PARSING_COND (status == CR_PARSING_ERROR);
  1045. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
  1046. &token2);
  1047. ENSURE_PARSING_COND (status == CR_OK
  1048. && token2 && token2->type == PC_TK);
  1049. status = CR_OK;
  1050. break;
  1051. case BO_TK:
  1052. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
  1053. &token2);
  1054. ENSURE_PARSING_COND (status == CR_OK && token2);
  1055. if (token2->type == BC_TK) {
  1056. cr_token_destroy (token2);
  1057. token2 = NULL;
  1058. goto done;
  1059. } else {
  1060. status = cr_tknzr_unget_token
  1061. (PRIVATE (a_this)->tknzr, token2);
  1062. token2 = NULL;
  1063. }
  1064. do {
  1065. status = cr_parser_parse_any_core (a_this);
  1066. } while (status == CR_OK);
  1067. ENSURE_PARSING_COND (status == CR_PARSING_ERROR);
  1068. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
  1069. &token2);
  1070. ENSURE_PARSING_COND (status == CR_OK
  1071. && token2 && token2->type == BC_TK);
  1072. status = CR_OK;
  1073. break;
  1074. default:
  1075. status = CR_PARSING_ERROR;
  1076. goto error;
  1077. }
  1078. done:
  1079. if (token1) {
  1080. cr_token_destroy (token1);
  1081. token1 = NULL;
  1082. }
  1083. if (token2) {
  1084. cr_token_destroy (token2);
  1085. token2 = NULL;
  1086. }
  1087. return CR_OK;
  1088. error:
  1089. if (token1) {
  1090. cr_token_destroy (token1);
  1091. token1 = NULL;
  1092. }
  1093. if (token2) {
  1094. cr_token_destroy (token2);
  1095. token2 = NULL;
  1096. }
  1097. cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
  1098. return status;
  1099. }
  1100. /**
  1101. *Parses an attribute selector as defined in the css2 spec in
  1102. *appendix D.1:
  1103. *attrib ::= '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S*
  1104. * [ IDENT | STRING ] S* ]? ']'
  1105. *
  1106. *@param a_this the "this pointer" of the current instance of
  1107. *#CRParser .
  1108. *@param a_sel out parameter. The successfully parsed attribute selector.
  1109. *@return CR_OK upon successfull completion, an error code otherwise.
  1110. */
  1111. static enum CRStatus
  1112. cr_parser_parse_attribute_selector (CRParser * a_this,
  1113. CRAttrSel ** a_sel)
  1114. {
  1115. enum CRStatus status = CR_OK;
  1116. CRInputPos init_pos;
  1117. CRToken *token = NULL;
  1118. CRAttrSel *result = NULL;
  1119. CRParsingLocation location = {0} ;
  1120. g_return_val_if_fail (a_this && a_sel, CR_BAD_PARAM_ERROR);
  1121. RECORD_INITIAL_POS (a_this, &init_pos);
  1122. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
  1123. ENSURE_PARSING_COND (status == CR_OK && token
  1124. && token->type == BO_TK);
  1125. cr_parsing_location_copy
  1126. (&location, &token->location) ;
  1127. cr_token_destroy (token);
  1128. token = NULL;
  1129. cr_parser_try_to_skip_spaces_and_comments (a_this);
  1130. result = cr_attr_sel_new ();
  1131. if (!result) {
  1132. cr_utils_trace_info ("result failed") ;
  1133. status = CR_OUT_OF_MEMORY_ERROR ;
  1134. goto error ;
  1135. }
  1136. cr_parsing_location_copy (&result->location,
  1137. &location) ;
  1138. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
  1139. ENSURE_PARSING_COND (status == CR_OK
  1140. && token && token->type == IDENT_TK);
  1141. result->name = token->u.str;
  1142. token->u.str = NULL;
  1143. cr_token_destroy (token);
  1144. token = NULL;
  1145. cr_parser_try_to_skip_spaces_and_comments (a_this);
  1146. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
  1147. ENSURE_PARSING_COND (status == CR_OK && token);
  1148. if (token->type == INCLUDES_TK) {
  1149. result->match_way = INCLUDES;
  1150. goto parse_right_part;
  1151. } else if (token->type == DASHMATCH_TK) {
  1152. result->match_way = DASHMATCH;
  1153. goto parse_right_part;
  1154. } else if (token->type == DELIM_TK && token->u.unichar == '=') {
  1155. result->match_way = EQUALS;
  1156. goto parse_right_part;
  1157. } else if (token->type == BC_TK) {
  1158. result->match_way = SET;
  1159. goto done;
  1160. }
  1161. parse_right_part:
  1162. if (token) {
  1163. cr_token_destroy (token);
  1164. token = NULL;
  1165. }
  1166. cr_parser_try_to_skip_spaces_and_comments (a_this);
  1167. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
  1168. ENSURE_PARSING_COND (status == CR_OK && token);
  1169. if (token->type == IDENT_TK) {
  1170. result->value = token->u.str;
  1171. token->u.str = NULL;
  1172. } else if (token->type == STRING_TK) {
  1173. result->value = token->u.str;
  1174. token->u.str = NULL;
  1175. } else {
  1176. status = CR_PARSING_ERROR;
  1177. goto error;
  1178. }
  1179. if (token) {
  1180. cr_token_destroy (token);
  1181. token = NULL;
  1182. }
  1183. cr_parser_try_to_skip_spaces_and_comments (a_this);
  1184. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr, &token);
  1185. ENSURE_PARSING_COND (status == CR_OK && token
  1186. && token->type == BC_TK);
  1187. done:
  1188. if (token) {
  1189. cr_token_destroy (token);
  1190. token = NULL;
  1191. }
  1192. if (*a_sel) {
  1193. status = cr_attr_sel_append_attr_sel (*a_sel, result);
  1194. CHECK_PARSING_STATUS (status, FALSE);
  1195. } else {
  1196. *a_sel = result;
  1197. }
  1198. cr_parser_clear_errors (a_this);
  1199. return CR_OK;
  1200. error:
  1201. if (result) {
  1202. cr_attr_sel_destroy (result);
  1203. result = NULL;
  1204. }
  1205. if (token) {
  1206. cr_token_destroy (token);
  1207. token = NULL;
  1208. }
  1209. cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
  1210. return status;
  1211. }
  1212. /**
  1213. *Parses a "property" as specified by the css2 spec at [4.1.1]:
  1214. *property : IDENT S*;
  1215. *
  1216. *@param a_this the "this pointer" of the current instance of #CRParser.
  1217. *@param GString a_property out parameter. The parsed property without the
  1218. *trailing spaces. If *a_property is NULL, this function allocates a
  1219. *new instance of GString and set it content to the parsed property.
  1220. *If not, the property is just appended to a_property's previous content.
  1221. *In both cases, it is up to the caller to free a_property.
  1222. *@return CR_OK upon successfull completion, CR_PARSING_ERROR if the
  1223. *next construction was not a "property", or an error code.
  1224. */
  1225. static enum CRStatus
  1226. cr_parser_parse_property (CRParser * a_this,
  1227. CRString ** a_property)
  1228. {
  1229. enum CRStatus status = CR_OK;
  1230. CRInputPos init_pos;
  1231. g_return_val_if_fail (a_this && PRIVATE (a_this)
  1232. && PRIVATE (a_this)->tknzr
  1233. && a_property,
  1234. CR_BAD_PARAM_ERROR);
  1235. RECORD_INITIAL_POS (a_this, &init_pos);
  1236. status = cr_parser_parse_ident (a_this, a_property);
  1237. CHECK_PARSING_STATUS (status, TRUE);
  1238. cr_parser_try_to_skip_spaces_and_comments (a_this);
  1239. cr_parser_clear_errors (a_this);
  1240. return CR_OK;
  1241. error:
  1242. cr_tknzr_set_cur_pos (PRIVATE (a_this)->tknzr, &init_pos);
  1243. return status;
  1244. }
  1245. /**
  1246. *Parses a "term" as defined in the css2 spec, appendix D.1:
  1247. *term ::= unary_operator? [NUMBER S* | PERCENTAGE S* | LENGTH S* |
  1248. *EMS S* | EXS S* | ANGLE S* | TIME S* | FREQ S* | function ] |
  1249. *STRING S* | IDENT S* | URI S* | RGB S* | UNICODERANGE S* | hexcolor
  1250. *
  1251. *TODO: handle parsing of 'RGB'
  1252. *
  1253. *@param a_term out parameter. The successfully parsed term.
  1254. *@return CR_OK upon successfull completion, an error code otherwise.
  1255. */
  1256. enum CRStatus
  1257. cr_parser_parse_term (CRParser * a_this, CRTerm ** a_term)
  1258. {
  1259. enum CRStatus status = CR_PARSING_ERROR;
  1260. CRInputPos init_pos;
  1261. CRTerm *result = NULL;
  1262. CRTerm *param = NULL;
  1263. CRToken *token = NULL;
  1264. CRString *func_name = NULL;
  1265. CRParsingLocation location = {0} ;
  1266. g_return_val_if_fail (a_this && a_term, CR_BAD_PARAM_ERROR);
  1267. RECORD_INITIAL_POS (a_this, &init_pos);
  1268. result = cr_term_new ();
  1269. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
  1270. &token);
  1271. if (status != CR_OK || !token)
  1272. goto error;
  1273. cr_parsing_location_copy (&location, &token->location) ;
  1274. if (token->type == DELIM_TK && token->u.unichar == '+') {
  1275. result->unary_op = PLUS_UOP;
  1276. cr_token_destroy (token) ;
  1277. token = NULL ;
  1278. cr_parser_try_to_skip_spaces_and_comments (a_this);
  1279. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
  1280. &token);
  1281. if (status != CR_OK || !token)
  1282. goto error;
  1283. } else if (token->type == DELIM_TK && token->u.unichar == '-') {
  1284. result->unary_op = MINUS_UOP;
  1285. cr_token_destroy (token) ;
  1286. token = NULL ;
  1287. cr_parser_try_to_skip_spaces_and_comments (a_this);
  1288. status = cr_tknzr_get_next_token (PRIVATE (a_this)->tknzr,
  1289. &token);
  1290. if (status != CR_OK || !token)
  1291. goto error;
  1292. }
  1293. if (token->type == EMS_TK
  1294. || token->type == EXS_TK
  1295. || token->type == LENGTH_TK
  1296. || token->type == ANGLE_TK
  1297. || token->type == TIME_TK
  1298. || token->type == FREQ_TK
  1299. || token->type == PERCENTAGE_TK
  1300. || token->type == NUMBER_TK) {
  1301. status = cr_term_set_number (result, token->u.num);
  1302. CHECK_PARSING_STATUS (status, TRUE);
  1303. token->u.num = NULL;
  1304. status = CR_OK;
  1305. } else if (token && token->type == FUNCTION_TK) {
  1306. status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr,
  1307. token);
  1308. token = NULL;
  1309. status = cr_parser_parse_function (a_this, &func_name,
  1310. &param);
  1311. if (status == CR_OK) {
  1312. status = cr_term_set_function (result,
  1313. func_name,
  1314. param);
  1315. CHECK_PARSING_STATUS (status, TRUE);
  1316. }
  1317. } else if (token && token->type == STRING_TK) {
  1318. status = cr_term_set_string (result,
  1319. token->u.str);
  1320. CHECK_PARSING_STATU