Skip to content

Commit

Permalink
Revise Example Metadata User Guide (#726)
Browse files Browse the repository at this point in the history
* Add small improvements

* Add new template

* add new templates and options

* add new template and option

* Support passing of command-line arguments

* add support for multi-file examples
  • Loading branch information
RexJaeschke authored Sep 26, 2023
1 parent 4d503b4 commit 4ad7d5b
Show file tree
Hide file tree
Showing 2 changed files with 210 additions and 18 deletions.
26 changes: 23 additions & 3 deletions tools/ExampleExtractor/ExampleAnnotationGrammar.g4
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ annotation_directive
| infer_output
| expected_exception
| additional_files
| extern_alias_support
| execution_arguments
;

JSON_string_value
Expand All @@ -31,9 +33,15 @@ template
;
template_name
: '"standalone-console"' // actually, a JSON_string_value with this content
| '"standalone-lib"' // actually, a JSON_string_value with this content
| '"code-in-main"' // actually, a JSON_string_value with this content
: '"code-in-class-lib"' // actually, a JSON_string_value with this content
| '"code-in-class-lib-without-using"' // actually, a JSON_string_value with this content
| '"code-in-main"' // actually, a JSON_string_value with this content
| '"code-in-main-without-using"' // actually, a JSON_string_value with this content
| '"code-in-partial-class"' // actually, a JSON_string_value with this content
| '"extern-lib"' // actually, a JSON_string_value with this content
| '"standalone-console"' // actually, a JSON_string_value with this content
| '"standalone-lib"' // actually, a JSON_string_value with this content
| '"standalone-lib-without-using"' // actually, a JSON_string_value with this content
;
name
Expand Down Expand Up @@ -97,6 +105,18 @@ additional_files
: 'additionalFiles' ':' '[' filename (',' filename)* ']'
;
extern_alias_support
: 'project' ':' filename
;
execution_arguments
: 'executionArgs' ':' '[' cmdlinearg (',' cmdlinearg)* ']'
;
cmdlinearg
: JSON_string_value
;
filename
: JSON_string_value
;
202 changes: 187 additions & 15 deletions tools/ExampleExtractor/ExtractorAndTesterUsersGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@
- [Expected Runtime Output](#expected-runtime-output)
- [Expected Exception](#expected-exception)
- [Including Support Files](#including-support-files)
- [Supporting External Aliases](#supporting-external-aliases)
- [Passing Command-Line Arguments](#passing-command-line-arguments)
- [Other Information](#other-information)
- [Unsafe Code](#unsafe-code)
- [Examples Containing Pseudo-Code](#examples-containing-pseudo-code)
- [Examples Involving Multiple Files](#examples-involving-multiple-files)
- [Using Chevron-Quotes for Emphasis](#using-chevron-quotes-for-emphasis)
- [Tips for Creating New Testable Examples](#tips-for-creating-new-testable-examples)

Expand Down Expand Up @@ -45,8 +48,11 @@ annotation_directive
| ignored_warnings
| expected_output
| infer_output
| ignore_output
| expected_exception
| additional_files
| extern_alias_support
| execution_arguments
;
```

Expand Down Expand Up @@ -80,27 +86,33 @@ In this case, the *example_annotation* is preceded by `> ` to match the example

### Templates

#### General

An annotation’s *template* is used to select certain compiler options, and to provide supporting machinery, as needed. This *annotation_directive* is required.

```ANTLR
template
: 'template' ':' template_name
;
template_name
: '"code-in-class-lib"' // actually, a JSON_string_value with this content
| '"code-in-class-lib-without-using"' // actually, a JSON_string_value with this content
| '"code-in-main"' // actually, a JSON_string_value with this content
| '"code-in-main-without-using"' // actually, a JSON_string_value with this content
| '"standalone-console"' // actually, a JSON_string_value with this content
: '"code-in-class-lib"' // actually, a JSON_string_value with this content
| '"code-in-class-lib-without-using"' // actually, a JSON_string_value with this content
| '"code-in-main"' // actually, a JSON_string_value with this content
| '"code-in-main-without-using"' // actually, a JSON_string_value with this content
| '"code-in-partial-class"' // actually, a JSON_string_value with this content
| '"extern-lib"' // actually, a JSON_string_value with this content
| '"standalone-console"' // actually, a JSON_string_value with this content
| '"standalone-console-without-using"' // actually, a JSON_string_value with this content
| '"standalone-lib"' // actually, a JSON_string_value with this content
| '"standalone-lib-without-using"' // actually, a JSON_string_value with this content
| '"standalone-lib"' // actually, a JSON_string_value with this content
| '"standalone-lib-without-using"' // actually, a JSON_string_value with this content
;
```

The unsuffixed and suffixed versions are identical, *except* that the unsuffixed ones have using directioves for all namespaces used by examples, while the suffixed ones do not. The unsuffixed versions are used by those few examples that begin with `#undef` or `#define`, which *must* precede using directives, and which might then have explicit using directives.

The template `standalone-console` indicates that the example is an application. For example:
#### standalone-console

This template indicates that the example is an application. For example:

````
> <!-- Example: {template:"standalone-console", …} -->
Expand All @@ -115,7 +127,9 @@ The template `standalone-console` indicates that the example is an application.
> ```
````

The template `standalone-lib` indicates that the example is a class library. For example:
#### standalone-lib

This template indicates that the example is a class library. For example:

````
> <!-- Example: {template:"standalone-lib", … } -->
Expand All @@ -130,7 +144,9 @@ The template `standalone-lib` indicates that the example is a class library. For
> ```
````

The template `code-in-main` indicates that the example needs to be wrapped inside an entry-point method inside a class. For example, the example:
#### code-in-main

This template indicates that the example needs to be wrapped inside an entry-point method inside a class. For example, the example:

````
> <!-- Example: {template:"code-in-main", … } -->
Expand Down Expand Up @@ -163,7 +179,9 @@ class Program
}
````

The template `code-in-class-lib` indicates that the example needs to be wrapped inside an entry-point method inside a class. For example, the example:
#### code-in-class-lib

This template indicates that the example needs to be wrapped inside an entry-point method inside a class. For example, the example:

````
> <!-- Example: {template:"code-in-class-lib", ..."} -->
Expand Down Expand Up @@ -199,6 +217,73 @@ class Class1
}
````

#### code-in-partial-class

This template indicates that the example is part of a multifile application. For example:

````
> <!-- Example: {template:"code-in-partial-class", name:"...", additionalFiles:["Caller.cs"], ...} -->
> ```csharp
> static D[] F()
> {
> ...
> }
> ```
````

which gets transformed into the following:

````
partial class Class1
{
static D[] F()
{
...
}
}
````

The additional file `Caller.cs` contains the following:

````
delegate void D();
partial class Class1
{
static void Main()
{
foreach (D d in F())
{
d();
}
}
}
````

We use this to supplement the example.

#### extern-lib

This template is used to create and map to one or more external aliases by using the `project` *annotation_directive*. For example:

````
> <!-- Example: {template:"extern-lib", name:"ExternAliasDirectives", project:"ExampleProject"} -->
> ```csharp
> extern alias X;
> extern alias Y;
>
> class Test
> {
> X::N.A a;
> X::N.B b1;
> Y::N.B b2;
> Y::N.C c;
> }
> ```
````

The template causes the example code to be compiled without change, along with definition of, and mapping to, the necessary support files for the referenced external aliases, `X` and `Y` via the file `ExampleProject.csproj` in the `extern-lib` template folder.

### Example Names

An annotation’s *name* is the name of the resulting test file directory, **which must be unique across the whole C# spec**. This *annotation_directive* is required. *name* should be a valid C# idenifier.
Expand Down Expand Up @@ -428,7 +513,9 @@ Here’s an *example_annotation* showing both expected and ignored warnings:

### Expected Runtime Output

During execution, some test applications are expected to write one or more lines of output to the console.
During execution, some test applications to write one or more lines of output to the console.

> **The tool trims all whitespace from the end of each output string *before* comparing it against any expected or console-block string. As such, make sure an example does *not* generate output lines containing trailing whitespace.**
**Scenario #1:**

Expand Down Expand Up @@ -511,6 +598,15 @@ Consider the following example, which writes two lines to the console:
> ```
````

**Scenario #3:**

In those cases in which the output is nondeterministic, we can ignore that output by using the following *annotation_directive*, which is optional.

```ANTLR
ignore_output
: 'ignoreOutput' ':' ('true' | 'false')
```

### Expected Exception

An expected exception can be identified and checked for. This *annotation_directive* is optional.
Expand Down Expand Up @@ -552,7 +648,7 @@ Consider the following example:

### Including Support Files

When an example relies on external support information (such as a type declaration or document-comment include file), one or more files containing that information can be copied to the output directory from the `additional-files` directory within the `template` directory. The *annotation_directive* `additionalFiles` achieves this, and is optional.
When an example relies on external support information (such as a type declaration, a document-comment include file, or an extension method), one or more files containing that information can be copied to the output directory from the `additional-files` directory within the `template` directory. The *annotation_directive* `additionalFiles` achieves this, and is optional.

```ANTLR
additional_files
Expand Down Expand Up @@ -592,6 +688,36 @@ namespace Acme
}
````

Regarding extension methods, these are all defined in the file Extensions.cs in the additional-files folder.

### Supporting External Aliases

```ANTLR
extern_alias_support
: 'project' ':' filename
;
filename
: JSON_string_value
;
```

This *annotation_directive* supports the template [`extern-lib`](#extern-lib)).

### Passing Command-Line Arguments

```ANTLR
execution_arguments
: 'executionArgs' ':' '[' cmdlinearg (',' cmdlinearg)* ']'
;
cmdlinearg
: JSON_string_value
;
```

Each *cmdlinearg* corresponds to a command-line argument to be passed to the program's entry point at execution, in the order specified, where the first occurence is array element 0.

This *annotation_directive* supports all templates that create an executable.

## Other Information

### Unsafe Code
Expand Down Expand Up @@ -620,6 +746,52 @@ Here’s an example:
> «statement»
> ```
### Examples Involving Multiple Files
Examples containing multiple source files in a single project are supported.
Although the example is written as a single markdown code block, line-oriented comments of the form `// File `*filename* are used to delineate the source files. For example,
````
> <!-- Example: {template:"standalone-lib", name:"ConditionalMethods3"} -->
> ```csharp
> // File Class1.cs:
> using System.Diagnostics;
> class Class1
> {
> [Conditional("DEBUG")]
> public static void F()
> {
> Console.WriteLine("Executed Class1.F");
> }
> }
>
> // File Class2.cs:
> #define DEBUG
> class Class2
> {
> public static void G()
> {
> Class1.F(); // F is called
> }
> }
>
> // File Class3.cs:
> #undef DEBUG
> class Class3
> {
> public static void H()
> {
> Class1.F(); // F is not called
> }
> }
> ```
````
From this code block, the extractor produces four files: Library.cs (based on the template `standalone-lib`), and Class1.cs, Class2.cs, and Class3.cs that contain the text for the corresponding source file (excluding the line-oriented comment delimiter).
As the three supporting files are created without a template, no using directives are implicitly available. As such, file Class1.cs must have an explicit one.
### Using Chevron-Quotes for Emphasis
In the Expressions chapter, we use «…» notation for emphasis, as follows:
Expand Down Expand Up @@ -681,7 +853,7 @@ If the runtime behavior is undefined, and the annotation is incomplete, use
<!-- Undefined$Example: { … } -->
````
If the example involves multiple source files, use
If the example involves multiple source files in multiple projects, use
````
<!-- RequiresSeparateFiles$Example: { … } -->
<!-- RequiresSeparateProjects$Example: { … } -->
````

0 comments on commit 4ad7d5b

Please sign in to comment.