hachem.wtf
aboutsummaryrefslogtreecommitdiff
path: root/tutorial
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-09-19 22:04:44 +0200
committerhachem <im@hachem.wtf>2026-09-19 22:04:44 +0200
commit2da2c56c72e7085bd9a52f848df90c6fc701de7f (patch)
tree96cf837a606297aed921b9adaac783940a1aa7cd /tutorial
parent6487b5b5301f6cb37c95c3571578d5a42adb2a1a (diff)
docs: add tutorial
Diffstat (limited to 'tutorial')
-rw-r--r--tutorial/01_hello.sfinae24
-rw-r--r--tutorial/02_numbers.sfinae61
-rw-r--r--tutorial/03_variables.sfinae46
-rw-r--r--tutorial/04_conditions.sfinae74
-rw-r--r--tutorial/05_loops.sfinae47
-rw-r--r--tutorial/06_functions.sfinae118
6 files changed, 370 insertions, 0 deletions
diff --git a/tutorial/01_hello.sfinae b/tutorial/01_hello.sfinae
new file mode 100644
index 0000000..81d84e6
--- /dev/null
+++ b/tutorial/01_hello.sfinae
@@ -0,0 +1,24 @@
+// hello, world
+//
+// there are no strings. there are no string literals. there is ::tuple, a type
+// made of other types, and ::integral_constant<::char, N>, a type that happens
+// to be a character. ::emit prints whatever a type has to say and then a newline
+// there are also no numbers, so every character is its own distance from '\0'
+
+::emit<
+ ::tuple<
+ ::integral_constant<::char, ::difference<'H', '\0'>>,
+ ::integral_constant<::char, ::difference<'e', '\0'>>,
+ ::integral_constant<::char, ::difference<'l', '\0'>>,
+ ::integral_constant<::char, ::difference<'l', '\0'>>,
+ ::integral_constant<::char, ::difference<'o', '\0'>>,
+ ::integral_constant<::char, ::difference<',', '\0'>>,
+ ::integral_constant<::char, ::difference<' ', '\0'>>,
+ ::integral_constant<::char, ::difference<'w', '\0'>>,
+ ::integral_constant<::char, ::difference<'o', '\0'>>,
+ ::integral_constant<::char, ::difference<'r', '\0'>>,
+ ::integral_constant<::char, ::difference<'l', '\0'>>,
+ ::integral_constant<::char, ::difference<'d', '\0'>>,
+ ::integral_constant<::char, ::difference<'!', '\0'>>
+ >
+>::commit;
diff --git a/tutorial/02_numbers.sfinae b/tutorial/02_numbers.sfinae
new file mode 100644
index 0000000..76f7dca
--- /dev/null
+++ b/tutorial/02_numbers.sfinae
@@ -0,0 +1,61 @@
+// lesson 2: numbers
+//
+// a digit is a syntax error. the only number literal is ::difference<'a', 'b'>,
+// the distance between two characters. 'F' - 'A' is 5. ::emit takes a type, and a
+// value is not a type, so wrap it in ::decltype(...) to make it one
+
+::emit<::decltype(::difference<'F', 'A'>)>::commit;
+
+// arithmetic is ::sum, ::product and ::quotient.
+// no operators
+// no precedence
+// no problem
+
+::emit<
+ ::decltype(
+ ::sum<
+ ::difference<'F', 'A'>,
+ ::difference<'C', 'A'>
+ >
+ )
+>::commit;
+
+::emit<
+ ::decltype(
+ ::product<
+ ::difference<'D', 'A'>,
+ ::difference<'E', 'A'>
+ >
+ )
+>::commit;
+
+::emit<
+ ::decltype(
+ ::quotient<
+ ::difference<'H', 'A'>,
+ ::difference<'C', 'A'>
+ >
+ )
+>::commit;
+
+// ::integral_constant<::size_t, V>::value is V. it exists so you can write more
+// ::extent<T>::value is the length of an array type, and 0 for anything else
+// this is the long way to write 20
+
+::emit<
+ ::decltype(
+ ::extent<
+ ::array<
+ ::byte,
+ ::integral_constant<
+ ::size_t,
+ ::difference<'Z', 'F'>
+ >::value
+ >
+ >::value
+ )
+>::commit;
+
+// emitting a type that is not a value prints the name of the type
+
+::emit<::remove_cv_t<::int32_t>>::commit;
diff --git a/tutorial/03_variables.sfinae b/tutorial/03_variables.sfinae
new file mode 100644
index 0000000..6f200f1
--- /dev/null
+++ b/tutorial/03_variables.sfinae
@@ -0,0 +1,46 @@
+// lesson 3: variables
+//
+// a name is a list of characters. a variable holds a type, not a value, and the
+// only type that carries a value is ::decltype(...). the ::conditional_t around
+// it is not required. it is just what everyone does
+
+::declare<
+ ::name<'x'>,
+ ::conditional_t<
+ ::true_type::value,
+ ::decltype(::difference<'F', 'A'>),
+ ::void
+ >
+>::commit;
+
+// reading it back is ::value_of<NAME>, which is a value, so ::decltype it again
+
+::emit<::decltype(::value_of<::name<'x'>>)>::commit;
+
+::declare<
+ ::name<'y'>,
+ ::decltype(
+ ::sum<
+ ::value_of<::name<'x'>>,
+ ::difference<'B', 'A'>
+ >
+ )
+>::commit;
+
+::emit<::decltype(::value_of<::name<'y'>>)>::commit;
+
+// there is no assignment. to change x, undeclare it and declare it again
+
+::undeclare<::name<'x'>>::commit;
+
+::declare<
+ ::name<'x'>,
+ ::decltype(
+ ::product<
+ ::value_of<::name<'y'>>,
+ ::difference<'C', 'A'>
+ >
+ )
+>::commit;
+
+::emit<::decltype(::value_of<::name<'x'>>)>::commit;
diff --git a/tutorial/04_conditions.sfinae b/tutorial/04_conditions.sfinae
new file mode 100644
index 0000000..94e0cbf
--- /dev/null
+++ b/tutorial/04_conditions.sfinae
@@ -0,0 +1,74 @@
+// lesson 4: conditions
+//
+// a condition is ::true_type::value, ::false_type::value, ::equal<V, V>::value,
+// ::less<V, V>::value, or ::is_same<T, T>::value. the ::value is mandatory
+// a condition without it is a type, and a type is not a condition.
+
+// ::conditional_t<C, T, T> picks a type. this prints 1
+
+::emit<
+ ::conditional_t<
+ ::true_type::value,
+ ::decltype(::difference<'B', 'A'>),
+ ::void
+ >
+>::commit;
+
+// this picks ::void, and emitting ::void prints nothing at all. not even a newline
+
+::emit<
+ ::conditional_t<
+ ::less<
+ ::difference<'B', 'A'>,
+ ::difference<'A', 'A'>
+ >::value,
+ ::decltype(::difference<'Z', 'A'>),
+ ::void
+ >
+>::commit;
+
+// ::enable_if_t<C, ::sequence<...>> runs the statements when C holds
+// the statements inside a sequence are separated by commas, and each one is
+// still committed on its own
+
+::enable_if_t<
+ ::is_same<
+ ::remove_cv_t<::int32_t>,
+ ::int32_t
+ >::value,
+ ::sequence<
+ ::emit<
+ ::tuple<
+ ::integral_constant<::char, ::difference<'s', '\0'>>,
+ ::integral_constant<::char, ::difference<'a', '\0'>>,
+ ::integral_constant<::char, ::difference<'m', '\0'>>,
+ ::integral_constant<::char, ::difference<'e', '\0'>>
+ >
+ >::commit
+ >
+>::commit;
+
+// ::static_assert<C> stops the program when C does not hold. it does this at
+// runtime, because that is when we know
+
+::static_assert<
+ ::equal<
+ ::difference<'C', 'A'>,
+ ::sum<
+ ::difference<'B', 'A'>,
+ ::difference<'B', 'A'>
+ >
+ >::value
+>::commit;
+
+// ::conditional<C, V, V> is the same idea for values. this prints 9
+
+::emit<
+ ::decltype(
+ ::conditional<
+ ::false_type::value,
+ ::difference<'A', 'A'>,
+ ::difference<'J', 'A'>
+ >
+ )
+>::commit;
diff --git a/tutorial/05_loops.sfinae b/tutorial/05_loops.sfinae
new file mode 100644
index 0000000..d974f99
--- /dev/null
+++ b/tutorial/05_loops.sfinae
@@ -0,0 +1,47 @@
+// lesson 5: loops
+//
+// ::repeat<V, ::sequence<...>> runs the sequence V times. that is the whole
+// loop. there is no index, no condition, no break.
+
+::repeat<
+ ::difference<'D', 'A'>,
+ ::sequence<
+ ::emit<
+ ::tuple<
+ ::integral_constant<::char, ::difference<'h', '\0'>>,
+ ::integral_constant<::char, ::difference<'i', '\0'>>
+ >
+ >::commit
+ >
+>::commit;
+
+// counting needs a variable, and changing a variable needs the undeclare dance,
+// and the dance needs a second variable to hold the value while the first one
+// is dead. this prints 1, 2, 3
+
+::declare<
+ ::name<'i'>,
+ ::decltype(::difference<'B', 'A'>)
+>::commit;
+
+::repeat<
+ ::difference<'D', 'A'>,
+ ::sequence<
+ ::emit<::decltype(::value_of<::name<'i'>>)>::commit,
+ ::declare<
+ ::name<'n', 'e', 'x', 't'>,
+ ::decltype(
+ ::sum<
+ ::value_of<::name<'i'>>,
+ ::difference<'B', 'A'>
+ >
+ )
+ >::commit,
+ ::undeclare<::name<'i'>>::commit,
+ ::declare<
+ ::name<'i'>,
+ ::decltype(::value_of<::name<'n', 'e', 'x', 't'>>)
+ >::commit,
+ ::undeclare<::name<'n', 'e', 'x', 't'>>::commit
+ >
+>::commit;
diff --git a/tutorial/06_functions.sfinae b/tutorial/06_functions.sfinae
new file mode 100644
index 0000000..a85e5fa
--- /dev/null
+++ b/tutorial/06_functions.sfinae
@@ -0,0 +1,118 @@
+// lesson 6: functions
+//
+// ::define<NAME, ::parameters<NAME, ...>, ::sequence<...>> defines a function
+// a function sees nothing but its parameters. it gives back a type with
+// ::return<TYPE>, or ::void if it never does. calling it is a type too:
+// ::invoke_result_t<NAME, ::arguments<TYPE, ...>>, and every argument must be
+// a ::decltype so it carries a value
+
+::define<
+ ::name<'s', 'q', 'u', 'a', 'r', 'e'>,
+ ::parameters<::name<'x'>>,
+ ::sequence<
+ ::return<
+ ::decltype(
+ ::product<
+ ::value_of<::name<'x'>>,
+ ::value_of<::name<'x'>>
+ >
+ )
+ >::commit
+ >
+>::commit;
+
+// prints 25
+
+::emit<
+ ::invoke_result_t<
+ ::name<'s', 'q', 'u', 'a', 'r', 'e'>,
+ ::arguments<::decltype(::difference<'F', 'A'>)>
+ >
+>::commit;
+
+// a function can return anything a type can be, which includes a tuple of
+// characters, which is the closest thing to a string you will get
+
+::define<
+ ::name<'g', 'r', 'e', 'e', 't'>,
+ ::parameters<>,
+ ::sequence<
+ ::return<
+ ::tuple<
+ ::integral_constant<::char, ::difference<'h', '\0'>>,
+ ::integral_constant<::char, ::difference<'e', '\0'>>,
+ ::integral_constant<::char, ::difference<'y', '\0'>>
+ >
+ >::commit
+ >
+>::commit;
+
+::emit<
+ ::invoke_result_t<
+ ::name<'g', 'r', 'e', 'e', 't'>,
+ ::arguments<>
+ >
+>::commit;
+
+// the result is a type. to use it as a value in arithmetic, ::declval<T>() gets
+// the value back out. the parentheses are mandatory. it is a function, after all
+// this prints 10
+
+::emit<
+ ::decltype(
+ ::sum<
+ ::declval<
+ ::invoke_result_t<
+ ::name<'s', 'q', 'u', 'a', 'r', 'e'>,
+ ::arguments<::decltype(::difference<'D', 'A'>)>
+ >
+ >(),
+ ::difference<'B', 'A'>
+ >
+ )
+>::commit;
+
+// recursion works, because nothing stopped it. factorial of 5
+
+::define<
+ ::name<'f', 'a', 'c', 't'>,
+ ::parameters<::name<'n'>>,
+ ::sequence<
+ ::enable_if_t<
+ ::less<
+ ::value_of<::name<'n'>>,
+ ::difference<'B', 'A'>
+ >::value,
+ ::sequence<
+ ::return<::decltype(::difference<'B', 'A'>)>::commit
+ >
+ >::commit,
+ ::return<
+ ::decltype(
+ ::product<
+ ::value_of<::name<'n'>>,
+ ::declval<
+ ::invoke_result_t<
+ ::name<'f', 'a', 'c', 't'>,
+ ::arguments<
+ ::decltype(
+ ::sum<
+ ::value_of<::name<'n'>>,
+ ::difference<'A', 'B'>
+ >
+ )
+ >
+ >
+ >()
+ >
+ )
+ >::commit
+ >
+>::commit;
+
+::emit<
+ ::invoke_result_t<
+ ::name<'f', 'a', 'c', 't'>,
+ ::arguments<::decltype(::difference<'F', 'A'>)>
+ >
+>::commit;