-
Notifications
You must be signed in to change notification settings - Fork 0
/
Translation.Rmd
288 lines (219 loc) · 7.64 KB
/
Translation.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
# Translation
```{r Translation-1, include = FALSE}
source("common.R")
source("dsl-html-attributes.R")
```
Needed libraries:
```{r Translation-2, warning=FALSE,message=FALSE}
library(rlang)
library(purrr)
```
## HTML (Exercises 21.2.6)
---
**Q1.** The escaping rules for `<script>` tags are different because they contain JavaScript, not HTML. Instead of escaping angle brackets or ampersands, you need to escape `</script>` so that the tag isn't closed too early. For example, `script("'</script>'")`, shouldn't generate this:
```html
<script>'</script>'</script>
```
But
```html
<script>'<\/script>'</script>
```
Adapt the `escape()` to follow these rules when a new argument `script` is set to `TRUE`.
**A1.** Let's first start with the boilerplate code included in the book:
```{r Translation-3}
escape <- function(x, ...) UseMethod("escape")
escape.character <- function(x, script = FALSE) {
if (script) {
x <- gsub("</script>", "<\\/script>", x, fixed = TRUE)
} else {
x <- gsub("&", "&", x)
x <- gsub("<", "<", x)
x <- gsub(">", ">", x)
}
html(x)
}
escape.advr_html <- function(x, ...) x
```
We will also need to tweak the boilerplate to pass this additional parameter to `escape()`:
```{r Translation-4}
html <- function(x) structure(x, class = "advr_html")
print.advr_html <- function(x, ...) {
out <- paste0("<HTML> ", x)
cat(paste(strwrap(out), collapse = "\n"), "\n", sep = "")
}
dots_partition <- function(...) {
dots <- list2(...)
if (is.null(names(dots))) {
is_named <- rep(FALSE, length(dots))
} else {
is_named <- names(dots) != ""
}
list(
named = dots[is_named],
unnamed = dots[!is_named]
)
}
tag <- function(tag, script = FALSE) {
force(script)
new_function(
exprs(... = ),
expr({
dots <- dots_partition(...)
attribs <- html_attributes(dots$named)
children <- map_chr(.x = dots$unnamed, .f = ~ escape(.x, !!script))
html(paste0(
!!paste0("<", tag), attribs, ">",
paste(children, collapse = ""),
!!paste0("</", tag, ">")
))
}),
caller_env()
)
}
void_tag <- function(tag) {
new_function(
exprs(... = ),
expr({
dots <- dots_partition(...)
if (length(dots$unnamed) > 0) {
abort(!!paste0("<", tag, "> must not have unnamed arguments"))
}
attribs <- html_attributes(dots$named)
html(paste0(!!paste0("<", tag), attribs, " />"))
}),
caller_env()
)
}
p <- tag("p")
script <- tag("script", script = TRUE)
```
```{r Translation-5}
script("'</script>'")
```
---
**Q2.** The use of `...` for all functions has some big downsides. There's no input validation and there will be little information in the documentation or autocomplete about how they are used in the function. Create a new function that, when given a named list of tags and their attribute names (like below), creates tag functions with named arguments.
```{r Translation-6, eval = FALSE}
list(
a = c("href"),
img = c("src", "width", "height")
)
```
All tags should get `class` and `id` attributes.
---
**Q3.** Reason about the following code that calls `with_html()` referencing objects from the environment. Will it work or fail? Why? Run the code to verify your predictions.
```{r Translation-7, eval = FALSE}
greeting <- "Hello!"
with_html(p(greeting))
p <- function() "p"
address <- "123 anywhere street"
with_html(p(address))
```
**A3.** To work with this, we first need to copy-paste relevant code from the book:
```{r Translation-8}
tags <- c(
"a", "abbr", "address", "article", "aside", "audio",
"b", "bdi", "bdo", "blockquote", "body", "button", "canvas",
"caption", "cite", "code", "colgroup", "data", "datalist",
"dd", "del", "details", "dfn", "div", "dl", "dt", "em",
"eventsource", "fieldset", "figcaption", "figure", "footer",
"form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header",
"hgroup", "html", "i", "iframe", "ins", "kbd", "label",
"legend", "li", "mark", "map", "menu", "meter", "nav",
"noscript", "object", "ol", "optgroup", "option", "output",
"p", "pre", "progress", "q", "ruby", "rp", "rt", "s", "samp",
"script", "section", "select", "small", "span", "strong",
"style", "sub", "summary", "sup", "table", "tbody", "td",
"textarea", "tfoot", "th", "thead", "time", "title", "tr",
"u", "ul", "var", "video"
)
void_tags <- c(
"area", "base", "br", "col", "command", "embed",
"hr", "img", "input", "keygen", "link", "meta", "param",
"source", "track", "wbr"
)
html_tags <- c(
tags %>% set_names() %>% map(tag),
void_tags %>% set_names() %>% map(void_tag)
)
with_html <- function(code) {
code <- enquo(code)
eval_tidy(code, html_tags)
}
```
Note that `with_html()` uses `eval_tidy()`, and therefore `code` argument is evaluated first in the `html_tags` named list, which acts as a data mask, and if no object is found in the data mask, searches in the caller environment.
For this reason, the first example code will work:
```{r Translation-9}
greeting <- "Hello!"
with_html(p(greeting))
```
The following code, however, is not going to work because there is already `address` element in the data mask, and so `p()` will take a function `address()` as an input, and `escape()` doesn't know how to deal with objects of `function` type:
```{r Translation-10, error=TRUE}
"address" %in% names(html_tags)
p <- function() "p"
address <- "123 anywhere street"
with_html(p(address))
```
---
**Q4.** Currently the HTML doesn't look terribly pretty, and it's hard to see the structure. How could you adapt `tag()` to do indenting and formatting? (You may need to do some research into block and inline tags.)
**A4.** Let's first have a look at what it currently looks like:
```{r Translation-11}
with_html(
body(
h1("A heading", id = "first"),
p("Some text &", b("some bold text.")),
img(src = "myimg.png", width = 100, height = 100)
)
)
```
We can improve this to follow the [Google HTML/CSS Style Guide](https://google.github.io/styleguide/htmlcssguide.html#HTML_Formatting_Rules).
For this, we need to create a new function to indent the code conditionally:
```{r}
print.advr_html <- function(x, ...) {
cat(paste("<HTML>", x, sep = "\n"))
}
indent <- function(x) {
paste0(" ", gsub("\n", "\n ", x))
}
format_code <- function(children, indent = FALSE) {
if (indent) {
paste0("\n", paste0(indent(children), collapse = "\n"), "\n")
} else {
paste(children, collapse = "")
}
}
```
We can then update the `body()` function to use this new helper:
```{r}
html_tags$body <- function(...) {
dots <- dots_partition(...)
attribs <- html_attributes(dots$named)
children <- map_chr(dots$unnamed, escape)
html(paste0(
"<body", attribs, ">",
format_code(children, indent = TRUE),
"</body>"
))
}
```
The new formatting looks much better:
```{r}
with_html(
body(
h1("A heading", id = "first"),
p("Some text &", b("some bold text.")),
img(src = "myimg.png", width = 100, height = 100)
)
)
```
---
## LaTeX (Exercises 21.3.8)
I didn't manage to solve these exercises, and so I'd recommend checking out the solutions in the [official solutions manual](https://advanced-r-solutions.rbind.io/translating-r-code.html#latex).
---
**Q1.** Add escaping. The special symbols that should be escaped by adding a backslash in front of them are `\`, `$`, and `%`. Just as with HTML, you'll need to make sure you don't end up double-escaping. So you'll need to create a small S3 class and then use that in function operators. That will also allow you to embed arbitrary LaTeX if needed.
---
**Q2.** Complete the DSL to support all the functions that `plotmath` supports.
---
## Session information
```{r Translation-12}
sessioninfo::session_info(include_base = TRUE)
```