Opened 9 years ago

Closed 8 years ago

#2770 closed defect (fixed)

r.mapcalc reports syntax error for valid script but no error for separate expressions

Reported by: marisn Owned by: grass-dev@…
Priority: normal Milestone: 7.0.4
Component: Raster Version: svn-trunk
Keywords: r.mapcalc Cc:
CPU: Unspecified Platform: Unspecified

Description

Seems that r.mapcalc doesn't like some sequences of valid expressions. Following examples show the same expressions copy/pasted into interactive r.mapcalc session all together and one by one. Notice - same syntax once causes "syntax error" but no "syntax error" is generated if execution happens to be performed on a separate r.mapcalc process.

r.mapcalc --overwrite
Enter expressions, "end" when done.
mapcalc> speed_up = if(speed < 5, speed + 1, speed)
mapcalc> max_speed = if( !isnull(speed[0,1]), 0,\
mapcalc>             if( !isnull(speed[0,2]), 1,\
mapcalc>             if( !isnull(speed[0,3]), 2,\
mapcalc>             if( !isnull(speed[0,4]), 3,\
mapcalc>             if( !isnull(speed[0,5]), 4, 5)))))
mapcalc> res_speed = if(speed_up > max_speed, max_speed, speed_up)
mapcalc> moved = if( res_speed[0, 0] == 0, 0,\
syntax error, unexpected '[', expecting ')'
mapcalc>         if( res_speed[0,-1] == 1, 1,\
mapcalc>         if( res_speed[0,-2] == 2, 2,\
mapcalc>         if( res_speed[0,-3] == 3, 3,\
mapcalc>         if( res_speed[0,-4] == 4, 4,\
mapcalc>         if( res_speed[0,-5] == 5, 5, null()))))))
mapcalc> end
Parse error

r.mapcalc --overwrite
Enter expressions, "end" when done.
mapcalc> speed_up = if(speed < 5, speed + 1, speed)
mapcalc> end

 100%
GRASS 7.1.svn (xy_loc):~/soft/grass_trunk > r.mapcalc --overwrite
Enter expressions, "end" when done.
mapcalc> max_speed = if( !isnull(speed[0,1]), 0,\
mapcalc>             if( !isnull(speed[0,2]), 1,\
mapcalc>             if( !isnull(speed[0,3]), 2,\
mapcalc>             if( !isnull(speed[0,4]), 3,\
mapcalc>             if( !isnull(speed[0,5]), 4, 5)))))
mapcalc> end

 100%
GRASS 7.1.svn (xy_loc):~/soft/grass_trunk > r.mapcalc --overwrite
Enter expressions, "end" when done.
mapcalc> res_speed = if(speed_up > max_speed, max_speed, speed_up)
mapcalc> end

 100%
GRASS 7.1.svn (xy_loc):~/soft/grass_trunk > r.mapcalc --overwrite
Enter expressions, "end" when done.
mapcalc> moved = if( res_speed[0, 0] == 0, 0,\
mapcalc>         if( res_speed[0,-1] == 1, 1,\
mapcalc>         if( res_speed[0,-2] == 2, 2,\
mapcalc>         if( res_speed[0,-3] == 3, 3,\
mapcalc>         if( res_speed[0,-4] == 4, 4,\
mapcalc>         if( res_speed[0,-5] == 5, 5, null()))))))
mapcalc> end

 100%

Change History (6)

in reply to:  description comment:1 by glynn, 9 years ago

Replying to marisn:

Seems that r.mapcalc doesn't like some sequences of valid expressions. Following examples show the same expressions copy/pasted into interactive r.mapcalc session all together and one by one. Notice - same syntax once causes "syntax error" but no "syntax error" is generated if execution happens to be performed on a separate r.mapcalc process.

This is not a bug. It could really do with better documentation, though.

A "name" (*) refers to a variable if it has previously occurred on the left-hand side of an assignment expression, and to an input map otherwise.

The neighbourhood modifier "map[row,col]" can only be applied to input maps, not to variables. Likewise for the @ and # modifiers.

Each assignment expression which occurs at the top level (i.e. not nested within a sub-expression) creates a variable, and also results in the creation of an output map. An assignment other than at the top level only creates a variable, not an output map. At the end of processing each row, any variables defined at the top level have their value written as the current row of the corresponding output map.

Any maps generated by an r.mapcalc command only exist after the entire command has completed. All maps are generated concurrently, row-by-row (i.e. there is an implicit "for row in rows {...}" around the entire expression).

At present, it's impossible to read from a map which is in the process of being created. And even if it was possible, reading from such a map with a positive row offset would be awkward; in the worst case, two maps could each depend upon not-yet-calculated rows of each other.

(*) A name is either a) any sequence of (letters, digits, any of the symbols dollar, period, backslash, underscore, backtick, left brace, right brace, or any character >= 127), excluding anything with a prefix which can be parsed as a number, or b) any sequence of characters delimited by single or double quotes.

(**) The set of characters which are allowed without quoting is probably too liberal; AFAICT, it's essentially anything that doesn't result in the grammar being ambiguous.

comment:2 by marisn, 8 years ago

Milestone: 7.0.3

If I understood correctly, r.mapcalc works like this:

for output_row in computational_region:
    for output_cell in output_row:
        first expression
        second expression
        third expression

thus "first" becomes a map only at the end of r.mapcalc run.

Leaving open as an update to documentation is needed (I'll try to write it if above logic is correct).

in reply to:  2 comment:3 by glynn, 8 years ago

Replying to marisn:

If I understood correctly, r.mapcalc works like this:

for output_row in computational_region:
    for output_cell in output_row:
        first expression
        second expression
        third expression

That's close enough for the purpose of this discussion.

But in the interests of pedantry accuracy, I'll point out that the "for output_cell" loop actually occurs within the evaluation of each expression and subexpression. All of the C functions used to implement r.mapcalc's operators and functions take entire rows as arguments and return an entire row as their result.

thus "first" becomes a map only at the end of r.mapcalc run.

Well, it's opened as an output map prior to reading any input; but output maps only become visible (or, if they already exist, the changes only become visible) when they are closed.

An "identifier" is treated as an input map if and only if it hasn't previously occurred on the left-hand side of an "=" anywhere in the input expression(s). If it has so occurred, then it's treated as a variable, regardless of whether any map with that name already exists, and regardless of whether the assignment occurs at the top level (creating an output map) or nested within an expression (e.g. as an argument to eval()).

comment:4 by neteler, 8 years ago

Milestone: 7.0.3

Ticket retargeted after milestone closed

comment:5 by neteler, 8 years ago

Milestone: 7.0.4

Ticket retargeted after 7.0.3 milestone closed

comment:6 by marisn, 8 years ago

Resolution: fixed
Status: newclosed

Probably documentation needs even more improvements, but at least part of explanations have been moved to the manual (r68268 and r68269).

Closing as "fixed" as the main issue was in the documentation and has been fixed there.

Note: See TracTickets for help on using tickets.