2 * parse-util.c: Various utility routines to parse or produce
3 * string representations of common reference types.
5 * Copyright (C) 2000-2007 Jody Goldberg (jody@gnome.org)
6 * Copyright (C) 2008-2009 Morten Welinder (terra@gnome.org)
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
23 #include <gnumeric-config.h>
25 #include <parse-util.h>
27 #include <application.h>
34 #include <number-match.h>
35 #include <gnm-format.h>
36 #include <expr-name.h>
39 #include <sheet-style.h>
40 /* For std_expr_name_handler: */
41 #include <expr-impl.h>
43 #include <goffice/goffice.h>
51 gnm_lexer_item_copy (GnmLexerItem
*li
)
53 return g_memdup (li
, sizeof (*li
));
57 gnm_lexer_item_get_type (void)
62 t
= g_boxed_type_register_static ("GnmLexerItem",
63 (GBoxedCopyFunc
)gnm_lexer_item_copy
,
64 (GBoxedFreeFunc
)g_free
);
70 col_name_internal (GString
*target
, int col
)
72 static int const steps
[] = {
77 26 * 26 * 26 * 26 * 26,
78 26 * 26 * 26 * 26 * 26 * 26,
86 g_string_append_printf (target
, "[C%d]", col
);
90 for (i
= 0; col
>= steps
[i
]; i
++)
93 g_string_set_size (target
, target
->len
+ (i
+ 1));
94 dst
= target
->str
+ target
->len
;
96 *--dst
= 'A' + col
% 26;
103 * @col: column number
105 * Returns: (transfer none): A string representation of @col
110 static GString
*buffer
= NULL
;
112 buffer
= g_string_new (NULL
);
113 g_string_truncate (buffer
, 0);
115 col_name_internal (buffer
, col
);
122 * @start_col: column number
123 * @end_col: column number
125 * Returns: (transfer none): A string representation of the columns from
126 * @start_col to @end_col.
129 cols_name (int start_col
, int end_col
)
131 static GString
*buffer
= NULL
;
133 buffer
= g_string_new (NULL
);
134 g_string_truncate (buffer
, 0);
136 col_name_internal (buffer
, start_col
);
137 if (start_col
!= end_col
) {
138 g_string_append_c (buffer
, ':');
139 col_name_internal (buffer
, end_col
);
148 * Returns: (transfer none):
151 col_parse (char const *str
, GnmSheetSize
const *ss
,
152 int *res
, unsigned char *relative
)
154 char const *ptr
, *start
= str
;
156 int max
= ss
->max_cols
;
158 if (!(*relative
= (*start
!= '$')))
161 for (ptr
= start
; col
< max
; ptr
++)
162 if (('a' <= *ptr
&& *ptr
<= 'z'))
163 col
= 26 * (col
+ 1) + (*ptr
- 'a');
164 else if (('A' <= *ptr
&& *ptr
<= 'Z'))
165 col
= 26 * (col
+ 1) + (*ptr
- 'A');
166 else if (ptr
!= start
) {
174 /***************************************************************************/
177 row_name_internal (GString
*target
, int row
)
179 g_string_append_printf (target
, "%d", row
+ 1);
186 * Returns: (transfer none): A string representation of @row
191 static GString
*buffer
= NULL
;
193 buffer
= g_string_new (NULL
);
194 g_string_truncate (buffer
, 0);
196 row_name_internal (buffer
, row
);
203 * @start_row: row number
204 * @end_row: row number
206 * Returns: (transfer none): A string representation of the rows from
207 * @start_row to @end_row.
210 rows_name (int start_row
, int end_row
)
212 static GString
*buffer
= NULL
;
214 buffer
= g_string_new (NULL
);
215 g_string_truncate (buffer
, 0);
217 row_name_internal (buffer
, start_row
);
218 if (start_row
!= end_row
) {
219 g_string_append_c (buffer
, ':');
220 row_name_internal (buffer
, end_row
);
229 * Returns: (transfer none):
232 row_parse (char const *str
, GnmSheetSize
const *ss
,
233 int *res
, unsigned char *relative
)
235 char const *end
, *ptr
= str
;
237 int max
= ss
->max_rows
;
239 if (!(*relative
= (*ptr
!= '$')))
242 /* Initial '0' is not allowed. */
243 if (*ptr
<= '0' || *ptr
> '9')
247 * Do not allow letters after the row number. If we did, then
248 * the name "K3P" would lex as the reference K3 followed by the
251 row
= strtol (ptr
, (char **)&end
, 10);
253 !g_unichar_isalnum (g_utf8_get_char (end
)) && *end
!= '_' &&
254 0 < row
&& row
<= max
) {
261 /***************************************************************************/
264 r1c1_add_index (GString
*target
, char type
, int num
, unsigned char relative
)
268 g_string_append_printf (target
, "%c[%d]", type
, num
);
270 g_string_append_c (target
, type
);
272 g_string_append_printf (target
, "%c%d", type
, num
+ 1);
276 wb_rel_uri (Workbook
*wb
, Workbook
*ref_wb
)
278 char const *uri
= go_doc_get_uri ((GODoc
*)wb
);
279 char const *ref_uri
= go_doc_get_uri ((GODoc
*)ref_wb
);
280 char *rel_uri
= go_url_make_relative (uri
, ref_uri
);
282 if (rel_uri
== NULL
|| rel_uri
[0] == '/') {
284 return g_strdup (uri
);
291 * cellref_as_string: (skip)
292 * @out: #GnmConventionsOut
294 * @no_sheetname: If %TRUE, suppress sheet name
296 * Emits a string containing representation of @ref as evaluated at @pp.
297 * @no_sheetname can be used to suppress the addition of the sheetname
298 * for non-local references.
301 cellref_as_string (GnmConventionsOut
*out
,
302 GnmCellRef
const *cell_ref
,
303 gboolean no_sheetname
)
305 GString
*target
= out
->accum
;
306 Sheet
const *sheet
= cell_ref
->sheet
;
308 /* If it is a non-local reference, add the path to the external sheet */
309 if (sheet
!= NULL
&& !no_sheetname
) {
310 if (out
->pp
->wb
== NULL
&& out
->pp
->sheet
== NULL
)
311 /* For the expression leak printer. */
312 g_string_append (target
, "'?'");
313 else if (NULL
== out
->pp
->wb
|| sheet
->workbook
== out
->pp
->wb
)
314 g_string_append (target
, sheet
->name_quoted
);
316 char *rel_uri
= wb_rel_uri (sheet
->workbook
, out
->pp
->wb
);
317 g_string_append_c (target
, '[');
318 g_string_append (target
, rel_uri
);
319 g_string_append_c (target
, ']');
320 g_string_append (target
, sheet
->name_quoted
);
323 g_string_append_unichar (target
, out
->convs
->sheet_name_sep
);
326 if (out
->convs
->r1c1_addresses
) { /* R1C1 handler */
327 r1c1_add_index (target
, 'R', cell_ref
->row
, cell_ref
->row_relative
);
328 r1c1_add_index (target
, 'C', cell_ref
->col
, cell_ref
->col_relative
);
331 Sheet
const *size_sheet
= eval_sheet (sheet
, out
->pp
->sheet
);
332 GnmSheetSize
const *ss
=
333 gnm_sheet_get_size2 (size_sheet
, out
->pp
->wb
);
335 gnm_cellpos_init_cellref_ss (&pos
, cell_ref
, &out
->pp
->eval
, ss
);
337 if (!cell_ref
->col_relative
)
338 g_string_append_c (target
, '$');
339 col_name_internal (target
, pos
.col
);
341 if (!cell_ref
->row_relative
)
342 g_string_append_c (target
, '$');
343 row_name_internal (target
, pos
.row
);
348 * rangeref_as_string: (skip)
349 * @out: #GnmConventionsOut
354 rangeref_as_string (GnmConventionsOut
*out
, GnmRangeRef
const *ref
)
357 GString
*target
= out
->accum
;
358 Sheet
*start_sheet
, *end_sheet
;
359 GnmSheetSize
const *end_ss
;
361 gnm_rangeref_normalize_pp (ref
, out
->pp
, &start_sheet
, &end_sheet
, &r
);
363 end_ss
= gnm_sheet_get_size2 (end_sheet
, out
->pp
->wb
);
366 if (NULL
!= out
->pp
->wb
&& ref
->a
.sheet
->workbook
!= out
->pp
->wb
) {
367 char *rel_uri
= wb_rel_uri (ref
->a
.sheet
->workbook
, out
->pp
->wb
);
368 g_string_append_c (target
, '[');
369 g_string_append (target
, rel_uri
);
370 g_string_append_c (target
, ']');
373 if (out
->pp
->wb
== NULL
&& out
->pp
->sheet
== NULL
)
374 /* For the expression leak printer. */
375 g_string_append (target
, "'?'");
376 else if (ref
->b
.sheet
== NULL
|| ref
->a
.sheet
== ref
->b
.sheet
)
377 g_string_append (target
, ref
->a
.sheet
->name_quoted
);
379 g_string_append (target
, ref
->a
.sheet
->name_quoted
);
380 g_string_append_c (target
, ':');
381 g_string_append (target
, ref
->b
.sheet
->name_quoted
);
383 g_string_append_unichar (target
, out
->convs
->sheet_name_sep
);
386 if (out
->convs
->r1c1_addresses
) { /* R1C1 handler */
387 /* be sure to use else if so that a1:iv65535 does not vanish */
388 if (r
.start
.col
== 0 && r
.end
.col
== end_ss
->max_cols
- 1) {
389 r1c1_add_index (target
, 'R', ref
->a
.row
, ref
->a
.row_relative
);
390 if (ref
->a
.row
!= ref
->b
.row
||
391 ref
->a
.row_relative
!= ref
->b
.row_relative
) {
392 g_string_append_c (target
, ':');
393 r1c1_add_index (target
, 'R', ref
->b
.row
, ref
->b
.row_relative
);
395 } else if (r
.start
.row
== 0 && r
.end
.row
== end_ss
->max_rows
- 1) {
396 r1c1_add_index (target
, 'C', ref
->a
.col
, ref
->a
.col_relative
);
397 if (ref
->a
.col
!= ref
->b
.col
||
398 ref
->a
.col_relative
!= ref
->b
.col_relative
) {
399 g_string_append_c (target
, ':');
400 r1c1_add_index (target
, 'C', ref
->b
.col
, ref
->b
.col_relative
);
403 r1c1_add_index (target
, 'R', ref
->a
.row
, ref
->a
.row_relative
);
404 r1c1_add_index (target
, 'C', ref
->a
.col
, ref
->a
.col_relative
);
405 if (r
.start
.col
!= r
.end
.col
||
406 ref
->a
.col_relative
!= ref
->b
.col_relative
||
407 r
.start
.row
!= r
.end
.row
||
408 ref
->a
.row_relative
!= ref
->b
.row_relative
) {
409 g_string_append_c (target
, ':');
410 r1c1_add_index (target
, 'R', ref
->b
.row
, ref
->b
.row_relative
);
411 r1c1_add_index (target
, 'C', ref
->b
.col
, ref
->b
.col_relative
);
415 /* be sure to use else if so that a1:iv65535 does not vanish */
416 if (r
.start
.col
== 0 && r
.end
.col
== end_ss
->max_cols
- 1) {
417 if (!ref
->a
.row_relative
)
418 g_string_append_c (target
, '$');
419 row_name_internal (target
, r
.start
.row
);
420 g_string_append_c (target
, ':');
421 if (!ref
->b
.row_relative
)
422 g_string_append_c (target
, '$');
423 row_name_internal (target
, r
.end
.row
);
424 } else if (r
.start
.row
== 0 && r
.end
.row
== end_ss
->max_rows
- 1) {
425 if (!ref
->a
.col_relative
)
426 g_string_append_c (target
, '$');
427 col_name_internal (target
, r
.start
.col
);
428 g_string_append_c (target
, ':');
429 if (!ref
->b
.col_relative
)
430 g_string_append_c (target
, '$');
431 col_name_internal (target
, r
.end
.col
);
433 if (!ref
->a
.col_relative
)
434 g_string_append_c (target
, '$');
435 col_name_internal (target
, r
.start
.col
);
436 if (!ref
->a
.row_relative
)
437 g_string_append_c (target
, '$');
438 row_name_internal (target
, r
.start
.row
);
440 if (r
.start
.col
!= r
.end
.col
||
441 ref
->a
.col_relative
!= ref
->b
.col_relative
||
442 r
.start
.row
!= r
.end
.row
||
443 ref
->a
.row_relative
!= ref
->b
.row_relative
) {
444 g_string_append_c (target
, ':');
445 if (!ref
->b
.col_relative
)
446 g_string_append_c (target
, '$');
447 col_name_internal (target
, r
.end
.col
);
448 if (!ref
->b
.row_relative
)
449 g_string_append_c (target
, '$');
450 row_name_internal (target
, r
.end
.row
);
457 * gnm_1_0_rangeref_as_string: (skip)
458 * @out: #GnmConventionsOut
461 * Simplified variant of rangeref_as_string that old versions of gnumeric can
462 * read. It drops support for full col/row references. We can remap them on
465 * This function also ignores R1C1 settings.
468 gnm_1_0_rangeref_as_string (GnmConventionsOut
*out
, GnmRangeRef
const *ref
)
471 GString
*target
= out
->accum
;
472 Sheet
*start_sheet
, *end_sheet
;
474 gnm_rangeref_normalize_pp (ref
, out
->pp
, &start_sheet
, &end_sheet
, &r
);
477 if (NULL
!= out
->pp
->wb
&& ref
->a
.sheet
->workbook
!= out
->pp
->wb
) {
478 char *rel_uri
= wb_rel_uri (ref
->a
.sheet
->workbook
, out
->pp
->wb
);
479 g_string_append_c (target
, '[');
480 g_string_append (target
, rel_uri
);
481 g_string_append_c (target
, ']');
484 if (out
->pp
->wb
== NULL
&& out
->pp
->sheet
== NULL
)
485 /* For the expression leak printer. */
486 g_string_append (target
, "'?'");
487 else if (ref
->b
.sheet
== NULL
|| ref
->a
.sheet
== ref
->b
.sheet
)
488 g_string_append (target
, ref
->a
.sheet
->name_quoted
);
490 g_string_append (target
, ref
->a
.sheet
->name_quoted
);
491 g_string_append_c (target
, ':');
492 g_string_append (target
, ref
->b
.sheet
->name_quoted
);
494 g_string_append_unichar (target
, out
->convs
->sheet_name_sep
);
497 if (!ref
->a
.col_relative
)
498 g_string_append_c (target
, '$');
499 col_name_internal (target
, r
.start
.col
);
500 if (!ref
->a
.row_relative
)
501 g_string_append_c (target
, '$');
502 row_name_internal (target
, r
.start
.row
);
504 if (r
.start
.col
!= r
.end
.col
||
505 ref
->a
.col_relative
!= ref
->b
.col_relative
||
506 r
.start
.row
!= r
.end
.row
||
507 ref
->a
.row_relative
!= ref
->b
.row_relative
) {
508 g_string_append_c (target
, ':');
509 if (!ref
->b
.col_relative
)
510 g_string_append_c (target
, '$');
511 col_name_internal (target
, r
.end
.col
);
512 if (!ref
->b
.row_relative
)
513 g_string_append_c (target
, '$');
514 row_name_internal (target
, r
.end
.row
);
519 cellref_a1_get (GnmCellRef
*out
, GnmSheetSize
const *ss
,
520 char const *in
, GnmCellPos
const *pos
)
525 g_return_val_if_fail (in
!= NULL
, NULL
);
526 g_return_val_if_fail (out
!= NULL
, NULL
);
528 in
= col_parse (in
, ss
, &col
, &out
->col_relative
);
532 in
= row_parse (in
, ss
, &row
, &out
->row_relative
);
536 /* Setup the cell reference information */
537 if (out
->row_relative
)
538 out
->row
= row
- pos
->row
;
542 if (out
->col_relative
)
543 out
->col
= col
- pos
->col
;
552 /* skip first character (which was R or C) */
554 r1c1_get_index (char const *str
, GnmSheetSize
const *ss
,
555 int *num
, unsigned char *relative
, gboolean is_col
)
559 int max
= is_col
? ss
->max_cols
: ss
->max_rows
;
565 *relative
= (*str
== '[');
568 else if (*str
== '-' || *str
== '+') { /* handle RC-10 as RC followed by -10 */
575 *num
= l
= strtol (str
, &end
, 10);
576 if (errno
== ERANGE
|| l
<= G_MININT
|| l
> G_MAXINT
) {
577 /* Note: this includes G_MININT to avoid negation overflow. */
585 } else if (*relative
) {
593 if (*num
<= 0 || *num
> max
)
601 cellref_r1c1_get (GnmCellRef
*out
, GnmSheetSize
const *ss
,
602 char const *in
, GnmCellPos
const *pos
)
605 if (*in
!= 'R' && *in
!= 'r')
607 if (NULL
== (in
= r1c1_get_index (in
, ss
,
608 &out
->row
, &out
->row_relative
,
611 if (*in
!= 'C' && *in
!= 'c')
613 if (NULL
== (in
= r1c1_get_index (in
, ss
,
614 &out
->col
, &out
->col_relative
,
617 if (g_ascii_isalpha (*in
))
623 * cellref_parse: (skip)
624 * @out: (out): destination GnmCellRef
625 * @ss: size of the sheet where parsing is being done
626 * @in: reference description text, no leading whitespace allowed.
627 * @pos: position parsing is being done at
629 * Converts the string representation of a #GnmCellRef into
630 * an internal representation.
632 * Returns: (transfer none): a pointer to the character following the
636 cellref_parse (GnmCellRef
*out
, GnmSheetSize
const *ss
,
637 char const *in
, GnmCellPos
const *pos
)
641 g_return_val_if_fail (in
!= NULL
, NULL
);
642 g_return_val_if_fail (out
!= NULL
, NULL
);
644 res
= cellref_a1_get (out
, ss
, in
, pos
);
647 return cellref_r1c1_get (out
, ss
, in
, pos
);
650 /****************************************************************************/
653 cell_coord_name2 (int col
, int row
, gboolean r1c1
)
655 static GString
*buffer
= NULL
;
657 g_string_truncate (buffer
, 0);
659 buffer
= g_string_new (NULL
);
662 r1c1_add_index (buffer
, 'R', row
, FALSE
);
663 r1c1_add_index (buffer
, 'C', col
, FALSE
);
665 col_name_internal (buffer
, col
);
666 row_name_internal (buffer
, row
);
673 * cell_coord_name: (skip)
674 * @col: column number
677 * Returns: (transfer none): a string representation of the cell at (@col,@row)
680 cell_coord_name (int col
, int row
)
682 return cell_coord_name2 (col
, row
, FALSE
);
686 * cellpos_as_string: (skip)
687 * @pos: A #GnmCellPos
689 * Returns: (transfer none): a string representation of the cell at @pos
692 cellpos_as_string (GnmCellPos
const *pos
)
694 g_return_val_if_fail (pos
!= NULL
, "ERROR");
696 return cell_coord_name (pos
->col
, pos
->row
);
700 parsepos_as_string (GnmParsePos
const *pp
)
702 g_return_val_if_fail (pp
!= NULL
, "ERROR");
704 return cell_coord_name2 (pp
->eval
.col
,
706 pp
->sheet
&& pp
->sheet
->convs
->r1c1_addresses
);
713 * Returns: (transfer none): the name of @cell, like "B11"
716 cell_name (GnmCell
const *cell
)
718 g_return_val_if_fail (cell
!= NULL
, "ERROR");
720 return cell_coord_name2 (cell
->pos
.col
,
722 cell
->base
.sheet
->convs
->r1c1_addresses
);
726 * cellpos_parse: (skip)
727 * @cell_str: a string representation of a cell name.
730 * @strict: if this is TRUE, then parsing stops at possible errors,
731 * otherwise an attempt is made to return cell names with
734 * Returns: (transfer none): pointer to following char on success, %NULL on
735 * failure. (In the strict case, that would be a pointer to the \0 or %NULL.)
738 cellpos_parse (char const *cell_str
, GnmSheetSize
const *ss
,
739 GnmCellPos
*res
, gboolean strict
)
741 unsigned char dummy_relative
;
743 cell_str
= col_parse (cell_str
, ss
, &res
->col
, &dummy_relative
);
747 cell_str
= row_parse (cell_str
, ss
, &res
->row
, &dummy_relative
);
751 if (*cell_str
!= 0 && strict
)
758 * gnm_expr_char_start_p: (skip)
761 * Can the supplied string be an expression ? It does not guarantee that it is,
762 * however, it is possible. If it is possible it strips off any header
763 * characters that are not relevant.
765 * NOTE : things like -1,234 will match
768 gnm_expr_char_start_p (char const * c
)
778 if (c0
== '=' || c0
== '@' || c0
== '+' || c0
== '-')
782 if (c0
== '=' || c0
== '@' || (c0
== '+' && c
[1] == 0))
785 if ((c0
== '-' || c0
== '+') && c0
!= c
[1]) {
789 * Ok, we have a string that
790 * 1. starts with a sign
791 * 2. does not start with the sign repeated (think --------)
792 * 3. is more than one character
794 * Now we check whether we have a number. We don't want
795 * numbers to be treated as formulae. FIXME: this really
796 * just checks for C-syntax numbers.
798 (void) gnm_strto (c
, &end
);
799 if (errno
|| *end
!= 0 || end
== c
)
800 return (c0
== '+') ? c
+ N
: c
;
801 /* Otherwise, it's a number. */
807 * parse_text_value_or_expr:
808 * @pos: If the string looks like an expression parse it at this location.
809 * @text: The text to be parsed.
810 * @val: (out): Returns a GnmValue* if the text was a value, otherwise NULL.
811 * @texpr: (out): Returns a GnmExprTop* if the text was an expression, otherwise NULL.
813 * Utility routine to parse a string and convert it into an expression or value.
815 * If there is a parse failure for an expression an error GnmValue with
816 * the syntax error is returned in @val.
819 parse_text_value_or_expr (GnmParsePos
const *pos
, char const *text
,
820 GnmValue
**val
, GnmExprTop
const **texpr
)
822 char const *expr_start
;
823 GODateConventions
const *date_conv
;
824 GOFormat
const *cur_fmt
;
825 GOFormat
const *cell_fmt
;
826 GnmStyle
const *cell_style
;
831 /* Determine context information. */
834 ? sheet_date_conv (pos
->sheet
)
836 ? workbook_date_conv (pos
->wb
)
838 cell_style
= pos
->sheet
839 ? sheet_style_get (pos
->sheet
, pos
->eval
.col
, pos
->eval
.row
)
841 cur_fmt
= cell_fmt
= cell_style
? gnm_style_get_format (cell_style
) : NULL
;
842 if (cell_fmt
&& go_format_is_general (cell_fmt
)) {
843 GnmCell
const *cell
= pos
->sheet
844 ? sheet_cell_get (pos
->sheet
, pos
->eval
.col
, pos
->eval
.row
)
846 if (cell
&& cell
->value
&& VALUE_FMT (cell
->value
))
847 cur_fmt
= VALUE_FMT (cell
->value
);
850 /* Does it match any formats? */
851 *val
= format_match (text
, cur_fmt
, date_conv
);
853 GOFormat
const *val_fmt
= VALUE_FMT (*val
);
854 /* Avoid value formats we don't need. */
855 if (val_fmt
&& go_format_eq (cell_fmt
, val_fmt
))
856 value_set_fmt (*val
, NULL
);
860 /* If it does not match known formats, see if it is an expression */
861 expr_start
= gnm_expr_char_start_p (text
);
862 if (NULL
!= expr_start
&& *expr_start
) {
863 *texpr
= gnm_expr_parse_str (expr_start
, pos
,
864 GNM_EXPR_PARSE_DEFAULT
, NULL
, NULL
);
869 /* Fall back on string */
870 *val
= value_new_string (text
);
874 parse_error_init (GnmParseError
*pe
)
884 parse_error_free (GnmParseError
*pe
)
886 if (pe
->err
!= NULL
) {
887 g_error_free (pe
->err
);
892 static GnmParseError
*
893 gnm_parse_error_copy (GnmParseError
*pe
)
895 GnmParseError
*res
= g_new (GnmParseError
, 1);
896 res
->begin_char
= pe
->begin_char
;
897 res
->end_char
= pe
->end_char
;
898 res
->err
= (pe
->err
)? g_error_copy (pe
->err
): NULL
;
903 gnm_parse_error_get_type (void)
908 t
= g_boxed_type_register_static ("GnmParseError",
909 (GBoxedCopyFunc
)gnm_parse_error_copy
,
910 (GBoxedFreeFunc
)parse_error_free
);
915 /***************************************************************************/
918 check_quoted (char const *start
, int *num_escapes
)
920 char const *str
= start
;
921 if (*str
== '\'' || *str
== '\"') {
922 char const quote
= *str
++;
924 for (; *str
&& *str
!= quote
; str
= g_utf8_next_char (str
))
925 if (*str
== '\\' && str
[1]) {
937 unquote (char *dst
, char const *src
, int n
)
940 if (*src
== '\\' && src
[1]) {
941 int l
= g_utf8_skip
[*(guchar
*)(++src
)];
942 strncpy (dst
, src
, l
);
953 * @convs: #GnmConventions const
957 * Returns : NULL if there is a valid workbook name but it is unknown.
958 * If the string is a valid workbook known name it returns a pointer
959 * the end of the name.
960 * Otherwise returns @start and does not modify @wb.
963 wbref_parse (GnmConventions
const *convs
,
964 char const *start
, Workbook
**wb
, Workbook
*ref_wb
)
966 /* Is this an external reference ? */
971 char const *end
= check_quoted (start
+1, &num_escapes
);
974 if (end
== start
+1) {
975 end
= strchr (start
, ']');
983 name
= g_strndup (start
+ 1, end
- start
- 1);
985 name
= g_malloc (1 + end
- start
- 2);
986 unquote (name
, start
+2, end
-start
-2);
989 tmp_wb
= (*convs
->input
.external_wb
) (convs
, ref_wb
, name
);
1002 * sheetref_parse: (skip)
1003 * @convs: #GnmConventions
1009 * Returns: (transfer none): %NULL if there is a valid sheet name but it
1010 * is unknown. If the string is a valid sheet name it returns a pointer
1011 * the end of the name. Otherwise returns @start and does not
1015 sheetref_parse (GnmConventions
const *convs
,
1016 char const *start
, Sheet
**sheet
, Workbook
const *wb
,
1019 GString
*sheet_name
;
1023 if (*start
== '\'' || *start
== '"') {
1024 sheet_name
= g_string_new (NULL
);
1025 end
= go_strunescape (sheet_name
, start
);
1027 g_string_free (sheet_name
, TRUE
);
1031 gboolean only_digits
= TRUE
;
1039 * Some names starting with digits are actually valid, but
1040 * unparse quoted. Things are quite tricky: most sheet names
1041 * starting with a digit are ok, but not those starting with
1042 * "[0-9]*\." or "[0-9]+[eE]".
1051 gunichar uc
= g_utf8_get_char (end
);
1052 if (g_unichar_isalpha (uc
) || uc
== '_') {
1053 if (only_digits
&& end
!= start
&&
1054 (uc
== 'e' || uc
== 'E')) {
1058 only_digits
= FALSE
;
1059 end
= g_utf8_next_char (end
);
1060 } else if (g_unichar_isdigit (uc
)) {
1061 end
= g_utf8_next_char (end
);
1062 } else if (uc
== '.') {
1063 /* Valid, except after only digits. */
1073 if (*end
!= '!' && (!allow_3d
|| *end
!= ':'))
1076 sheet_name
= g_string_new_len (start
, end
- start
);
1079 *sheet
= workbook_sheet_by_name (wb
, sheet_name
->str
);
1083 g_string_free (sheet_name
, TRUE
);
1088 r1c1_rangeref_parse (GnmRangeRef
*res
, char const *ptr
, GnmParsePos
const *pp
)
1091 GnmSheetSize
const *a_ss
, *b_ss
;
1092 Sheet
const *a_sheet
, *b_sheet
;
1094 a_sheet
= eval_sheet (res
->a
.sheet
, pp
->sheet
);
1095 b_sheet
= eval_sheet (res
->b
.sheet
, a_sheet
);
1097 a_ss
= gnm_sheet_get_size2 (a_sheet
, pp
->wb
);
1098 b_ss
= gnm_sheet_get_size2 (b_sheet
, pp
->wb
);
1100 if (*ptr
== 'R' || *ptr
== 'r') {
1101 ptr
= r1c1_get_index (ptr
, a_ss
,
1102 &res
->a
.row
, &res
->a
.row_relative
,
1106 if (*ptr
!= 'C' && *ptr
!= 'c') {
1107 if (g_ascii_isalpha (*ptr
))
1110 res
->a
.col_relative
= FALSE
;
1113 res
->b
.col
= a_ss
->max_cols
- 1;
1114 if (ptr
[0] != ':' || (ptr
[1] != 'R' && ptr
[1] != 'r'))
1116 tmp
= r1c1_get_index (ptr
+1, a_ss
,
1117 &res
->b
.row
, &res
->b
.row_relative
,
1120 return ptr
; /* fallback to just the initial R */
1123 ptr
= r1c1_get_index (ptr
, a_ss
,
1124 &res
->a
.col
, &res
->a
.col_relative
,
1131 if (ptr
[0] != ':' || (ptr
[1] != 'R' && ptr
[1] != 'r') ||
1132 NULL
== (tmp
= r1c1_get_index (ptr
+1, b_ss
,
1133 &res
->b
.row
, &res
->b
.row_relative
, FALSE
)) ||
1134 (*tmp
!= 'C' && *tmp
!= 'c') ||
1135 NULL
== (tmp
= r1c1_get_index (tmp
, b_ss
,
1136 &res
->b
.col
, &res
->b
.col_relative
, FALSE
)))
1139 } else if (*ptr
== 'C' || *ptr
== 'c') {
1140 if (NULL
== (ptr
= r1c1_get_index (ptr
, a_ss
,
1141 &res
->a
.col
, &res
->a
.col_relative
, TRUE
)))
1143 if (g_ascii_isalpha (*ptr
))
1146 res
->a
.row_relative
= FALSE
;
1149 res
->b
.row
= b_ss
->max_rows
- 1;
1150 if (ptr
[0] != ':' || (ptr
[1] != 'C' && ptr
[1] != 'c'))
1152 tmp
= r1c1_get_index (ptr
, b_ss
,
1153 &res
->b
.col
, &res
->b
.col_relative
,
1156 return ptr
; /* fallback to just the initial C */
1164 * rangeref_parse: (skip)
1165 * @res: (out): #GnmRangeRef
1166 * @start: the start of the string to parse
1167 * @pp: the location to parse relative to
1168 * @convs: #GnmConventions
1170 * Returns: (transfer none): a pointer to the first invalid character.
1171 * If the result != @start then @res is valid.
1174 rangeref_parse (GnmRangeRef
*res
, char const *start
, GnmParsePos
const *pp
,
1175 GnmConventions
const *convs
)
1177 char const *ptr
= start
, *start_sheet
, *start_wb
, *tmp1
, *tmp2
;
1180 Sheet
*a_sheet
, *b_sheet
;
1181 GnmSheetSize
const *a_ss
, *b_ss
;
1183 g_return_val_if_fail (start
!= NULL
, start
);
1184 g_return_val_if_fail (pp
!= NULL
, start
);
1187 ref_wb
= wb
? wb
: pp
->sheet
->workbook
;
1189 start_sheet
= wbref_parse (convs
, start
, &wb
, ref_wb
);
1190 if (start_sheet
== NULL
)
1191 return start
; /* TODO error unknown workbook */
1192 ptr
= sheetref_parse (convs
, start_sheet
, &res
->a
.sheet
, wb
, TRUE
);
1194 return start
; /* TODO error unknown sheet */
1195 if (ptr
!= start_sheet
) {
1198 if (*ptr
== ':') { /* 3d ref */
1199 ptr
= sheetref_parse (convs
, ptr
+1, &res
->b
.sheet
, wb
, FALSE
);
1201 return start
; /* TODO error unknown sheet */
1203 res
->b
.sheet
= NULL
;
1206 return start
; /* TODO syntax error */
1208 ref
= value_error_name (GNM_ERROR_REF
, FALSE
);
1209 if (strncmp (ptr
, ref
, strlen (ref
)) == 0) {
1210 res
->a
.sheet
= invalid_sheet
;
1211 res
->a
.col
= res
->a
.row
= 0;
1212 res
->a
.col_relative
= res
->a
.row_relative
= FALSE
;
1213 res
->b
.sheet
= res
->a
.sheet
;
1214 ptr
+= strlen (ref
);
1218 if (start_sheet
!= start_wb
)
1219 return start
; /* Workbook, but no sheet. */
1220 res
->b
.sheet
= NULL
;
1223 if (convs
->r1c1_addresses
) { /* R1C1 handler */
1224 const char *tmp1
= r1c1_rangeref_parse (res
, ptr
, pp
);
1225 return (tmp1
!= NULL
) ? tmp1
: start
;
1228 a_sheet
= eval_sheet (res
->a
.sheet
, pp
->sheet
);
1229 b_sheet
= eval_sheet (res
->b
.sheet
, a_sheet
);
1231 a_ss
= gnm_sheet_get_size2 (a_sheet
, pp
->wb
);
1232 b_ss
= gnm_sheet_get_size2 (b_sheet
, pp
->wb
);
1234 tmp1
= col_parse (ptr
, a_ss
, &res
->a
.col
, &res
->a
.col_relative
);
1235 if (tmp1
== NULL
) { /* check for row only ref 2:3 */
1236 tmp1
= row_parse (ptr
, a_ss
,
1237 &res
->a
.row
, &res
->a
.row_relative
);
1238 if (!tmp1
|| *tmp1
++ != ':') /* row only requires : even for singleton */
1240 tmp2
= row_parse (tmp1
, b_ss
,
1241 &res
->b
.row
, &res
->b
.row_relative
);
1244 res
->a
.col_relative
= res
->b
.col_relative
= FALSE
;
1246 res
->b
.col
= b_ss
->max_cols
- 1;
1247 if (res
->a
.row_relative
)
1248 res
->a
.row
-= pp
->eval
.row
;
1249 if (res
->b
.row_relative
)
1250 res
->b
.row
-= pp
->eval
.row
;
1254 tmp2
= row_parse (tmp1
, a_ss
, &res
->a
.row
, &res
->a
.row_relative
);
1255 if (tmp2
== NULL
) { /* check for col only ref B:C or R1C1 style */
1256 if (*tmp1
++ != ':') /* col only requires : even for singleton */
1258 tmp2
= col_parse (tmp1
, a_ss
,
1259 &res
->b
.col
, &res
->b
.col_relative
);
1262 res
->a
.row_relative
= res
->b
.row_relative
= FALSE
;
1264 res
->b
.row
= b_ss
->max_rows
- 1;
1265 if (res
->a
.col_relative
)
1266 res
->a
.col
-= pp
->eval
.col
;
1267 if (res
->b
.col_relative
)
1268 res
->b
.col
-= pp
->eval
.col
;
1272 if (res
->a
.col_relative
)
1273 res
->a
.col
-= pp
->eval
.col
;
1274 if (res
->a
.row_relative
)
1275 res
->a
.row
-= pp
->eval
.row
;
1281 tmp1
= col_parse (ptr
+1, b_ss
, &res
->b
.col
, &res
->b
.col_relative
);
1283 goto singleton
; /* strange, but valid singleton */
1284 tmp2
= row_parse (tmp1
, b_ss
, &res
->b
.row
, &res
->b
.row_relative
);
1286 goto singleton
; /* strange, but valid singleton */
1288 if (res
->b
.col_relative
)
1289 res
->b
.col
-= pp
->eval
.col
;
1290 if (res
->b
.row_relative
)
1291 res
->b
.row
-= pp
->eval
.row
;
1295 res
->b
.col
= res
->a
.col
;
1296 res
->b
.row
= res
->a
.row
;
1297 res
->b
.col_relative
= res
->a
.col_relative
;
1298 res
->b
.row_relative
= res
->a
.row_relative
;
1302 /* ------------------------------------------------------------------------- */
1305 std_expr_func_handler (GnmConventionsOut
*out
, GnmExprFunction
const *func
)
1307 char const *name
= gnm_func_get_name (func
->func
,
1308 out
->convs
->localized_function_names
);
1309 GString
*target
= out
->accum
;
1311 g_string_append (target
, name
);
1312 /* FIXME: possibly a space here. */
1313 gnm_expr_list_as_string (func
->argc
, func
->argv
, out
);
1317 std_expr_name_handler (GnmConventionsOut
*out
, GnmExprName
const *name
)
1319 GnmNamedExpr
const *thename
= name
->name
;
1320 GString
*target
= out
->accum
;
1322 if (!expr_name_is_active (thename
)) {
1323 g_string_append (target
,
1324 value_error_name (GNM_ERROR_REF
,
1325 out
->convs
->output
.translated
));
1329 if (name
->optional_scope
!= NULL
) {
1330 Workbook
*out_wb
= out
->pp
->wb
1332 : out
->pp
->sheet
->workbook
;
1333 if (name
->optional_scope
->workbook
!= out_wb
) {
1334 char *rel_uri
= wb_rel_uri (name
->optional_scope
->workbook
, out_wb
);
1335 g_string_append_c (target
, '[');
1336 g_string_append (target
, rel_uri
);
1337 g_string_append_c (target
, ']');
1340 g_string_append (target
, name
->optional_scope
->name_quoted
);
1341 g_string_append_unichar (target
, out
->convs
->sheet_name_sep
);
1343 } else if (out
->pp
->sheet
!= NULL
&&
1344 thename
->pos
.sheet
!= NULL
&&
1345 thename
->pos
.sheet
!= out
->pp
->sheet
) {
1346 g_string_append (target
, thename
->pos
.sheet
->name_quoted
);
1347 g_string_append_unichar (target
, out
->convs
->sheet_name_sep
);
1348 } else if (out
->pp
->sheet
&&
1349 thename
->pos
.sheet
== NULL
&&
1350 expr_name_lookup (out
->pp
, expr_name_name (thename
)) != thename
) {
1351 /* Special syntax for global names shadowed by sheet names. */
1352 g_string_append (target
, "[]");
1355 g_string_append (target
, expr_name_name (thename
));
1359 std_output_string (GnmConventionsOut
*out
, GOString
const *str
)
1361 go_strescape (out
->accum
, str
->str
);
1364 /* ------------------------------------------------------------------------- */
1367 std_sheet_name_quote (GnmConventions
const *convs
,
1370 gunichar uc
= g_utf8_get_char (str
);
1371 GString
*res
= g_string_sized_new (20);
1376 if (g_ascii_isalpha (uc
)) {
1380 } else if (g_unichar_isalpha (uc
) || uc
== '_') {
1383 p
= g_utf8_next_char (str
);
1387 /* FIXME: What about '?' and '\\'. I cannot enter those. */
1389 for (; *p
; p
= g_utf8_next_char (p
)) {
1390 uc
= g_utf8_get_char (p
);
1392 if (g_ascii_isalpha (uc
)) {
1395 } else if (g_ascii_isdigit (uc
)) {
1398 } else if (uc
== '.' || uc
== '_' || g_unichar_isalpha (uc
))
1399 nletters
= ndigits
= -1;
1405 static const GnmSheetSize max_size
= {
1406 GNM_MAX_COLS
, GNM_MAX_ROWS
1409 * Excel also quotes things that look like cell references.
1410 * Precisely, check for a match against
1411 * ([A-Za-z]+)0*([1-9][0-9]*)
1412 * where $1 is a valid column name and $2 is a valid row
1413 * number. (The 0* is an Excel bug.)
1417 unsigned char col_relative
, row_relative
;
1418 if (!col_parse (str
, &max_size
, &col
, &col_relative
))
1424 if (!row_parse (p
, &max_size
, &row
, &row_relative
))
1431 g_string_append (res
, str
);
1435 g_string_append_c (res
, '\'');
1436 /* This is UTF-8 safe. */
1437 for (; *str
; str
++) {
1439 if (c
== '\'' || c
== '\\')
1440 g_string_append_c (res
, '\\');
1441 g_string_append_c (res
, c
);
1443 g_string_append_c (res
, '\'');
1449 std_name_parser (char const *str
,
1450 G_GNUC_UNUSED GnmConventions
const *convs
)
1452 gunichar uc
= g_utf8_get_char (str
);
1454 if (!g_unichar_isalpha (uc
) && uc
!= '_' && uc
!= '\\')
1458 str
= g_utf8_next_char (str
);
1459 uc
= g_utf8_get_char (str
);
1460 } while (g_unichar_isalnum (uc
) ||
1469 static GnmExpr
const *
1470 std_func_map (GnmConventions
const *convs
, Workbook
*scope
,
1471 char const *name
, GnmExprList
*args
)
1473 GnmFunc
*f
= convs
->localized_function_names
1474 ? gnm_func_lookup_localized (name
, scope
)
1475 : gnm_func_lookup (name
, scope
);
1478 f
= convs
->localized_function_names
1479 ? gnm_func_add_placeholder_localized (NULL
, name
)
1480 : gnm_func_add_placeholder_localized (name
, NULL
);
1483 return gnm_expr_new_funcall (f
, args
);
1487 std_external_wb (G_GNUC_UNUSED GnmConventions
const *convs
,
1489 const char *wb_name
)
1491 const char *ref_uri
= ref_wb
? go_doc_get_uri ((GODoc
*)ref_wb
) : NULL
;
1492 return gnm_app_workbook_get_by_name (wb_name
, ref_uri
);
1496 std_string_parser (char const *in
, GString
*target
,
1497 G_GNUC_UNUSED GnmConventions
const *convs
)
1499 return go_strunescape (target
, in
);
1503 * gnm_conventions_new_full:
1506 * Construct a GnmConventions of @size.
1508 * Returns: (transfer full): A #GnmConventions with default values.
1511 gnm_conventions_new_full (unsigned size
)
1513 GnmConventions
*convs
;
1515 g_return_val_if_fail (size
>= sizeof (GnmConventions
), NULL
);
1517 convs
= g_malloc0 (size
);
1518 convs
->ref_count
= 1;
1520 convs
->r1c1_addresses
= FALSE
;
1521 convs
->localized_function_names
= FALSE
;
1523 convs
->sheet_name_sep
= '!';
1524 convs
->intersection_char
= ' ';
1525 convs
->exp_is_left_associative
= FALSE
;
1526 convs
->input
.range_ref
= rangeref_parse
;
1527 convs
->input
.string
= std_string_parser
;
1528 convs
->input
.name
= std_name_parser
;
1529 convs
->input
.name_validate
= expr_name_validate
;
1530 convs
->input
.func
= std_func_map
;
1531 convs
->input
.external_wb
= std_external_wb
;
1533 convs
->output
.decimal_digits
= -1;
1534 convs
->output
.translated
= TRUE
;
1535 convs
->output
.string
= std_output_string
;
1536 convs
->output
.name
= std_expr_name_handler
;
1537 convs
->output
.func
= std_expr_func_handler
;
1538 convs
->output
.cell_ref
= cellref_as_string
;
1539 convs
->output
.range_ref
= rangeref_as_string
;
1540 convs
->output
.boolean
= NULL
;
1541 convs
->output
.quote_sheet_name
= std_sheet_name_quote
;
1547 * gnm_conventions_new:
1549 * A convenience wrapper around gnm_conventions_new_full
1550 * that constructs a GnmConventions of std size.
1552 * Returns: (transfer full): A #GnmConventions with default values.
1555 gnm_conventions_new (void)
1557 return gnm_conventions_new_full (sizeof (GnmConventions
));
1561 * gnm_conventions_unref: (skip)
1562 * @c: (transfer full): #GnmConventions
1564 * Release a reference to a #GnmConvention
1567 gnm_conventions_unref (GnmConventions
*c
)
1572 g_return_if_fail (c
->ref_count
> 0);
1575 if (c
->ref_count
> 0)
1582 * gnm_conventions_ref: (skip)
1583 * @c: (transfer none) (nullable): #GnmConventions
1585 * Returns: (transfer full) (nullable): a new reference to @c
1588 gnm_conventions_ref (GnmConventions
const *c
)
1590 GnmConventions
*uc
= (GnmConventions
*)c
;
1597 gnm_conventions_get_type (void)
1602 t
= g_boxed_type_register_static ("GnmConventions",
1603 (GBoxedCopyFunc
)gnm_conventions_ref
,
1604 (GBoxedFreeFunc
)gnm_conventions_unref
);
1609 /* ------------------------------------------------------------------------- */
1611 GnmConventions
const *gnm_conventions_default
;
1612 GnmConventions
const *gnm_conventions_xls_r1c1
;
1615 parse_util_init (void)
1617 GnmConventions
*convs
;
1619 convs
= gnm_conventions_new ();
1620 convs
->range_sep_colon
= TRUE
;
1621 convs
->r1c1_addresses
= FALSE
;
1622 /* Not ready for general use yet. */
1623 convs
->localized_function_names
= g_getenv ("GNM_LOCAL_FUNCS") != NULL
;
1624 gnm_conventions_default
= convs
;
1626 convs
= gnm_conventions_new ();
1627 convs
->range_sep_colon
= TRUE
;
1628 convs
->r1c1_addresses
= TRUE
;
1629 convs
->localized_function_names
= gnm_conventions_default
->localized_function_names
;
1630 gnm_conventions_xls_r1c1
= convs
;
1634 parse_util_shutdown (void)
1636 gnm_conventions_unref ((GnmConventions
*)gnm_conventions_default
);
1637 gnm_conventions_default
= NULL
;
1638 gnm_conventions_unref ((GnmConventions
*)gnm_conventions_xls_r1c1
);
1639 gnm_conventions_xls_r1c1
= NULL
;
1642 /* ------------------------------------------------------------------------- */
1644 * gnm_expr_conv_quote:
1645 * @convs: #GnmConventions
1646 * @str: string to quote
1648 * Returns: (transfer full): A quoted string according to @convs. If no
1649 * quoting is necessary, a literal copy of @str will be returned.
1652 gnm_expr_conv_quote (GnmConventions
const *convs
,
1655 g_return_val_if_fail (convs
!= NULL
, NULL
);
1656 g_return_val_if_fail (convs
->output
.quote_sheet_name
!= NULL
, NULL
);
1657 g_return_val_if_fail (str
!= NULL
, NULL
);
1658 g_return_val_if_fail (str
[0] != 0, NULL
);
1660 return convs
->output
.quote_sheet_name (convs
, str
);