Skip to content

Commit 4495b65

Browse files
committed
Add syntax highlighting for inline code blocks using #!cpp shebangs
Note: I deliberately did not add shebangs for: - `inline code` that wouldn't benefit from them (e.g., had nothing significant to highlight) so as to keep the Markdown more readable - `inline code` that I didn't want to highlight, mainly Cpp2 code that used Cpp1 reserved keywords in a non-reserved way (mainly metafunctions like @enum and @union)
1 parent eedc605 commit 4495b65

File tree

10 files changed

+127
-114
lines changed

10 files changed

+127
-114
lines changed

docs/cpp2/common.md

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@ As always, `main` is the entry point of the program. For example:
66

77
`main` can have either:
88

9-
- No parameters: `main: () /*etc.*/`
9+
- No parameters:   **`#!cpp main: () /*etc.*/`**
10+
11+
- One parameter of implicit type named `args`:   **`#!cpp main: (args) /*etc.*/`**
12+
13+
- The type of `args` cannot be explicitly specified. It is always `cpp2::args_t`, which behaves similarly to a `#!cpp const std::array<std::string_view>`.
1014

11-
- One parameter of implicit type named `args`: `main: (args) /*etc.*/`
12-
- The type of `args` cannot be explicitly specified. It is always `cpp2::args_t`, which behaves similarly to a `const std::array<std::string_view>`.
1315
- Using `args` performs zero heap allocations. Every `string_view` is directly bound to the string storage provided by host environment.
16+
1417
- `args.argc` and `args.argv` additionally provide access to the raw C/C++ `main` parameters.
1518

1619
``` cpp title="main with (args)" hl_lines="5 9"
@@ -29,16 +32,16 @@ main: (args) -> int
2932

3033
`main` can return:
3134

32-
- `void`, the default return value for functions. In this case, the compiled Cpp1 code returns an `int` (as required by the standard) with value `0`.
35+
- `#!cpp void`, the default return value for functions. No `#!cpp return` statement is allowed in the body. In this case, the compiled Cpp1 code behaves as if `main` returned `#!cpp int`.
3336

34-
- `int`. If the body has no `return` statement, the default is to `return 0;` at the end of the function body.
37+
- `#!cpp int`. If the body has no `#!cpp return` statement, the default is to `#!cpp return 0;` at the end of the function body.
3538

3639
- Some other type that your Cpp1 compiler(s) supports as a nonstandard extension.
3740

3841

3942
## Comments
4043

41-
The usual `// line comments` and `/* stream comments */` are supported. For example:
44+
The usual `#!cpp // line comments` and `#!cpp /* stream comments */` are supported. For example:
4245

4346
``` cpp title="Writing comments"
4447
// A line comment: After //, the entire
@@ -82,21 +85,21 @@ Cpp2 supports the same fundamental types as today's Cpp1, but additionally provi
8285

8386
| Variable-width types <br> (Cpp2-compatible single-word names) | Synonym for (these multi-word<br> names are not allowed in Cpp2) |
8487
|---|---|
85-
| `ushort` | `unsigned short` |
86-
| `uint` | `unsigned int` |
87-
| `ulong` | `unsigned long` |
88-
| `longlong` | `long long` |
89-
| `ulonglong` | `unsigned long long` |
90-
| `longdouble` | `long double` |
88+
| `ushort` | `#!cpp unsigned short` |
89+
| `uint` | `#!cpp unsigned int` |
90+
| `ulong` | `#!cpp unsigned long` |
91+
| `longlong` | `#!cpp long long` |
92+
| `ulonglong` | `#!cpp unsigned long long` |
93+
| `longdouble` | `#!cpp long double` |
9194

9295
| For compatibility/interop only,<br> so deliberately ugly names | Synonym for | Notes |
9396
|---|---|---|
94-
| `_schar` | `signed char` | Normally, prefer `i8` instead |
95-
| `_uchar` | `unsigned char` | Normally, prefer `u8` instead |
97+
| `_schar` | `#!cpp signed char` | Normally, prefer `i8` instead |
98+
| `_uchar` | `#!cpp unsigned char` | Normally, prefer `u8` instead |
9699

97100
## Type qualifiers
98101

99-
Types can be qualified with `const` and `*`. Types are written left-to-right, so a qualifier always applies to what immediately follows it. For example, to declare a `const` pointer to a non-`const` pointer to a `const i32` object, write:
102+
Types can be qualified with `#!cpp const` and `#!cpp *`. Types are written left-to-right, so a qualifier always applies to what immediately follows it. For example, to declare a `#!cpp const` pointer to a non-`#!cpp const` pointer to a `#!cpp const i32` object, write:
100103

101104
``` cpp title="Using type qualifiers"
102105
// A const pointer to a non-const pointer to a const i32 object
@@ -105,13 +108,13 @@ p: const * * const i32;
105108

106109
## Literals
107110

108-
Cpp2 supports the same `'c'`haracter, `"string"`, binary, integer, and floating point literals as Cpp1, including most Unicode encoding prefixes and raw string literals.
111+
Cpp2 supports the same `#!cpp 'c'`haracter, `#!cpp "string"`, binary, integer, and floating point literals as Cpp1, including most Unicode encoding prefixes and raw string literals.
109112

110113
Cpp2 supports using Cpp1 user-defined literals for compatibility, to support seamlessly using existing libraries. However, because Cpp2 has unified function call syntax (UFCS), the preferred way to author the equivalent in Cpp2 is to just write a function or type name as a `.` call suffix. For example:
111114

112115
- You can create a `u8` value by writing either `u8(123)` or **`123.u8()`**. [^u8using]
113116

114-
- You can write a 'constexpr' function like `nm: (value: i64) -> my_nanometer_type == { /*...*/ }` that takes an integer and returns a value of a strongly typed "nanometer" type, and then create a `nm` value by writing either `nm(123)` or **`123.nm()`**.
117+
- You can write a 'constexpr' function like `#!cpp nm: (value: i64) -> my_nanometer_type == { /*...*/ }` that takes an integer and returns a value of a strongly typed "nanometer" type, and then create a `nm` value by writing either `nm(123)` or **`123.nm()`**.
115118

116119
Both **`123.nm()`** and **`123.u8()`** are very similar to user-defined literal syntax, and more general.
117120

@@ -132,8 +135,8 @@ if !vec.empty() {
132135
| Unary operator | Cpp2 example | Cpp1 equivalent |
133136
|---|---|---|
134137
| `!` | `!vec.empty()` | `!vec.empty()` |
135-
| `+` | `+100` | `+100` |
136-
| `-` | `-100` | `-100` |
138+
| `+` | `#!cpp +100` | `#!cpp +100` |
139+
| `-` | `#!cpp -100` | `#!cpp -100` |
137140

138141
The operators `.`, `*`, `&`, `~`, `++`, `--`, `()`, `[]`, and `$` are postfix. For example:
139142

@@ -152,25 +155,25 @@ Postfix notation lets the code read fluidly left-to-right, in the same order in
152155
153156
| Unary operator | Cpp2 example | Cpp1 equivalent |
154157
|---|---|---|
155-
| `.` | `obj.f()` | `obj.f()` |
156-
| `*` | `pobj*.f()` | `(*pobj).f()` or `pobj->f()` |
157-
| `&` | `obj&` | `&obj` |
158-
| `~` | `val~` | `~val` |
159-
| `++` | `iter++` | `++iter` |
160-
| `--` | `iter--` | `--iter` |
161-
| `(` `)` | `f( 1, 2, 3)` | `f( 1, 2, 3)` |
162-
| `[` `]` | `vec[123]` | `vec[123]` |
163-
| `$` | `val$` | (reflection — no C++23 equivalent) |
158+
| `#!cpp .` | `#!cpp obj.f()` | `#!cpp obj.f()` |
159+
| `#!cpp *` | `#!cpp pobj*.f()` | `#!cpp (*pobj).f()` or `#!cpp pobj->f()` |
160+
| `#!cpp &` | `#!cpp obj&` | `#!cpp &obj` |
161+
| `#!cpp ~` | `#!cpp val~` | `#!cpp ~val` |
162+
| `#!cpp ++` | `#!cpp iter++` | `#!cpp ++iter` |
163+
| `#!cpp --` | `#!cpp iter--` | `#!cpp --iter` |
164+
| `(` `)` | `#!cpp f( 1, 2, 3)` | `#!cpp f( 1, 2, 3)` |
165+
| `[` `]` | `#!cpp vec[123]` | `#!cpp vec[123]` |
166+
| `$` | `val$` | _reflection — no Cpp1 equivalent yet_ |
164167

165168
> Because `++` and `--` always have in-place update semantics, we never need to remember "use prefix `++`/`--` unless you need a copy of the old value." If you do need a copy of the old value, just take the copy before calling `++`/`--`.
166169
167170
Unary suffix operators must not be preceded by whitespace. When `*`, `&`, and `~` are used as binary operators they must be preceded by whitespace. For example:
168171

169172
| Unary postfix operators that<br>are also binary operators | Cpp2 example | Cpp1 equivalent |
170173
|---|---|---|
171-
| `*` | `pobj* * 42` | `(*pobj)*42` |
172-
| `&` | `obj& & mask` <p> (note: allowed in unsafe code only) | `&obj & mask` |
173-
| `~` | `~val ~ bitcomplement` | `val~ ~ bitcomplement` |
174+
| `#!cpp *` | `#!cpp pobj* * 42` | `#!cpp (*pobj)*42` |
175+
| `#!cpp &` | `#!cpp obj& & mask` <p> (note: allowed in unsafe code only) | `#!cpp &obj & mask` |
176+
| `#!cpp ~` | `#!cpp ~val ~ bitcomplement` | `#!cpp val~ ~ bitcomplement` |
174177

175178
For more details, see [Design note: Postfix unary operators vs binary operators](https://github.com/hsutter/cppfront/wiki/Design-note%3A-Postfix-unary-operators-vs-binary-operators).
176179

docs/cpp2/declarations.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ All Cpp2 declarations are written as **"_name_ `:` _kind_ `=` _statement_"**.
88

99
- The `=` is pronounced **"defined as."**
1010

11-
- The _statement_ is typically an expression statement (e.g., `a + b();`) or a compound statement (e.g., `{ /*...*/ return c(d) / e; }`).
11+
- The _statement_ is typically an expression statement (e.g., `#!cpp a + b();`) or a compound statement (e.g., `#!cpp { /*...*/ return c(d) / e; }`).
1212

13-
- Various parts of the syntax allow a `_` "don't care" wildcard or can be omitted entirely to accept a default (e.g., `x: int = 0;` can be equivalently written `x: _ = 0;` or `x := 0;` both of which deduce the type).
13+
- Various parts of the syntax allow a `_` "don't care" wildcard or can be omitted entirely to accept a default (e.g., `#!cpp x: int = 0;` can be equivalently written `#!cpp x: _ = 0;` or `#!cpp x := 0;` both of which deduce the type).
14+
15+
> Note: When the type is omitted, whitespace does not matter, and writing `#!cpp x: = 0;` or `#!cpp x : = 0;` or `#!cpp x := 0;` or other whitespace is just a stylistic choice. This documentation's style uses the last one, except when there are multiple adjacent declaration lines this style lines up their `:` and `=`.
1416
1517

1618
## Examples

docs/cpp2/expressions.md

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ For details, see [Design note: Explicit discard](https://github.com/hsutter/cppf
4040

4141
## `is` — safe type/value queries
4242

43-
An `x is C` expression allows safe type and value queries, and evaluates to `true` if `x` matches constraint `C`. It supports both static and dynamic queries, including customization, with support for standard library dynamic types like `std::variant`, `std::optional`, `std::expected`, and `std::any` provided out of the box.
43+
An `x is C` expression allows safe type and value queries, and evaluates to `#!cpp true` if `x` matches constraint `C`. It supports both static and dynamic queries, including customization, with support for standard library dynamic types like `std::variant`, `std::optional`, `std::expected`, and `std::any` provided out of the box.
4444

4545
There are two kinds of `is`:
4646

@@ -57,8 +57,8 @@ There are two kinds of `is`:
5757

5858
| Value constraint kind | Example |
5959
|---|---|
60-
| Value | `x is 0` |
61-
| Value predicate | `x is (in(10, 20))` |
60+
| Value | `#!cpp x is 0` |
61+
| Value predicate | `#!cpp x is (in(10, 20))` |
6262

6363
`is` is useful throughout the language, including in `inspect` pattern matching alternatives. `is` is extensible, and works out of the box with `std::variant`, `std::optional`, `std::expected`, and `std::any`. For examples, see:
6464

@@ -78,11 +78,13 @@ Here are some `is` queries with their Cpp1 equivalents. In this table, uppercase
7878
|---|---|
7979
| `X is Y && Y is X` | `std::is_same_v<X,Y>` |
8080
| `D is B` | `std::is_base_of<B,D>` |
81-
| `pb is *D` | `dynamic_cast<D*>(pb) != nullptr` |
81+
| `#!cpp pb is *D` | `#!cpp dynamic_cast<D*>(pb) != nullptr` |
8282
| `v is T` | `std::holds_alternative<T>(v)` |
83-
| `a is T` | `a.type() == typeid(T)` |
83+
| `a is T` | `#!cpp a.type() == typeid(T)` |
8484
| `o is T` | `o.has_value()` |
8585

86+
> Note: `is` unifies a variety of differently-named Cpp1 language and library queries under one syntax, and supports only the type-safe ones.
87+
8688

8789
## `as` — safe casts and conversions
8890

@@ -113,19 +115,24 @@ Here are some `as` casts with their Cpp1 equivalents. In this table, uppercase n
113115
| Some sample `as` casts | Cpp1 equivalent
114116
|---|---|
115117
| `x as Y` | `Y{x}` |
116-
| `pb as *D` | `dynamic_cast<D*>(pb)` |
118+
| `#!cpp pb as *D` | `#!cpp dynamic_cast<D*>(pb)` |
117119
| `v as T` | `std::get<T>(v)` |
118120
| `a as T` | `std::any_cast<T>(a)` |
119121
| `o as T` | `o.value()` |
120122

123+
> Note: `as` unifies a variety of differently-named Cpp1 language and library casts and conversions under one syntax, and supports only the type-safe ones.
124+
121125

122126
## `inspect` — pattern matching
123127

124128
An `inspect expr -> Type` expression allows pattern matching using `is`.
125129

126130
- `expr` is evaluated once.
131+
127132
- Each alternative spelled `is C` is evaluated in order as if called with `expr is C`.
128-
- If an alternative evaluates to `true`, then its `= alternative;` body is used as the value of the entire `inspect` expression, and the meaning is the same as if the entire `inspect` expression had been written as just `:Type = alternative;` — i.e., an unnamed object expression (aka 'temporary object') of type `Type` initialized with `alternative`.
133+
134+
- If an alternative evaluates to `#!cpp true`, then its `#!cpp = alternative;` body is used as the value of the entire `inspect` expression, and the meaning is the same as if the entire `inspect` expression had been written as just `#!cpp :Type = alternative;` — i.e., an unnamed object expression (aka 'temporary object') of type `Type` initialized with `alternative`.
135+
129136
- A catchall `is _` is required.
130137

131138
For example:

docs/cpp2/functions.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ A function call like `f(x)` is a normal function call that will call non-member
1111

1212
A function call like `x.f()` is a unified function call syntax (aka UFCS) call. It will call a member function if one is available, and otherwise will call `f(x)`. Having UFCS is important for generic code that may want to call a member or a non-member function, whichever is available. It's also important to enable fluid programming styles and natural IDE autocompletion support.
1313

14-
An operator notation call like `a + b` will call an overloaded operator function if one is available, as usual in C++.
14+
An operator notation call like `#!cpp a + b` will call an overloaded operator function if one is available, as usual in C++.
1515

1616
For example:
1717

@@ -56,7 +56,7 @@ There are six ways to pass parameters that cover all use cases:
5656

5757
| Parameter kind | "Pass me an `x` I can ______" | Accepts arguments that are | Special semantics |
5858
|---|---|---|---|
59-
| `in` (default) | read from | anything | always `const`<p>automatically passes by value if cheaply copyable |
59+
| `in` (default) | read from | anything | always `#!cpp const`<p>automatically passes by value if cheaply copyable |
6060
| `copy` | take a copy of | anything | acts like a normal local variable initialized with the argument |
6161
| `inout` | read from and write to | lvalues | |
6262
| `out` | write to (including construct) | lvalues, including uninitialized lvalues | must `=` assign/construct before other uses |
@@ -65,7 +65,7 @@ There are six ways to pass parameters that cover all use cases:
6565

6666

6767

68-
> Note: All parameters and other objects in Cpp2 are `const` by default, except for local variables. For details, see [Design note: `const` objects by default](https://github.com/hsutter/cppfront/wiki/Design-note%3A-const-objects-by-default).
68+
> Note: All parameters and other objects in Cpp2 are `#!cpp const` by default, except for local variables. For details, see [Design note: `#!cpp const` objects by default](https://github.com/hsutter/cppfront/wiki/Design-note%3A-const-objects-by-default).
6969
7070

7171
## Return values
@@ -114,22 +114,22 @@ main: ()
114114

115115
> Cpp2 imbues Cpp1 code with nondiscardable semantics, while staying fully compatible as usual:
116116
>
117-
> - A function written in Cpp2 syntax that returns something other than `void` is always compiled to Cpp1 with `[[nodiscard]]`.
117+
> - A function written in Cpp2 syntax that returns something other than `#!cpp void` is always compiled to Cpp1 with `[[nodiscard]]`.
118118
>
119-
> - A function call written in Cpp2 `x.f()` member call syntax always treats a non-`void` return type as not discardable, even if the function was written in Cpp1 syntax that did not write `[[nodiscard]]`.
119+
> - A function call written in Cpp2 `x.f()` member call syntax always treats a non-`#!cpp void` return type as not discardable, even if the function was written in Cpp1 syntax that did not write `[[nodiscard]]`.
120120
121121

122122
## Control flow
123123

124-
## `if`, `else` — Branches
124+
## `#!cpp if`, `#!cpp else` — Branches
125125

126126
TODO
127127

128-
## `for`, `while`, `do` — Loops
128+
## `#!cpp for`, `#!cpp while`, `#!cpp do` — Loops
129129

130130
TODO
131131

132-
Loops can be named using the usual **name `:`** name introduction syntax, and `break` and `continue` can refer to those names. For example:
132+
Loops can be named using the usual **name `:`** syntax that introduces all names, and `#!cpp break` and `#!cpp continue` can refer to those names. For example:
133133

134134
``` cpp title="Using named break and continue" hl_lines="6 10"
135135
outer: while i<M next i++ { // loop named "outer"

docs/cpp2/metafunctions.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,9 @@ skat_game: @enum<i16> type = {
9595

9696
Consider `hearts`: It's a member object declaration, but it doesn't have a type (or a default value) which is normally illegal, but here it's okay because the `@enum<i16>` metafunction fills them in: It iterates over all the data members and gives each one the underlying type (here explicitly specified as `i16`, otherwise it would be computed as the smallest signed type that's big enough), and an initializer (by default one higher than the previous enumerator).
9797

98-
Unlike C `enum`, this `@enum` is scoped and strongly typed (does not implicitly convert to the underlying type).
98+
Unlike C `#!cpp enum`, this `@enum` is scoped and strongly typed (does not implicitly convert to the underlying type).
9999

100-
Unlike C++11 `enum class`, it's "just a `type`" which means it can naturally also have member functions and other things that a type can have:
100+
Unlike C++11 `#!cpp enum class`, it's "just a `type`" which means it can naturally also have member functions and other things that a type can have:
101101

102102
``` cpp title="An @enum type with a member function" hl_lines="1"
103103
janus: @enum type = {
@@ -167,7 +167,7 @@ main: () = {
167167
}
168168
```
169169

170-
Unlike C `union`, this `@union` is safe to use because it always ensures only the active type is accessed.
170+
Unlike C `#!cpp union`, this `@union` is safe to use because it always ensures only the active type is accessed.
171171

172172
Unlike C++11 `std::variant`, this `@union` is easier to use because its alternatives are anonymous, and safer to use because each union type is a distinct type. [^variant]
173173

docs/cpp2/objects.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ Additionally, at function local scope an object `obj` can be initialized separat
5555

5656
- Declare `obj` without an initializer, such as `obj: some_type;`. This allocates stack space for the object, but does not construct it.
5757

58-
- `obj` must have a definite first use on every `if`/`else` branch path, and
58+
- `obj` must have a definite first use on every `#!cpp if`/`#!cpp else` branch path, and
5959

6060
- that definite first use must be of the form `obj = value;` which is a constructor call, or else pass `obj` as an `out` argument to an `out` parameter (which is also effectively a constructor call, and performs the construction in the callee).
6161

@@ -92,18 +92,18 @@ load_from_disk: (out buffer) = {
9292
} // constructs it; otherwise, assigns
9393
```
9494

95-
In the above example, note the simple rule for branches: The local variable must be initialized on both the `if` and `else` branches, or neither branch.
95+
In the above example, note the simple rule for branches: The local variable must be initialized on both the `#!cpp if` and `#!cpp else` branches, or neither branch.
9696

9797

9898
## Heap objects
9999

100-
Objects can also be allocated on the heap using `arena.new <T> (/*initializer, arguments)` where `arena` is any object that acts as a memory arena and provides a `.new` function template. Two memory arena objects are provided in namespace `cpp2`:
100+
Objects can also be allocated on the heap using `#!cpp arena.new <T> (/*initializer, arguments*/)` where `arena` is any object that acts as a memory arena and provides a `#!cpp .new` function template. Two memory arena objects are provided in namespace `cpp2`:
101101

102-
- `unique.new<T>` calls `std::make_unique` and returns a `std::unique_ptr`.
102+
- `#!cpp unique.new<T>` calls `std::make_unique<T>` and returns a `std::unique_ptr<T>`.
103103

104-
- `shared.new<T>` calls `std::make_shared` and returns a `std::shared_ptr`.
104+
- `#!cpp shared.new<T>` calls `std::make_shared<T>` and returns a `std::shared_ptr<T>`.
105105

106-
The default is `unique.new` if you don't specify an arena object.
106+
The default is `#!cpp unique.new` if you don't specify an arena object.
107107

108108
For example (see [types](types.md) for more details about writing types):
109109

0 commit comments

Comments
 (0)