# deparse_call

TODO: reorganize, put failing alternatives on top, then really wrong stuff, then inaccuracies then readability.

`constructive::deparse_call()` converts calls (language objects) to code. It is an alternative to `base::deparse()` or `rlang::expr_deparse()` with a slightly different scope, and 3 main differences:

- `deparse_call()` faisl if the call is not syntactic (if it cannot be the output of `parse(text=x)[[1]]`), for instance if its AST contains elements that are not syntactic tokens

    x <- call('+', c(1, 2))
    base::deparse(x)
    #> [1] "+c(1, 2)"
    rlang::expr_deparse(x)
    #> [1] "+<dbl: 1, 2>"
    constructive::deparse_call(x)
    #> Error in `constructive::deparse_call()`:
    #> ! `call` must only be made of symbols and syntactic literals
    #> Caused by error in `deparse_call_impl()`:
    #> ! Found element of type 'double' and length '2':
    #> c(1, 2)

    # this is different
    y <- quote(+c(1, 2))
    x[[2]]
    #> [1] 1 2
    y[[2]]
    #> c(1, 2)

- `deparse_call()` never makes compromises to make code more readable at the expense of accuracy.

    x <- quote(`*`(a + b, c))
    base::deparse(x)
    #> [1] "(a + b) * c"
    rlang::expr_deparse(x)
    #> [1] "(a + b) * c"
    constructive::deparse_call(x)
    #> `*`(a + b, c)

    y <- quote((a + b) * c)
    base::deparse(y)
    #> [1] "(a + b) * c"
    rlang::expr_deparse(y)
    #> [1] "(a + b) * c"
    constructive::deparse_call(y)
    #> (a + b) * c

    # x and y are different, parentheses are code!
    x[[2]]
    #> a + b
    y[[2]]
    #> (a + b)

- `deparse_call()` handles many more contrived cases. It strives to provide an accurate syntactic representation for every possible syntactic language object, however unprobable or unpractical they might be.

    x <- call("[")
    base::deparse(x)
    #> [1] "NULL[]"
    rlang::expr_deparse(x)
    #> [1] "NULL[]"
    constructive::deparse_call(x)
    #> `[`()

## `deparse_call()` is more accurate

We present more differences below, where at least one of the alternatives is not deparsing faithfully.

| deparse_call() | deparse() | expr_deparse()
--- | --- | --- | ---
`call('+', c(1, 2))` cannot be obtained by parsing code | ERROR | `+c(1, 2)` | `+<dbl: 1, 2>`
Infix `::` and `:::` can only be called on symbols | ``::`(1, 2)` | `1::2` | `1::2`
Infix `$` and `@` can only have a symbol rhs | ``$`("a", 1)` | ``$`("a", 1)` | `"a"$1`
Infix `$` and `@` create different calls when rhs is symbol or string | `a$"b"` | `a$b` | `a$"b"`
Binary ops cannot be used as prefixes | ``*`(1)` | `*1` | ``*`(1)`
Binary ops cannot be used infix with > 2 args | ``*`(1, 2, 3)` | ``*`(1, 2, 3)` | `1 * 2`
Binary ops cannot be used infix with empty args | ``*`(1, )` | `1 *` | `1 *`
Parentheses need function call notation if 0 arg | ``(`()` | `(NULL)` | `(NULL)`
Parentheses need function call notation if > 1 arg | ``(`(1, 2)` | `(1)` | `(1)`
Calling `=` is different from passing an arg | `list(`=`(x, 1))` | `list((x = 1))` | `list(x = 1)`
Precedence must be respected, but adding extra parentheses to respect precedence is not accurate | ``-`(1 + 2)` | `-(1 + 2)` | `-1 + 2`
| ``+`(repeat { }, 1)` | `(repeat {` `}) + 1` | `repeat { } + 1`
| ``<-`(x <- 1, 2)` | `(x <- 1) <- 2` | `(x <- 1) <- 2`
| ``*`(a + b, c)` | `(a + b) * c` | `(a + b) * c`
| ``+`(x, y)(z)` | `(x + y)(z)` | ``+`(x, y)(z)`
| ``^`(1^2, 4)` | `(1^2)^4` | `(1^2)^4`
| ``+`(1, 2 + 3)` | `1 + (2 + 3)` | `1 + (2 + 3)`
Brackets calling no arg is different from subsetting NULL | ``[`()` | `NULL[]` | `NULL[]`
Empty bracket syntax means doesn’t mean no 2nd arg, it means 2nd arg is empty symbol, so for 1 arg we need function notation | ``[`(x)` | `x[]` | `x[]`
Brackets with an empty first arg need function call notation | ``[`(, )` | `[]` | `[]`
Brackets taking a call to a lower precedence op as a first arg need function call notation | ``[`(a + b, 1)` | `(a + b)[1]` | `a + b[1]`
Invalid function definitions can be valid code | ``function`(1, 2)` | ERROR | SEGFAULT
| ``function`(1(2), 3)` | `function(1, 2) 3` | ERROR
Curly braces need function call notation if they have empty args | ``{`(1, )` | `{` `1` `` `}` | `{` `1` `` `}`
Control flow constructs need function call notation if they’re used as callers | ``if`(TRUE, { })(1)` | `(if (TRUE) {` `})(1)` | ``if`(TRUE, { })(1)`
Symbols with non syntactic names need backquotes | ``*a*`` | `*a*` | ``*a*``
This includes emojis | ``\xf0\x9f\x90\xb6`` | `🐶` | ``🐶``

## `deparse_call()` is clearer

In the following `base::deparse()` and `rlang::expr_deparse()` are not wrong, but `constructive::deparse_call()` is clearer.

| constructive::deparse_call() | base::deparse() | rlang::expr_deparse()
--- | --- | --- | ---
Simple quotes make strings that use double quotes more readable | `'"oh" "hey" "there"'` | `"\"oh\" \"hey\" \"there\""` | `"\"oh\" \"hey\" \"there\""`
Raw strings make more complex strings more readable | `r"["oh"\'hey'\"there"]"` | `"\"oh\"\\'hey'\\\"there\""` | `"\"oh\"\\'hey'\\\"there\""`
Homoglyphs are dangerous, we can use the `\U{XX}` notation | `"\U{410} \U{A0} A"` | `"А A"` | `"А A"`
For symbols we need the `\xXX` notation | `c(`\xd0\x90`, "\U{A0}" = 1)` | `c(А, ` ` = 1)` | `c(А, ` ` = 1)`
Emojis depend on font so are ambiguous | `"\U{1F436}"` | `"🐶"` | `"🐶"`

## `deparse_call()` fails rather than making things up

    x <- call("(", -1)
    base::deparse(x)
    #> [1] "(-1)"
    rlang::expr_deparse(x)
    #> [1] "(-1)"
    constructive::deparse_call(x)
    #> Error in `constructive::deparse_call()`:
    #> ! `call` must only be made of symbols and syntactic literals
    #> Caused by error in `deparse_call_impl()`:
    #> ! Found element of type 'double' and length '1':
    #> -1

    # this is different! `-` is code!
    y <- quote((-1))
    base::deparse(y)
    #> [1] "(-1)"
    rlang::expr_deparse(y)
    #> [1] "(-1)"
    constructive::deparse_call(y)
    #> (-1)

    x <- call("fun", quote(expr = ))
    base::deparse(x)
    #> [1] "fun()"
    rlang::expr_deparse(x)
    #> [1] "fun()"
    constructive::deparse_call(x) # this is wrong!
    #> Error in `constructive::deparse_call()`:
    #> ! `call` must only be made of symbols and syntactic literals
    #> Caused by error in `deparse_call_impl()`:
    #> ! Found empty symbol used as sole argument of a function:
    #> as.call(list(quote(fun), quote(expr = )))

    # no agument and 1 missing argument is not the same!
    y <- call("fun")
    base::deparse(y)
    #> [1] "fun()"
    rlang::expr_deparse(y)
    #> [1] "fun()"
    constructive::deparse_call(y)
    #> fun()

    x <- call("!", quote(expr = ))
    base::deparse(x)
    #> [1] "!"
    rlang::expr_deparse(x)
    #> [1] "!"
    constructive::deparse_call(x)
    #> Error in `constructive::deparse_call()`:
    #> ! `call` must only be made of symbols and syntactic literals
    #> Caused by error in `deparse_call_impl()`:
    #> ! Found empty symbol used as sole argument of a function:
    #> as.call(list(quote(`!`), quote(expr = )))