Skip to content

Commit

Permalink
Update the readme for v0.2.
Browse files Browse the repository at this point in the history
  • Loading branch information
GGG-KILLER committed Apr 9, 2021
1 parent db2ccf4 commit 2ab8e52
Showing 1 changed file with 79 additions and 41 deletions.
120 changes: 79 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Loretta
A C# (G)Lua lexer, parser, code analysis and transformation (rather weak currently) and code generation toolkit.
A C# (G)Lua lexer, parser, code analysis, transformation and code generation toolkit.

This is a rewrite from scratch based on [The Complete Syntax of Lua](https://www.lua.org/manual/5.2/manual.html#9) with a few extensions:
This is (another) rewrite from scratch based on Roslyn and [The Complete Syntax of Lua](https://www.lua.org/manual/5.2/manual.html#9) with a few extensions:
1. Operators introduced in Garry's Mod Lua (glua):
- `&&` for `and`;
- `||` for `or`;
Expand All @@ -11,42 +11,80 @@ This is a rewrite from scratch based on [The Complete Syntax of Lua](https://www
- C style single line comment: `// ...`;
- C style multi line comment: `/* */`;
3. Characters accepted as part of identifiers by LuaJIT (emojis, non-rendering characters, [or basically any byte above `0x7F`](https://github.com/GGG-KILLER/Loretta/blob/e9b0bef6b3959c3365c311e80c68391dc6ac66e0/Loretta/Utilities/CharUtils.cs#L65-L87));
4. Roblox compound assignment: `+=`, `-=`, `*=`, `/=`, `^=`, `%=`, `..=`.

## Using Loretta
1. Pick a [preset](https://github.com/GGG-KILLER/Loretta/blob/e9b0bef6b3959c3365c311e80c68391dc6ac66e0/Loretta/LuaOptions.cs#L32-L119);
2. Initialize a new `LuaLexerBuilder` and a `LuaParserBuilder` by passing the selected preset to its constructor. These should be used for the lifetime of your application;
3. Then to parse code:
1. Obtain an `IProgress<Diagnostic>` to be able to receive generated diagnostics (GParse provides a simple one through `GParse.Collections.DiagnosticList` which implements both `IProgress<Diagnostic>` and `IReadOnlyList<Diagnostic>`);
2. Create a lexer by calling `LuaLexerBuilder.CreateLexer(String, IProgress<Diagnostic>)` with the code to be parsed and the created `IProgress<Diagnostic>`;
3. Create a token reader by passing the built lexer to `TokenReader<LuaTokenType>`'s constructor;
4. Create a parser by calling `LuaParserBuilder.CreateLexer(ITokenReader<LuaTokenType>, IProgress<Diagnostic>)` with the created token reader and `IProgress<Diagnostic>`;
5. Call the `LuaParser.Parse()` method to parse the full code (may throw `FatalParsingException`s depending on the code's content);
6. Then with the obtained tree you can:
1. Transform code with a tree folder (Loretta comes with a very simple `ConstantFolder` as an example);
2. Process the tree with a tree walker (Loretta comes with a `FormattedLuaCodeSerializer` to transform the tree back into code and as an example).

### Code sample:

```cs
public static class CodeFormatter
{
private static readonly LuaLexerBuilder _lexerBuilder = new LuaLexerBuilder ( LuaOptions.GMod );
private static readonly LuaParserBuilder _parserBuilder = new LuaParserBuilder ( LuaOptions.GMod );

public static (IEnumerable<Diagnostic> diagnostics, String? formatted) Format ( String code )
{
var diagnostics = new DiagnosticList ( );
var lexer = this._lexerBuilder.CreateLexer ( code, diagnostics );
var tokenReader = new TokenReader<LuaTokenType> ( );
var parser = this._parserBuilder.CreateParser ( lexer, diagnostics );
var tree = parser.Parse ( );
if ( diagnostics.Any ( diagnostic => diagnostic.Severity == DiagnosticSeverity.Error ) )
{
return (diagnostics, null);
}

return (diagnostics, FormattedLuaCodeSerializer.Format ( LuaOptions.GMod, tree ));
}
}
```
4. Roblox compound assignment: `+=`, `-=`, `*=`, `/=`, `^=`, `%=`, `..=`;
5. Lua 5.3 bitwise operators.

## Using Loretta v0.2

### Parsing text
1. (Optional) Pick a [`LuaSyntaxOptions` preset](src/Compilers/Lua/Portable/LuaSyntaxOptions.cs#L12-L104) and then create a `LuaParseOptions` from it. If no preset is picked, `LuaSyntaxOptions.All` is used by default;
2. (Optional) Create a `SourceText` from your code (using one of the `SourceText.From` overloads);
3. Call `LuaSyntaxTree.ParseText` with your `SourceText`/`string`, (optional) `LuaParseOptions`, (optional) `path` and (optional) `CancellationToken`;
4. Do whatever you want with the returned `LuaSyntaxTree`.

#### Formatting Code
The `NormalizeWhitespace` method replaces all whitespace and and end of line trivia by normalized (standard code style) ones.

### Accessing scope information
If you'd like to get scoping and variable information, create a new `Script` from your `SyntaxTree`s and then do one of the following:
- Access `Script.RootScope` to get the global scope;
- Call [`Script.GetScope(SyntaxNode)`](#using-scopes) to get an `IScope`;
- Call [`Script.GetVariable(SyntaxNode)`](#using-variables) to get an `IVariable`;
- Call [`Script.GetLabel(SyntaxNode)`](#using-labels) on a `GotoStatementSyntax` or a `GotoLabelStatementSyntax` to get an `IGotoLabel`;

#### Using Variables
There are 4 kinds of variables:
- `VariableKind.Local` a variable declared in a `LocalVariableDeclarationStatementSyntax`;
- `VariableKind.Global` a variable used without a previous declaration;
- `VariableKind.Parameter` a function parameter;
- `VariableKind.Iteration` a variable that is an iteration variable from a `NumericForLoopSyntax` or `GenericForLoopSyntax`;

The interface for variables is `IVariable` which exposes the following information:
- `IVariable.Kind`- The `VariableKind`;
- `IVariable.Scope` - The containing scope;
- `IVariable.Name` - The variable name (might be `...` for varargs);
- `IVariable.Declaration` - The place where the variable was declared (`null` for the implcit `arg` and `...` variables available in all files and global variables);
- `IVariable.ReferencingScopes` - The scopes that have statements that **directly** reference this variable;
- `IVariable.CapturingScopes` - Scopes that capture this variable as an upvalue;
- `IVariable.ReadLocations` - Nodes that read from this variable;
- `IVariable.WriteLocations` - Nodes that write to this variable;

#### Using Scopes
There are 4 kinds of scopes:
- `ScopeKind.Global` - There is only one of these, the `Script.RootScope`. It implements [`IScope`](#IScope) and only contains globals;
- [`ScopeKind.File`](#IFileScope) - These implement [`IFileScope`](#IFileScope) and are the root scopes for files (`LuaSyntaxTree`s);
- [`ScopeKind.Function`](#IFunctionScope) - These implement [`IFunctionScope`](#IFunctionScope) and are generated for these nodes:
- `AnonymousFunctionExpressionSyntax`;
- `LocalFunctionDeclarationStatementSyntax`;
- `FunctionDeclarationStatementSyntax`.
- [`ScopeKind.Block`](#IScope) - These implement only [`IScope`](#IScope) and are generated for normal blocks from these nodes:
- `NumericForStatementSyntax`;
- `GenericForStatementSyntax`;
- `WhileStatementSyntax`;
- `RepeatUntilStatementSyntax`;
- `IfStatementSyntax`;
- `ElseIfClauseSyntax`;
- `ElseClauseSyntax`;
- `DoStatementSyntax`.

##### `IScope`
`IScope`s are the most basic kind of scope and all other scopes derive from it.
The information exposed by them is:
- `IScope.Kind` - The `ScopeKind`;
- `IScope.Node` - The `SyntaxNode` that originated the scope. Will be `null` for global and file scopes;
- `IScope.Parent` - The scope's parent `IScope`. Will be `null` for the global scope;
- `IScope.DeclaredVariables` - The `IVariable`s that were declared in this scope;
- `IScope.ReferencedVariables` - The `IVariable`s that are referenced by this scope or its children;
- `IScope.GotoLabels` - The `IGotoLabel`s directly contained by this scope.

##### `IFunctionScope`
`IFunctionScope`s are scopes from function declarations.
They have everything from [`IScope`s](#IScope) and also:
- `IFunctionScope.Parameters` - The `IVariable` parameters for this function;
- `IFunctionScope.CapturedVariables` - The `IVariable`s captured as upvalues by this function.

##### `IFileScope`
`IFileScope`s are scopes for entire files (`LuaSyntaxTree`s).
They have everything from [`IScope`s](#IScope) and also:
- `IFileScope.ArgsVariable` - The implicit `args` variable available in all lua script files;
- `IFileScope.VarArgParameter` - The implicit vararg parameter available in all lua script files.

0 comments on commit 2ab8e52

Please sign in to comment.