1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * parse-util.c: Various utility routines to parse or produce
4 * string representations of common reference types.
6 * Copyright (C) 2000-2007 Jody Goldberg (jody@gnome.org)
7 * Copyright (C) 2008-2009 Morten Welinder (terra@gnome.org)
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
24 #include <gnumeric-config.h>
26 #include "parse-util.h"
28 #include "application.h"
35 #include "number-match.h"
36 #include "gnm-format.h"
37 #include "expr-name.h"
40 #include "sheet-style.h"
41 /* For std_expr_name_handler: */
42 #include "expr-impl.h"
44 #include <goffice/goffice.h>
52 gnm_lexer_item_copy (GnmLexerItem
*li
)
54 return g_memdup (li
, sizeof (*li
));
58 gnm_lexer_item_get_type (void)
63 t
= g_boxed_type_register_static ("GnmLexerItem",
64 (GBoxedCopyFunc
)gnm_lexer_item_copy
,
65 (GBoxedFreeFunc
)g_free
);
71 col_name_internal (GString
*target
, int col
)
73 static int const steps
[] = {
78 26 * 26 * 26 * 26 * 26,
79 26 * 26 * 26 * 26 * 26 * 26,
87 g_string_append_printf (target
, "[C%d]", col
);
91 for (i
= 0; col
>= steps
[i
]; i
++)
94 g_string_set_size (target
, target
->len
+ (i
+ 1));
95 dst
= target
->str
+ target
->len
;
97 *--dst
= 'A' + col
% 26;
104 * @col: column number
106 * Returns: (transfer none): A string representation of @col
111 static GString
*buffer
= NULL
;
113 buffer
= g_string_new (NULL
);
114 g_string_truncate (buffer
, 0);
116 col_name_internal (buffer
, col
);
123 * @start_col: column number
124 * @end_col: column number
126 * Returns: (transfer none): A string representation of the columns from
127 * @start_col to @end_col.
130 cols_name (int start_col
, int end_col
)
132 static GString
*buffer
= NULL
;
134 buffer
= g_string_new (NULL
);
135 g_string_truncate (buffer
, 0);
137 col_name_internal (buffer
, start_col
);
138 if (start_col
!= end_col
) {
139 g_string_append_c (buffer
, ':');
140 col_name_internal (buffer
, end_col
);
149 * Returns: (transfer none):
152 col_parse (char const *str
, GnmSheetSize
const *ss
,
153 int *res
, unsigned char *relative
)
155 char const *ptr
, *start
= str
;
157 int max
= ss
->max_cols
;
159 if (!(*relative
= (*start
!= '$')))
162 for (ptr
= start
; col
< max
; ptr
++)
163 if (('a' <= *ptr
&& *ptr
<= 'z'))
164 col
= 26 * (col
+ 1) + (*ptr
- 'a');
165 else if (('A' <= *ptr
&& *ptr
<= 'Z'))
166 col
= 26 * (col
+ 1) + (*ptr
- 'A');
167 else if (ptr
!= start
) {
175 /***************************************************************************/
178 row_name_internal (GString
*target
, int row
)
180 g_string_append_printf (target
, "%d", row
+ 1);
187 * Returns: (transfer none): A string representation of @row
192 static GString
*buffer
= NULL
;
194 buffer
= g_string_new (NULL
);
195 g_string_truncate (buffer
, 0);
197 row_name_internal (buffer
, row
);
204 * @start_row: row number
205 * @end_row: row number
207 * Returns: (transfer none): A string representation of the rows from
208 * @start_row to @end_row.
211 rows_name (int start_row
, int end_row
)
213 static GString
*buffer
= NULL
;
215 buffer
= g_string_new (NULL
);
216 g_string_truncate (buffer
, 0);
218 row_name_internal (buffer
, start_row
);
219 if (start_row
!= end_row
) {
220 g_string_append_c (buffer
, ':');
221 row_name_internal (buffer
, end_row
);
230 * Returns: (transfer none):
233 row_parse (char const *str
, GnmSheetSize
const *ss
,
234 int *res
, unsigned char *relative
)
236 char const *end
, *ptr
= str
;
238 int max
= ss
->max_rows
;
240 if (!(*relative
= (*ptr
!= '$')))
243 /* Initial '0' is not allowed. */
244 if (*ptr
<= '0' || *ptr
> '9')
248 * Do not allow letters after the row number. If we did, then
249 * the name "K3P" would lex as the reference K3 followed by the
252 row
= strtol (ptr
, (char **)&end
, 10);
254 !g_unichar_isalnum (g_utf8_get_char (end
)) && *end
!= '_' &&
255 0 < row
&& row
<= max
) {
262 /***************************************************************************/
265 r1c1_add_index (GString
*target
, char type
, int num
, unsigned char relative
)
269 g_string_append_printf (target
, "%c[%d]", type
, num
);
271 g_string_append_c (target
, type
);
273 g_string_append_printf (target
, "%c%d", type
, num
+ 1);
277 wb_rel_uri (Workbook
*wb
, Workbook
*ref_wb
)
279 char const *uri
= go_doc_get_uri ((GODoc
*)wb
);
280 char const *ref_uri
= go_doc_get_uri ((GODoc
*)ref_wb
);
281 char *rel_uri
= go_url_make_relative (uri
, ref_uri
);
283 if (rel_uri
== NULL
|| rel_uri
[0] == '/') {
285 return g_strdup (uri
);
292 * cellref_as_string: (skip)
293 * @out: #GnmConventionsOut
295 * @no_sheetname: If %TRUE, suppress sheet name
297 * Emits a string containing representation of @ref as evaluated at @pp.
298 * @no_sheetname can be used to suppress the addition of the sheetname
299 * for non-local references.
302 cellref_as_string (GnmConventionsOut
*out
,
303 GnmCellRef
const *cell_ref
,
304 gboolean no_sheetname
)
306 GString
*target
= out
->accum
;
307 Sheet
const *sheet
= cell_ref
->sheet
;
309 /* If it is a non-local reference, add the path to the external sheet */
310 if (sheet
!= NULL
&& !no_sheetname
) {
311 if (out
->pp
->wb
== NULL
&& out
->pp
->sheet
== NULL
)
312 /* For the expression leak printer. */
313 g_string_append (target
, "'?'");
314 else if (NULL
== out
->pp
->wb
|| sheet
->workbook
== out
->pp
->wb
)
315 g_string_append (target
, sheet
->name_quoted
);
317 char *rel_uri
= wb_rel_uri (sheet
->workbook
, out
->pp
->wb
);
318 g_string_append_c (target
, '[');
319 g_string_append (target
, rel_uri
);
320 g_string_append_c (target
, ']');
321 g_string_append (target
, sheet
->name_quoted
);
324 g_string_append_unichar (target
, out
->convs
->sheet_name_sep
);
327 if (out
->convs
->r1c1_addresses
) { /* R1C1 handler */
328 r1c1_add_index (target
, 'R', cell_ref
->row
, cell_ref
->row_relative
);
329 r1c1_add_index (target
, 'C', cell_ref
->col
, cell_ref
->col_relative
);
332 Sheet
const *size_sheet
= eval_sheet (sheet
, out
->pp
->sheet
);
333 GnmSheetSize
const *ss
=
334 gnm_sheet_get_size2 (size_sheet
, out
->pp
->wb
);
336 gnm_cellpos_init_cellref_ss (&pos
, cell_ref
, &out
->pp
->eval
, ss
);
338 if (!cell_ref
->col_relative
)
339 g_string_append_c (target
, '$');
340 col_name_internal (target
, pos
.col
);
342 if (!cell_ref
->row_relative
)
343 g_string_append_c (target
, '$');
344 row_name_internal (target
, pos
.row
);
349 * rangeref_as_string: (skip)
350 * @out: #GnmConventionsOut
355 rangeref_as_string (GnmConventionsOut
*out
, GnmRangeRef
const *ref
)
358 GString
*target
= out
->accum
;
359 Sheet
*start_sheet
, *end_sheet
;
360 GnmSheetSize
const *end_ss
;
362 gnm_rangeref_normalize_pp (ref
, out
->pp
, &start_sheet
, &end_sheet
, &r
);
364 end_ss
= gnm_sheet_get_size2 (end_sheet
, out
->pp
->wb
);
367 if (NULL
!= out
->pp
->wb
&& ref
->a
.sheet
->workbook
!= out
->pp
->wb
) {
368 char *rel_uri
= wb_rel_uri (ref
->a
.sheet
->workbook
, out
->pp
->wb
);
369 g_string_append_c (target
, '[');
370 g_string_append (target
, rel_uri
);
371 g_string_append_c (target
, ']');
374 if (out
->pp
->wb
== NULL
&& out
->pp
->sheet
== NULL
)
375 /* For the expression leak printer. */
376 g_string_append (target
, "'?'");
377 else if (ref
->b
.sheet
== NULL
|| ref
->a
.sheet
== ref
->b
.sheet
)
378 g_string_append (target
, ref
->a
.sheet
->name_quoted
);
380 g_string_append (target
, ref
->a
.sheet
->name_quoted
);
381 g_string_append_c (target
, ':');
382 g_string_append (target
, ref
->b
.sheet
->name_quoted
);
384 g_string_append_unichar (target
, out
->convs
->sheet_name_sep
);
387 if (out
->convs
->r1c1_addresses
) { /* R1C1 handler */
388 /* be sure to use else if so that a1:iv65535 does not vanish */
389 if (r
.start
.col
== 0 && r
.end
.col
== end_ss
->max_cols
- 1) {
390 r1c1_add_index (target
, 'R', ref
->a
.row
, ref
->a
.row_relative
);
391 if (ref
->a
.row
!= ref
->b
.row
||
392 ref
->a
.row_relative
!= ref
->b
.row_relative
) {
393 g_string_append_c (target
, ':');
394 r1c1_add_index (target
, 'R', ref
->b
.row
, ref
->b
.row_relative
);
396 } else if (r
.start
.row
== 0 && r
.end
.row
== end_ss
->max_rows
- 1) {
397 r1c1_add_index (target
, 'C', ref
->a
.col
, ref
->a
.col_relative
);
398 if (ref
->a
.col
!= ref
->b
.col
||
399 ref
->a
.col_relative
!= ref
->b
.col_relative
) {
400 g_string_append_c (target
, ':');
401 r1c1_add_index (target
, 'C', ref
->b
.col
, ref
->b
.col_relative
);
404 r1c1_add_index (target
, 'R', ref
->a
.row
, ref
->a
.row_relative
);
405 r1c1_add_index (target
, 'C', ref
->a
.col
, ref
->a
.col_relative
);
406 if (r
.start
.col
!= r
.end
.col
||
407 ref
->a
.col_relative
!= ref
->b
.col_relative
||
408 r
.start
.row
!= r
.end
.row
||
409 ref
->a
.row_relative
!= ref
->b
.row_relative
) {
410 g_string_append_c (target
, ':');
411 r1c1_add_index (target
, 'R', ref
->b
.row
, ref
->b
.row_relative
);
412 r1c1_add_index (target
, 'C', ref
->b
.col
, ref
->b
.col_relative
);
416 /* be sure to use else if so that a1:iv65535 does not vanish */
417 if (r
.start
.col
== 0 && r
.end
.col
== end_ss
->max_cols
- 1) {
418 if (!ref
->a
.row_relative
)
419 g_string_append_c (target
, '$');
420 row_name_internal (target
, r
.start
.row
);
421 g_string_append_c (target
, ':');
422 if (!ref
->b
.row_relative
)
423 g_string_append_c (target
, '$');
424 row_name_internal (target
, r
.end
.row
);
425 } else if (r
.start
.row
== 0 && r
.end
.row
== end_ss
->max_rows
- 1) {
426 if (!ref
->a
.col_relative
)
427 g_string_append_c (target
, '$');
428 col_name_internal (target
, r
.start
.col
);
429 g_string_append_c (target
, ':');
430 if (!ref
->b
.col_relative
)
431 g_string_append_c (target
, '$');
432 col_name_internal (target
, r
.end
.col
);
434 if (!ref
->a
.col_relative
)
435 g_string_append_c (target
, '$');
436 col_name_internal (target
, r
.start
.col
);
437 if (!ref
->a
.row_relative
)
438 g_string_append_c (target
, '$');
439 row_name_internal (target
, r
.start
.row
);
441 if (r
.start
.col
!= r
.end
.col
||
442 ref
->a
.col_relative
!= ref
->b
.col_relative
||
443 r
.start
.row
!= r
.end
.row
||
444 ref
->a
.row_relative
!= ref
->b
.row_relative
) {
445 g_string_append_c (target
, ':');
446 if (!ref
->b
.col_relative
)
447 g_string_append_c (target
, '$');
448 col_name_internal (target
, r
.end
.col
);
449 if (!ref
->b
.row_relative
)
450 g_string_append_c (target
, '$');
451 row_name_internal (target
, r
.end
.row
);
458 * gnm_1_0_rangeref_as_string: (skip)
459 * @out: #GnmConventionsOut
462 * Simplified variant of rangeref_as_string that old versions of gnumeric can
463 * read. It drops support for full col/row references. We can remap them on
466 * This function also ignores R1C1 settings.
469 gnm_1_0_rangeref_as_string (GnmConventionsOut
*out
, GnmRangeRef
const *ref
)
472 GString
*target
= out
->accum
;
473 Sheet
*start_sheet
, *end_sheet
;
475 gnm_rangeref_normalize_pp (ref
, out
->pp
, &start_sheet
, &end_sheet
, &r
);
478 if (NULL
!= out
->pp
->wb
&& ref
->a
.sheet
->workbook
!= out
->pp
->wb
) {
479 char *rel_uri
= wb_rel_uri (ref
->a
.sheet
->workbook
, out
->pp
->wb
);
480 g_string_append_c (target
, '[');
481 g_string_append (target
, rel_uri
);
482 g_string_append_c (target
, ']');
485 if (out
->pp
->wb
== NULL
&& out
->pp
->sheet
== NULL
)
486 /* For the expression leak printer. */
487 g_string_append (target
, "'?'");
488 else if (ref
->b
.sheet
== NULL
|| ref
->a
.sheet
== ref
->b
.sheet
)
489 g_string_append (target
, ref
->a
.sheet
->name_quoted
);
491 g_string_append (target
, ref
->a
.sheet
->name_quoted
);
492 g_string_append_c (target
, ':');
493 g_string_append (target
, ref
->b
.sheet
->name_quoted
);
495 g_string_append_unichar (target
, out
->convs
->sheet_name_sep
);
498 if (!ref
->a
.col_relative
)
499 g_string_append_c (target
, '$');
500 col_name_internal (target
, r
.start
.col
);
501 if (!ref
->a
.row_relative
)
502 g_string_append_c (target
, '$');
503 row_name_internal (target
, r
.start
.row
);
505 if (r
.start
.col
!= r
.end
.col
||
506 ref
->a
.col_relative
!= ref
->b
.col_relative
||
507 r
.start
.row
!= r
.end
.row
||
508 ref
->a
.row_relative
!= ref
->b
.row_relative
) {
509 g_string_append_c (target
, ':');
510 if (!ref
->b
.col_relative
)
511 g_string_append_c (target
, '$');
512 col_name_internal (target
, r
.end
.col
);
513 if (!ref
->b
.row_relative
)
514 g_string_append_c (target
, '$');
515 row_name_internal (target
, r
.end
.row
);
520 cellref_a1_get (GnmCellRef
*out
, GnmSheetSize
const *ss
,
521 char const *in
, GnmCellPos
const *pos
)
526 g_return_val_if_fail (in
!= NULL
, NULL
);
527 g_return_val_if_fail (out
!= NULL
, NULL
);
529 in
= col_parse (in
, ss
, &col
, &out
->col_relative
);
533 in
= row_parse (in
, ss
, &row
, &out
->row_relative
);
537 /* Setup the cell reference information */
538 if (out
->row_relative
)
539 out
->row
= row
- pos
->row
;
543 if (out
->col_relative
)
544 out
->col
= col
- pos
->col
;
553 /* skip first character (which was R or C) */
555 r1c1_get_index (char const *str
, GnmSheetSize
const *ss
,
556 int *num
, unsigned char *relative
, gboolean is_col
)
560 int max
= is_col
? ss
->max_cols
: ss
->max_rows
;
566 *relative
= (*str
== '[');
569 else if (*str
== '-' || *str
== '+') { /* handle RC-10 as RC followed by -10 */
576 *num
= l
= strtol (str
, &end
, 10);
577 if (errno
== ERANGE
|| l
<= G_MININT
|| l
> G_MAXINT
) {
578 /* Note: this includes G_MININT to avoid negation overflow. */
586 } else if (*relative
) {
594 if (*num
<= 0 || *num
> max
)
602 cellref_r1c1_get (GnmCellRef
*out
, GnmSheetSize
const *ss
,
603 char const *in
, GnmCellPos
const *pos
)
606 if (*in
!= 'R' && *in
!= 'r')
608 if (NULL
== (in
= r1c1_get_index (in
, ss
,
609 &out
->row
, &out
->row_relative
,
612 if (*in
!= 'C' && *in
!= 'c')
614 if (NULL
== (in
= r1c1_get_index (in
, ss
,
615 &out
->col
, &out
->col_relative
,
618 if (g_ascii_isalpha (*in
))
624 * cellref_parse: (skip)
625 * @out: (out): destination GnmCellRef
626 * @ss: size of the sheet where parsing is being done
627 * @in: reference description text, no leading whitespace allowed.
628 * @pos: position parsing is being done at
630 * Converts the string representation of a #GnmCellRef into
631 * an internal representation.
633 * Returns: (transfer none): a pointer to the character following the
637 cellref_parse (GnmCellRef
*out
, GnmSheetSize
const *ss
,
638 char const *in
, GnmCellPos
const *pos
)
642 g_return_val_if_fail (in
!= NULL
, NULL
);
643 g_return_val_if_fail (out
!= NULL
, NULL
);
645 res
= cellref_a1_get (out
, ss
, in
, pos
);
648 return cellref_r1c1_get (out
, ss
, in
, pos
);
651 /****************************************************************************/
654 cell_coord_name2 (int col
, int row
, gboolean r1c1
)
656 static GString
*buffer
= NULL
;
658 g_string_truncate (buffer
, 0);
660 buffer
= g_string_new (NULL
);
663 r1c1_add_index (buffer
, 'R', row
, FALSE
);
664 r1c1_add_index (buffer
, 'C', col
, FALSE
);
666 col_name_internal (buffer
, col
);
667 row_name_internal (buffer
, row
);
674 * cell_coord_name: (skip)
675 * @col: column number
678 * Returns: (transfer none): a string representation of the cell at (@col,@row)
681 cell_coord_name (int col
, int row
)
683 return cell_coord_name2 (col
, row
, FALSE
);
687 * cellpos_as_string: (skip)
688 * @pos: A #GnmCellPos
690 * Returns: (transfer none): a string representation of the cell at @pos
693 cellpos_as_string (GnmCellPos
const *pos
)
695 g_return_val_if_fail (pos
!= NULL
, "ERROR");
697 return cell_coord_name (pos
->col
, pos
->row
);
701 parsepos_as_string (GnmParsePos
const *pp
)
703 g_return_val_if_fail (pp
!= NULL
, "ERROR");
705 return cell_coord_name2 (pp
->eval
.col
,
707 pp
->sheet
&& pp
->sheet
->convs
->r1c1_addresses
);
714 * Returns: (transfer none): the name of @cell, like "B11"
717 cell_name (GnmCell
const *cell
)
719 g_return_val_if_fail (cell
!= NULL
, "ERROR");
721 return cell_coord_name2 (cell
->pos
.col
,
723 cell
->base
.sheet
->convs
->r1c1_addresses
);
727 * cellpos_parse: (skip)
728 * @cell_str: a string representation of a cell name.
731 * @strict: if this is TRUE, then parsing stops at possible errors,
732 * otherwise an attempt is made to return cell names with
735 * Returns: (transfer none): pointer to following char on success, %NULL on
736 * failure. (In the strict case, that would be a pointer to the \0 or %NULL.)
739 cellpos_parse (char const *cell_str
, GnmSheetSize
const *ss
,
740 GnmCellPos
*res
, gboolean strict
)
742 unsigned char dummy_relative
;
744 cell_str
= col_parse (cell_str
, ss
, &res
->col
, &dummy_relative
);
748 cell_str
= row_parse (cell_str
, ss
, &res
->row
, &dummy_relative
);
752 if (*cell_str
!= 0 && strict
)
759 * gnm_expr_char_start_p: (skip)
762 * Can the supplied string be an expression ? It does not guarantee that it is,
763 * however, it is possible. If it is possible it strips off any header
764 * characters that are not relevant.
766 * NOTE : things like -1,234 will match
769 gnm_expr_char_start_p (char const * c
)
779 if (c0
== '=' || c0
== '@' || c0
== '+' || c0
== '-')
783 if (c0
== '=' || c0
== '@' || (c0
== '+' && c
[1] == 0))
786 if ((c0
== '-' || c0
== '+') && c0
!= c
[1]) {
790 * Ok, we have a string that
791 * 1. starts with a sign
792 * 2. does not start with the sign repeated (think --------)
793 * 3. is more than one character
795 * Now we check whether we have a number. We don't want
796 * numbers to be treated as formulae. FIXME: this really
797 * just checks for C-syntax numbers.
799 (void) gnm_strto (c
, &end
);
800 if (errno
|| *end
!= 0 || end
== c
)
801 return (c0
== '+') ? c
+ N
: c
;
802 /* Otherwise, it's a number. */
808 * parse_text_value_or_expr:
809 * @pos: If the string looks like an expression parse it at this location.
810 * @text: The text to be parsed.
811 * @val: (out): Returns a GnmValue* if the text was a value, otherwise NULL.
812 * @texpr: (out): Returns a GnmExprTop* if the text was an expression, otherwise NULL.
814 * Utility routine to parse a string and convert it into an expression or value.
816 * If there is a parse failure for an expression an error GnmValue with
817 * the syntax error is returned in @val.
820 parse_text_value_or_expr (GnmParsePos
const *pos
, char const *text
,
821 GnmValue
**val
, GnmExprTop
const **texpr
)
823 char const *expr_start
;
824 GODateConventions
const *date_conv
;
825 GOFormat
const *cur_fmt
;
826 GOFormat
const *cell_fmt
;
827 GnmStyle
const *cell_style
;
832 /* Determine context information. */
835 ? sheet_date_conv (pos
->sheet
)
837 ? workbook_date_conv (pos
->wb
)
839 cell_style
= pos
->sheet
840 ? sheet_style_get (pos
->sheet
, pos
->eval
.col
, pos
->eval
.row
)
842 cur_fmt
= cell_fmt
= cell_style
? gnm_style_get_format (cell_style
) : NULL
;
843 if (cell_fmt
&& go_format_is_general (cell_fmt
)) {
844 GnmCell
const *cell
= pos
->sheet
845 ? sheet_cell_get (pos
->sheet
, pos
->eval
.col
, pos
->eval
.row
)
847 if (cell
&& cell
->value
&& VALUE_FMT (cell
->value
))
848 cur_fmt
= VALUE_FMT (cell
->value
);
851 /* Does it match any formats? */
852 *val
= format_match (text
, cur_fmt
, date_conv
);
854 GOFormat
const *val_fmt
= VALUE_FMT (*val
);
855 /* Avoid value formats we don't need. */
856 if (val_fmt
&& go_format_eq (cell_fmt
, val_fmt
))
857 value_set_fmt (*val
, NULL
);
861 /* If it does not match known formats, see if it is an expression */
862 expr_start
= gnm_expr_char_start_p (text
);
863 if (NULL
!= expr_start
&& *expr_start
) {
864 *texpr
= gnm_expr_parse_str (expr_start
, pos
,
865 GNM_EXPR_PARSE_DEFAULT
, NULL
, NULL
);
870 /* Fall back on string */
871 *val
= value_new_string (text
);
875 parse_error_init (GnmParseError
*pe
)
885 parse_error_free (GnmParseError
*pe
)
887 if (pe
->err
!= NULL
) {
888 g_error_free (pe
->err
);
893 static GnmParseError
*
894 gnm_parse_error_copy (GnmParseError
*pe
)
896 GnmParseError
*res
= g_new (GnmParseError
, 1);
897 res
->begin_char
= pe
->begin_char
;
898 res
->end_char
= pe
->end_char
;
899 res
->err
= (pe
->err
)? g_error_copy (pe
->err
): NULL
;
904 gnm_parse_error_get_type (void)
909 t
= g_boxed_type_register_static ("GnmParseError",
910 (GBoxedCopyFunc
)gnm_parse_error_copy
,
911 (GBoxedFreeFunc
)parse_error_free
);
916 /***************************************************************************/
919 check_quoted (char const *start
, int *num_escapes
)
921 char const *str
= start
;
922 if (*str
== '\'' || *str
== '\"') {
923 char const quote
= *str
++;
925 for (; *str
&& *str
!= quote
; str
= g_utf8_next_char (str
))
926 if (*str
== '\\' && str
[1]) {
938 unquote (char *dst
, char const *src
, int n
)
941 if (*src
== '\\' && src
[1]) {
942 int l
= g_utf8_skip
[*(guchar
*)(++src
)];
943 strncpy (dst
, src
, l
);
954 * @convs: #GnmConventions const
958 * Returns : NULL if there is a valid workbook name but it is unknown.
959 * If the string is a valid workbook known name it returns a pointer
960 * the end of the name.
961 * Otherwise returns @start and does not modify @wb.
964 wbref_parse (GnmConventions
const *convs
,
965 char const *start
, Workbook
**wb
, Workbook
*ref_wb
)
967 /* Is this an external reference ? */
972 char const *end
= check_quoted (start
+1, &num_escapes
);
975 if (end
== start
+1) {
976 end
= strchr (start
, ']');
984 name
= g_strndup (start
+ 1, end
- start
- 1);
986 name
= g_malloc (1 + end
- start
- 2);
987 unquote (name
, start
+2, end
-start
-2);
990 tmp_wb
= (*convs
->input
.external_wb
) (convs
, ref_wb
, name
);
1003 * sheetref_parse: (skip)
1004 * @convs: #GnmConventions
1010 * Returns: (transfer none): %NULL if there is a valid sheet name but it
1011 * is unknown. If the string is a valid sheet name it returns a pointer
1012 * the end of the name. Otherwise returns @start and does not
1016 sheetref_parse (GnmConventions
const *convs
,
1017 char const *start
, Sheet
**sheet
, Workbook
const *wb
,
1020 GString
*sheet_name
;
1024 if (*start
== '\'' || *start
== '"') {
1025 sheet_name
= g_string_new (NULL
);
1026 end
= go_strunescape (sheet_name
, start
);
1028 g_string_free (sheet_name
, TRUE
);
1032 gboolean only_digits
= TRUE
;
1040 * Some names starting with digits are actually valid, but
1041 * unparse quoted. Things are quite tricky: most sheet names
1042 * starting with a digit are ok, but not those starting with
1043 * "[0-9]*\." or "[0-9]+[eE]".
1052 gunichar uc
= g_utf8_get_char (end
);
1053 if (g_unichar_isalpha (uc
) || uc
== '_') {
1054 if (only_digits
&& end
!= start
&&
1055 (uc
== 'e' || uc
== 'E')) {
1059 only_digits
= FALSE
;
1060 end
= g_utf8_next_char (end
);
1061 } else if (g_unichar_isdigit (uc
)) {
1062 end
= g_utf8_next_char (end
);
1063 } else if (uc
== '.') {
1064 /* Valid, except after only digits. */
1074 if (*end
!= '!' && (!allow_3d
|| *end
!= ':'))
1077 sheet_name
= g_string_new_len (start
, end
- start
);
1080 *sheet
= workbook_sheet_by_name (wb
, sheet_name
->str
);
1084 g_string_free (sheet_name
, TRUE
);
1089 r1c1_rangeref_parse (GnmRangeRef
*res
, char const *ptr
, GnmParsePos
const *pp
)
1092 GnmSheetSize
const *a_ss
, *b_ss
;
1093 Sheet
const *a_sheet
, *b_sheet
;
1095 a_sheet
= eval_sheet (res
->a
.sheet
, pp
->sheet
);
1096 b_sheet
= eval_sheet (res
->b
.sheet
, a_sheet
);
1098 a_ss
= gnm_sheet_get_size2 (a_sheet
, pp
->wb
);
1099 b_ss
= gnm_sheet_get_size2 (b_sheet
, pp
->wb
);
1101 if (*ptr
== 'R' || *ptr
== 'r') {
1102 ptr
= r1c1_get_index (ptr
, a_ss
,
1103 &res
->a
.row
, &res
->a
.row_relative
,
1107 if (*ptr
!= 'C' && *ptr
!= 'c') {
1108 if (g_ascii_isalpha (*ptr
))
1111 res
->a
.col_relative
= FALSE
;
1114 res
->b
.col
= a_ss
->max_cols
- 1;
1115 if (ptr
[0] != ':' || (ptr
[1] != 'R' && ptr
[1] != 'r'))
1117 tmp
= r1c1_get_index (ptr
+1, a_ss
,
1118 &res
->b
.row
, &res
->b
.row_relative
,
1121 return ptr
; /* fallback to just the initial R */
1124 ptr
= r1c1_get_index (ptr
, a_ss
,
1125 &res
->a
.col
, &res
->a
.col_relative
,
1132 if (ptr
[0] != ':' || (ptr
[1] != 'R' && ptr
[1] != 'r') ||
1133 NULL
== (tmp
= r1c1_get_index (ptr
+1, b_ss
,
1134 &res
->b
.row
, &res
->b
.row_relative
, FALSE
)) ||
1135 (*tmp
!= 'C' && *tmp
!= 'c') ||
1136 NULL
== (tmp
= r1c1_get_index (tmp
, b_ss
,
1137 &res
->b
.col
, &res
->b
.col_relative
, FALSE
)))
1140 } else if (*ptr
== 'C' || *ptr
== 'c') {
1141 if (NULL
== (ptr
= r1c1_get_index (ptr
, a_ss
,
1142 &res
->a
.col
, &res
->a
.col_relative
, TRUE
)))
1144 if (g_ascii_isalpha (*ptr
))
1147 res
->a
.row_relative
= FALSE
;
1150 res
->b
.row
= b_ss
->max_rows
- 1;
1151 if (ptr
[0] != ':' || (ptr
[1] != 'C' && ptr
[1] != 'c'))
1153 tmp
= r1c1_get_index (ptr
, b_ss
,
1154 &res
->b
.col
, &res
->b
.col_relative
,
1157 return ptr
; /* fallback to just the initial C */
1165 * rangeref_parse: (skip)
1166 * @res: (out): #GnmRangeRef
1167 * @start: the start of the string to parse
1168 * @pp: the location to parse relative to
1169 * @convs: #GnmConventions
1171 * Returns: (transfer none): a pointer to the first invalid character.
1172 * If the result != @start then @res is valid.
1175 rangeref_parse (GnmRangeRef
*res
, char const *start
, GnmParsePos
const *pp
,
1176 GnmConventions
const *convs
)
1178 char const *ptr
= start
, *start_sheet
, *start_wb
, *tmp1
, *tmp2
;
1181 Sheet
*a_sheet
, *b_sheet
;
1182 GnmSheetSize
const *a_ss
, *b_ss
;
1184 g_return_val_if_fail (start
!= NULL
, start
);
1185 g_return_val_if_fail (pp
!= NULL
, start
);
1188 ref_wb
= wb
? wb
: pp
->sheet
->workbook
;
1190 start_sheet
= wbref_parse (convs
, start
, &wb
, ref_wb
);
1191 if (start_sheet
== NULL
)
1192 return start
; /* TODO error unknown workbook */
1193 ptr
= sheetref_parse (convs
, start_sheet
, &res
->a
.sheet
, wb
, TRUE
);
1195 return start
; /* TODO error unknown sheet */
1196 if (ptr
!= start_sheet
) {
1199 if (*ptr
== ':') { /* 3d ref */
1200 ptr
= sheetref_parse (convs
, ptr
+1, &res
->b
.sheet
, wb
, FALSE
);
1202 return start
; /* TODO error unknown sheet */
1204 res
->b
.sheet
= NULL
;
1207 return start
; /* TODO syntax error */
1209 ref
= value_error_name (GNM_ERROR_REF
, FALSE
);
1210 if (strncmp (ptr
, ref
, strlen (ref
)) == 0) {
1211 res
->a
.sheet
= invalid_sheet
;
1212 res
->a
.col
= res
->a
.row
= 0;
1213 res
->a
.col_relative
= res
->a
.row_relative
= FALSE
;
1214 res
->b
.sheet
= res
->a
.sheet
;
1215 ptr
+= strlen (ref
);
1219 if (start_sheet
!= start_wb
)
1220 return start
; /* Workbook, but no sheet. */
1221 res
->b
.sheet
= NULL
;
1224 if (convs
->r1c1_addresses
) { /* R1C1 handler */
1225 const char *tmp1
= r1c1_rangeref_parse (res
, ptr
, pp
);
1226 return (tmp1
!= NULL
) ? tmp1
: start
;
1229 a_sheet
= eval_sheet (res
->a
.sheet
, pp
->sheet
);
1230 b_sheet
= eval_sheet (res
->b
.sheet
, a_sheet
);
1232 a_ss
= gnm_sheet_get_size2 (a_sheet
, pp
->wb
);
1233 b_ss
= gnm_sheet_get_size2 (b_sheet
, pp
->wb
);
1235 tmp1
= col_parse (ptr
, a_ss
, &res
->a
.col
, &res
->a
.col_relative
);
1236 if (tmp1
== NULL
) { /* check for row only ref 2:3 */
1237 tmp1
= row_parse (ptr
, a_ss
,
1238 &res
->a
.row
, &res
->a
.row_relative
);
1239 if (!tmp1
|| *tmp1
++ != ':') /* row only requires : even for singleton */
1241 tmp2
= row_parse (tmp1
, b_ss
,
1242 &res
->b
.row
, &res
->b
.row_relative
);
1245 res
->a
.col_relative
= res
->b
.col_relative
= FALSE
;
1247 res
->b
.col
= b_ss
->max_cols
- 1;
1248 if (res
->a
.row_relative
)
1249 res
->a
.row
-= pp
->eval
.row
;
1250 if (res
->b
.row_relative
)
1251 res
->b
.row
-= pp
->eval
.row
;
1255 tmp2
= row_parse (tmp1
, a_ss
, &res
->a
.row
, &res
->a
.row_relative
);
1256 if (tmp2
== NULL
) { /* check for col only ref B:C or R1C1 style */
1257 if (*tmp1
++ != ':') /* col only requires : even for singleton */
1259 tmp2
= col_parse (tmp1
, a_ss
,
1260 &res
->b
.col
, &res
->b
.col_relative
);
1263 res
->a
.row_relative
= res
->b
.row_relative
= FALSE
;
1265 res
->b
.row
= b_ss
->max_rows
- 1;
1266 if (res
->a
.col_relative
)
1267 res
->a
.col
-= pp
->eval
.col
;
1268 if (res
->b
.col_relative
)
1269 res
->b
.col
-= pp
->eval
.col
;
1273 if (res
->a
.col_relative
)
1274 res
->a
.col
-= pp
->eval
.col
;
1275 if (res
->a
.row_relative
)
1276 res
->a
.row
-= pp
->eval
.row
;
1282 tmp1
= col_parse (ptr
+1, b_ss
, &res
->b
.col
, &res
->b
.col_relative
);
1284 goto singleton
; /* strange, but valid singleton */
1285 tmp2
= row_parse (tmp1
, b_ss
, &res
->b
.row
, &res
->b
.row_relative
);
1287 goto singleton
; /* strange, but valid singleton */
1289 if (res
->b
.col_relative
)
1290 res
->b
.col
-= pp
->eval
.col
;
1291 if (res
->b
.row_relative
)
1292 res
->b
.row
-= pp
->eval
.row
;
1296 res
->b
.col
= res
->a
.col
;
1297 res
->b
.row
= res
->a
.row
;
1298 res
->b
.col_relative
= res
->a
.col_relative
;
1299 res
->b
.row_relative
= res
->a
.row_relative
;
1303 /* ------------------------------------------------------------------------- */
1306 std_expr_func_handler (GnmConventionsOut
*out
, GnmExprFunction
const *func
)
1308 char const *name
= gnm_func_get_name (func
->func
,
1309 out
->convs
->localized_function_names
);
1310 GString
*target
= out
->accum
;
1312 g_string_append (target
, name
);
1313 /* FIXME: possibly a space here. */
1314 gnm_expr_list_as_string (func
->argc
, func
->argv
, out
);
1318 std_expr_name_handler (GnmConventionsOut
*out
, GnmExprName
const *name
)
1320 GnmNamedExpr
const *thename
= name
->name
;
1321 GString
*target
= out
->accum
;
1323 if (!expr_name_is_active (thename
)) {
1324 g_string_append (target
,
1325 value_error_name (GNM_ERROR_REF
,
1326 out
->convs
->output
.translated
));
1330 if (name
->optional_scope
!= NULL
) {
1331 Workbook
*out_wb
= out
->pp
->wb
1333 : out
->pp
->sheet
->workbook
;
1334 if (name
->optional_scope
->workbook
!= out_wb
) {
1335 char *rel_uri
= wb_rel_uri (name
->optional_scope
->workbook
, out_wb
);
1336 g_string_append_c (target
, '[');
1337 g_string_append (target
, rel_uri
);
1338 g_string_append_c (target
, ']');
1341 g_string_append (target
, name
->optional_scope
->name_quoted
);
1342 g_string_append_unichar (target
, out
->convs
->sheet_name_sep
);
1344 } else if (out
->pp
->sheet
!= NULL
&&
1345 thename
->pos
.sheet
!= NULL
&&
1346 thename
->pos
.sheet
!= out
->pp
->sheet
) {
1347 g_string_append (target
, thename
->pos
.sheet
->name_quoted
);
1348 g_string_append_unichar (target
, out
->convs
->sheet_name_sep
);
1349 } else if (out
->pp
->sheet
&&
1350 thename
->pos
.sheet
== NULL
&&
1351 expr_name_lookup (out
->pp
, expr_name_name (thename
)) != thename
) {
1352 /* Special syntax for global names shadowed by sheet names. */
1353 g_string_append (target
, "[]");
1356 g_string_append (target
, expr_name_name (thename
));
1360 std_output_string (GnmConventionsOut
*out
, GOString
const *str
)
1362 go_strescape (out
->accum
, str
->str
);
1365 /* ------------------------------------------------------------------------- */
1368 std_sheet_name_quote (GnmConventions
const *convs
,
1371 gunichar uc
= g_utf8_get_char (str
);
1372 GString
*res
= g_string_sized_new (20);
1377 if (g_ascii_isalpha (uc
)) {
1381 } else if (g_unichar_isalpha (uc
) || uc
== '_') {
1384 p
= g_utf8_next_char (str
);
1388 /* FIXME: What about '?' and '\\'. I cannot enter those. */
1390 for (; *p
; p
= g_utf8_next_char (p
)) {
1391 uc
= g_utf8_get_char (p
);
1393 if (g_ascii_isalpha (uc
)) {
1396 } else if (g_ascii_isdigit (uc
)) {
1399 } else if (uc
== '.' || uc
== '_' || g_unichar_isalpha (uc
))
1400 nletters
= ndigits
= -1;
1406 static const GnmSheetSize max_size
= {
1407 GNM_MAX_COLS
, GNM_MAX_ROWS
1410 * Excel also quotes things that look like cell references.
1411 * Precisely, check for a match against
1412 * ([A-Za-z]+)0*([1-9][0-9]*)
1413 * where $1 is a valid column name and $2 is a valid row
1414 * number. (The 0* is an Excel bug.)
1418 unsigned char col_relative
, row_relative
;
1419 if (!col_parse (str
, &max_size
, &col
, &col_relative
))
1425 if (!row_parse (p
, &max_size
, &row
, &row_relative
))
1432 g_string_append (res
, str
);
1436 g_string_append_c (res
, '\'');
1437 /* This is UTF-8 safe. */
1438 for (; *str
; str
++) {
1440 if (c
== '\'' || c
== '\\')
1441 g_string_append_c (res
, '\\');
1442 g_string_append_c (res
, c
);
1444 g_string_append_c (res
, '\'');
1450 std_name_parser (char const *str
,
1451 G_GNUC_UNUSED GnmConventions
const *convs
)
1453 gunichar uc
= g_utf8_get_char (str
);
1455 if (!g_unichar_isalpha (uc
) && uc
!= '_' && uc
!= '\\')
1459 str
= g_utf8_next_char (str
);
1460 uc
= g_utf8_get_char (str
);
1461 } while (g_unichar_isalnum (uc
) ||
1470 static GnmExpr
const *
1471 std_func_map (GnmConventions
const *convs
, Workbook
*scope
,
1472 char const *name
, GnmExprList
*args
)
1474 GnmFunc
*f
= convs
->localized_function_names
1475 ? gnm_func_lookup_localized (name
, scope
)
1476 : gnm_func_lookup (name
, scope
);
1479 f
= convs
->localized_function_names
1480 ? gnm_func_add_placeholder_localized (NULL
, name
)
1481 : gnm_func_add_placeholder_localized (name
, NULL
);
1484 return gnm_expr_new_funcall (f
, args
);
1488 std_external_wb (G_GNUC_UNUSED GnmConventions
const *convs
,
1490 const char *wb_name
)
1492 const char *ref_uri
= ref_wb
? go_doc_get_uri ((GODoc
*)ref_wb
) : NULL
;
1493 return gnm_app_workbook_get_by_name (wb_name
, ref_uri
);
1497 std_string_parser (char const *in
, GString
*target
,
1498 G_GNUC_UNUSED GnmConventions
const *convs
)
1500 return go_strunescape (target
, in
);
1504 * gnm_conventions_new_full:
1507 * Construct a GnmConventions of @size.
1509 * Returns: (transfer full): A #GnmConventions with default values.
1512 gnm_conventions_new_full (unsigned size
)
1514 GnmConventions
*convs
;
1516 g_return_val_if_fail (size
>= sizeof (GnmConventions
), NULL
);
1518 convs
= g_malloc0 (size
);
1519 convs
->ref_count
= 1;
1521 convs
->r1c1_addresses
= FALSE
;
1522 convs
->localized_function_names
= FALSE
;
1524 convs
->sheet_name_sep
= '!';
1525 convs
->intersection_char
= ' ';
1526 convs
->exp_is_left_associative
= FALSE
;
1527 convs
->input
.range_ref
= rangeref_parse
;
1528 convs
->input
.string
= std_string_parser
;
1529 convs
->input
.name
= std_name_parser
;
1530 convs
->input
.name_validate
= expr_name_validate
;
1531 convs
->input
.func
= std_func_map
;
1532 convs
->input
.external_wb
= std_external_wb
;
1534 convs
->output
.decimal_digits
= -1;
1535 convs
->output
.translated
= TRUE
;
1536 convs
->output
.string
= std_output_string
;
1537 convs
->output
.name
= std_expr_name_handler
;
1538 convs
->output
.func
= std_expr_func_handler
;
1539 convs
->output
.cell_ref
= cellref_as_string
;
1540 convs
->output
.range_ref
= rangeref_as_string
;
1541 convs
->output
.boolean
= NULL
;
1542 convs
->output
.quote_sheet_name
= std_sheet_name_quote
;
1548 * gnm_conventions_new:
1550 * A convenience wrapper around gnm_conventions_new_full
1551 * that constructs a GnmConventions of std size.
1553 * Returns: (transfer full): A #GnmConventions with default values.
1556 gnm_conventions_new (void)
1558 return gnm_conventions_new_full (sizeof (GnmConventions
));
1562 * gnm_conventions_unref: (skip)
1563 * @c: (transfer full): #GnmConventions
1565 * Release a reference to a #GnmConvention
1568 gnm_conventions_unref (GnmConventions
*c
)
1573 g_return_if_fail (c
->ref_count
> 0);
1576 if (c
->ref_count
> 0)
1583 * gnm_conventions_ref: (skip)
1584 * @c: (transfer none) (nullable): #GnmConventions
1586 * Returns: (transfer full) (nullable): a new reference to @c
1589 gnm_conventions_ref (GnmConventions
const *c
)
1591 GnmConventions
*uc
= (GnmConventions
*)c
;
1598 gnm_conventions_get_type (void)
1603 t
= g_boxed_type_register_static ("GnmConventions",
1604 (GBoxedCopyFunc
)gnm_conventions_ref
,
1605 (GBoxedFreeFunc
)gnm_conventions_unref
);
1610 /* ------------------------------------------------------------------------- */
1612 GnmConventions
const *gnm_conventions_default
;
1613 GnmConventions
const *gnm_conventions_xls_r1c1
;
1616 parse_util_init (void)
1618 GnmConventions
*convs
;
1620 convs
= gnm_conventions_new ();
1621 convs
->range_sep_colon
= TRUE
;
1622 convs
->r1c1_addresses
= FALSE
;
1623 /* Not ready for general use yet. */
1624 convs
->localized_function_names
= g_getenv ("GNM_LOCAL_FUNCS") != NULL
;
1625 gnm_conventions_default
= convs
;
1627 convs
= gnm_conventions_new ();
1628 convs
->range_sep_colon
= TRUE
;
1629 convs
->r1c1_addresses
= TRUE
;
1630 convs
->localized_function_names
= gnm_conventions_default
->localized_function_names
;
1631 gnm_conventions_xls_r1c1
= convs
;
1635 parse_util_shutdown (void)
1637 gnm_conventions_unref ((GnmConventions
*)gnm_conventions_default
);
1638 gnm_conventions_default
= NULL
;
1639 gnm_conventions_unref ((GnmConventions
*)gnm_conventions_xls_r1c1
);
1640 gnm_conventions_xls_r1c1
= NULL
;
1643 /* ------------------------------------------------------------------------- */
1645 * gnm_expr_conv_quote:
1646 * @convs: #GnmConventions
1647 * @str: string to quote
1649 * Returns: (transfer full): A quoted string according to @convs. If no
1650 * quoting is necessary, a literal copy of @str will be returned.
1653 gnm_expr_conv_quote (GnmConventions
const *convs
,
1656 g_return_val_if_fail (convs
!= NULL
, NULL
);
1657 g_return_val_if_fail (convs
->output
.quote_sheet_name
!= NULL
, NULL
);
1658 g_return_val_if_fail (str
!= NULL
, NULL
);
1659 g_return_val_if_fail (str
[0] != 0, NULL
);
1661 return convs
->output
.quote_sheet_name (convs
, str
);