mirror of
https://github.com/alex-s168/website.git
synced 2025-09-10 01:05:07 +02:00
init
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
build
|
||||||
|
pub.sh
|
||||||
|
*.html
|
||||||
|
*.pdf
|
20
build.sh
Executable file
20
build.sh
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
set -e
|
||||||
|
|
||||||
|
rm -rf build
|
||||||
|
mkdir build
|
||||||
|
|
||||||
|
compile () {
|
||||||
|
typst compile --root . --features html -j 4 $@
|
||||||
|
}
|
||||||
|
|
||||||
|
page () {
|
||||||
|
compile --format pdf --input web=false "pages/$1" "build/$1.min.pdf"
|
||||||
|
compile --format html --input web=true "pages/$1" "build/$1.desktop.html"
|
||||||
|
compile --format html --input web=false "pages/$1" "build/$1.min.html"
|
||||||
|
}
|
||||||
|
|
||||||
|
page "article-make-regex-engine-1.typ"
|
||||||
|
page "project-etc-nand.typ"
|
||||||
|
page "index.typ"
|
||||||
|
|
||||||
|
cp build/index.typ.desktop.html build/index.html
|
243
common.typ
Normal file
243
common.typ
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
#let to-bool(str) = {
|
||||||
|
if str == "true" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if str == "false" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
assert(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
#let is-web = to-bool(sys.inputs.at("web", default: "true"))
|
||||||
|
#let is-html() = { return target() == "html" }
|
||||||
|
|
||||||
|
#let res-path() = {
|
||||||
|
if is-html() {
|
||||||
|
"res/"
|
||||||
|
} else {
|
||||||
|
"/res/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#let title(content) = {
|
||||||
|
context if is-html() {
|
||||||
|
html.elem("h1", content)
|
||||||
|
} else {
|
||||||
|
text(23pt, content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#let br() = {
|
||||||
|
context if is-html() {
|
||||||
|
html.elem("br")
|
||||||
|
}
|
||||||
|
"\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
#let space(num: 1) = {
|
||||||
|
context if is-html() {
|
||||||
|
[~] * (num - 1) + [ ]
|
||||||
|
} else {
|
||||||
|
" " * num
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#let html-p(txt) = {
|
||||||
|
context if is-html() {
|
||||||
|
html.elem("p", txt)
|
||||||
|
} else {
|
||||||
|
text(txt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#let sized-p(size, txt) = {
|
||||||
|
context if is-html() {
|
||||||
|
html.elem("p", attrs: (style: "font-size: " + str(size.abs.pt()) + "pt"), txt)
|
||||||
|
} else {
|
||||||
|
text(size, txt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#let html-frame(content) = {
|
||||||
|
context if is-html() {
|
||||||
|
html.frame(content)
|
||||||
|
} else {
|
||||||
|
content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#let html-opt-elem(kind, attrs, content) = {
|
||||||
|
context if is-html() {
|
||||||
|
html.elem(kind, attrs: attrs, content)
|
||||||
|
} else {
|
||||||
|
content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#let html-span(attrs, content) = {
|
||||||
|
html-opt-elem("span", attrs, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
#let html-bold(content) = {
|
||||||
|
context if is-html() {
|
||||||
|
html.elem("b", content)
|
||||||
|
} else {
|
||||||
|
content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#let html-style(class:"", style, content) = {
|
||||||
|
html-span((class:class, style: style), content)
|
||||||
|
}
|
||||||
|
|
||||||
|
#let slink(target, link-text) = text(fill: blue)[
|
||||||
|
#underline[#link(target, link-text)]
|
||||||
|
]
|
||||||
|
|
||||||
|
#let flink(target, link-text) = text(fill: blue)[
|
||||||
|
#underline[#link(target, link-text)]
|
||||||
|
#footnote[#text(fill: blue)[#link(target)]]
|
||||||
|
]
|
||||||
|
|
||||||
|
#let section(b) = block(breakable: false, [
|
||||||
|
\
|
||||||
|
#b
|
||||||
|
])
|
||||||
|
|
||||||
|
#let take-while(array, fn) = {
|
||||||
|
let out = ();
|
||||||
|
for x in array {
|
||||||
|
if fn(x) {
|
||||||
|
out += (x,);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#let gen-tree-from-headings(
|
||||||
|
hide-first: false,
|
||||||
|
marker: context "├─" + space(),
|
||||||
|
marker-end: context "└─" + space(),
|
||||||
|
side: context "|" + space(num:2),
|
||||||
|
side-empty: context space(num:3),
|
||||||
|
elemfn: x => x,
|
||||||
|
headings,
|
||||||
|
) = {
|
||||||
|
for (idx, item) in headings.enumerate() {
|
||||||
|
let content = [];
|
||||||
|
for ind in range(item.level - 1) {
|
||||||
|
let will-future = take-while(headings.slice(idx),
|
||||||
|
x => x.level > ind)
|
||||||
|
.any(x => x.level == ind + 1);
|
||||||
|
content += if will-future {
|
||||||
|
side
|
||||||
|
} else {
|
||||||
|
side-empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
content += if take-while(headings.slice(idx + 1),
|
||||||
|
x => x.level >= item.level)
|
||||||
|
.any(x => x.level == item.level) {
|
||||||
|
marker
|
||||||
|
} else {
|
||||||
|
marker-end
|
||||||
|
}
|
||||||
|
elemfn(content, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#let len2css(len, default: "auto", map-pt: x => str(x)+"pt") = {
|
||||||
|
if len == auto or type(len) == dictionary {
|
||||||
|
return default
|
||||||
|
}
|
||||||
|
if type(len) == relative and float(len.ratio) * 100 != 0 {
|
||||||
|
assert(len.length.pt() == 0)
|
||||||
|
return str(float(len.ratio) * 100) + "%";
|
||||||
|
}
|
||||||
|
if type(len) == relative {
|
||||||
|
len = len.length
|
||||||
|
}
|
||||||
|
return map-pt(len.abs.pt())
|
||||||
|
}
|
||||||
|
|
||||||
|
#let option-map(option, default, fn) = {
|
||||||
|
if option == none { default }
|
||||||
|
else { fn(option) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#let stroke2css(stroke) = {
|
||||||
|
if type(stroke) == dictionary {
|
||||||
|
return "none"
|
||||||
|
}
|
||||||
|
let th = len2css(stroke.thickness, default: "1pt")
|
||||||
|
return th + " solid black" // TODO: paint
|
||||||
|
}
|
||||||
|
|
||||||
|
#let css-style(it) = {
|
||||||
|
return "
|
||||||
|
display: inline-block;
|
||||||
|
border: "+stroke2css(it.stroke)+";
|
||||||
|
border-radius: "+len2css(it.radius, default: "0px")+";
|
||||||
|
" + option-map(len2css(it.width, default:none), "", x => "width:"+x+";") +"
|
||||||
|
" + option-map(len2css(it.height, default:none), "", x => "height:"+x+";") +"
|
||||||
|
padding: " + len2css(it.inset)
|
||||||
|
}
|
||||||
|
|
||||||
|
#let html-script(code) = {
|
||||||
|
[#context if is-html(){
|
||||||
|
html.elem("script", code)
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
#let html-script-dom-onload(code) = {
|
||||||
|
html-script("document.addEventListener('DOMContentLoaded', function() { "+code+" })")
|
||||||
|
}
|
||||||
|
|
||||||
|
#let column-fixed(..contents) = {
|
||||||
|
html-style(class:"column-fixed", "display: inline-flex; position: fixed; justify-content: center; width: 25%")[
|
||||||
|
#table(..contents)
|
||||||
|
|
||||||
|
#context if is-html() {
|
||||||
|
html.elem("style", "
|
||||||
|
.column-fixed > table > tbody > tr > td > * { width: 100%; }
|
||||||
|
")
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#let table-of-contents() = {
|
||||||
|
html-style(class:"table-of-contents", "", [
|
||||||
|
#box(
|
||||||
|
stroke: black,
|
||||||
|
radius: 2pt,
|
||||||
|
inset: 3%,
|
||||||
|
)[
|
||||||
|
Table of contents\
|
||||||
|
#context {
|
||||||
|
let idx = state("stupid-gen-page-state", 0);
|
||||||
|
gen-tree-from-headings(query(heading),
|
||||||
|
elemfn: (content, x) => {
|
||||||
|
[#content #context html-span((class:"headingr",id:"headingr-"+str(idx.get())), link(x.location(), x.body)) #context idx.update(x=> x + 1) #br()]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
#html-script-dom-onload("
|
||||||
|
let tags = ['h2','h3','h4'].flatMap(x => Array.from(document.getElementsByTagName(x)));
|
||||||
|
document.getElementById('headingr-0').classList.add('current')
|
||||||
|
document.addEventListener('scroll', (event) => {
|
||||||
|
let curr = tags.map(x => [x, (x.getBoundingClientRect().top + x.getBoundingClientRect().bottom) / 2]).filter(x => x[1] >= 0).sort((a,b) => a[1] > b[1])[0][0];
|
||||||
|
let idx = tags.sort((a,b) => a.getBoundingClientRect().top > b.getBoundingClientRect().top).map((x, idx) => [idx, x]).filter(x => x[1] == curr)[0][0];
|
||||||
|
Array.from(document.getElementsByClassName('headingr')).map(x => x.classList.remove('current'))
|
||||||
|
document.getElementById('headingr-'+idx).classList.add('current')
|
||||||
|
});
|
||||||
|
")
|
||||||
|
] + [
|
||||||
|
#context if is-html() {
|
||||||
|
html.elem("style", "
|
||||||
|
.table-of-contents > p > span { width: 100%; }
|
||||||
|
")
|
||||||
|
}
|
||||||
|
])
|
||||||
|
}
|
24
components/pcb-view.typ
Normal file
24
components/pcb-view.typ
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#import "../common.typ": *
|
||||||
|
|
||||||
|
#let pcb(front,back, size-percent:100) = {
|
||||||
|
context if is-html() {
|
||||||
|
if is-web {
|
||||||
|
html.elem("img", attrs:(draggable:"false", tite:"Click Me!", src:front, data-other:back, onclick:"swapFrontBack(this);", style:"width:"+str(size-percent)+"%; cursor:pointer;"))
|
||||||
|
} else {
|
||||||
|
html.elem("img", attrs:(src:front, style:"width:100%;"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
[#image(front) #image(back)]
|
||||||
|
}
|
||||||
|
context if is-html() and is-web {
|
||||||
|
html-script("
|
||||||
|
function swapFrontBack(img) {
|
||||||
|
let oldsrc = img.src;
|
||||||
|
img.src = img.dataset.other;
|
||||||
|
img.dataset.other = oldsrc;
|
||||||
|
}
|
||||||
|
")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
115
core-page-style.typ
Normal file
115
core-page-style.typ
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
#import "common.typ": *
|
||||||
|
|
||||||
|
|
||||||
|
#let small-font-size = 14pt
|
||||||
|
#let default-font-size = 17pt
|
||||||
|
|
||||||
|
#let core-page-style(content) = {[
|
||||||
|
|
||||||
|
|
||||||
|
#show: x => context {
|
||||||
|
set page(width: auto, height: auto) if is-web and not is-html()
|
||||||
|
set page(paper: "a4") if not is-web and not is-html()
|
||||||
|
x
|
||||||
|
}
|
||||||
|
|
||||||
|
#set text(font: "DejaVu Sans Mono", size: default-font-size)
|
||||||
|
|
||||||
|
#show raw: it => box(
|
||||||
|
stroke: black,
|
||||||
|
radius: 2pt,
|
||||||
|
inset: if is-html() { 1.4pt } else { 4pt },
|
||||||
|
outset: 0pt,
|
||||||
|
baseline: 3.1pt,
|
||||||
|
text(it)
|
||||||
|
)
|
||||||
|
|
||||||
|
#show box: it => {
|
||||||
|
context if is-html() {
|
||||||
|
html.elem("span", attrs: (style: css-style(it)))[#it.body]
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#show underline: it => {
|
||||||
|
context if is-html() {
|
||||||
|
html.elem("u", it.body)
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#show heading: it => underline[#it #v(3pt)]
|
||||||
|
|
||||||
|
#set underline(stroke: 1pt, offset: 2pt)
|
||||||
|
|
||||||
|
#show footnote: it => if is-web { [] } else { it }
|
||||||
|
#show footnote.entry: it => if is-web { [] } else { it }
|
||||||
|
|
||||||
|
#context if is-html() {
|
||||||
|
html.elem("style", "
|
||||||
|
@font-face {
|
||||||
|
font-family: 'DejaVu Sans Mono';
|
||||||
|
src: url('res/DejaVuSansMono-Bold.woff2') format('woff2'),
|
||||||
|
url('res/DejaVuSansMono-Bold.woff') format('woff'),
|
||||||
|
local('DejaVu Sans Mono'),
|
||||||
|
local('Courier New'),
|
||||||
|
local(Courier),
|
||||||
|
local(monospace);
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'DejaVu Sans Mono';
|
||||||
|
src: url('res/DejaVuSansMono.woff2') format('woff2'),
|
||||||
|
url('res/DejaVuSansMono.woff') format('woff'),
|
||||||
|
local('DejaVu Sans Mono'),
|
||||||
|
local('Courier New'),
|
||||||
|
local(Courier),
|
||||||
|
local(monospace);
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: DejaVu Sans Mono;
|
||||||
|
font-size: "+len2css(default-font-size)+";
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
width: 100%;
|
||||||
|
display: inline;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,h2,h3,h4 {
|
||||||
|
margin-top: 1%;
|
||||||
|
margin-bottom: 0.75%;
|
||||||
|
" + if is-web { "margin-left: -0.75%;" } else { "" }
|
||||||
|
+
|
||||||
|
"
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-top: 0.75%;
|
||||||
|
margin-bottom: 0.75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin-top: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#content
|
||||||
|
]
|
||||||
|
}
|
224
pages/article-make-regex-engine-1.typ
Normal file
224
pages/article-make-regex-engine-1.typ
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
#import "../common.typ": *
|
||||||
|
#import "../simple-page-layout.typ": *
|
||||||
|
#import "../core-page-style.typ": *
|
||||||
|
|
||||||
|
#simple-page(
|
||||||
|
gen-table-of-contents: true
|
||||||
|
)[
|
||||||
|
|
||||||
|
#section[
|
||||||
|
#title[Making a simple RegEx engine]
|
||||||
|
|
||||||
|
#title[Part 1: Introduction to RegEx]
|
||||||
|
|
||||||
|
#sized-p(small-font-size)[
|
||||||
|
Written by alex_s168
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
#if is-web {section[
|
||||||
|
Note that the #min-pdf-link[PDF Version] of this page might look a bit better styling wise.
|
||||||
|
]}
|
||||||
|
|
||||||
|
#section[
|
||||||
|
= Introduction
|
||||||
|
If you are any kind of programmer,
|
||||||
|
you've probably heard of #flink("https://en.wikipedia.org/wiki/Regular_expression", "RegEx")
|
||||||
|
|
||||||
|
RegEx (Regular expression) is kind of like a small programming language
|
||||||
|
used to define string search and replace patterns.
|
||||||
|
|
||||||
|
\
|
||||||
|
RegEx might seem overwhelming at first, but you can learn the most important features of RegEx very quickly.
|
||||||
|
|
||||||
|
\
|
||||||
|
It is important to mention that there is not a single standard for RegEx syntax,
|
||||||
|
but instead each "implementation" has it's own quirks, and additional features.
|
||||||
|
Most common features however behave identically on most RegEx "engines"/implementations.
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
= Syntax
|
||||||
|
The behavior of RegEx expressions / patterns depends on the match options passed to the RegEx engine.
|
||||||
|
|
||||||
|
Common match options: <match-options>
|
||||||
|
- Anchored at start and end of line
|
||||||
|
- Case insensitive
|
||||||
|
- multi-line or instead whole string
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
== "Atoms"
|
||||||
|
In this article, we will refer to single expression parts as "atoms".
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
=== Characters
|
||||||
|
Just use the character that you want to match. For example ```re a``` to match an `a`.
|
||||||
|
This however does not work for all characters, because many are part of special RegEx syntax.
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
=== Escaped Characters <escaped-chars>
|
||||||
|
Thee previously mentioned special characters like `[` can be matched by putting a backslash in front of them: ```re \[```
|
||||||
|
|
||||||
|
#context html-frame[
|
||||||
|
#table(
|
||||||
|
columns: (auto,auto),
|
||||||
|
table.header(
|
||||||
|
[Pattern],
|
||||||
|
[Description]
|
||||||
|
),
|
||||||
|
|
||||||
|
[```re \\```], [match a literal backslash],
|
||||||
|
[```re \n```], [match a new-line],
|
||||||
|
)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
=== Character Groups <char-groups>
|
||||||
|
RegEx engines already define some groups of characters that can make writing RegEx expressions quicker.
|
||||||
|
|
||||||
|
#context html-frame[
|
||||||
|
#table(
|
||||||
|
columns: (auto,auto),
|
||||||
|
table.header(
|
||||||
|
[Pattern],
|
||||||
|
[Description],
|
||||||
|
),
|
||||||
|
|
||||||
|
[```re .```], [any character except for line breaks],
|
||||||
|
[```re \s```], [any whitespace or line break],
|
||||||
|
[```re \S```], [any character except whitespaces or line breaks],
|
||||||
|
[```re \d```], [any digit from 0 to 9],
|
||||||
|
[```re \D```], [any character except digits from 0 to 9],
|
||||||
|
[```re \w```], [a letter, digit, or underscore],
|
||||||
|
[```re \W```], [any character except for letters, digits, and underscores],
|
||||||
|
)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
=== Anchors
|
||||||
|
```re ^``` is used to assert the beginning of a line in multi-line mode,
|
||||||
|
or the beginning of the string in whole-string mode.
|
||||||
|
|
||||||
|
```re $``` is used to assert the end of a line in multi-line mode,
|
||||||
|
or the end of the string in whole-string mode.
|
||||||
|
|
||||||
|
The behaviours of these depend on the #slink(<match-options>)[match options]
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
== Greedy VS Lazy <greedy>
|
||||||
|
Some combinators will either match "lazy", or "greedy".
|
||||||
|
|
||||||
|
Lazy is when the engine only matches as many characters required to get to the next step.
|
||||||
|
This should almost always be used.
|
||||||
|
|
||||||
|
Greedy matching is when the engine tries to match as many characters as possible.
|
||||||
|
The problem with this is that it might cause "backtracking",
|
||||||
|
which happens when the engine goes back in the pattern multiple times to ensure that as many characters
|
||||||
|
as possible where matched. This can cause big performance issues.
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
== Combinators
|
||||||
|
Multiple atoms can be combined together to form more complex patterns.
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
=== Chain
|
||||||
|
When two expressions are next to each other, they will be chained together,
|
||||||
|
which means that both will be evaluated in-order.
|
||||||
|
|
||||||
|
Example: ```re x\d``` matches a `x` and then a digit, like for example `x9`
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
=== Or
|
||||||
|
Two expressions separated by a `|` cause the RegEx engine to first try to match the left side,
|
||||||
|
and only if it fails, it tries the right side instead.
|
||||||
|
|
||||||
|
Note that "or" has a long left and right scope,
|
||||||
|
which means that ```re ab|cd``` will match either ```re ab``` or ```re cd```
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
=== Or-Not
|
||||||
|
Tries to match the expression on the left to it, but won't error if it doesn't succeed.
|
||||||
|
|
||||||
|
Note that "or-not" has a short left scope,
|
||||||
|
which means that ```re ab?``` will always match ```re a```, and then try to match ```re b```
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
=== Repeated
|
||||||
|
A expression followed by either a ```re *``` for #slink(<greedy>)[greedy] repeat,
|
||||||
|
or a ```re *?``` for #slink(<greedy>)[lazy] repeat.
|
||||||
|
|
||||||
|
This matches as many times as possible, but can also match the pattern zero times.
|
||||||
|
|
||||||
|
Note that this has a short left scope.
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
=== Repeated At Least Once
|
||||||
|
A expression followed by either a ```re +``` for #slink(<greedy>)[greedy] repeat,
|
||||||
|
or a ```re +?``` for #slink(<greedy>)[lazy] repeat.
|
||||||
|
|
||||||
|
This matches as many times as possible, and at least one time.
|
||||||
|
|
||||||
|
Note that this has a short left scope.
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
=== (Non-Capture) Group <non-capture-group>
|
||||||
|
Groups multiple expressions together for scoping.
|
||||||
|
|
||||||
|
Example: ```re (?:abc)``` will just match `abc`
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
=== Capture Group
|
||||||
|
Similar to #slink(<non-capture-group>)[Non-Capture Groups] except that they capture the matched text.
|
||||||
|
This allows the matched text of the inner expression to be extracted later.
|
||||||
|
|
||||||
|
Capture group IDs are enumerated from left to right, starting with 1.
|
||||||
|
|
||||||
|
Example: ```re (abc)de``` will match `abcde`,
|
||||||
|
and store `abc` in group 1.
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
=== Character Set
|
||||||
|
By surrounding multiple characters in square brackets,
|
||||||
|
the engine will match any of them.
|
||||||
|
Special characters or expressions won't be parsed inside them,
|
||||||
|
which means that this can also be used to escape characters.
|
||||||
|
|
||||||
|
For example: ```re [abc]``` will match either `a`, `b` or `c`.
|
||||||
|
|
||||||
|
and ```re [ab(?:c)]``` will match either `a`, `b`, `(`, `?`, `:`, `c`, or `)`.
|
||||||
|
|
||||||
|
#slink(<char-groups>)[Character groups] and #slink(<escaped-chars>)[escaped characters]
|
||||||
|
still work inside character sets.
|
||||||
|
|
||||||
|
Character sets can also contain ranges.
|
||||||
|
For example: ```re [0-9a-z]``` will match either any digit, or any lowercase letter.
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
= Conclusion
|
||||||
|
RegEx is perfect for when you just want to match some patterns,
|
||||||
|
but the syntax can make patterns very hard to read or modify.
|
||||||
|
|
||||||
|
In the next article, we will start to dive into implementing RegEx.
|
||||||
|
|
||||||
|
Stay tuned!
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
]
|
81
pages/index.typ
Normal file
81
pages/index.typ
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#import "../common.typ": *
|
||||||
|
#import "../simple-page-layout.typ": *
|
||||||
|
|
||||||
|
#let gen-page(content) = {
|
||||||
|
core-page-style[
|
||||||
|
#if is-web {
|
||||||
|
table(
|
||||||
|
stroke: none,
|
||||||
|
columns: (25%, 50%, 25%),
|
||||||
|
[],
|
||||||
|
[
|
||||||
|
#html-style("position: absolute; left: 28%; width: 100%")[
|
||||||
|
#box(width: 50%, content)
|
||||||
|
]
|
||||||
|
],
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
content
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#let tree-list(..elements) = {
|
||||||
|
gen-tree-from-headings(elemfn: (content, x) => [
|
||||||
|
#html-opt-elem("p", (style:"line-height:1.1"))[
|
||||||
|
#html-style("display:flex; text-indent:0pt;")[
|
||||||
|
#html-style("margin-right: 11pt;", content)
|
||||||
|
#html-style("flex:1;", x.body)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
], elements.pos())
|
||||||
|
}
|
||||||
|
|
||||||
|
#gen-page[
|
||||||
|
|
||||||
|
#br()
|
||||||
|
#title[alex_s168's]
|
||||||
|
#br()
|
||||||
|
|
||||||
|
Articles
|
||||||
|
#br()
|
||||||
|
#tree-list(
|
||||||
|
(level:1, body: [ Making a simple RegEx engine ]),
|
||||||
|
(level:2, body: html-href("article-make-regex-engine-1.typ.desktop.html")[ Part 1: Introduction to RegEx ]),
|
||||||
|
)
|
||||||
|
#br()
|
||||||
|
|
||||||
|
Socials
|
||||||
|
#br()
|
||||||
|
#tree-list(
|
||||||
|
(level:1, body: html-href("https://github.com/alex-s168")[ GitHub ]),
|
||||||
|
(level:1, body: [Discord: alex_s168]),
|
||||||
|
(level:1, body: html-href("mailto:alexandernutz68@gmail.com")[ E-Mail ]),
|
||||||
|
(level:1, body: html-href("https://codeberg.org/alex-s168")[ Codeberg ]),
|
||||||
|
)
|
||||||
|
#br()
|
||||||
|
|
||||||
|
Working on
|
||||||
|
#br()
|
||||||
|
#tree-list(
|
||||||
|
(level:1, body: [ Programming languages and compilers ]),
|
||||||
|
(level:2, body: [ #link("https://github.com/vxcc-backend/vxcc")[ vxcc-old ]: (discontinued) Simple optimizing compiler backend ]),
|
||||||
|
(level:2, body: [ #link("https://github.com/alex-s168/uiuac")[ uiuac ]: (discontinued) Optimizing compiler for the #link("https://uiua.org")[Uiua programming language] ]),
|
||||||
|
(level:2, body: [ #link("https://github.com/Lambda-Mountain-Compiler-Backend/lambda-mountain")[ LSTS's standard library ] ]),
|
||||||
|
(level:2, body: [ #link("https://github.com/h6-lang/h6")[ h6 ]: Minimal stack-based programming language ]),
|
||||||
|
(level:2, body: [ #link("https://github.com/alex-s168/lil-rs")[ lil-rs ]: Rust implementation of #link("http://beyondloom.com/decker/lil.html")[lil] ]),
|
||||||
|
|
||||||
|
(level:1, body: [ Misc. ]),
|
||||||
|
(level:2, body: [ #link("https://github.com/alex-s168/tpre")[ tpre ]: Fast and minimal RegEx engine ]),
|
||||||
|
|
||||||
|
(level:1, body: [ PCBs ]),
|
||||||
|
(level:2, body: [ #link("project-etc-nand.typ.desktop.html")[ etc-nand ]: #link("https://github.com/ETC-A/etca-spec/")[ ETC.A ] CPU from NAND gates ]),
|
||||||
|
|
||||||
|
(level:1, body: [ FPGA designs ]),
|
||||||
|
(level:2, body: [ RMII MAC in #link("https://www.chisel-lang.org/")[ Chisel ] ]),
|
||||||
|
)
|
||||||
|
|
||||||
|
#br()#br()
|
||||||
|
This website is written almost entierly in typst.
|
||||||
|
|
||||||
|
]
|
84
pages/project-etc-nand.typ
Normal file
84
pages/project-etc-nand.typ
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
#import "../common.typ": *
|
||||||
|
#import "../simple-page-layout.typ": *
|
||||||
|
#import "../components/pcb-view.typ": *
|
||||||
|
|
||||||
|
#let pcb-size-percent = 80
|
||||||
|
#let qpcb(file) = {
|
||||||
|
let p = res-path()+"etc-nand/"+file
|
||||||
|
pcb(p+"_front.png", p+"_back.png", size-percent: pcb-size-percent)
|
||||||
|
}
|
||||||
|
|
||||||
|
#simple-page(
|
||||||
|
gen-table-of-contents: true
|
||||||
|
)[
|
||||||
|
|
||||||
|
|
||||||
|
#section[
|
||||||
|
#title[ etc-nand ]
|
||||||
|
]
|
||||||
|
|
||||||
|
#if is-web {section[
|
||||||
|
Note that the #min-pdf-link[PDF Version] of this page might look a bit better styling wise.
|
||||||
|
|
||||||
|
You can click the PCB images to switch to the other side.
|
||||||
|
]}
|
||||||
|
|
||||||
|
#section[
|
||||||
|
= Overview
|
||||||
|
|
||||||
|
etc-nand is a real-world #link("https://github.com/ETC-A/etca-spec/")[ ETC.A ] CPU built from almost only quad NAND gate ICs (74hc00)
|
||||||
|
|
||||||
|
It will probably be finished in a few months.
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
== Estimates
|
||||||
|
|
||||||
|
Estimated gate count:
|
||||||
|
- 2800 NAND gates
|
||||||
|
- 320 tristate buffers
|
||||||
|
|
||||||
|
#br()
|
||||||
|
Estimated component counts:
|
||||||
|
- 700x 74hc00 quad NAND gates
|
||||||
|
- 40x 74HC54 octal tristate buffers
|
||||||
|
- a few simple resistors
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
== Planned Specifications
|
||||||
|
ETC.A base instruction set + byte operations + S&F + Von Neumann
|
||||||
|
|
||||||
|
The CPU will communicate with peripherals over a 16 bit data + 15 bit address memory bus
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
= Purchase
|
||||||
|
You will be able to purchase one in the future.
|
||||||
|
|
||||||
|
Stay tuned!
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
= Images
|
||||||
|
Images of PCBs that are either already manifactured or currently beeing manifactured by JLCPCB.
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
== 16 bit register
|
||||||
|
#context qpcb("reg16")
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
== 8 bit ALU slice
|
||||||
|
A #link(<add8>)[8 bit adder module] will be placed in the middle
|
||||||
|
#context qpcb("alu8")
|
||||||
|
]
|
||||||
|
|
||||||
|
#section[
|
||||||
|
== 8 bit adder <add8>
|
||||||
|
#context qpcb("add8")
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
]
|
BIN
res/etc-nand/add8_back.png
Normal file
BIN
res/etc-nand/add8_back.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
BIN
res/etc-nand/add8_front.png
Normal file
BIN
res/etc-nand/add8_front.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 72 KiB |
BIN
res/etc-nand/alu8_back.png
Normal file
BIN
res/etc-nand/alu8_back.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 92 KiB |
BIN
res/etc-nand/alu8_front.png
Normal file
BIN
res/etc-nand/alu8_front.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 137 KiB |
BIN
res/etc-nand/reg16_back.png
Normal file
BIN
res/etc-nand/reg16_back.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 80 KiB |
BIN
res/etc-nand/reg16_front.png
Normal file
BIN
res/etc-nand/reg16_front.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 117 KiB |
47
simple-page-layout.typ
Normal file
47
simple-page-layout.typ
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#import "common.typ": *
|
||||||
|
#import "core-page-style.typ": *
|
||||||
|
|
||||||
|
#let html-href(target, content) = {
|
||||||
|
[#context if is-html() {
|
||||||
|
html.elem("a", attrs:(href:target), content)
|
||||||
|
} else { content }]
|
||||||
|
}
|
||||||
|
|
||||||
|
#let min-pdf-link(content) = {
|
||||||
|
[#context if is-html() {
|
||||||
|
html.elem("a", attrs:(href:"#",onclick:"gotoMinPdf();"), content)
|
||||||
|
} else { content }]
|
||||||
|
}
|
||||||
|
|
||||||
|
#let simple-page(gen-table-of-contents: true, gen-index-ref: true, content) = {
|
||||||
|
core-page-style[
|
||||||
|
#if is-web {
|
||||||
|
table(
|
||||||
|
stroke: none,
|
||||||
|
columns: (25%, 50%, 25%),
|
||||||
|
column-fixed(
|
||||||
|
[#if gen-table-of-contents { [#table-of-contents()] }],
|
||||||
|
min-pdf-link("Minimal PDF Version"),
|
||||||
|
[#if gen-index-ref {[
|
||||||
|
#context br()
|
||||||
|
#context html-href("index.html")[#html-bold[Website Home]]
|
||||||
|
]}]
|
||||||
|
),
|
||||||
|
[
|
||||||
|
#let off = 3;
|
||||||
|
#html-style("position: absolute; left: "+str(25+off)+"%; width: "+str(75-off)+"%")[
|
||||||
|
#box(width: 50%, content)
|
||||||
|
]
|
||||||
|
],
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
content
|
||||||
|
}
|
||||||
|
|
||||||
|
#html-script("
|
||||||
|
function gotoMinPdf() {
|
||||||
|
window.location.href = window.location.href.replace(/\.\w+.html/g, '.min.pdf');
|
||||||
|
}
|
||||||
|
")
|
||||||
|
]
|
||||||
|
}
|
Reference in New Issue
Block a user