Skip to content

Commit 1c4e03c

Browse files
authored
Fixes and optimizations to compiler (browserengineering#226)
* Fix import of constants * Optimize text metrics call in rt.js * Fix tests for new constants import code
1 parent a1731ac commit 1c4e03c

File tree

6 files changed

+55
-35
lines changed

6 files changed

+55
-35
lines changed

infra/compile.py

+16-5
Original file line numberDiff line numberDiff line change
@@ -533,15 +533,16 @@ def compile_expr(tree, ctx):
533533
elif isinstance(tree, ast.Tuple) or isinstance(tree, ast.List):
534534
return "[" + ", ".join([compile_expr(a, ctx) for a in tree.elts]) + "]"
535535
elif isinstance(tree, ast.Name):
536-
assert tree.id == "self" or tree.id in ctx or tree.id in LAB_IMPORT_CLASSES, f"Could not find variable {tree.id}"
537536
if tree.id == "self":
538537
return "this"
539538
elif tree.id in RT_IMPORTS or tree.id in LAB_IMPORT_CLASSES:
540539
return tree.id
541-
elif ctx.is_global_constant(tree.id):
540+
elif ctx.is_global_constant(tree.id) or tree.id in LAB_IMPORT_CONSTANTS:
542541
return "constants.{}".format(tree.id)
543-
else:
542+
elif tree.id in ctx:
544543
return tree.id
544+
else:
545+
raise AssertionError(f"Could not find variable {tree.id}")
545546
elif isinstance(tree, ast.Constant):
546547
if isinstance(tree.value, str):
547548
return compile_str(tree.value)
@@ -592,16 +593,26 @@ def compile(tree, ctx, indent=0):
592593
with open(filename) as file:
593594
outline = outlines.outline(AST39.parse(file.read(), filename))
594595

596+
to_import = []
597+
to_bind = []
595598
for name in names:
596599
if name.isupper(): # Global constant
597600
LAB_IMPORT_CONSTANTS.append(name)
598-
if name[0].isupper(): # Class
601+
to_import.append("constants as {}_constants".format(tree.module))
602+
to_bind.append(name)
603+
elif name[0].isupper(): # Class
599604
LAB_IMPORT_CLASSES.append(name)
600605
load_outline_for_class(outline, name)
606+
to_import.append(name)
601607
else: # function
602608
LAB_IMPORT_FNS.append(name)
609+
to_import.append(name)
603610

604-
return " " * indent + "import {{ {} }} from \"./{}.js\";".format(", ".join(names), tree.module)
611+
import_line = "import {{ {} }} from \"./{}.js\";".format(", ".join(sorted(set(to_import))), tree.module)
612+
out = " " * indent + import_line
613+
for const in to_bind:
614+
out += "\n" + " " * indent + "constants.{} = {}_constants.{};".format(const, tree.module, const)
615+
return out
605616
elif isinstance(tree, ast.ClassDef):
606617
assert not tree.bases
607618
assert not tree.keywords

infra/compiler.md

+11-3
Original file line numberDiff line numberDiff line change
@@ -282,14 +282,22 @@ deduplicate the code a bit:
282282
import { request } from "./lab1.js";
283283
>>> assert "request" in LAB_IMPORT_FNS
284284
>>> Test.stmt("from lab2 import HSTEP")
285-
import { HSTEP } from "./lab2.js";
285+
import { constants as lab2_constants } from "./lab2.js";
286+
constants.HSTEP = lab2_constants.HSTEP;
286287
>>> assert "HSTEP" in LAB_IMPORT_CONSTANTS
287288
>>> Test.stmt("from lab4 import Element")
288289
import { Element } from "./lab4.js";
289-
>>> assert "HSTEP" in LAB_IMPORT_CLASSES
290+
>>> assert "Element" in LAB_IMPORT_CLASSES
290291
>>> Test.stmt("from lab2 import WIDTH, HEIGHT, HSTEP, VSTEP")
291-
import { WIDTH, HEIGHT, HSTEP, VSTEP } from "./lab2.js";
292+
import { constants as lab2_constants } from "./lab2.js";
293+
constants.WIDTH = lab2_constants.WIDTH;
294+
constants.HEIGHT = lab2_constants.HEIGHT;
295+
constants.HSTEP = lab2_constants.HSTEP;
296+
constants.VSTEP = lab2_constants.VSTEP;
292297
>>> assert "WIDTH" in LAB_IMPORT_CONSTANTS
298+
299+
Note that `from ... import` statements for constants are complex, due
300+
to the use of a global collector object for constants.
293301

294302
Functions become function definitions:
295303

src/lab6.hints

-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,5 @@
22
{"code": "'://' in url", "type": "str"},
33
{"code": "self.s[self.i] in chars", "type": "list"},
44
{"code": "'style' in node.attributes", "type": "dict"},
5-
{"code": "child.tag in BLOCK_ELEMENTS", "type": "list"},
65
{"code": "'href' in node.attributes", "type": "dict"}
76
]

src/lab6.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,8 @@ def matches(self, node):
147147
return False
148148

149149
def __repr__(self):
150-
return ("DescendantSelector(ancestor={}, " +
151-
"descendant={}, priority={}").format(
152-
self.ancestor, self.descendant, self.priority)
150+
return ("DescendantSelector(ancestor={}, descendant={}, priority={}") \
151+
.format(self.ancestor, self.descendant, self.priority)
153152

154153
INHERITED_PROPERTIES = {
155154
"font-size": "16px",

src/lab7.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@
1313
from lab3 import FONTS, get_font
1414
from lab4 import Text, Element, print_tree, HTMLParser
1515
from lab5 import BLOCK_ELEMENTS, layout_mode, DrawRect
16-
from lab6 import DocumentLayout, BlockLayout, InlineLayout, DrawText
17-
from lab6 import CSSParser, cascade_priority, style, resolve_url, tree_to_list
16+
from lab6 import DrawText, CSSParser, cascade_priority, style, resolve_url, tree_to_list
1817

1918
class LineLayout:
2019
def __init__(self, node, parent, previous):

www/widgets/rt.js

+25-21
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,8 @@ static tkinter(options) {
223223
this.style = params.style ?? "normal";
224224
this.string = (this.style == "roman" ? "normal" : this.style) +
225225
" " + this.weight + " " + this.size * rt_constants.ZOOM + "px serif";
226+
227+
this.$metrics = null;
226228
}
227229

228230
measure(text) {
@@ -232,28 +234,30 @@ static tkinter(options) {
232234
}
233235

234236
metrics(field) {
235-
let ctx = rt_constants.TKELEMENT.getContext('2d');
236-
ctx.textBaseline = "alphabetic";
237-
ctx.font = this.string;
238-
let m = ctx.measureText("Hxy");
239-
let asc, desc;
240-
241-
// Only Safari provides emHeight properties as of 2021-04
242-
// We fake them in the other browsers by guessing that emHeight = font.size
243-
// This is not quite right but is close enough for many fonts...
244-
if (m.emHeightAscent && m.emHeightDescent) {
245-
asc = ctx.measureText("Hxy").emHeightAscent / rt_constants.ZOOM;
246-
desc = ctx.measureText("Hxy").emHeightDescent / rt_constants.ZOOM;
247-
} else {
248-
asc = ctx.measureText("Hxy").actualBoundingBoxAscent / rt_constants.ZOOM;
249-
desc = ctx.measureText("Hxy").actualBoundingBoxDescent / rt_constants.ZOOM;
250-
let gap = this.size - (asc + desc)
251-
asc += gap / 2;
252-
desc += gap / 2;
237+
if (!this.$metrics) {
238+
let ctx = rt_constants.TKELEMENT.getContext('2d');
239+
ctx.textBaseline = "alphabetic";
240+
ctx.font = this.string;
241+
let m = ctx.measureText("Hxy");
242+
let asc, desc;
243+
244+
// Only Safari provides emHeight properties as of 2021-04
245+
// We fake them in the other browsers by guessing that emHeight = font.size
246+
// This is not quite right but is close enough for many fonts...
247+
if (m.emHeightAscent && m.emHeightDescent) {
248+
asc = ctx.measureText("Hxy").emHeightAscent / rt_constants.ZOOM;
249+
desc = ctx.measureText("Hxy").emHeightDescent / rt_constants.ZOOM;
250+
} else {
251+
asc = ctx.measureText("Hxy").actualBoundingBoxAscent / rt_constants.ZOOM;
252+
desc = ctx.measureText("Hxy").actualBoundingBoxDescent / rt_constants.ZOOM;
253+
let gap = this.size - (asc + desc)
254+
asc += gap / 2;
255+
desc += gap / 2;
256+
}
257+
this.$metrics = { ascent: asc, descent: desc, linespace: asc + desc, fixed: 0 };
253258
}
254-
let obj = { ascent: asc, descent: desc, linespace: asc + desc, fixed: 0 };
255-
if (field) return obj[field]
256-
else return obj;
259+
if (field) return this.$metrics[field]
260+
else return this.$metrics;
257261
}
258262
}
259263

0 commit comments

Comments
 (0)