initial commit
161
README.md
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
# Typstuff, stuff I made with Typst <!-- omit from toc -->
|
||||||
|
|
||||||
|
This repository lists some of the stuff I have made using Typst
|
||||||
|
|
||||||
|
Smaller scripts will be included directly in this repository
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Table of contents <!-- omit from toc -->
|
||||||
|
|
||||||
|
- [RIVET](#rivet)
|
||||||
|
- [Chronos](#chronos)
|
||||||
|
- [Circuiteria](#circuiteria)
|
||||||
|
- [Functastic](#functastic)
|
||||||
|
- [Math tools](#math-tools)
|
||||||
|
- [Sankeynoa](#sankeynoa)
|
||||||
|
- [RISCV Utilities](#riscv-utilities)
|
||||||
|
- [Simple UML class diagrams](#simple-uml-class-diagrams)
|
||||||
|
- [Tasty TacOS](#tasty-tacos)
|
||||||
|
- [Leetcode trees](#leetcode-trees)
|
||||||
|
- [Cafeteria menus](#cafeteria-menus)
|
||||||
|
- [Avent of Code](#avent-of-code)
|
||||||
|
|
||||||
|
|
||||||
|
## RIVET
|
||||||
|
*Links*:
|
||||||
|
[Repo](https://git.kb28.ch/HEL/rivet-typst) /
|
||||||
|
[Universe](https://typst.app/universe/package/rivet)
|
||||||
|
|
||||||
|
*Description*:
|
||||||
|
Register / Instruction Visualizer & Explainer Tool with Typst, using CeTZ
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="images/rivet.png" width="400">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## Chronos
|
||||||
|
*Links*:
|
||||||
|
[Repo](https://git.kb28.ch/HEL/chronos) /
|
||||||
|
[Universe](https://typst.app/universe/package/chronos)
|
||||||
|
|
||||||
|
*Description*:
|
||||||
|
A Typst package to draw sequence diagrams with CeTZ
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="images/chronos.png" width="150">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## Circuiteria
|
||||||
|
*Links*:
|
||||||
|
[Repo](https://git.kb28.ch/HEL/circuiteria) /
|
||||||
|
[Universe](https://typst.app/universe/package/circuiteria)
|
||||||
|
|
||||||
|
*Description*:
|
||||||
|
Drawing block circuits with Typst made easy, using CeTZ
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="images/circuiteria.png" width="300">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## Functastic
|
||||||
|
*Links*:
|
||||||
|
[Repo](https://git.kb28.ch/HEL/functastic)
|
||||||
|
|
||||||
|
*Description*:
|
||||||
|
A Typst package to create sign tables of mathematical functions
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="images/functastic.png" width="400">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## Math tools
|
||||||
|
*Links*:
|
||||||
|
[Repo](https://git.kb28.ch/HEL/math-tools)
|
||||||
|
|
||||||
|
*Description*:
|
||||||
|
Useful mathematical functions
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="images/math_tools.png" width="400">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## Sankeynoa
|
||||||
|
*Links*:
|
||||||
|
[Repo](https://git.kb28.ch/HEL/sankeynoa)
|
||||||
|
|
||||||
|
*Description*:
|
||||||
|
Beautiful Sankey diagrams using CeTZ
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="images/sankeynoa.png" width="400">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## RISCV Utilities
|
||||||
|
*Links*:
|
||||||
|
[Directory](./misc/riscv/)
|
||||||
|
|
||||||
|
*Description*:
|
||||||
|
Some RISCV utilities to show instructions
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="images/riscv.png" width="500">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## Simple UML class diagrams
|
||||||
|
*Links*:
|
||||||
|
[Directory](./misc/uml/)
|
||||||
|
|
||||||
|
*Description*:
|
||||||
|
Functions do make simple class diagrams and directly parse UML
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="images/uml.png" width="400">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## Tasty TacOS
|
||||||
|
*Links*:
|
||||||
|
[Repo](https://git.kb28.ch/HEL/tasty-tacos)
|
||||||
|
|
||||||
|
*Description*:
|
||||||
|
Various algorithms from the OS course
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="images/tasty_tacos.png" width="500">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## Leetcode trees
|
||||||
|
*Links*:
|
||||||
|
[Directory](./misc/leetcode_trees/)
|
||||||
|
|
||||||
|
*Description*:
|
||||||
|
Function to easily display leetcode tree exercises in a CeTZ canvas
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="images/leetcode_trees.png" width="300">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## Cafeteria menus
|
||||||
|
*Links*:
|
||||||
|
[Directory](./misc/menus/)
|
||||||
|
|
||||||
|
*Description*:
|
||||||
|
Cafeteria menus for the week, used by a Discord and a Telegram bot
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="images/menus.png" width="500">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## Avent of Code
|
||||||
|
*Links*:
|
||||||
|
[Repo](https://git.kb28.ch/HEL/AdventOfCode2024)
|
||||||
|
|
||||||
|
*Description*:
|
||||||
|
My attempt at Advent of Code (2024)
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="images/aoc.png" width="300">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
BIN
images/aoc.png
Normal file
After Width: | Height: | Size: 111 KiB |
BIN
images/chronos.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
images/circuiteria.png
Normal file
After Width: | Height: | Size: 158 KiB |
BIN
images/functastic.png
Normal file
After Width: | Height: | Size: 122 KiB |
BIN
images/leetcode_trees.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
images/math_tools.png
Normal file
After Width: | Height: | Size: 129 KiB |
BIN
images/menus.png
Normal file
After Width: | Height: | Size: 173 KiB |
BIN
images/riscv.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
images/rivet.png
Normal file
After Width: | Height: | Size: 378 KiB |
BIN
images/sankeynoa.png
Normal file
After Width: | Height: | Size: 88 KiB |
BIN
images/tasty_tacos.png
Normal file
After Width: | Height: | Size: 127 KiB |
BIN
images/uml.png
Normal file
After Width: | Height: | Size: 214 KiB |
67
misc/blobs/blobs.typ
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#import "@preview/cetz:0.3.1": draw
|
||||||
|
|
||||||
|
#let rand-consts = (
|
||||||
|
a: 1103515245,
|
||||||
|
c: 12345,
|
||||||
|
m: (1).bit-lshift(31)
|
||||||
|
)
|
||||||
|
#let random(seed) = {
|
||||||
|
let seed = calc.rem(rand-consts.a * seed + rand-consts.c, rand-consts.m)
|
||||||
|
let value = seed / rand-consts.m
|
||||||
|
return (seed, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
#let draw-blob(
|
||||||
|
seed,
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
n-pts: 8,
|
||||||
|
curviness: 0.4,
|
||||||
|
debug: false,
|
||||||
|
..args
|
||||||
|
) = {
|
||||||
|
let pts = ()
|
||||||
|
let angle-step = 360deg / n-pts
|
||||||
|
let seed = seed
|
||||||
|
let v
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
draw.circle(center, radius: radius, stroke: (dash: "dashed"))
|
||||||
|
}
|
||||||
|
for i in range(n-pts) {
|
||||||
|
(seed, v) = random(seed)
|
||||||
|
let angle = angle-step * (i + v - 0.5)
|
||||||
|
|
||||||
|
(seed, v) = random(seed)
|
||||||
|
let f = calc.rem(i, 2) * 2 - 1
|
||||||
|
let r = radius * (1 + f * v * curviness)
|
||||||
|
|
||||||
|
let dx = r * calc.cos(angle)
|
||||||
|
let dy = r * calc.sin(angle)
|
||||||
|
let pt = (center.first() + dx, center.last() + dy)
|
||||||
|
pts.push(pt)
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
draw.circle(
|
||||||
|
pt,
|
||||||
|
radius: .1,
|
||||||
|
stroke: none,
|
||||||
|
fill: black
|
||||||
|
)
|
||||||
|
draw.line(
|
||||||
|
center,
|
||||||
|
(rel: center, to: (angle-step * i, radius)),
|
||||||
|
stroke: (dash: "dashed")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = args.named().at("name", default: none)
|
||||||
|
draw.group(name: name, {
|
||||||
|
draw.hobby(..pts, close: true, ..args)
|
||||||
|
if name != none {
|
||||||
|
draw.copy-anchors(name)
|
||||||
|
draw.anchor("center", center)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
BIN
misc/blobs/main.png
Normal file
After Width: | Height: | Size: 30 KiB |
79
misc/blobs/main.typ
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#import "@preview/cetz:0.3.1": canvas, draw
|
||||||
|
#import "blobs.typ": draw-blob
|
||||||
|
|
||||||
|
#set page(width: auto, height: auto, margin: 1cm)
|
||||||
|
|
||||||
|
#canvas({
|
||||||
|
draw-blob(
|
||||||
|
2, (0, 0), 1.5,
|
||||||
|
stroke: green,
|
||||||
|
name: "blob1"
|
||||||
|
)
|
||||||
|
|
||||||
|
draw-blob(
|
||||||
|
8, (5, 0), 1.5,
|
||||||
|
n-pts: 10,
|
||||||
|
stroke: orange,
|
||||||
|
name: "blob2"
|
||||||
|
)
|
||||||
|
|
||||||
|
draw.circle(
|
||||||
|
"blob1.center",
|
||||||
|
radius: .05,
|
||||||
|
fill: black,
|
||||||
|
stroke: none,
|
||||||
|
name: "x"
|
||||||
|
)
|
||||||
|
|
||||||
|
draw.circle(
|
||||||
|
"blob2.center",
|
||||||
|
radius: .05,
|
||||||
|
fill: black,
|
||||||
|
stroke: none,
|
||||||
|
name: "y"
|
||||||
|
)
|
||||||
|
|
||||||
|
draw.content(
|
||||||
|
"x",
|
||||||
|
[$arrow(x)$],
|
||||||
|
anchor: "east",
|
||||||
|
padding: 3pt
|
||||||
|
)
|
||||||
|
|
||||||
|
draw.content(
|
||||||
|
"y",
|
||||||
|
[$arrow(y)$],
|
||||||
|
anchor: "west",
|
||||||
|
padding: 3pt
|
||||||
|
)
|
||||||
|
|
||||||
|
draw.content(
|
||||||
|
(-1.2, .8),
|
||||||
|
text(fill: green)[$RR^n$]
|
||||||
|
)
|
||||||
|
|
||||||
|
draw.content(
|
||||||
|
(4.6, 1.4),
|
||||||
|
text(fill: orange)[$RR^m$]
|
||||||
|
)
|
||||||
|
|
||||||
|
let mid = (2.5, .5)
|
||||||
|
|
||||||
|
draw.bezier-through(
|
||||||
|
"x.north-east", mid, "y.north-west",
|
||||||
|
stroke: blue + .5pt,
|
||||||
|
mark: (end: ">", fill: blue),
|
||||||
|
name: "arrow"
|
||||||
|
)
|
||||||
|
draw.content(
|
||||||
|
mid,
|
||||||
|
[$f$],
|
||||||
|
anchor: "south",
|
||||||
|
padding: 3pt
|
||||||
|
)
|
||||||
|
|
||||||
|
draw.content(
|
||||||
|
(2.5, -1),
|
||||||
|
text(fill: blue)[$f: RR^n -> RR^m$]
|
||||||
|
)
|
||||||
|
})
|
BIN
misc/leetcode_trees/leetcode_trees.pdf
Normal file
97
misc/leetcode_trees/leetcode_trees.typ
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
#import "@preview/cetz:0.3.1": canvas, draw
|
||||||
|
|
||||||
|
#let leetcode-tree(nodes, highlighted: ()) = {
|
||||||
|
let height = calc.ceil(
|
||||||
|
calc.log(
|
||||||
|
nodes.len() + 1,
|
||||||
|
base: 2
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let dy = 1
|
||||||
|
let dx = 0.8
|
||||||
|
let r = 0.3
|
||||||
|
let width = dx * (calc.pow(2, height - 1) + 1)
|
||||||
|
|
||||||
|
for lvl in range(height) {
|
||||||
|
let n = calc.pow(2, lvl)
|
||||||
|
let y = -lvl * dy
|
||||||
|
|
||||||
|
for i in range(n) {
|
||||||
|
let j = i + n - 1
|
||||||
|
if j >= nodes.len() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
let node = nodes.at(j)
|
||||||
|
if node != none {
|
||||||
|
let x = width * (i + .5) / n
|
||||||
|
let name = "l" + str(lvl) + "-n" + str(i)
|
||||||
|
let is-highlighted = (j in highlighted) or (lvl, i) in highlighted
|
||||||
|
draw.circle(
|
||||||
|
(x, y),
|
||||||
|
radius: r,
|
||||||
|
name: name,
|
||||||
|
fill: if is-highlighted {
|
||||||
|
blue.lighten(75%)
|
||||||
|
} else {
|
||||||
|
none
|
||||||
|
}
|
||||||
|
)
|
||||||
|
draw.content(
|
||||||
|
name + ".center",
|
||||||
|
str(node)
|
||||||
|
)
|
||||||
|
|
||||||
|
if lvl != 0 {
|
||||||
|
let pi = i.bit-rshift(1)
|
||||||
|
draw.line(
|
||||||
|
"l" + str(lvl - 1) + "-n" + str(pi),
|
||||||
|
name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#set page(width: auto, height: auto, margin: 1cm)
|
||||||
|
|
||||||
|
#canvas({
|
||||||
|
leetcode-tree(
|
||||||
|
(2,1,3,none,none,none,4),
|
||||||
|
highlighted: (2,)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
#pagebreak()
|
||||||
|
|
||||||
|
#canvas({
|
||||||
|
leetcode-tree(
|
||||||
|
(1,2,3,4,5,6,none,none,none,7,8)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
#pagebreak()
|
||||||
|
|
||||||
|
#canvas({
|
||||||
|
leetcode-tree(
|
||||||
|
(99,3,2,none,6,4,5,none,none,none,none,8,7)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
#pagebreak()
|
||||||
|
|
||||||
|
#canvas({
|
||||||
|
leetcode-tree(
|
||||||
|
(0,3,1,none,none,none,2)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
#pagebreak()
|
||||||
|
|
||||||
|
#canvas({
|
||||||
|
leetcode-tree(
|
||||||
|
(0,3,1,2)
|
||||||
|
)
|
||||||
|
})
|
65
misc/menus/menu.json
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"date": "2024-09-09",
|
||||||
|
"menus": [
|
||||||
|
{
|
||||||
|
"name": "Risotto à la courge",
|
||||||
|
"extra": [
|
||||||
|
"Chips de courge",
|
||||||
|
"Fromage râpé"
|
||||||
|
],
|
||||||
|
"prices": {
|
||||||
|
"E": 8.1,
|
||||||
|
"C": 11.0,
|
||||||
|
"D": 9.9,
|
||||||
|
"V": 12.6
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ragoût de cerf",
|
||||||
|
"extra": [
|
||||||
|
"Spätzli au beurre",
|
||||||
|
"Choux de Bruxelles et chou rouge"
|
||||||
|
],
|
||||||
|
"prices": {
|
||||||
|
"E": 11.5,
|
||||||
|
"C": 14.4,
|
||||||
|
"D": 13.3,
|
||||||
|
"V": 16.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date": "2024-09-10",
|
||||||
|
"menus": [
|
||||||
|
{
|
||||||
|
"name": "Rösti aux champignons",
|
||||||
|
"extra": [
|
||||||
|
"Sauce crême",
|
||||||
|
"Salade du chef"
|
||||||
|
],
|
||||||
|
"prices": {
|
||||||
|
"E": 7.0,
|
||||||
|
"C": 9.0,
|
||||||
|
"D": 8.0,
|
||||||
|
"V": 10.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Boulette de poulet",
|
||||||
|
"extra": [
|
||||||
|
"Sauce alfredo",
|
||||||
|
"Pâtes spaghetti",
|
||||||
|
"Jardinière de légumes"
|
||||||
|
],
|
||||||
|
"prices": {
|
||||||
|
"E": 10.5,
|
||||||
|
"C": 13.4,
|
||||||
|
"D": 12.3,
|
||||||
|
"V": 15.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
BIN
misc/menus/menu.png
Normal file
After Width: | Height: | Size: 173 KiB |
202
misc/menus/menu.typ
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
#let path = sys.inputs.at("path", default: "menu.json")
|
||||||
|
#let all-combos = sys.inputs.at("all-combos", default: "false") == "true"
|
||||||
|
#let categories = sys.inputs.at("categories", default: "E,D,C,V").split(",")
|
||||||
|
#let dark = sys.inputs.at("dark", default: "false") == "true"
|
||||||
|
#let days = json(path)
|
||||||
|
#let nbsp = sym.space.nobreak
|
||||||
|
|
||||||
|
#let colors = if dark {(
|
||||||
|
bg: rgb("#1f1f24"),
|
||||||
|
text: white,
|
||||||
|
categories: rgb("#70c947"),
|
||||||
|
menu-sep: rgb("#ababab"),
|
||||||
|
title-underline: rgb("#eb2324"),
|
||||||
|
day-sep: rgb("#a3cf40")
|
||||||
|
)} else {(
|
||||||
|
bg: white,
|
||||||
|
text: black,
|
||||||
|
categories: rgb("#577F25"),
|
||||||
|
menu-sep: rgb("#ababab"),
|
||||||
|
title-underline: rgb("#e34243"),
|
||||||
|
day-sep: rgb("#82BC00")
|
||||||
|
)}
|
||||||
|
|
||||||
|
#let width = if days.len() > 1 {20cm} else {10cm}
|
||||||
|
|
||||||
|
#set page(
|
||||||
|
margin: 0cm,
|
||||||
|
height: auto,
|
||||||
|
width: width,
|
||||||
|
fill: colors.bg
|
||||||
|
)
|
||||||
|
|
||||||
|
#set text(
|
||||||
|
fill: colors.text
|
||||||
|
)
|
||||||
|
|
||||||
|
#let parse-date(date-txt) = {
|
||||||
|
let (year, month, day) = date-txt.split("-")
|
||||||
|
return datetime(year: int(year), month: int(month), day: int(day))
|
||||||
|
}
|
||||||
|
|
||||||
|
#let fmt-number(
|
||||||
|
number,
|
||||||
|
decimals: 2,
|
||||||
|
decimal-sep: ".",
|
||||||
|
thousands-sep: "'"
|
||||||
|
) = {
|
||||||
|
let integer = calc.trunc(number)
|
||||||
|
let decimal = calc.fract(number)
|
||||||
|
let res = str(integer).clusters()
|
||||||
|
.rev()
|
||||||
|
.chunks(3)
|
||||||
|
.map(c => c.join(""))
|
||||||
|
.join(thousands-sep)
|
||||||
|
.rev()
|
||||||
|
|
||||||
|
if decimals != 0 {
|
||||||
|
res += decimal-sep
|
||||||
|
decimal = decimal * calc.pow(10, decimals)
|
||||||
|
decimal = calc.round(decimal)
|
||||||
|
decimal = str(decimal) + ("0" * decimals)
|
||||||
|
res += decimal.slice(0, decimals)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
#let day-names = (
|
||||||
|
"Lundi",
|
||||||
|
"Mardi",
|
||||||
|
"Mercredi",
|
||||||
|
"Jeudi",
|
||||||
|
"Vendredi",
|
||||||
|
"Samedi",
|
||||||
|
"Dimanche"
|
||||||
|
)
|
||||||
|
|
||||||
|
#let display-menus(
|
||||||
|
days,
|
||||||
|
categories,
|
||||||
|
day-sep-stroke: colors.day-sep + 1pt,
|
||||||
|
title-underline-stroke: colors.title-underline + 2pt,
|
||||||
|
menu-sep-stroke: colors.menu-sep + 1pt,
|
||||||
|
price-categ-color: colors.categories
|
||||||
|
) = {
|
||||||
|
set text(font: "Liberation Sans", hyphenate: true)
|
||||||
|
let cols = calc.min(2, days.len())
|
||||||
|
|
||||||
|
let cells = ()
|
||||||
|
|
||||||
|
for day in days {
|
||||||
|
let date = parse-date(day.date)
|
||||||
|
let weekday = date.weekday() - 1
|
||||||
|
let day-name = day-names.at(weekday)
|
||||||
|
let menus = ()
|
||||||
|
for menu in day.menus {
|
||||||
|
let price-cell = grid.cell(rowspan: 2)[]
|
||||||
|
let prices = menu.at(
|
||||||
|
"prices",
|
||||||
|
default: (:)
|
||||||
|
)
|
||||||
|
if menu.keys().contains("price") {
|
||||||
|
prices = ("": menu.price)
|
||||||
|
}
|
||||||
|
|
||||||
|
let pairs = prices.pairs()
|
||||||
|
.filter(p => p.first() in categories)
|
||||||
|
.sorted(key: p => p.last())
|
||||||
|
|
||||||
|
let prices = pairs.map(p => {
|
||||||
|
let categ = emph(
|
||||||
|
text(
|
||||||
|
fill: price-categ-color,
|
||||||
|
p.first()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
let price = fmt-number(p.last())
|
||||||
|
categ + ":" + nbsp + "CHF" + nbsp + price
|
||||||
|
})
|
||||||
|
price-cell = grid.cell(
|
||||||
|
rowspan: menu.extra.len() + 1,
|
||||||
|
stack(dir: ttb, spacing: .4em, ..prices)
|
||||||
|
)
|
||||||
|
//let price = "CHF" + sym.space.nobreak + str(menu.price)
|
||||||
|
menus.push(
|
||||||
|
grid(
|
||||||
|
columns: (1fr, auto),
|
||||||
|
column-gutter: .4em,
|
||||||
|
row-gutter: .4em,
|
||||||
|
[*#menu.name*],
|
||||||
|
price-cell,
|
||||||
|
..menu.extra.map(l => text(10pt, l))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let title = align(
|
||||||
|
center,
|
||||||
|
stack(
|
||||||
|
dir: ttb,
|
||||||
|
spacing: .5em,
|
||||||
|
text(size: 14pt, weight: "bold", day-name),
|
||||||
|
text(size: 10pt, date.display("[day].[month]"))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
let title-box = box(
|
||||||
|
width: 50%,
|
||||||
|
inset: .6em,
|
||||||
|
stroke: (bottom: title-underline-stroke),
|
||||||
|
title
|
||||||
|
)
|
||||||
|
let title-cell = grid.cell(align: center, title-box)
|
||||||
|
|
||||||
|
cells.push(box(width: 100%, grid(
|
||||||
|
columns: (100%),
|
||||||
|
inset: (x, y) => (
|
||||||
|
top: if y == 0 {0pt} else {.8em},
|
||||||
|
bottom: if y == 0 {0pt} else {2em}
|
||||||
|
),
|
||||||
|
stroke: (_, y) => (top: if y < 2 {none} else {menu-sep-stroke}),
|
||||||
|
title-cell,
|
||||||
|
..menus
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if calc.rem(cells.len(), 2) == 1 {
|
||||||
|
cells.last() = grid.cell(colspan: calc.min(2, cols), cells.last())
|
||||||
|
}
|
||||||
|
|
||||||
|
grid(
|
||||||
|
columns: (100% / cols,) * cols,
|
||||||
|
inset: (x: 1.5em, y: .8em),
|
||||||
|
stroke: (x, y) => {
|
||||||
|
let borders = (:)
|
||||||
|
if x != 0 {
|
||||||
|
borders.insert("left", day-sep-stroke)
|
||||||
|
}
|
||||||
|
if y != 0 {
|
||||||
|
borders.insert("top", day-sep-stroke)
|
||||||
|
}
|
||||||
|
return borders
|
||||||
|
},
|
||||||
|
..cells
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#{
|
||||||
|
if all-combos {
|
||||||
|
let n-combos = calc.pow(2, categories.len())
|
||||||
|
for i in range(n-combos) {
|
||||||
|
let filtered-categories = categories.enumerate().filter(p => {
|
||||||
|
let (j, categ) = p
|
||||||
|
return i.bit-rshift(j).bit-and(1) != 0
|
||||||
|
}).map(p => p.last())
|
||||||
|
display-menus(days, filtered-categories)
|
||||||
|
pagebreak(weak: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
display-menus(days, categories)
|
||||||
|
}
|
||||||
|
}
|
BIN
misc/menus/menu_dark.png
Normal file
After Width: | Height: | Size: 174 KiB |
BIN
misc/menus/menu_student.png
Normal file
After Width: | Height: | Size: 121 KiB |
BIN
misc/riscv/main.pdf
Normal file
39
misc/riscv/main.typ
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#import "riscv.typ"
|
||||||
|
|
||||||
|
#set page(width: auto, height: auto, margin: 1cm)
|
||||||
|
|
||||||
|
#riscv.explain-instruction-encoding("lui x0, 0x12")
|
||||||
|
|
||||||
|
#riscv.explain-instruction-encoding("add sp, a0, a1")
|
||||||
|
|
||||||
|
#pagebreak()
|
||||||
|
|
||||||
|
#riscv.riscv-to-bin(```
|
||||||
|
0x00000300 main: jal ra, simple # call
|
||||||
|
0x00000304 add s0, s1, s1
|
||||||
|
0x0000051c simple: jalr x0, ra, 0 # return
|
||||||
|
```)
|
||||||
|
|
||||||
|
#pagebreak()
|
||||||
|
|
||||||
|
#riscv.riscv-to-bin(```
|
||||||
|
f1:
|
||||||
|
addi sp, sp, -20 # make space on stack for 5 words
|
||||||
|
sw a0, 16(sp)
|
||||||
|
sw a1, 12(sp)
|
||||||
|
sw ra, 8(sp) # save ra on stack
|
||||||
|
sw s4, 4(sp)
|
||||||
|
sw s5, 0(sp)
|
||||||
|
jal ra, f2
|
||||||
|
lw ra, 8(sp) # restore ra (and other regs) from stack
|
||||||
|
addi sp, sp, 20 # deallocate stack space
|
||||||
|
jalr zero, ra, 0
|
||||||
|
|
||||||
|
# f2 (leaf-function) only uses s4 and calls no functions
|
||||||
|
f2:
|
||||||
|
addi sp, sp, -4 # make space on stack for 1 word
|
||||||
|
sw s4, 0(sp)
|
||||||
|
lw s4, 0(sp)
|
||||||
|
addi sp, sp, 4 # deallocate stack space
|
||||||
|
jalr zero, ra, 0
|
||||||
|
```)
|
455
misc/riscv/riscv.typ
Normal file
@ -0,0 +1,455 @@
|
|||||||
|
#let colors = (
|
||||||
|
address: rgb(228, 103, 103),
|
||||||
|
label: rgb(119, 97, 204),
|
||||||
|
op: rgb(235, 105, 40)
|
||||||
|
)
|
||||||
|
|
||||||
|
#let opcodes = (
|
||||||
|
"lui": 0b0110111,
|
||||||
|
"auipc": 0b0010111,
|
||||||
|
"jal": 0b1101111,
|
||||||
|
"jalr": 0b1100111,
|
||||||
|
"beq": 0b1100011,
|
||||||
|
"bne": 0b1100011,
|
||||||
|
"blt": 0b1100011,
|
||||||
|
"bge": 0b1100011,
|
||||||
|
"bltu": 0b1100011,
|
||||||
|
"bgeu": 0b1100011,
|
||||||
|
"lb": 0b0000011,
|
||||||
|
"lh": 0b0000011,
|
||||||
|
"lw": 0b0000011,
|
||||||
|
"lbu": 0b0000011,
|
||||||
|
"lhu": 0b0000011,
|
||||||
|
"sb": 0b0100011,
|
||||||
|
"sh": 0b0100011,
|
||||||
|
"sw": 0b0100011,
|
||||||
|
"addi": 0b0010011,
|
||||||
|
"slti": 0b0010011,
|
||||||
|
"sltiu": 0b0010011,
|
||||||
|
"xori": 0b0010011,
|
||||||
|
"ori": 0b0010011,
|
||||||
|
"andi": 0b0010011,
|
||||||
|
"slli": 0b0010011,
|
||||||
|
"srli": 0b0010011,
|
||||||
|
"srai": 0b0010011,
|
||||||
|
"add": 0b0110011,
|
||||||
|
"sub": 0b0110011,
|
||||||
|
"sll": 0b0110011,
|
||||||
|
"slt": 0b0110011,
|
||||||
|
"sltu": 0b0110011,
|
||||||
|
"xor": 0b0110011,
|
||||||
|
"srl": 0b0110011,
|
||||||
|
"sra": 0b0110011,
|
||||||
|
"or": 0b0110011,
|
||||||
|
"and": 0b0110011,
|
||||||
|
)
|
||||||
|
|
||||||
|
#let types = (
|
||||||
|
"lui": "U",
|
||||||
|
"auipc": "U",
|
||||||
|
"jal": "J",
|
||||||
|
"jalr": "I",
|
||||||
|
"beq": "B",
|
||||||
|
"bne": "B",
|
||||||
|
"blt": "B",
|
||||||
|
"bge": "B",
|
||||||
|
"bltu": "B",
|
||||||
|
"bgeu": "B",
|
||||||
|
"lb": "L",
|
||||||
|
"lh": "L",
|
||||||
|
"lw": "L",
|
||||||
|
"lbu": "L",
|
||||||
|
"lhu": "L",
|
||||||
|
"sb": "S",
|
||||||
|
"sh": "S",
|
||||||
|
"sw": "S",
|
||||||
|
"addi": "I",
|
||||||
|
"slti": "I",
|
||||||
|
"sltiu": "I",
|
||||||
|
"xori": "I",
|
||||||
|
"ori": "I",
|
||||||
|
"andi": "I",
|
||||||
|
"slli": "I",
|
||||||
|
"srli": "I",
|
||||||
|
"srai": "I",
|
||||||
|
"add": "R",
|
||||||
|
"sub": "R",
|
||||||
|
"sll": "R",
|
||||||
|
"slt": "R",
|
||||||
|
"sltu": "R",
|
||||||
|
"xor": "R",
|
||||||
|
"srl": "R",
|
||||||
|
"sra": "R",
|
||||||
|
"or": "R",
|
||||||
|
"and": "R",
|
||||||
|
)
|
||||||
|
|
||||||
|
#let funct3 = (
|
||||||
|
"jalr": 0b000,
|
||||||
|
"beq": 0b000,
|
||||||
|
"bne": 0b001,
|
||||||
|
"blt": 0b100,
|
||||||
|
"bge": 0b101,
|
||||||
|
"bltu": 0b110,
|
||||||
|
"bgeu": 0b111,
|
||||||
|
"lb": 0b000,
|
||||||
|
"lh": 0b001,
|
||||||
|
"lw": 0b010,
|
||||||
|
"lbu": 0b100,
|
||||||
|
"lhu": 0b101,
|
||||||
|
"sb": 0b000,
|
||||||
|
"sh": 0b001,
|
||||||
|
"sw": 0b010,
|
||||||
|
"addi": 0b000,
|
||||||
|
"slti": 0b010,
|
||||||
|
"sltiu": 0b011,
|
||||||
|
"xori": 0b100,
|
||||||
|
"ori": 0b110,
|
||||||
|
"andi": 0b111,
|
||||||
|
"slli": 0b001,
|
||||||
|
"srli": 0b101,
|
||||||
|
"srai": 0b101,
|
||||||
|
"add": 0b000,
|
||||||
|
"sub": 0b000,
|
||||||
|
"sll": 0b001,
|
||||||
|
"slt": 0b010,
|
||||||
|
"sltu": 0b011,
|
||||||
|
"xor": 0b100,
|
||||||
|
"srl": 0b101,
|
||||||
|
"sra": 0b101,
|
||||||
|
"or": 0b110,
|
||||||
|
"and": 0b111,
|
||||||
|
)
|
||||||
|
|
||||||
|
#let funct7 = (
|
||||||
|
"slli": 0b0000000,
|
||||||
|
"srli": 0b0000000,
|
||||||
|
"srai": 0b0100000,
|
||||||
|
"add": 0b0000000,
|
||||||
|
"sub": 0b0100000,
|
||||||
|
"sll": 0b0000000,
|
||||||
|
"slt": 0b0000000,
|
||||||
|
"sltu": 0b0000000,
|
||||||
|
"xor": 0b0000000,
|
||||||
|
"srl": 0b0000000,
|
||||||
|
"sra": 0b0100000,
|
||||||
|
"or": 0b0000000,
|
||||||
|
"and": 0b0000000,
|
||||||
|
)
|
||||||
|
|
||||||
|
#let regs = (
|
||||||
|
"zero": 0, "ra": 1, "sp": 2, "gp": 3,
|
||||||
|
"tp": 4, "t0": 5, "t1": 6, "t2": 7,
|
||||||
|
"s0": 8, "fp": 8, "s1": 9, "a0": 10, "a1": 11,
|
||||||
|
"a2": 12, "a3": 13, "a4": 14, "a5": 15,
|
||||||
|
"a6": 16, "a7": 17, "s2": 18, "s3": 19,
|
||||||
|
"s4": 20, "s5": 21, "s6": 22, "s7": 23,
|
||||||
|
"s8": 24, "s9": 25, "s10": 26, "s11": 27,
|
||||||
|
"t3": 28, "t4": 29, "t5": 30, "t6": 31
|
||||||
|
)
|
||||||
|
|
||||||
|
#let zfill(string, length, c: "0") = {
|
||||||
|
let res = c * length + string
|
||||||
|
return res.slice(-length)
|
||||||
|
}
|
||||||
|
|
||||||
|
#let int-to-bin(value, bits) = {
|
||||||
|
if value < 0 {
|
||||||
|
let m = (1).bit-lshift(bits) - 1
|
||||||
|
let v = str((-value).bit-xor(m) + 1, base: 2)
|
||||||
|
return zfill(v, bits, c: "1")
|
||||||
|
}
|
||||||
|
let v = str(value, base: 2)
|
||||||
|
return zfill(v, bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
#let parse-reg(value) = {
|
||||||
|
let number = value.last()
|
||||||
|
if not value.starts-with("x") {
|
||||||
|
number = regs.at(value)
|
||||||
|
}
|
||||||
|
return int(number)
|
||||||
|
}
|
||||||
|
|
||||||
|
#let parse-imm(value) = {
|
||||||
|
let imm = eval(value)
|
||||||
|
assert(type(imm) == int)
|
||||||
|
return imm
|
||||||
|
}
|
||||||
|
|
||||||
|
#let parse-addr(value) = {
|
||||||
|
let matches = value.match(regex("(.*)\((.*)\)"))
|
||||||
|
let imm = matches.captures.first()
|
||||||
|
let reg = matches.captures.last()
|
||||||
|
imm = parse-imm(imm)
|
||||||
|
reg = parse-reg(reg)
|
||||||
|
return (reg, imm)
|
||||||
|
}
|
||||||
|
|
||||||
|
#let parse-args(instr, instr-type, args) = {
|
||||||
|
let parsed = (:)
|
||||||
|
let r1 = parse-reg(args.first())
|
||||||
|
|
||||||
|
if instr-type in ("R", "I", "L", "U", "J") {
|
||||||
|
parsed.insert("rd", r1)
|
||||||
|
} else if instr-type == "B" {
|
||||||
|
parsed.insert("rs1", r1)
|
||||||
|
} else if instr-type == "S" {
|
||||||
|
parsed.insert("rs2", r1)
|
||||||
|
}
|
||||||
|
|
||||||
|
let r2 = none
|
||||||
|
let r3 = none
|
||||||
|
let imm = none
|
||||||
|
if instr-type == "R" {
|
||||||
|
r2 = parse-reg(args.at(1))
|
||||||
|
r3 = parse-reg(args.at(2))
|
||||||
|
} else if instr-type in ("I", "B") {
|
||||||
|
r2 = parse-reg(args.at(1))
|
||||||
|
imm = parse-imm(args.at(2))
|
||||||
|
} else if instr-type in ("U", "J") {
|
||||||
|
imm = parse-imm(args.at(1))
|
||||||
|
} else if instr-type in ("L", "S") {
|
||||||
|
(r2, imm) = parse-addr(args.at(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
if instr-type == "R" {
|
||||||
|
parsed.insert("rs1", r2)
|
||||||
|
parsed.insert("rs2", r3)
|
||||||
|
} else if instr-type in ("I", "L", "S") {
|
||||||
|
parsed.insert("rs1", r2)
|
||||||
|
parsed.insert("imm", imm)
|
||||||
|
} else if instr-type == "B" {
|
||||||
|
parsed.insert("rs2", r2)
|
||||||
|
parsed.insert("imm", imm)
|
||||||
|
} else if instr-type in ("U", "J") {
|
||||||
|
parsed.insert("imm", imm)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsed
|
||||||
|
}
|
||||||
|
|
||||||
|
#let format-imm(imm, sections) = {
|
||||||
|
let value = 0
|
||||||
|
for section in sections {
|
||||||
|
if type(section) == int {
|
||||||
|
let bit = imm.bit-rshift(section).bit-and(1)
|
||||||
|
value = value.bit-lshift(1).bit-or(bit)
|
||||||
|
} else {
|
||||||
|
let (start, end) = section
|
||||||
|
if start > end {
|
||||||
|
(start, end) = (end, start)
|
||||||
|
}
|
||||||
|
let span = end - start + 1
|
||||||
|
let mask = (1).bit-lshift(span) - 1
|
||||||
|
let bits = imm.bit-rshift(start).bit-and(mask)
|
||||||
|
value = value.bit-lshift(span).bit-or(bits)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
#let instr-to-bin(instruction) = {
|
||||||
|
let (instr, ..args) = instruction.split(" ")
|
||||||
|
args = args.join("").split(",").map(a => a.trim())
|
||||||
|
instr = lower(instr)
|
||||||
|
let parts = ()
|
||||||
|
let parts-names = ()
|
||||||
|
let opcode = opcodes.at(instr)
|
||||||
|
let instr-type = types.at(instr)
|
||||||
|
let code = 0
|
||||||
|
|
||||||
|
let parsed-args = parse-args(instr, instr-type, args)
|
||||||
|
let imm = parsed-args.at("imm", default: none)
|
||||||
|
|
||||||
|
let add-part(code, parts, part, bits) = {
|
||||||
|
code = code.bit-lshift(bits).bit-or(part)
|
||||||
|
parts.push(int-to-bin(part, bits))
|
||||||
|
return (code, parts)
|
||||||
|
}
|
||||||
|
|
||||||
|
if instr in ("slli", "srli", "srai") {
|
||||||
|
let f7 = funct7.at(instr)
|
||||||
|
(code, parts) = add-part(code, parts, f7, 7)
|
||||||
|
parts-names.push("funct7")
|
||||||
|
let v = format-imm(imm, ((4,0),))
|
||||||
|
(code, parts) = add-part(code, parts, v, 5)
|
||||||
|
parts-names.push("shamt")
|
||||||
|
|
||||||
|
} else if instr-type == "R" {
|
||||||
|
let v = funct7.at(instr)
|
||||||
|
parts-names.push("funct7")
|
||||||
|
(code, parts) = add-part(code, parts, v, 7)
|
||||||
|
} else if instr-type in ("I", "L") {
|
||||||
|
let v = format-imm(imm, ((11,0),))
|
||||||
|
(code, parts) = add-part(code, parts, v, 12)
|
||||||
|
parts-names.push("imm[11:0]")
|
||||||
|
} else if instr-type == "S" {
|
||||||
|
let v = format-imm(imm, ((11, 5),))
|
||||||
|
(code, parts) = add-part(code, parts, v, 7)
|
||||||
|
parts-names.push("imm[11:5]")
|
||||||
|
} else if instr-type == "B" {
|
||||||
|
let v = format-imm(imm, (12, (10, 5)))
|
||||||
|
(code, parts) = add-part(code, parts, v, 7)
|
||||||
|
parts-names.push("imm[12|10:5]")
|
||||||
|
} else if instr-type == "U" {
|
||||||
|
let v = format-imm(imm, ((19, 0),))
|
||||||
|
(code, parts) = add-part(code, parts, v, 20)
|
||||||
|
parts-names.push("imm[31:12]")
|
||||||
|
} else if instr-type == "J" {
|
||||||
|
let v = format-imm(imm, (20, (10, 1), 11, (19, 12)))
|
||||||
|
(code, parts) = add-part(code, parts, v, 20)
|
||||||
|
parts-names.push("imm[20|10:1|11|19:12]")
|
||||||
|
}
|
||||||
|
|
||||||
|
if instr-type in ("R", "S", "B") {
|
||||||
|
(code, parts) = add-part(code, parts, parsed-args.rs2, 5)
|
||||||
|
parts-names.push("rs2")
|
||||||
|
}
|
||||||
|
if instr-type in ("R", "I", "L", "S", "B") {
|
||||||
|
(code, parts) = add-part(code, parts, parsed-args.rs1, 5)
|
||||||
|
parts-names.push("rs1")
|
||||||
|
(code, parts) = add-part(code, parts, funct3.at(instr), 3)
|
||||||
|
parts-names.push("funct3")
|
||||||
|
}
|
||||||
|
|
||||||
|
if instr-type in ("R", "I", "L", "U", "J") {
|
||||||
|
(code, parts) = add-part(code, parts, parsed-args.rd, 5)
|
||||||
|
parts-names.push("rd")
|
||||||
|
} else if instr-type == "S" {
|
||||||
|
let v = format-imm(imm, ((4, 0),))
|
||||||
|
(code, parts) = add-part(code, parts, v, 5)
|
||||||
|
parts-names.push("imm[4:0]")
|
||||||
|
} else if instr-type == "B" {
|
||||||
|
let v = format-imm(imm, ((4, 1), 11))
|
||||||
|
(code, parts) = add-part(code, parts, v, 5)
|
||||||
|
parts-names.push("imm[4:1|11]")
|
||||||
|
}
|
||||||
|
|
||||||
|
(code, parts) = add-part(code, parts, opcode, 7)
|
||||||
|
parts-names.push("opcode")
|
||||||
|
|
||||||
|
return (
|
||||||
|
instruction: instruction,
|
||||||
|
parts: parts,
|
||||||
|
names: parts-names,
|
||||||
|
code: code
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#let color-instruction(line) = {
|
||||||
|
let res = ""
|
||||||
|
if line.starts-with("0x") {
|
||||||
|
let (addr, ..rest) = line.split(" ")
|
||||||
|
line = rest.join(" ").trim()
|
||||||
|
res += text(raw(addr), fill: colors.address) + " "
|
||||||
|
}
|
||||||
|
|
||||||
|
if ":" in line {
|
||||||
|
let label
|
||||||
|
(label, line) = line.split(":")
|
||||||
|
line = line.trim()
|
||||||
|
res += text(raw(label), fill: colors.label) + ": "
|
||||||
|
if line.len() == 0 {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let (op, ..args) = line.split(" ")
|
||||||
|
args = args.join("").split(",").map(a => a.trim())
|
||||||
|
args = args.map(a => raw(a))
|
||||||
|
|
||||||
|
res += text(raw(op), fill: colors.op) + " "
|
||||||
|
res += args.join(", ")
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
#let explain-instruction-encoding(instruction) = {
|
||||||
|
let encoded = instr-to-bin(instruction)
|
||||||
|
let parts = encoded.parts
|
||||||
|
let names = encoded.names
|
||||||
|
let code = encoded.code
|
||||||
|
parts.zip(names).map(((v, n)) => $underbrace(#raw(v), #n)$).join(" ")
|
||||||
|
[ \= #raw(instruction) \= 0x#raw(zfill(str(code, base: 16), 8))]
|
||||||
|
}
|
||||||
|
|
||||||
|
#let replace-label-refs(instr, labels, addr) = {
|
||||||
|
let (op, ..args) = instr.split(" ")
|
||||||
|
args = args.join("").split(",").map(a => a.trim())
|
||||||
|
if op in ("beq", "bne", "blt", "bge", "bltu", "bgeu", "jal") {
|
||||||
|
let label = args.last()
|
||||||
|
let target = labels.at(label)
|
||||||
|
let offset = target - addr
|
||||||
|
args.last() = str(offset)
|
||||||
|
}
|
||||||
|
return op + " " + args.join(", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
#let riscv-to-bin(riscv, start-address: 0) = {
|
||||||
|
let src = riscv
|
||||||
|
if type(riscv) == raw or type(riscv) == content {
|
||||||
|
src = riscv.text
|
||||||
|
}
|
||||||
|
|
||||||
|
let lines = src.split("\n")
|
||||||
|
.map(l => l.split("#").first().trim())
|
||||||
|
.filter(l => l.len() != 0)
|
||||||
|
let labels = (:)
|
||||||
|
let addr = start-address
|
||||||
|
|
||||||
|
let lines2 = ()
|
||||||
|
let addresses = ()
|
||||||
|
for line in lines {
|
||||||
|
let set-addr = none
|
||||||
|
if line.starts-with("0x") {
|
||||||
|
let rest
|
||||||
|
(set-addr, ..rest) = line.split(" ")
|
||||||
|
line = rest.join(" ")
|
||||||
|
addr = eval(set-addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ":" in line {
|
||||||
|
let label
|
||||||
|
(label, line) = line.split(":")
|
||||||
|
labels.insert(label, addr)
|
||||||
|
line = line.trim()
|
||||||
|
if line.len() == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lines2.push(line.trim())
|
||||||
|
addresses.push(addr)
|
||||||
|
if set-addr == none {
|
||||||
|
addr += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let cells = ()
|
||||||
|
for (line, addr) in lines2.zip(addresses) {
|
||||||
|
let instr = replace-label-refs(line, labels, addr)
|
||||||
|
let encoded = instr-to-bin(instr)
|
||||||
|
let addr-txt = "0x" + zfill(str(addr, base: 16), 8)
|
||||||
|
let code-txt = "0x" + zfill(str(encoded.code, base: 16), 8)
|
||||||
|
addr-txt = raw(addr-txt)
|
||||||
|
addr-txt = text(addr-txt, fill: colors.address)
|
||||||
|
|
||||||
|
|
||||||
|
let label = ""
|
||||||
|
if addr in labels.values() {
|
||||||
|
label = labels.pairs().filter(p => p.last() == addr).first().first()
|
||||||
|
label += ":"
|
||||||
|
}
|
||||||
|
label = text(raw(label), fill: colors.label)
|
||||||
|
cells.push(addr-txt)
|
||||||
|
cells.push(label)
|
||||||
|
cells.push(raw(code-txt))
|
||||||
|
cells.push(color-instruction(line))
|
||||||
|
}
|
||||||
|
table(
|
||||||
|
align: left + horizon,
|
||||||
|
stroke: none,
|
||||||
|
columns: 4,
|
||||||
|
inset: 5pt,
|
||||||
|
..cells
|
||||||
|
)
|
||||||
|
}
|
BIN
misc/riscv/riscv_1.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
misc/riscv/riscv_2.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
misc/riscv/riscv_3.png
Normal file
After Width: | Height: | Size: 180 KiB |
BIN
misc/uml/main.pdf
Normal file
104
misc/uml/main.typ
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
#import "uml.typ"
|
||||||
|
|
||||||
|
#set page(width: auto, height: auto, margin: 1cm)
|
||||||
|
|
||||||
|
#uml.diagram({
|
||||||
|
import uml: *
|
||||||
|
|
||||||
|
class("Person", abstract, {
|
||||||
|
attr("name", "str")
|
||||||
|
attr("phoneNumber", "str")
|
||||||
|
attr("emailAddress", "str")
|
||||||
|
method("purchaseParkingPass")
|
||||||
|
})
|
||||||
|
class("Address", {
|
||||||
|
attr("street", "str")
|
||||||
|
attr("city", "str")
|
||||||
|
attr("state", "str")
|
||||||
|
attr("postalCode", "int")
|
||||||
|
attr("country", "str")
|
||||||
|
method("validate", "bool", private)
|
||||||
|
method("outputAsLabel", "str")
|
||||||
|
})
|
||||||
|
class("Student", {
|
||||||
|
attr("studentNumber", "int")
|
||||||
|
attr("averageMark", "int")
|
||||||
|
method("isEligibleToEnroll", "bool", ("str",))
|
||||||
|
method("getSeminarsTaken", "int")
|
||||||
|
})
|
||||||
|
class("Professor", {
|
||||||
|
attr("salary", "int", package)
|
||||||
|
attr("staffNumber", "int", protected)
|
||||||
|
attr("yearsOfService", "int", private)
|
||||||
|
attr("numberOfClasses", "int")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
#pagebreak()
|
||||||
|
|
||||||
|
#uml.diagram({
|
||||||
|
import uml: *
|
||||||
|
|
||||||
|
class("Pageable", interface, {
|
||||||
|
attr("UNKNOWN_N_OF_PAGES", "int", default: -1)
|
||||||
|
})
|
||||||
|
|
||||||
|
class("SQLStatement", {
|
||||||
|
method("executeQuery", "ResultSet", (sql: "String"))
|
||||||
|
method("isPoolable", "Boolean")
|
||||||
|
method("getQueryTimeout", "int")
|
||||||
|
method("clearWarnings")
|
||||||
|
})
|
||||||
|
|
||||||
|
class("SearchService", {
|
||||||
|
attr("config", "Configuration", private)
|
||||||
|
attr("engine", "SearchEngine", private)
|
||||||
|
method("search", "SearchResult", (query: "SearchRequest"))
|
||||||
|
method("createEngine", "SearchEngine", private, static)
|
||||||
|
})
|
||||||
|
|
||||||
|
class("SearchService", grouped: true, {
|
||||||
|
attr("config", "Configuration", private)
|
||||||
|
attr("engine", "SearchEngine", private)
|
||||||
|
method("search", "SearchResult", (query: "SearchRequest"))
|
||||||
|
method("createEngine", "SearchEngine", private, static)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
#pagebreak()
|
||||||
|
|
||||||
|
#import "parser.typ": parse
|
||||||
|
#parse(```
|
||||||
|
@class-grouped=true
|
||||||
|
class Button {
|
||||||
|
-theController : Controller*
|
||||||
|
+press() : void
|
||||||
|
+setTheController(p : Controller*) : void
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Controller {
|
||||||
|
-theLight : Light*
|
||||||
|
-lampState : boolean
|
||||||
|
+evButtonPressed() : void
|
||||||
|
+setTheLight(p : Light*) : void
|
||||||
|
-toggle() : void
|
||||||
|
}
|
||||||
|
|
||||||
|
class Light {
|
||||||
|
+on() : void
|
||||||
|
+off() : void
|
||||||
|
}
|
||||||
|
```)
|
||||||
|
|
||||||
|
#pagebreak()
|
||||||
|
|
||||||
|
#parse(```
|
||||||
|
class City {
|
||||||
|
+resources: HashMap[ResourceType, Int]
|
||||||
|
+globalHappiness: Double
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Building {
|
||||||
|
+getResourcesOnBuilt(): HashMap[ResourceType, Int]
|
||||||
|
}
|
||||||
|
```)
|
94
misc/uml/parser.typ
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
#import "uml.typ"
|
||||||
|
|
||||||
|
#let parse-args(data) = {
|
||||||
|
let args = data.split(",").map(a => a.trim()).filter(a => a.len() != 0)
|
||||||
|
let res
|
||||||
|
if ":" in data {
|
||||||
|
res = (:)
|
||||||
|
} else {
|
||||||
|
res = ()
|
||||||
|
}
|
||||||
|
|
||||||
|
for arg in args {
|
||||||
|
let parts = arg.split(":")
|
||||||
|
let arg-name = none
|
||||||
|
let arg-type
|
||||||
|
if parts.len() == 1 {
|
||||||
|
if type(res) == dictionary {
|
||||||
|
panic("Cannot mix named and unnamed arguments")
|
||||||
|
}
|
||||||
|
arg-type = parts.first().trim()
|
||||||
|
res.push(arg-type)
|
||||||
|
} else {
|
||||||
|
arg-name = parts.first().trim()
|
||||||
|
arg-type = parts.slice(1).join(":").trim()
|
||||||
|
res.insert(arg-name, arg-type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
#let parse(data) = {
|
||||||
|
let elements = ()
|
||||||
|
|
||||||
|
if type(data) == raw or type(data) == content {
|
||||||
|
data = data.text
|
||||||
|
}
|
||||||
|
|
||||||
|
let classes = data.matches(regex("(?ms)(abstract class|class|interface) (.*?)\s*\{(.*?)\}"))
|
||||||
|
|
||||||
|
for class in classes {
|
||||||
|
let family-name = class.captures.first()
|
||||||
|
let family = (
|
||||||
|
class: none,
|
||||||
|
"abstract class": uml.abstract,
|
||||||
|
"interface": uml.interface,
|
||||||
|
"enum": uml.enum,
|
||||||
|
).at(family-name)
|
||||||
|
|
||||||
|
let name = class.captures.at(1)
|
||||||
|
let class-data = class.captures.last().trim()
|
||||||
|
|
||||||
|
let members = class-data.matches(regex("(?m)^\s*(.*?)\s*(:\s*([^:]*?))?$")).filter(m => m.text.len() != 0)
|
||||||
|
let attributes = ()
|
||||||
|
let methods = ()
|
||||||
|
for member in members {
|
||||||
|
let member-name = member.captures.first()
|
||||||
|
let val-type = member.captures.last()
|
||||||
|
let visibility = none
|
||||||
|
|
||||||
|
if member-name.starts-with(regex("\+|-|~|#")) {
|
||||||
|
let vis = member-name.first()
|
||||||
|
member-name = member-name.slice(1)
|
||||||
|
visibility = (
|
||||||
|
"+": uml.public,
|
||||||
|
"-": uml.private,
|
||||||
|
"~": uml.package,
|
||||||
|
"#": uml.protected
|
||||||
|
).at(vis)
|
||||||
|
}
|
||||||
|
|
||||||
|
if member-name.ends-with(")") {
|
||||||
|
let func = member-name.match(regex("^(.*?)\((.*)\)$"))
|
||||||
|
member-name = func.captures.first()
|
||||||
|
let args = parse-args(func.captures.last())
|
||||||
|
methods += uml.method(member-name, visibility, val-type, args)
|
||||||
|
} else {
|
||||||
|
attributes += uml.attr(member-name, visibility, val-type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
elements += uml.class(name, family, attributes + methods)
|
||||||
|
}
|
||||||
|
|
||||||
|
let defaults = (:)
|
||||||
|
let params = data.matches(regex("(?m)^\s*@\s*(.*?)\s*=\s*(.*)\s*$"))
|
||||||
|
for param in params {
|
||||||
|
let key = param.captures.first()
|
||||||
|
let value = eval(param.captures.last(), scope: (uml: uml))
|
||||||
|
defaults.insert(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return uml.diagram(elements, defaults)
|
||||||
|
}
|
569
misc/uml/uml.typ
Normal file
@ -0,0 +1,569 @@
|
|||||||
|
#import "@preview/cetz:0.2.2": canvas, draw
|
||||||
|
|
||||||
|
#let class-counter = counter("uml-class")
|
||||||
|
|
||||||
|
#let class-colors = (
|
||||||
|
rgb("#C02B22"),
|
||||||
|
rgb("#D4721C"),
|
||||||
|
rgb("D3BC2F"),
|
||||||
|
rgb("60CB12"),
|
||||||
|
rgb("#2ECC40"),
|
||||||
|
rgb("#39CCA5"),
|
||||||
|
rgb("#38B4DA"),
|
||||||
|
rgb("#0074d9"),
|
||||||
|
rgb("#450DC9"),
|
||||||
|
rgb("#890DC9"),
|
||||||
|
)
|
||||||
|
|
||||||
|
#let visibility(vis, symbol) = (type: "visibility", visibility: vis, symbol: symbol)
|
||||||
|
|
||||||
|
#let public = visibility("public", "+")
|
||||||
|
#let private = visibility("private", "-")
|
||||||
|
#let protected = visibility("protected", "#")
|
||||||
|
#let package = visibility("package", "~")
|
||||||
|
|
||||||
|
#let class-family(family, prefix: none) = (
|
||||||
|
type: "class-family",
|
||||||
|
family: family,
|
||||||
|
prefix: prefix
|
||||||
|
)
|
||||||
|
#let abstract = class-family("abstract")
|
||||||
|
#let interface = class-family("interface", prefix: "interface")
|
||||||
|
#let trait = class-family("trait")
|
||||||
|
#let enum = class-family("enum")
|
||||||
|
#let utility = class-family("utility", prefix: "utility")
|
||||||
|
|
||||||
|
#let staticity(static) = (type: "staticity", static: static)
|
||||||
|
#let static = staticity(true)
|
||||||
|
#let non-static = staticity(false)
|
||||||
|
|
||||||
|
#let class(name, ..args, grouped: none) = {
|
||||||
|
let attributes = ()
|
||||||
|
let methods = ()
|
||||||
|
let visibility = none
|
||||||
|
let family = none
|
||||||
|
|
||||||
|
let args = args.pos()
|
||||||
|
if args.len() != 0 {
|
||||||
|
for arg in args {
|
||||||
|
if type(arg) == dictionary {
|
||||||
|
if arg.type == "visibility" {
|
||||||
|
visibility = arg
|
||||||
|
} else if arg.type == "class-family" {
|
||||||
|
family = arg
|
||||||
|
}
|
||||||
|
} else if type(arg) == array {
|
||||||
|
for elmt in arg {
|
||||||
|
if type(elmt) == dictionary {
|
||||||
|
if elmt.type == "visibility" {
|
||||||
|
visibility = elmt
|
||||||
|
} else if elmt.type == "attribute" {
|
||||||
|
attributes.push(elmt)
|
||||||
|
} else if elmt.type == "method" {
|
||||||
|
methods.push(elmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ((
|
||||||
|
type: "class",
|
||||||
|
name: name,
|
||||||
|
visibility: visibility,
|
||||||
|
family: family,
|
||||||
|
attributes: attributes,
|
||||||
|
methods: methods,
|
||||||
|
color: rgb(184, 84, 80),
|
||||||
|
grouped: grouped
|
||||||
|
),)
|
||||||
|
}
|
||||||
|
|
||||||
|
#let attr(name, default: none, ..args) = {
|
||||||
|
let visibility = none
|
||||||
|
let staticity = none
|
||||||
|
let val-type = none
|
||||||
|
for arg in args.pos() {
|
||||||
|
if type(arg) == dictionary {
|
||||||
|
if arg.type == "visibility" {
|
||||||
|
visibility = arg
|
||||||
|
} else if arg.type == "type" {
|
||||||
|
val-type = arg
|
||||||
|
} else if arg.type == "staticity" {
|
||||||
|
staticity = staticity
|
||||||
|
}
|
||||||
|
} else if type(arg) == str {
|
||||||
|
val-type = arg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ((
|
||||||
|
type: "attribute",
|
||||||
|
name: name,
|
||||||
|
visibility: visibility,
|
||||||
|
staticity: staticity,
|
||||||
|
val-type: val-type,
|
||||||
|
default: default
|
||||||
|
),)
|
||||||
|
}
|
||||||
|
|
||||||
|
#let method(name, ..args) = {
|
||||||
|
let visibility = none
|
||||||
|
let staticity = none
|
||||||
|
let parameters = none
|
||||||
|
let return-type = none
|
||||||
|
|
||||||
|
for arg in args.pos() {
|
||||||
|
if type(arg) == dictionary {
|
||||||
|
if "type" in arg {
|
||||||
|
if arg.type == "visibility" {
|
||||||
|
visibility = arg
|
||||||
|
} else if arg.type == "staticity" {
|
||||||
|
staticity = arg
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parameters = arg
|
||||||
|
}
|
||||||
|
} else if type(arg) == str {
|
||||||
|
return-type = arg
|
||||||
|
} else if type(arg) == array {
|
||||||
|
parameters = arg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ((
|
||||||
|
type: "method",
|
||||||
|
name: name,
|
||||||
|
visibility: visibility,
|
||||||
|
staticity: staticity,
|
||||||
|
parameters: parameters,
|
||||||
|
return-type: return-type
|
||||||
|
),)
|
||||||
|
}
|
||||||
|
|
||||||
|
#let render-attr(attr, defaults, show-visibility: true) = {
|
||||||
|
let visibility = if attr.visibility == none {
|
||||||
|
defaults.attr-visibility
|
||||||
|
} else {
|
||||||
|
attr.visibility
|
||||||
|
}
|
||||||
|
let staticity = if attr.staticity == none {
|
||||||
|
defaults.attr-staticity
|
||||||
|
} else {
|
||||||
|
attr.staticity
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = attr.name
|
||||||
|
if staticity.static {
|
||||||
|
name = underline(name)
|
||||||
|
}
|
||||||
|
let txt = name
|
||||||
|
if show-visibility {
|
||||||
|
txt = visibility.symbol + txt
|
||||||
|
}
|
||||||
|
|
||||||
|
if attr.val-type != none {
|
||||||
|
txt += ": " + attr.val-type
|
||||||
|
}
|
||||||
|
if attr.default != none {
|
||||||
|
txt += " = " + str(attr.default)
|
||||||
|
}
|
||||||
|
return txt
|
||||||
|
}
|
||||||
|
|
||||||
|
#let render-method(method, defaults, show-visibility: true) = {
|
||||||
|
let visibility = if method.visibility == none {
|
||||||
|
defaults.method-visibility
|
||||||
|
} else {
|
||||||
|
method.visibility
|
||||||
|
}
|
||||||
|
let staticity = if method.staticity == none {
|
||||||
|
defaults.method-staticity
|
||||||
|
} else {
|
||||||
|
method.staticity
|
||||||
|
}
|
||||||
|
let params = ()
|
||||||
|
|
||||||
|
if type(method.parameters) == array {
|
||||||
|
params = method.parameters
|
||||||
|
} else if type(method.parameters) == dictionary {
|
||||||
|
params = method.parameters
|
||||||
|
.pairs()
|
||||||
|
.map(((n, t)) => n + ": " + t)
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = method.name
|
||||||
|
if staticity.static {
|
||||||
|
name = underline(name)
|
||||||
|
}
|
||||||
|
let txt = name
|
||||||
|
if show-visibility {
|
||||||
|
txt = visibility.symbol + txt
|
||||||
|
}
|
||||||
|
txt += "(" + params.join(", ") + ")"
|
||||||
|
if method.return-type != none {
|
||||||
|
txt += ": " + method.return-type
|
||||||
|
}
|
||||||
|
|
||||||
|
return txt
|
||||||
|
}
|
||||||
|
|
||||||
|
#let get-attributes-by-visibility(class, defaults) = {
|
||||||
|
let by-vis = (:)
|
||||||
|
for attr in class.attributes {
|
||||||
|
let visibility = if attr.visibility == none {
|
||||||
|
defaults.attr-visibility
|
||||||
|
} else {
|
||||||
|
attr.visibility
|
||||||
|
}
|
||||||
|
if not visibility.visibility in by-vis {
|
||||||
|
by-vis.insert(visibility.visibility, ())
|
||||||
|
}
|
||||||
|
by-vis.at(visibility.visibility).push(attr)
|
||||||
|
}
|
||||||
|
return by-vis
|
||||||
|
}
|
||||||
|
|
||||||
|
#let get-methods-by-visibility(class, defaults) = {
|
||||||
|
let by-vis = (:)
|
||||||
|
for method in class.methods {
|
||||||
|
let visibility = if method.visibility == none {
|
||||||
|
defaults.method-visibility
|
||||||
|
} else {
|
||||||
|
method.visibility
|
||||||
|
}
|
||||||
|
if not visibility.visibility in by-vis {
|
||||||
|
by-vis.insert(visibility.visibility, ())
|
||||||
|
}
|
||||||
|
by-vis.at(visibility.visibility).push(method)
|
||||||
|
}
|
||||||
|
return by-vis
|
||||||
|
}
|
||||||
|
|
||||||
|
#let render-class-attributes(class, defaults) = {
|
||||||
|
let grouped = if class.grouped == none {
|
||||||
|
defaults.class-grouped
|
||||||
|
} else {
|
||||||
|
class.grouped
|
||||||
|
}
|
||||||
|
let cells = ()
|
||||||
|
|
||||||
|
if not grouped {
|
||||||
|
for attr in class.attributes {
|
||||||
|
cells.push(render-attr(attr, defaults, show-visibility: true))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let by-vis = get-attributes-by-visibility(class, defaults)
|
||||||
|
for (vis, attrs) in by-vis {
|
||||||
|
cells.push(table.cell(vis + ":"))
|
||||||
|
cells += attrs.map(attr => {
|
||||||
|
let txt = render-attr(attr, defaults, show-visibility: false)
|
||||||
|
pad(left: 1em, txt)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cells
|
||||||
|
}
|
||||||
|
|
||||||
|
#let render-class-methods(class, defaults) = {
|
||||||
|
let grouped = if class.grouped == none {
|
||||||
|
defaults.class-grouped
|
||||||
|
} else {
|
||||||
|
class.grouped
|
||||||
|
}
|
||||||
|
let cells = ()
|
||||||
|
|
||||||
|
if not grouped {
|
||||||
|
for method in class.methods {
|
||||||
|
cells.push(render-method(method, defaults, show-visibility: true))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let by-vis = get-methods-by-visibility(class, defaults)
|
||||||
|
for (vis, attrs) in by-vis {
|
||||||
|
cells.push(table.cell(vis + ":"))
|
||||||
|
cells += attrs.map(method => {
|
||||||
|
let txt = render-method(method, defaults, show-visibility: false)
|
||||||
|
pad(left: 1em, txt)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cells
|
||||||
|
}
|
||||||
|
|
||||||
|
#let render-class(class, defaults) = context {
|
||||||
|
let family = if class.family == none {
|
||||||
|
defaults.class-family
|
||||||
|
} else {
|
||||||
|
class.family
|
||||||
|
}
|
||||||
|
|
||||||
|
let cells = ()
|
||||||
|
cells += render-class-attributes(class, defaults)
|
||||||
|
|
||||||
|
cells.push(table.hline())
|
||||||
|
|
||||||
|
cells += render-class-methods(class, defaults)
|
||||||
|
|
||||||
|
let name = [*#class.name*]
|
||||||
|
let family = class.family
|
||||||
|
if family == abstract {
|
||||||
|
name = emph(name)
|
||||||
|
}
|
||||||
|
if family != none and family.prefix != none {
|
||||||
|
name = [*«#family.prefix»*\ #name]
|
||||||
|
}
|
||||||
|
let class-i = class-counter.get().first()
|
||||||
|
let col = class-colors.at(calc.rem(class-i, class-colors.len()))
|
||||||
|
|
||||||
|
set table.hline(stroke: col)
|
||||||
|
table(
|
||||||
|
inset: (top: 0.5em, bottom: 0.5em, left: 0.5em, right: 0.5em),
|
||||||
|
stroke: (x, y) => {
|
||||||
|
let s = (left: col, right: col)
|
||||||
|
if y == 0 {
|
||||||
|
s.insert("top", col)
|
||||||
|
s.insert("bottom", col)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
},
|
||||||
|
align: (x, y) => {
|
||||||
|
if y == 0 {
|
||||||
|
return center + horizon
|
||||||
|
}
|
||||||
|
return left + horizon
|
||||||
|
},
|
||||||
|
fill: (x, y) => if y == 0 {
|
||||||
|
col.lighten(75%)
|
||||||
|
} else {
|
||||||
|
none
|
||||||
|
},
|
||||||
|
table.header(name),
|
||||||
|
..cells,
|
||||||
|
table.hline()
|
||||||
|
)
|
||||||
|
class-counter.step()
|
||||||
|
}
|
||||||
|
|
||||||
|
#let diagram(..args) = {
|
||||||
|
class-counter.update(0)
|
||||||
|
|
||||||
|
let args = args.pos()
|
||||||
|
|
||||||
|
let defaults = (
|
||||||
|
class-family: none,
|
||||||
|
class-grouped: false,
|
||||||
|
attr-visibility: public,
|
||||||
|
attr-staticity: non-static,
|
||||||
|
method-visibility: public,
|
||||||
|
method-staticity: non-static,
|
||||||
|
)
|
||||||
|
let elements = ()
|
||||||
|
if args.len() != 0 {
|
||||||
|
for arg in args {
|
||||||
|
if type(arg) == dictionary {
|
||||||
|
defaults += arg
|
||||||
|
} else if type(arg) == array {
|
||||||
|
elements = arg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let cells = ()
|
||||||
|
for element in elements {
|
||||||
|
if element.type == "class" {
|
||||||
|
cells.push(render-class(element, defaults))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
box(
|
||||||
|
stroke: black,
|
||||||
|
inset: 1em,
|
||||||
|
grid(
|
||||||
|
columns: 2,
|
||||||
|
column-gutter: 1em,
|
||||||
|
align: center + horizon,
|
||||||
|
row-gutter: 1em,
|
||||||
|
..cells
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#let render-attr2(pos, id, attr, defaults, show-visibility: true) = {
|
||||||
|
let visibility = if attr.visibility == none {
|
||||||
|
defaults.attr-visibility
|
||||||
|
} else {
|
||||||
|
attr.visibility
|
||||||
|
}
|
||||||
|
let staticity = if attr.staticity == none {
|
||||||
|
defaults.attr-staticity
|
||||||
|
} else {
|
||||||
|
attr.staticity
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = attr.name
|
||||||
|
if staticity.static {
|
||||||
|
name = underline(name)
|
||||||
|
}
|
||||||
|
let txt = name
|
||||||
|
if show-visibility {
|
||||||
|
txt = visibility.symbol + txt
|
||||||
|
}
|
||||||
|
|
||||||
|
if attr.val-type != none {
|
||||||
|
txt += ": " + attr.val-type
|
||||||
|
}
|
||||||
|
if attr.default != none {
|
||||||
|
txt += " = " + str(attr.default)
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.content(pos, txt, name: id)
|
||||||
|
}
|
||||||
|
|
||||||
|
#let render-method2(method, defaults, show-visibility: true) = {
|
||||||
|
let visibility = if method.visibility == none {
|
||||||
|
defaults.method-visibility
|
||||||
|
} else {
|
||||||
|
method.visibility
|
||||||
|
}
|
||||||
|
let staticity = if method.staticity == none {
|
||||||
|
defaults.method-staticity
|
||||||
|
} else {
|
||||||
|
method.staticity
|
||||||
|
}
|
||||||
|
let params = ()
|
||||||
|
|
||||||
|
if type(method.parameters) == array {
|
||||||
|
params = method.parameters
|
||||||
|
} else if type(method.parameters) == dictionary {
|
||||||
|
params = method.parameters
|
||||||
|
.pairs()
|
||||||
|
.map(((n, t)) => n + ": " + t)
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = method.name
|
||||||
|
if staticity.static {
|
||||||
|
name = underline(name)
|
||||||
|
}
|
||||||
|
let txt = name
|
||||||
|
if show-visibility {
|
||||||
|
txt = visibility.symbol + txt
|
||||||
|
}
|
||||||
|
txt += "(" + params.join(", ") + ")"
|
||||||
|
if method.return-type != none {
|
||||||
|
txt += ": " + method.return-type
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.content((), txt)
|
||||||
|
}
|
||||||
|
|
||||||
|
#let render-class-attributes2(class, defaults) = {
|
||||||
|
let grouped = if class.grouped == none {
|
||||||
|
defaults.class-grouped
|
||||||
|
} else {
|
||||||
|
class.grouped
|
||||||
|
}
|
||||||
|
let cells = ()
|
||||||
|
let prev = ()
|
||||||
|
|
||||||
|
if not grouped {
|
||||||
|
for (i, attr) in class.attributes.enumerate() {
|
||||||
|
let id = "class-"+class.name+"-attr-"+str(i)
|
||||||
|
let txt = render-attr(attr, defaults, show-visibility: true)
|
||||||
|
draw.content((rel: (0, -0.4em), to: prev), name: id, anchor: "north-west", txt)
|
||||||
|
prev = id + ".south-west"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let by-vis = get-attributes-by-visibility(class, defaults)
|
||||||
|
for (vis, attrs) in by-vis {
|
||||||
|
cells.push(table.cell(vis + ":"))
|
||||||
|
let vis-id = "class-"+class.name+"-attrs-"+vis
|
||||||
|
draw.content((rel: (0, -0.4em), to: prev), name: vis-id, anchor: "north-west", vis + ":")
|
||||||
|
prev = vis-id + ".south-west"
|
||||||
|
|
||||||
|
for (i, attr) in attrs.enumerate() {
|
||||||
|
let txt = render-attr(attr, defaults, show-visibility: false)
|
||||||
|
txt = pad(left: 1em, txt)
|
||||||
|
let id = vis-id + "-" + str(i)
|
||||||
|
draw.content((rel: (0, -0.4em), to: prev), name: id, anchor: "north-west", txt)
|
||||||
|
prev = id + ".south-west"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#let render-class-methods2(class, defaults) = {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#let render-class2(class, defaults) = {
|
||||||
|
let attr-group = render-class-attributes2(class, defaults)
|
||||||
|
let method-group = render-class-methods2(class, defaults)
|
||||||
|
|
||||||
|
let family = if class.family == none {
|
||||||
|
defaults.class-family
|
||||||
|
} else {
|
||||||
|
class.family
|
||||||
|
}
|
||||||
|
let name = [*#class.name*]
|
||||||
|
let family = class.family
|
||||||
|
if family == abstract {
|
||||||
|
name = emph(name)
|
||||||
|
}
|
||||||
|
if family != none and family.prefix != none {
|
||||||
|
name = [*«#family.prefix»*\ #name]
|
||||||
|
}
|
||||||
|
let class-i = class-counter.get().first()
|
||||||
|
let col = class-colors.at(calc.rem(class-i, class-colors.len()))
|
||||||
|
|
||||||
|
draw.group(name: "class-"+class.name, {
|
||||||
|
draw.group(name: "class-"+class.name+"-name", padding: 5pt, {
|
||||||
|
draw.content((), name)
|
||||||
|
})
|
||||||
|
draw.on-layer(-1, {
|
||||||
|
draw.rect("class-"+class.name+"-name.north-west", "class-"+class.name+"-name.south-east", fill: col.lighten(75%), stroke: col, layer: -1)
|
||||||
|
})
|
||||||
|
draw.group(name: "class-"+class.name+"-attrs", padding: 0.2em, {
|
||||||
|
attr-group
|
||||||
|
})
|
||||||
|
method-group
|
||||||
|
})
|
||||||
|
draw.rect("class-"+class.name+".north-west", "class-"+class.name+".south-east", stroke: col)
|
||||||
|
}
|
||||||
|
|
||||||
|
#let render-class3(class, defaults) = {
|
||||||
|
let id = "class-" + class.name
|
||||||
|
return draw.content((rel: (3, 0), to: ()), render-class(class, defaults), name: id)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#let diagram2(..args) = context {
|
||||||
|
class-counter.update(0)
|
||||||
|
|
||||||
|
let args = args.pos()
|
||||||
|
|
||||||
|
let defaults = (
|
||||||
|
class-family: none,
|
||||||
|
class-grouped: false,
|
||||||
|
attr-visibility: public,
|
||||||
|
attr-staticity: non-static,
|
||||||
|
method-visibility: public,
|
||||||
|
method-staticity: non-static,
|
||||||
|
)
|
||||||
|
let elements = ()
|
||||||
|
if args.len() != 0 {
|
||||||
|
for arg in args {
|
||||||
|
if type(arg) == dictionary {
|
||||||
|
defaults += arg
|
||||||
|
} else if type(arg) == array {
|
||||||
|
elements = arg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let cells = ()
|
||||||
|
canvas({
|
||||||
|
for element in elements {
|
||||||
|
if element.type == "class" {
|
||||||
|
render-class3(element, defaults)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
draw.line("class-Test", "class-Essai", mark: (end: "diamond", scale: 2))
|
||||||
|
})
|
||||||
|
}
|