You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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)
Copy file name to clipboardExpand all lines: docs/cpp2/common.md
+34-31Lines changed: 34 additions & 31 deletions
Original file line number
Diff line number
Diff line change
@@ -6,11 +6,14 @@ As always, `main` is the entry point of the program. For example:
6
6
7
7
`main` can have either:
8
8
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>`.
10
14
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>`.
13
15
- Using `args` performs zero heap allocations. Every `string_view` is directly bound to the string storage provided by host environment.
16
+
14
17
-`args.argc` and `args.argv` additionally provide access to the raw C/C++ `main` parameters.
15
18
16
19
```cpp title="main with (args)" hl_lines="5 9"
@@ -29,16 +32,16 @@ main: (args) -> int
29
32
30
33
`main` can return:
31
34
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`.
33
36
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.
35
38
36
39
- Some other type that your Cpp1 compiler(s) supports as a nonstandard extension.
37
40
38
41
39
42
## Comments
40
43
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:
42
45
43
46
```cpp title="Writing comments"
44
47
// A line comment: After //, the entire
@@ -82,21 +85,21 @@ Cpp2 supports the same fundamental types as today's Cpp1, but additionally provi
82
85
83
86
| Variable-width types <br> (Cpp2-compatible single-word names) | Synonym for (these multi-word<br> names are not allowed in Cpp2) |
84
87
|---|---|
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`|
91
94
92
95
| For compatibility/interop only,<br> so deliberately ugly names | Synonym for | Notes |
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:
100
103
101
104
```cpp title="Using type qualifiers"
102
105
// A const pointer to a non-const pointer to a const i32 object
@@ -105,13 +108,13 @@ p: const * * const i32;
105
108
106
109
## Literals
107
110
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.
109
112
110
113
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:
111
114
112
115
- You can create a `u8` value by writing either `u8(123)` or **`123.u8()`**. [^u8using]
113
116
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()`**.
115
118
116
119
Both **`123.nm()`** and **`123.u8()`** are very similar to user-defined literal syntax, and more general.
117
120
@@ -132,8 +135,8 @@ if !vec.empty() {
132
135
| Unary operator | Cpp2 example | Cpp1 equivalent |
133
136
|---|---|---|
134
137
|`!`|`!vec.empty()`|`!vec.empty()`|
135
-
|`+`|`+100`|`+100`|
136
-
|`-`|`-100`|`-100`|
138
+
|`+`|`#!cpp +100`|`#!cpp +100`|
139
+
|`-`|`#!cpp -100`|`#!cpp -100`|
137
140
138
141
The operators `.`, `*`, `&`, `~`, `++`, `--`, `()`, `[]`, and `$` are postfix. For example:
139
142
@@ -152,25 +155,25 @@ Postfix notation lets the code read fluidly left-to-right, in the same order in
152
155
153
156
| Unary operator | Cpp2 example | Cpp1 equivalent |
154
157
|---|---|---|
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_|
164
167
165
168
> 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 `++`/`--`.
166
169
167
170
Unary suffix operators must not be preceded by whitespace. When `*`, `&`, and `~` are used as binary operators they must be preceded by whitespace. For example:
168
171
169
172
| Unary postfix operators that<br>are also binary operators | Cpp2 example | Cpp1 equivalent |
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).
Copy file name to clipboardExpand all lines: docs/cpp2/declarations.md
+4-2Lines changed: 4 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -8,9 +8,11 @@ All Cpp2 declarations are written as **"_name_ `:` _kind_ `=` _statement_"**.
8
8
9
9
- The `=` is pronounced **"defined as."**
10
10
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; }`).
12
12
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 `=`.
Copy file name to clipboardExpand all lines: docs/cpp2/expressions.md
+14-7Lines changed: 14 additions & 7 deletions
Original file line number
Diff line number
Diff line change
@@ -40,7 +40,7 @@ For details, see [Design note: Explicit discard](https://github.com/hsutter/cppf
40
40
41
41
## `is` — safe type/value queries
42
42
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.
44
44
45
45
There are two kinds of `is`:
46
46
@@ -57,8 +57,8 @@ There are two kinds of `is`:
57
57
58
58
| Value constraint kind | Example |
59
59
|---|---|
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))`|
62
62
63
63
`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:
64
64
@@ -78,11 +78,13 @@ Here are some `is` queries with their Cpp1 equivalents. In this table, uppercase
78
78
|---|---|
79
79
|`X is Y && Y is X`|`std::is_same_v<X,Y>`|
80
80
|`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`|
82
82
|`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)`|
84
84
|`o is T`|`o.has_value()`|
85
85
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
+
86
88
87
89
## `as` — safe casts and conversions
88
90
@@ -113,19 +115,24 @@ Here are some `as` casts with their Cpp1 equivalents. In this table, uppercase n
113
115
| Some sample `as` casts | Cpp1 equivalent
114
116
|---|---|
115
117
|`x as Y`|`Y{x}`|
116
-
|`pb as *D`|`dynamic_cast<D*>(pb)`|
118
+
|`#!cpp pb as *D`|`#!cpp dynamic_cast<D*>(pb)`|
117
119
|`v as T`|`std::get<T>(v)`|
118
120
|`a as T`|`std::any_cast<T>(a)`|
119
121
|`o as T`|`o.value()`|
120
122
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
+
121
125
122
126
## `inspect` — pattern matching
123
127
124
128
An `inspect expr -> Type` expression allows pattern matching using `is`.
125
129
126
130
-`expr` is evaluated once.
131
+
127
132
- 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`.
Copy file name to clipboardExpand all lines: docs/cpp2/functions.md
+8-8Lines changed: 8 additions & 8 deletions
Original file line number
Diff line number
Diff line change
@@ -11,7 +11,7 @@ A function call like `f(x)` is a normal function call that will call non-member
11
11
12
12
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.
13
13
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++.
15
15
16
16
For example:
17
17
@@ -56,7 +56,7 @@ There are six ways to pass parameters that cover all use cases:
56
56
57
57
| Parameter kind | "Pass me an `x` I can ______" | Accepts arguments that are | Special semantics |
58
58
|---|---|---|---|
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 |
60
60
|`copy`| take a copy of | anything | acts like a normal local variable initialized with the argument |
61
61
|`inout`| read from and write to | lvalues ||
62
62
|`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:
65
65
66
66
67
67
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).
69
69
70
70
71
71
## Return values
@@ -114,22 +114,22 @@ main: ()
114
114
115
115
> Cpp2 imbues Cpp1 code with nondiscardable semantics, while staying fully compatible as usual:
116
116
>
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]]`.
118
118
>
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]]`.
120
120
121
121
122
122
## Control flow
123
123
124
-
## `if`, `else` — Branches
124
+
## `#!cpp if`, `#!cpp else` — Branches
125
125
126
126
TODO
127
127
128
-
## `for`, `while`, `do` — Loops
128
+
## `#!cpp for`, `#!cpp while`, `#!cpp do` — Loops
129
129
130
130
TODO
131
131
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:
133
133
134
134
```cpp title="Using named break and continue" hl_lines="6 10"
Copy file name to clipboardExpand all lines: docs/cpp2/metafunctions.md
+3-3Lines changed: 3 additions & 3 deletions
Original file line number
Diff line number
Diff line change
@@ -95,9 +95,9 @@ skat_game: @enum<i16> type = {
95
95
96
96
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).
97
97
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).
99
99
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:
101
101
102
102
```cpp title="An @enum type with a member function" hl_lines="1"
103
103
janus: @enum type = {
@@ -167,7 +167,7 @@ main: () = {
167
167
}
168
168
```
169
169
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.
171
171
172
172
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]
Copy file name to clipboardExpand all lines: docs/cpp2/objects.md
+6-6Lines changed: 6 additions & 6 deletions
Original file line number
Diff line number
Diff line change
@@ -55,7 +55,7 @@ Additionally, at function local scope an object `obj` can be initialized separat
55
55
56
56
- Declare `obj` without an initializer, such as `obj: some_type;`. This allocates stack space for the object, but does not construct it.
57
57
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
59
59
60
60
- 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).
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.
96
96
97
97
98
98
## Heap objects
99
99
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`:
101
101
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>`.
103
103
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>`.
105
105
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.
107
107
108
108
For example (see [types](types.md) for more details about writing types):
0 commit comments