[flang] Markdown improvements.

Original-commit: flang-compiler/f18@f40b5e40df
This commit is contained in:
peter klausler 2018-02-06 11:14:53 -08:00
parent 1e69ed0c1b
commit 6e22a3563e
2 changed files with 139 additions and 128 deletions

View File

@ -10,49 +10,54 @@
1. File names should use dashes, not underscores. C++ sources have the
extension ".cc", not ".C" or ".cpp" or ".cxx". Don't create needless
source directory hierarchies.
1. Header files should be idempotent. Use the usual "#ifndef FORTRAN_header_H_",
"#define FORTRAN_header_H_", and "#endif // FORTRAN_header_H_" technique.
1. #include every header defining an entity that your project header or source
1. Header files should be idempotent. Use the usual technique:
```
#ifndef FORTRAN_header_H_
#define FORTRAN_header_H_
// code
#endif // FORTRAN_header_H_
```
1. `#include` every header defining an entity that your project header or source
file actually uses directly. (Exception: when foo.cc starts, as it should,
with #include "foo.h", and foo.h includes bar.h in order to define the
interface to the module foo, you don't have to redundantly #include "bar.h"
with `#include "foo.h"`, and foo.h includes bar.h in order to define the
interface to the module foo, you don't have to redundantly `#include "bar.h"`
in foo.cc.)
1. In the source file "foo.cc", put the #include of "foo.h" first.
Then #include other project headers in alphabetic order; then C++ standard
1. In the source file "foo.cc", put the `#include "foo.h"` first.
Then `#include` other project headers in alphabetic order; then C++ standard
headers, also alphabetically; then C and system headers.
1. Don't include the standard iostream header. If you need it for debugging,
1. Don't use `#include <iostream>`. If you need it for debugging,
remove the inclusion before committing.
### Naming
1. C++ names that correspond to STL names should look like those STL names
(e.g., *clear()* and *size()* member functions in a class that implements
(e.g., `clear()` and `size()` member functions in a class that implements
a container).
1. Non-public data members should be named with leading miniscule (lower-case)
letters, internal camelCase capitalization, and a trailing underscore,
e.g. "DoubleEntryBookkeepingSystem myLedger_;". POD structures with
e.g. `DoubleEntryBookkeepingSystem myLedger_;`. POD structures with
only public data members shouldn't use trailing underscores, since they
don't have class functions in which data members need to be distinguishable.
1. Accessor member functions are named with the non-public data member's name,
less the trailing underscore. Mutator member functions are named *set_...*
and should return "*this". Don't define accessors or mutators needlessly.
less the trailing underscore. Mutator member functions are named `set_...`
and should return `*this`. Don't define accessors or mutators needlessly.
1. Other class functions should be named with leading capital letters,
CamelCase, and no underscores, and, like all functions, should be based
on imperative verbs, e.g. *HaltAndCatchFire()*.
on imperative verbs, e.g. `HaltAndCatchFire()`.
1. It is fine to use short names for local variables with limited scopes,
especially when you can declare them directly in a for()/while()/if()
especially when you can declare them directly in a `for()`/`while()`/`if()`
condition. Otherwise, prefer complete English words to abbreviations
when creating names.
### Commentary
1. Use // for all comments except for short notes within expressions.
1. When // follows code on a line, precede it with two spaces.
1. Use `//` for all comments except for short `/*notes*/` within expressions.
1. When `//` follows code on a line, precede it with two spaces.
1. Comments should matter. Assume that the reader knows current C++ at least as
well as you do and avoid distracting her by calling out usage of new
features in comments.
### Layout
Always run *clang-format* before committing code. Other developers should
be able to run "git pull", then *clang-format*, and see only their own
Always run `clang-format` before committing code. Other developers should
be able to run `git pull`, then `clang-format`, and see only their own
changes.
Here's what you can expect to see *clang-format* do:
Here's what you can expect to see `clang-format` do:
1. Indent with two spaces.
1. Don't indent public:, protected:, and private:
accessibility labels.
@ -65,12 +70,12 @@ names.
Don't try to make columns of variable names or comments
align vertically -- they are maintenance problems.
Always wrap the bodies of if(), else, while(), for(), do, &c.
Always wrap the bodies of `if()`, `else`, `while()`, `for()`, `do`, &c.
with braces, even when the body is a single statement or empty. The
opening { goes on
opening `{` goes on
the end of the line, not on the next line. Functions also put the opening
{ after the formal arguments or new-style result type, not on the next
line. Use {} for empty inline constructors and destructors in classes.
`{` after the formal arguments or new-style result type, not on the next
line. Use `{}` for empty inline constructors and destructors in classes.
Don't waste space on the screen with needless blank lines or elaborate block
commentary (lines of dashes, boxes of asterisks, &c.). Write code so as to be
@ -79,39 +84,40 @@ easily read and understood with a minimum of scrolling.
Use *C++17*, unless some compiler to which we must be portable lacks a feature
you are considering.
1. Never throw or catch exceptions.
1. Never use run-time type information or dynamic_cast<>.
1. Never use run-time type information or `dynamic_cast<>`.
1. Never declare static data that executes a constructor.
Use {braced initializers} in all circumstances where they work, including
(This is why `#include <iostream>` is contraindicated.)
Use `{braced initializers}` in all circumstances where they work, including
default data member initialization. They inhibit implicit truncation.
Don't use "= expr" initialization just to effect implicit truncation;
prefer an explicit static_cast<>.
1. Avoid unsigned types apart from size_t, which must be used with care.
When *int* just obviously works, just use *int*. When you need something
bigger than *int*, use std::int64_t rather than *long* or long long.
Don't use `= expr` initialization just to effect implicit truncation;
prefer an explicit `static_cast<>`.
1. Avoid unsigned types apart from `size_t`, which must be used with care.
When `int` just obviously works, just use `int`. When you need something
bigger than `int`, use `std::int64_t` rather than `long` or `long long`.
1. Use namespaces to avoid conflicts with client code. Use one top-level
project namespace. Don't introduce needless nested namespaces within a
project when names don't conflict or better solutions exist. Never use
"using namespace ...;", especially not "using namespace std;". Access
STL entities with names like std::unique_ptr<>, without a leading "::".
`using namespace ...;`, especially not `using namespace std;`. Access
STL entities with names like `std::unique_ptr<>`, without a leading `::`.
1. Prefer static functions to functions in anonymous namespaces in source files.
1. Use *auto* judiciously. When the type of a local variable is known,
monomorphic, and easy to type, be explicit rather than using *auto*.
1. Use `auto` judiciously. When the type of a local variable is known,
monomorphic, and easy to type, be explicit rather than using `auto`.
1. Use move semantics and smart pointers to make dynamic memory ownership
clear. Consider reworking any code that uses malloc() or a (non-placement)
operator new.
1. Use references for const arguments; prefer const references to values for
all but small types that are trivially copyable (e.g., *int*). Use non-const
pointers for output arguments. Put output arguments last (pace the standard
C library conventions for memcpy() & al.).
1. Prefer *typename* to *class* in template argument declarations.
1. Prefer enum class to plain enum wherever enum class will work.
1. Use constexpr and const generously.
1. When a switch() statement's labels do not cover all possible case values
explicitly, it should contains either a "default:;" at its end or a
default: label that obviously crashes.
clear. Consider reworking any code that uses `malloc()` or a (non-placement)
`operator new`.
1. Use references for `const` arguments; prefer `const` references to values for
all but small types that are trivially copyable (e.g., use `const std::string &`
and `int`). Use non-`const` pointers for output arguments. Put output arguments
last (_pace_ the standard C library conventions for `memcpy()` & al.).
1. Prefer `typename` to `class` in template argument declarations.
1. Prefer `enum class` to plain `enum` wherever `enum class` will work.
1. Use `constexpr` and `const` generously.
1. When a `switch()` statement's labels do not cover all possible case values
explicitly, it should contains either a `default:;` at its end or a
`default:` label that obviously crashes.
#### Classes
1. Define only POD structures with struct.
1. Don't use "this->" in (non-static) member functions.
1. Define only POD structures with `struct`.
1. Don't use `this->` in (non-static) member functions.
1. Define accessor and mutator member functions (implicitly) inline in the
class, after constructors and assignments. Don't needlessly define
(implicit) inline member functions in classes unless they really solve a
@ -121,7 +127,7 @@ interfaces, at least to the extent that C++ allows.
1. When copy constructors and copy assignment are not necessary,
and move constructors/assignment is present, don't declare them and they
will be implicitly deleted. When neither copy nor move constructors
or assignments should exist for a class, explicitly delete all of them.
or assignments should exist for a class, explicitly `=delete` all of them.
1. Make single-argument constructors (other than copy and move constructors)
explicit unless you really want to define an implicit conversion.
#### Overall design preferences

View File

@ -4,31 +4,31 @@ descent parser. It is composed from a *parser combinator* library that
defines a few fundamental parsers and a few ways to compose them into more
powerful parsers.
For our purposes here, a *parser* is any object that can attempt to recognize
For our purposes here, a *parser* is any object that attempts to recognize
an instance of some syntax from an input stream. It may succeed or fail.
On success, it may return some semantic value to its caller.
In C++ terms, a parser is any instance of a class that
1. has a *constexpr* default constructor,
1. defines a resultType type, and
1. provides a member or static function that accepts a pointer to a
ParseState as its argument and returns a std::optional<resultType> as a
result, with the presence or absence of a value in the std::optional<>
1. has a `constexpr` default constructor,
1. defines a type named `resultType`, and
1. provides a function (`const` member or static) that accepts a pointer to a
ParseState as its argument and returns a `std::optional<resultType>` as a
result, with the presence or absence of a value in the `std::optional<>`
signifying success or failure, respectively.
> std::optional<resultType> Parse(ParseState *) const;
The resultType of a parser is typically the class type of some particular
```
std::optional<resultType> Parse(ParseState *) const;
```
The `resultType` of a parser is typically the class type of some particular
node type in the parse tree.
*ParseState* is a class that encapsulates a position in the source stream,
collects messages, and holds a few state flags that determive tokenization
(e.g., are we in a character literal?). Instances of *ParseState* are
`ParseState` is a class that encapsulates a position in the source stream,
collects messages, and holds a few state flags that determine tokenization
(e.g., are we in a character literal?). Instances of `ParseState` are
independent and complete -- they are cheap to duplicate whenever necessary to
implement backtracking.
The constexpr default constructor of a parser is important. The functions
(below) that operate on instances of parsers are themselves all constexpr.
The `constexpr` default constructor of a parser is important. The functions
(below) that operate on instances of parsers are themselves all `constexpr`.
This use of compile-time expressions allows the entirety of a recursive
descent parser for a language to be constructed at compilation time through
the use of templates.
@ -36,110 +36,115 @@ the use of templates.
### Fundamental Predefined Parsers
These objects and functions are (or return) the fundamental parsers:
* *ok* is a trivial parser that always succeeds without advancing.
* "pure(x)" returns a trivial parser that always succeeds without advancing,
returning some value *x*.
* "fail<T>(msg)" denotes a trivial parser that always fails, emitting the
given message. The template parameter is the type of the value that
the parser never returns.
* *cut* is a trivial parser that always fails silently.
* "guard(pred)" returns a parser that succeeds if and only if the predicate
* `ok` is a trivial parser that always succeeds without advancing.
* `pure(x)` returns a trivial parser that always succeeds without advancing,
returning some value `x`.
* `fail<T>(msg)` denotes a trivial parser that always fails, emitting the
given message as a side effect. The template parameter is the type of
the value that the parser never returns.
* `cut` is a trivial parser that always fails silently.
* `guard(pred)` returns a parser that succeeds if and only if the predicate
expression evaluates to true.
* *rawNextChar* returns the next raw character, and fails at EOF.
* *cookedNextChar* returns the next character after preprocessing, skipping
* `rawNextChar` returns the next raw character, and fails at EOF.
* `cookedNextChar` returns the next character after preprocessing, skipping
Fortran line continuations and comments; it also fails at EOF
### Combinators
These functions and operators combine parsers to generate new parsers.
These functions and operators combine existing parsers to generate new parsers.
They are `constexpr`, so they should be viewed as type-safe macros.
* "!p" succeeds if p fails, and fails if p succeeds.
* "p >> q" fails if p does, otherwise running q and returning its value when
* `!p` succeeds if p fails, and fails if p succeeds.
* `p >> q` fails if p does, otherwise running q and returning its value when
it succeeds.
* "p / q" fails if p does, otherwise running q and returning *p's* value
* `p / q` fails if p does, otherwise running q and returning p's value
if q succeeds.
* "p || q" succeeds if p does, otherwise running q. The two parsers must
* `p || q` succeeds if p does, otherwise running q. The two parsers must
have the same type, and the value returned by the first succeeding parser
is the value of the combination.
* "lookAhead(p)" succeeds if p does, but doesn't modify any state.
* "attempt(p)" succeeds if p does, safely preserving state on failure.
* "many(p)" recognizes a greedy sequence of zero or more nonempty successes
of *p*, and returns std::list<> of their values. It always succeeds.
* "some(p)" recognized a greedy sequence of one or more successes of *p*.
* `lookAhead(p)` succeeds if p does, but doesn't modify any state.
* `attempt(p)` succeeds if p does, safely preserving state on failure.
* `many(p)` recognizes a greedy sequence of zero or more nonempty successes
of p, and returns `std::list<>` of their values. It always succeeds.
* `some(p)` recognized a greedy sequence of one or more successes of p.
It fails if p immediately fails.
* "skipMany(p)" is the same as "many(p)", but it discards the results.
* "maybe(p)" tries to match *p*, returning an "std::optional<T>" value.
* `skipMany(p)` is the same as `many(p)`, but it discards the results.
* `maybe(p)` tries to match p, returning an `std::optional<T>` value.
It always succeeds.
* "defaulted(p)" matches *p*, and when *p* fails it returns a
default-constructed instance of *p*'s resultType. It always succeeds.
* "nonemptySeparated(p, q)" repeatedly matches "p q p q p q ... p",
returning a std::list<> of only the values of the p's. It fails if
*p* immediately fails.
* "extension(p)" parses *p* if strict standard compliance is disabled,
* `defaulted(p)` matches p, and when p fails it returns a
default-constructed instance of p's resultType. It always succeeds.
* `nonemptySeparated(p, q)` repeatedly matches "p q p q p q ... p",
returning a `std::list<>` of only the values of the p's. It fails if
p immediately fails.
* `extension(p)` parses p if strict standard compliance is disabled,
or with a warning if nonstandard usage warnings are enabled.
* "deprecated(p)" parses *p* if strict standard compliance is disabled,
* `deprecated(p)` parses p if strict standard compliance is disabled,
with a warning if deprecated usage warnings are enabled.
* "inContext(..., p)" runs *p* within an error message context.
* `inContext(..., p)` runs p within an error message context.
Note that "a >> b >> c / d / e" matches a sequence of five parsers,
but returns only the result that was obtained by matching c.
Note that
```
a >> b >> c / d / e
```
matches a sequence of five parsers, but returns only the result that was
obtained by matching `c`.
### Applicatives
The following *applicative* combinators combine parsers and modify or
collect the values that they return.
* "construct<T>{}(p1, p2, ...)" matches zero or more parsers in succession,
* `construct<T>{}(p1, p2, ...)` matches zero or more parsers in succession,
collecting their results and then passing them with move semantics to a
constructor for the type *T* if they all succeed.
* "applyFunction(f, p1, p2, ...)" matches one or more parsers in succession,
constructor for the type T if they all succeed.
* `applyFunction(f, p1, p2, ...)` matches one or more parsers in succession,
collecting their results and passing them as rvalue reference arguments to
some function, returning its result.
* "applyLambda([](&&x){}, p1, p2, ...)" is the same thing, but for lambdas
* `applyLambda([](&&x){}, p1, p2, ...)` is the same thing, but for lambdas
and other function objects.
* "applyMem(mf, p1, p2, ...)" is the same thing, but invokes a member
* `applyMem(mf, p1, p2, ...)` is the same thing, but invokes a member
function of the result of the first parser for updates in place.
### Non-Advancing State Inquiries and Updates
These are non-advancing state inquiry and update parsers:
* *getColumn* returns the 1-based column position.
* *inCharLiteral* succeeds under withinCharLiteral.
* *inFortran* succeeds unless in a preprocessing directive.
* *inFixedForm* succeeds in fixed-form source.
* *setInFixedForm* sets the fixed-form flag, returning its prior value.
* *columns* returns the 1-based column number after which source is clipped.
* "setColumns(c)" sets the column limit and returns its prior value.
* `getColumn` returns the 1-based column position.
* `inCharLiteral` succeeds under withinCharLiteral.
* `inFortran` succeeds unless in a preprocessing directive.
* `inFixedForm` succeeds in fixed-form source.
* `setInFixedForm` sets the fixed-form flag, returning its prior value.
* `columns` returns the 1-based column number after which source is clipped.
* `setColumns(c)` sets the column limit and returns its prior value.
### Monadic Combination
When parsing depends on the result values of earlier parses, the
"monadic bind" combinator is available.
*monadic bind* combinator is available.
Please try to avoid using it, as it makes automatic analysis of the
grammar difficult.
It has the syntax "p >>= f", and it constructs a parser that matches p,
It has the syntax `p >>= f`, and it constructs a parser that matches p,
yielding some value x on success, then matches the parser returned from
the function call "f(x)".
the function call `f(x)`.
### Token Parsers
Last, we have these basic parsers on which the actual grammar of the Fortran
is built. All of the following parsers consume characters acquired from
*cookedNextChar*.
`cookedNextChar`.
* *spaces* always succeeds after consuming any spaces or tabs
* *digit* matches one cooked decimal digit (0-9)
* *letter* matches one cooked letter (A-Z)
* "CharMatch<'c'>{}" matches one specific cooked character.
* "..."_tok match the content of the string, skipping spaces before and
* `spaces` always succeeds after consuming any spaces or tabs
* `digit` matches one cooked decimal digit (0-9)
* `letter` matches one cooked letter (A-Z)
* `CharMatch<'c'>{}` matches one specific cooked character.
* `"..."_tok` match the content of the string, skipping spaces before and
after, and with multiple spaces accepted for any internal space.
(Note that the _tok suffix is optional when the parser appears before
the combinator ">>" or after "/".)
* "parenthesized(p)" is shorthand for "(" >> p / ")".
* "bracketed(p)" is shorthand for "[" >> p / "]".
* "withinCharLiteral(p)" applies the parser *p*, tokenizing for
(Note that the `_tok` suffix is optional when the parser appears before
the combinator`">>` or after `/`.)
* `parenthesized(p)` is shorthand for `"(" >> p / ")"`.
* `bracketed(p)` is shorthand for `"[" >> p / "]"`.
* `withinCharLiteral(p)` applies the parser p, tokenizing for
CHARACTER/Hollerith literals.
* "nonEmptyListOf(p)" matches a comma-separated list of one or more
instances of *p*.
* "optionalListOf(p)" is the same thing, but can be empty, and always succeeds.
* `nonEmptyListOf(p)` matches a comma-separated list of one or more
instances of p.
* `optionalListOf(p)` is the same thing, but can be empty, and always succeeds.
### Debugging Parser
Last, the parser "..."_debug emit the string to the standard error and succeeds.
It is useful for tracing while debugging a parser but should obviously not
be committed for production code.
Last, the parser `"..."_debug` emits the string to the standard error
and succeeds. It is useful for tracing while debugging a parser but should
obviously not be committed for production code.