### Initialize Fluent Parser Builder Source: https://github.com/b3b00/csly/wiki/parser-fluent-api Shows the initialization of the FluentParserBuilder with token and output types, an instance of the parser, the root rule name, and the language for error messages. This is the starting point for configuring the parser. ```csharp var parserBuilder = FluentParserBuilder.NewBuilder(object parserInstance, string rootRule, string lang); ``` -------------------------------- ### C# Lexer Tokenization Examples Source: https://github.com/b3b00/csly/blob/dev/index.md Demonstrates how to tokenize a source string using the SLY lexer. It shows two ways to obtain an IEnumerable>: directly using the static Tokenize method or by building a lexer instance first. ```csharp IList> tokens = Lexer.Tokenize(source).ToList>(); ``` ```csharp ILexer lexer = LexerBuilder.BuildLexer(); var tokens = lexer.Tokenize(source).ToList(); ``` -------------------------------- ### Full Prolog Identifier Management Example in C# Source: https://github.com/b3b00/csly/wiki/GenericLexerCallbacks This comprehensive C# example integrates `CallBacks` and `TokenCallback` attributes to manage Prolog identifiers. It defines `PrologTokens` enum and `PrologTokensCallbacks` class to correctly subtype `IDENTIFIER` tokens into `VARIABLE` or `ATOM` based on casing rules. ```csharp [CallBacks(typeof(PrologTokensCallbacks))] public enum PrologTokens { [Lexeme(GenericToken.Identifier)] IDENTIFIER = 1, VARIABLE = 2, ATOM = 3 } public class PrologTokensCallbacks { [TokenCallback((int)CallbackTokens.IDENTIFIER)] public static Token TranslateIdentifier(Token token) { if (char.IsUpper(token.Value[0])) { token.TokenID = CallbackTokens.VARIABLE; } else { token.TokenID = CallbackTokens.ATOM; } return token; } } ``` -------------------------------- ### Build CSLY Parser using ParserBuilder in C# Source: https://github.com/b3b00/csly/wiki/Getting-started Constructs a CSLY parser instance using the ParserBuilder. This method specifies the token type, result type, the parser implementation class, the desired parser type (LL_RECURSIVE_DESCENT), and the starting grammar rule. ```csharp using sly.parser; using sly.parser.generator; public class SomeClass { public static Parser GetParser() { var parserInstance = new ExpressionParser(); var builder = new ParserBuilder(); var Parser = builder.BuildParser(parserInstance, ParserType.LL_RECURSIVE_DESCENT, "expression").Result; return Parser; } } ``` -------------------------------- ### C# Fluent Lexer Builder Initialization Source: https://github.com/b3b00/csly/wiki/lexer-fluent-api Initializes a new instance of the FluentLexerBuilder for a specific token enum. This is the starting point for configuring a lexer using the fluent API. ```csharp var lexerBuilder = FluentLexerBuilder.NewBuilder(); ``` -------------------------------- ### Fluent Parser API Example in C# Source: https://context7.com/b3b00/csly/llms.txt This C# code demonstrates building a parser programmatically using CSLY's fluent API. It defines a lexer for identifiers and integers, and then constructs a parser with operator precedence for arithmetic expressions. The fluent API allows for defining productions, left/right associative operators, and prefix operators without using attributes. ```csharp using sly.parser.fluent; public enum ExprToken { ID, INT } public class ExprParser { // Empty parser instance required for expression generation } // Build lexer fluently var lexerBuilder = FluentLexerBuilder.NewBuilder(); var lexerResult = lexerBuilder .IgnoreEol(true) .IgnoreWhiteSpace(true) .AlphaId(ExprToken.ID) .Int(ExprToken.INT) .Build("en"); if (!lexerResult.IsOk) { Console.WriteLine("Lexer build failed"); return; } // Build parser fluently var parserBuilder = FluentParserBuilder.NewBuilder(new ExprParser(), "root", "en"); var parserResult = parserBuilder .Production("root : ExprParser_expressions", (object[] args) => (int)args[0]) .Left("'-'", 10, (object[] args) => (int)args[0] - (int)args[2]) .Right("'+'", 10, (object[] args) => (int)args[0] + (int)args[2]) .Left("'/'", 50, (object[] args) => (int)args[0] / (int)args[2]) .Right("'*'", 50, (object[] args) => (int)args[0] * (int)args[2]) .Prefix("'-'", 100, (object[] args) => -(int)args[1]) .Operand("value : INT", (object[] args) => ((Token)args[0]).IntValue) .WithLexerbuilder(lexerBuilder) .BuildParser(); if (parserResult.IsOk) { var parser = parserResult.Result; var result = parser.Parse("-1 + 2 * 3"); if (result.IsOk) { Console.WriteLine($"Result: {result.Result}"); // Output: Result: 5 (which is -1 + 6) } } ``` -------------------------------- ### Configure Sugar and UpTo Lexemes in Csly Source: https://github.com/b3b00/csly/wiki/GenericLexer Covers the configuration of SugarToken and UpTo lexemes. SugarTokens are general-purpose lexemes starting with an alpha character and are configurable. UpTo lexemes match characters until a specified pattern is encountered. ```C# [Lexeme(GenericToken.SugarToken, "$")] [Sugar("$")] [Lexeme(GenericToken.UpTo, "<", ">")] [UpTo("<", ">")] ``` -------------------------------- ### Static Generic Lexeme Construction in Csly Source: https://github.com/b3b00/csly/wiki/GenericLexer Demonstrates the construction of static generic lexemes in Csly. This method maps a generic token to a lexer token using a single parameter, the mapped generic token. Examples include String, Int, Double, and Identifier. ```C# // Example for String lexeme [Lexeme(GenericToken.String)] // Example for Int lexeme [Lexeme(GenericToken.Int)] // Example for Double lexeme [Lexeme(GenericToken.Double)] // Example for Identifier lexeme [Lexeme(GenericToken.Identifier)] ``` -------------------------------- ### EBNF Repeater Modifier - List (C#) Source: https://github.com/b3b00/csly/wiki/EBNF-parser Demonstrates how the '*' repeater modifier in EBNF, when applied to a non-terminal, results in a List being passed to the production method. This example shows parsing a list of JSON values. ```csharp [Production("listElements: value additionalValue*")] public JSon listElements(JSon head, List tail) { JList values = new JList(head); values.AddRange(tail); return values; } ``` -------------------------------- ### Build Arithmetic Expression Parser with BNF in C# Source: https://context7.com/b3b00/csly/llms.txt Define grammar rules using BNF notation with Production attributes on visitor methods. This example implements a recursive descent parser for arithmetic expressions supporting addition, subtraction, and parenthesized grouping. Returns computed integer results from parsed expressions. ```csharp using sly.lexer; using sly.parser.generator; public class ExpressionParser { [Production("expression: INT")] public int IntExpr(Token intToken) { return intToken.IntValue; } [Production("expression: term PLUS expression")] public int Addition(int left, Token operatorToken, int right) { return left + right; } [Production("expression: term MINUS expression")] public int Subtraction(int left, Token operatorToken, int right) { return left - right; } [Production("term: INT")] public int Term(Token intToken) { return intToken.IntValue; } [Production("term: LPAREN expression RPAREN")] public int GroupedExpression(Token lparen, int value, Token rparen) { return value; } } using sly.parser; using sly.parser.generator; var parserInstance = new ExpressionParser(); var builder = new ParserBuilder(); var buildResult = builder.BuildParser(parserInstance, ParserType.LL_RECURSIVE_DESCENT, "expression"); if (buildResult.IsOk) { var parser = buildResult.Result; var parseResult = parser.Parse("42 + 42"); if (!parseResult.IsError && parseResult.Result != null) { Console.WriteLine($"Result: {parseResult.Result}"); } else if (parseResult.Errors != null && parseResult.Errors.Any()) { foreach (var error in parseResult.Errors) { Console.WriteLine($"Error: {error.ErrorMessage}"); } } } ``` -------------------------------- ### Define Indentation-Aware Lexer with Tokens in CSLY Source: https://github.com/b3b00/csly/wiki/indented-languages Example lexer implementation with IndentationAware enabled, defining identifier, keyword, operator, and integer tokens. The lexer automatically generates INDENT and UINDENT tokens for indentation-delimited language parsing. ```csharp [Lexer(IndentationAWare = true)] public enum IndentedLangLexer { [Lexeme(GenericToken.Identifier, IdentifierType.Alpha)] ID = 1, [Lexeme(GenericToken.KeyWord, "if")] IF = 2, [Lexeme(GenericToken.KeyWord, "else")] ELSE = 3, [Lexeme(GenericToken.SugarToken, "==")] EQ = 4, [Lexeme(GenericToken.SugarToken, "=")] SET = 5, [Lexeme(GenericToken.Int)] INT = 6 } ``` -------------------------------- ### C# Example: Explicit Tokens in EBNF Parser Source: https://github.com/b3b00/csly/wiki/EBNF-parser Demonstrates defining lexer tokens (Id, Dbl) and then using explicit tokens like 'if', 'then', 'else', '==' directly within production rules. This C# code requires the SLY library and is compatible with the Generic Lexer. ```csharp public enum Lex { [AlphaId] Id, [Double] Dbl } public class Parse { [Production("program : statement*")] public string Program(List statements) { StringBuilder builder = new StringBuilder(); foreach (var statement in statements) { builder.AppendLine(statement); } return builder.ToString(); } [Production("statement : Id '='[d] Parse_expressions ")] public string Assignment(Token id, string expression) { return $"{id.Value} = {expression}"; } [Production("condition : Id '=='[d] Parse_expressions ")] public string Condition(Token id, string expression) { return $"{id.Value} == {expression}"; } [Production("statement : 'if'[d] condition 'then'[d] statement 'else'[d] statement")] public string IfThenElse(string condition, string thenStatement, string elseStatement) { StringBuilder builder = new StringBuilder(); builder.AppendLine($"{condition} :"); builder.AppendLine($" - {thenStatement}"); builder.AppendLine($" - {elseStatement}"); return builder.ToString(); } #region expressions [Operand] [Production("operand : Id")] [Production("operand : Dbl")] public string Operand(Token oper) { return oper.Value; } [Infix("'+'", Associativity.Left, 10)] public string Plus(string left, Token oper, string right) { return $"( {left} + {right} )"; } [Infix("'*T"", Associativity.Left, 20)] public string Times(string left, Token oper, string right) { return $"( {left} * {right} )"; } #endregion } ``` -------------------------------- ### Define Lexer with Fluent API in C# Source: https://context7.com/b3b00/csly/llms.txt Create a lexer programmatically using FluentLexerBuilder without C# attributes. Supports token types, whitespace/EOL handling, comments, and mode-based tokenization. The example demonstrates defining identifiers, integers, comments, and mode-switching tokens (currency symbols). ```csharp using sly.lexer.fluent; public enum MyToken { ID, INT, COMMENT, DOLLAR, MONEY, EURO } var lexerBuilder = FluentLexerBuilder.NewBuilder(); BuildResult> lexerResult = lexerBuilder .AlphaId(MyToken.ID) .IgnoreEol(true) .IgnoreWhiteSpace(true) .Int(MyToken.INT) .SingleLineComment(MyToken.COMMENT, "#").OnChannel(Channels.Main) .Sugar(MyToken.DOLLAR, "$").PushToMode("money") .UpTo(MyToken.MONEY, "€").WithModes("money") .Sugar(MyToken.EURO, "€").WithModes("money").PopMode() .Build("en"); if (lexerResult.IsOk) { ILexer lexer = lexerResult.Result; var tokens = lexer.Tokenize(@" identifier 42 # comment $ money content € "); } ``` -------------------------------- ### C# Expression Parser with Operator Precedence using CSLY Source: https://context7.com/b3b00/csly/llms.txt Defines a C# expression parser using CSLY's generator attributes. It specifies infix, prefix, and postfix operators with their associativity and precedence. The parser handles basic arithmetic operations, negation, and factorial. It also defines operands, including double, integer, and parenthesized expressions. The code snippet includes building and using the parser with example inputs. ```csharp using sly.parser.generator; [ParserRoot("root")] public class SimpleExpressionParser { [Production("root : SimpleExpressionParser_expressions")] public double Root(double value) => value; // Infix operators with precedence 10 (lower precedence) [Operation((int)ExpressionToken.PLUS, Affix.InFix, Associativity.Right, 10)] [Operation("MINUS", Affix.InFix, Associativity.Left, 10)] public double BinaryTermExpression(double left, Token operation, double right) { switch (operation.TokenID) { case ExpressionToken.PLUS: return left + right; case ExpressionToken.MINUS: return left - right; default: return 0; } } // Infix operators with precedence 50 (higher precedence) [Operation((int)ExpressionToken.TIMES, Affix.InFix, Associativity.Right, 50)] [Operation("DIVIDE", Affix.InFix, Associativity.Left, 50)] public double BinaryFactorExpression(double left, Token operation, double right) { switch (operation.TokenID) { case ExpressionToken.TIMES: return left * right; case ExpressionToken.DIVIDE: return left / right; default: return 0; } } // Prefix operator with highest precedence (100) [Prefix((int)ExpressionToken.MINUS, Associativity.Right, 100)] public double PreFixExpression(Token operation, double value) { return -value; } // Postfix operator [Postfix((int)ExpressionToken.FACTORIAL, Associativity.Right, 100)] public double PostFixExpression(double value, Token operation) { var factorial = 1; for (var i = 1; i <= value; i++) factorial = factorial * i; return factorial; } // Define operands [Operand] [Production("operand : primary_value")] public double OperandValue(double value) { return value; } [Production("primary_value : DOUBLE")] public double OperandDouble(Token value) { return value.DoubleValue; } [Production("primary_value : INT")] public double OperandInt(Token value) { return value.DoubleValue; } [Production("primary_value : LPAREN SimpleExpressionParser_expressions RPAREN")] public double OperandParens(Token lparen, double value, Token rparen) { return value; } } // Build and use expression parser (MUST use EBNF_LL_RECURSIVE_DESCENT) var builder = new ParserBuilder(); var buildResult = builder.BuildParser( new SimpleExpressionParser(), ParserType.EBNF_LL_RECURSIVE_DESCENT, "root" ); if (buildResult.IsOk) { var parser = buildResult.Result; var result = parser.Parse("2 + 3 * 4"); // result.Result == 14 (not 20, because * has higher precedence) result = parser.Parse("-5!"); // result.Result == -120 (factorial has higher precedence than negation) } ``` -------------------------------- ### Build the Parser Source: https://github.com/b3b00/csly/wiki/parser-fluent-api Illustrates the process of building the final IParser object by calling the BuildParser method on the parser builder. Includes checking the result for success and obtaining the parser instance. ```csharp var parserBuilder = FluentParserBuilder.NewBuilder(new ExprParser(),"root","en"); // .... BuildResult> parserResult = lexerBuilder.BuildParser(); if (parserResult.IsOk()) { IParser parser = parserResult.Result; } ``` -------------------------------- ### Configure Identifier Lexemes in Csly Source: https://github.com/b3b00/csly/wiki/GenericLexer Defines different types of identifiers supported by Csly, including Alpha, AlphaNum, AlphaNumDash, and Custom. Custom identifiers allow specifying start and rest patterns using '_c_' and '_l-u_' placeholders. Examples demonstrate configuration for different identifier patterns. ```C# [Lexeme(GenericToken.Identifier, IdentifierType.Alpha)] [AlphaId] [Lexeme(GenericToken.Identifier, IdentifierType.AlphaNum)] [AlphaNumId] [Lexeme(GenericToken.Identifier, IdentifierType.AlphaNumDash)] [AlphaNumDashId] [Lexeme(GenericToken.Identifier, IdentifierType.Custom, "_A-Za-z", "-_0-9A-Za-z")] [CustomId("_A-Za-z", "-_0-9A-Za-z")] ``` -------------------------------- ### C# Building and Tokenizing with Fluent Lexer Source: https://github.com/b3b00/csly/wiki/lexer-fluent-api Demonstrates the final steps of building the lexer and tokenizing input. It shows how to call the `Build` method with a language code and then use the resulting `ILexer` to tokenize a string. ```csharp public enum MyToken { ID, INT, COMMENT, DOLLAR, MONEY, EURO } public class FluentLexerTest { public void LexerTest() { var lexerBuilder = FluentLexerBuilder.NewBuilder(); BuildResult> lexerResult = lexerBuilder .AlphaId(MyToken.ID) .IgnoreEol(true) .IgnoreWhiteSpace(true) .Int(MyToken.INT) .SingleLineComment(MyToken.COMMENT, "#").OnChannel(Channels.Main) .Sugar(MyToken.DOLLAR,"$").PushToMode("money") .UpTo(MyToken.MONEY, "€").WithModes("money") .Sugar(MyToken.EURO, "€").WithModes("money").PopMode() .Build("en"); if (lexerResult.IsOk()) { ILexer lexer = lexerResult.Result; var tokens = lexer.Tokenize(@"identifier 42\n# comment\n$ money content € "); } } } ``` -------------------------------- ### Build Csly Parser Instance Source: https://github.com/b3b00/csly/wiki/Implementing-a-BNF-parser Demonstrates how to build a parser instance using `ParserBuilder.BuildParser`. This method requires the parser definition, the type of parser (e.g., `LL_RECURSIVE_DESCENT`), and the root rule. It returns a `BuildResult` containing either the configured parser or a list of errors if the build fails. ```csharp Parser parser = null; ExpressionParser expressionParserDefinition = new ExpressionParser(); // ExpressionToken is the token enum type // int is the type of a parse evaluation BuildResult> ParserResult = ParserBuilder.BuildParser(expressionParserDefinition, ParserType.LL_RECURSIVE_DESCENT, "expression"); if (parserResult.IsOk) { // everythin'fine : we have a configured parser parser = parserResult.Result; } else { // something's wrong foreach(var error in parserResult.Errors) { Console.WriteLine($"{error.Code} : {error.Message}"); } } ``` -------------------------------- ### C# Multi-Line Comment Definition Source: https://github.com/b3b00/csly/wiki/GenericLexer Defines a specific token for multi-line comments. It requires both the multi-line start and end delimiters (e.g., '/*' and '*/') to be provided. This comment type is removed from the token stream by default. ```csharp [MultiLineComment(multilinestart, multilineend)] MULTI_LINE_COMMENT ``` -------------------------------- ### FSMBuilder: Moving to a State in C# Source: https://github.com/b3b00/csly/wiki/GenericLexerExtension This C# code illustrates how to navigate to a specific state within the finite state machine (FSM) using the `FSMBuilder`. The `GoTo(string nodeName)` method allows the builder to transition to a state identified by its name, which is crucial for defining complex lexer logic and tokenization rules. ```csharp fsmBuilder.GoTo("nodeName"); ``` -------------------------------- ### C# Lexer Configuration Methods Source: https://github.com/b3b00/csly/wiki/lexer-fluent-api Demonstrates common lexer configuration options like ignoring end-of-line characters, whitespace, enabling indentation awareness, ignoring keyword case, and setting up callbacks or post-processors. ```csharp var lexerBuilder = FluentLexerBuilder.NewBuilder(); lexerBuilder.IgnoreEol(true); lexerBuilder.IgnoreWhiteSpace(true); lexerBuilder.IsIndentationAware(true); lexerBuilder.IgnoreKeywordCase(true); // lexerBuilder.WithCallBack(tokenId, callback); // lexerBuilder.UseLexerPostProcessor(lexerPostProcessor); // lexerBuilder.UseExtensionBuilder(extensionBuilder); ``` -------------------------------- ### C# Building and Using a Parser Instance Source: https://github.com/b3b00/csly/blob/dev/index.md Demonstrates how to build a parser instance using CSLY's ParserBuilder. It involves creating an instance of the parser definition class, specifying the token type and evaluation result type, selecting the parser type (e.g., LL_RECURSIVE_DESCENT), and defining the root rule. The built parser can then be used to parse expression strings. ```csharp ExpressionParser expressionParserDefinition = new ExpressionParser(); // here see the typing : // ExpressionToken is the token enum type // int is the type of a parse evaluation Parser Parser = ParserBuilder.BuildParser(expressionParserDefinition, ParserType.LL_RECURSIVE_DESCENT, "expression"); string expression = "1 + 1"; ParseResult r = Parser.Parse(expression); if (!r.IsError && r.Result != null && r.Result is int) { Console.WriteLine($"result of {expression} is {(int)r.Result}"); } else { if (r.Errors != null && r.Errors.Any()) { // display errors r.Errors.ForEach(error => Console.WriteLine(error.ErrorMessage)); } } ``` -------------------------------- ### Compile Syntax Tree to Graphviz Dot File in C# Source: https://github.com/b3b00/csly/wiki/ViewSyntaxTree This snippet demonstrates how to compile a concrete syntax tree obtained from parsing an input string into a Graphviz dot file using CSLY's GraphVizEBNFSyntaxTreeVisitor. It shows the process of parsing, visiting the tree, compiling the graph, and saving it to a file. Dependencies include the CSLY parsing and visitor classes, and .NET file I/O operations. The input is a parsed result, and the output is a Graphviz dot file. ```csharp var result = parser.Result.Parse("2 + 2 * 3"); var tree = result.SyntaxTree; var graphviz = new GraphVizEBNFSyntaxTreeVisitor(); var root = graphviz.VisitTree(tree); string graph = graphviz.Graph.Compile(); File.Delete("c:\\temp\\tree.dot"); File.AppendAllText("c:\\temp\\tree.dot", graph); ``` -------------------------------- ### Build and Use C# Lexer Independently Source: https://github.com/b3b00/csly/wiki/Lexer This C# example shows how to build and use a CSLY lexer independently of the parser. It utilizes the LexerBuilder to create a lexer for a specific token enum (ExpressionToken) and then tokenizes a source string. ```csharp var source = "some source to be lexed" ILexer lexer = LexerBuilder.BuildLexer(); var tokens = lexer.Tokenize(source).ToList(); ``` -------------------------------- ### Configure Parser Optimizations and Lexer Source: https://github.com/b3b00/csly/wiki/parser-fluent-api Details fluent methods for configuring parser behavior, including enabling memoization, broadening the token window, and auto-closing indentations. It also shows how to integrate a pre-built lexer using WithLexerbuilder. ```csharp // UseMemoization(bool use = true) // UseBroadenTokenWindow(bool use = true) // UseAutoCloseIndentations(bool use = true) // WithLexerbuilder(IFluentLexerBuilder lexerBuilder) ``` -------------------------------- ### C# Generic Comment Definition Source: https://github.com/b3b00/csly/wiki/GenericLexer Declares a generic comment token that supports single-line, multi-line start, and multi-line end delimiters. The specific delimiters are defined by the language context (e.g., '//', '/*', '*/' for C-derived languages). This comment is removed from the token stream by default. ```csharp [Comments(singleline, multilinestart, multilineend)] COMMENT ``` -------------------------------- ### C# Labels, Channels, and Modes Configuration Source: https://github.com/b3b00/csly/wiki/lexer-fluent-api Illustrates how to configure labels, channels, and modes for tokens. This includes assigning labels for different languages, setting the token channel, and managing modes for context-sensitive parsing. ```csharp var lexerBuilder = FluentLexerBuilder.NewBuilder(); lexerBuilder.WithLabel("en", "Identifier"); lexerBuilder.OnChannel(Channels.Main); lexerBuilder.PushToMode("money"); lexerBuilder.PopMode(); ``` -------------------------------- ### Define Extension Enum with Lexeme Attributes Source: https://github.com/b3b00/csly/wiki/GenericLexerExtension Shows how to define an enum for extension tokens with LexemeAttribute decorators. In this example, a DATE token is marked as an Extension type and a DOUBLE token is marked as a Double type to handle overlapping lexeme patterns. ```C# public enum Extensions { [Lexeme(GenericToken.Extension)] DATE, [Lexeme(GenericToken.Double)] DOUBLE, } ``` -------------------------------- ### Configure and Build Fluent Lexer Source: https://github.com/b3b00/csly/wiki/parser-fluent-api Demonstrates how to use FluentLexerBuilder to define token types, ignore whitespace and end-of-line characters, and build a lexer. The lexer can then be used to tokenize input strings. Assumes pre-defined token types and output classes. ```csharp public class ExprOutput { public string Name {get; set;} } public enum ExprToken { ID, INT } public class FluentExprLexer { public void ExprLexerTest() { var lexerBuilder = FluentLexerBuilder.NewBuilder(); BuildResult> lexerResult = lexerBuilder .IgnoreEol(true) // ignore end of lines .IgnoreWhiteSpace(true) // ignore white spaces .AlphaId(MyToken.ID) .Int(MyToken.INT) .Build("en"); if (lexerResult.IsOk) { ILexer lexer = lexerResult.Result; lexer.Tokenize(@" identifier 42 # comment $ money content € "); } } } ``` -------------------------------- ### C# Combined String Lexeme Definitions Source: https://github.com/b3b00/csly/wiki/GenericLexer Demonstrates how to define multiple string lexeme patterns for the same token type within a single lexer. This example allows matching both single-quoted strings with a backslash escape and single-quoted strings where a single quote escapes itself, as well as double-quoted strings with a backslash escape. ```csharp [Lexeme(GenericToken.String,"'","'")] [Lexeme(GenericToken.String,"'","\\")] STRING ``` -------------------------------- ### Implement Expression Parser Visitor Methods in C# Source: https://github.com/b3b00/csly/wiki/Getting-started Implements the visitor methods for the expression parser grammar. These methods correspond to the production rules and define how the syntax tree is traversed and evaluated. The return type is the result of the parsed expression (int), and parameters match the rule's clauses. ```csharp public class ExpressionParser { [Production("expression: INT")] public int intExpr(Token intToken) { return intToken.IntValue; } [Production("expression: term PLUS expression")] public int Expression(int left, Token operatorToken, int right) { return left + right; } [Production("term: INT")] public int Expression(Token intToken) { return intToken.IntValue; } } ``` -------------------------------- ### C# Lexer and Parser Construction with Fluent Builders Source: https://github.com/b3b00/csly/wiki/error-messages Demonstrates how to build a lexer and an EBNF parser using FluentLexerBuilder and FluentEBNFParserBuilder. It covers ignoring whitespace and case, defining keywords, and setting up production rules. The parser is then used to parse an input string and check for errors. ```csharp var lexer = FluentLexerBuilder.NewBuilder() .IgnoreEol(true) .IgnoreWhiteSpace(true) .IgnoreKeywordCase(true) .Keyword(ContextualToken.A, "a") .Keyword(ContextualToken.B, "b") .Keyword(ContextualToken.C, "c"); var build = FluentEBNFParserBuilder.NewBuilder(new FluentTests(), "root", "en") .Production("root : A B C", (objects => "ok")) .WithLexerbuilder(lexer) .BuildParser(); var parsed = build.Result.Parse("foo bar baz"); if (parsed.IsError) { foreach(var error in parsed.Errors) { // do something with errors } } ``` -------------------------------- ### Define a Production Rule Source: https://github.com/b3b00/csly/wiki/parser-fluent-api Demonstrates how to define a simple production rule using the Production method. This method takes a rule string and a callback function that is executed when the rule matches. The callback receives parsed elements as objects that need to be cast. ```csharp public class ExprParser { // .... } var parserBuilder = FluentParserBuilder.NewBuilder(new ExprParser(), "root", "en"); parserBuilder.Production("root : ID") ``` -------------------------------- ### Build and use a generic lexer with CSLY LexerBuilder Source: https://github.com/b3b00/csly/blob/dev/lexer.md Constructs a lexer instance from a token enum using LexerBuilder.BuildLexer(). The returned ILexer interface provides the Tokenize method to process source code. This approach allows explicit lexer creation separate from parser instantiation. ```csharp ILexer lexer = LexerBuilder.BuildLexer(); var tokens = lexer.Tokenize(source).ToList(); ``` -------------------------------- ### Parse with Context - Variable Resolution in C# Source: https://github.com/b3b00/csly/wiki/Implementing-a-BNF-parser Demonstrates how to parse expressions with context by extending the production rule for identifiers. The OperandVariable method resolves variable names from a Dictionary context, returning the variable value if found or 0 otherwise. This example shows calling ParseWithContext with an expression string and a context dictionary containing variable mappings. ```C# [Production("primary_value : IDENTIFIER")] public int OperandVariable(Token identifier, Dictionary context) { if (context.ContainsKey(identifier.Value)) { return context[identifier.Value]; } else { return 0; } } var res = parser.ParseWithContext("2 + a", new Dictionary {{"a", 2}}); ``` -------------------------------- ### Configure Date and Keyword Lexemes in Csly Source: https://github.com/b3b00/csly/wiki/GenericLexer Explains how to configure date and keyword lexemes. Date lexemes require format and separator specifications. Keywords are treated as special identifiers and can be configured with specific patterns for performance. ```C# [Lexeme(GenericToken.Date, DateFormat.YYYYMMDD, '/')] [Date(DateFormat.YYYYMMDD, '/')] [Lexeme(GenericToken.KeyWord, "if"] // Static keyword mapping [Keyword("if")] [Lexeme(GenericToken.KeyWord, "while")] // Dynamic keyword pattern [Keyword("while")] ``` -------------------------------- ### FSMBuilder: Marking Node Names in C# Source: https://github.com/b3b00/csly/wiki/GenericLexerExtension This C# code demonstrates how to assign a name to a node in the finite state machine (FSM) using the `FSMBuilder`. The `Mark(string name)` method assigns a specific name to the current node, allowing it to be referenced later using the `GoTo` method for defining transitions and structuring the FSM. ```csharp fsmBuilder.Mark("stateName"); ``` -------------------------------- ### EBNF Alternate Choices in C# Source: https://context7.com/b3b00/csly/llms.txt Illustrates the use of alternate choices within EBNF production rules using the `[choice1 | choice2]` syntax. This allows defining multiple parsing options for a single rule without separate productions. It covers terminal choices and choices with repetition. ```csharp public class AlternateChoiceParser { // Terminal choice [Production("choice : [ a | b | c ]")] public string Choice(Token token) { return token.Value; } // Choice with repetition modifier [Production("choices : [ a | b | c ]*")] public string MultipleChoices(List> tokens) { return string.Join(",", tokens.Select(t => t.Value)); } // Discarded choice (only for terminals) [Production("statement : x [ SEMICOLON | COMMA ] [d] y")] public string Statement(Token x, Token y) { return $"{x.Value}{y.Value}"; } } ``` -------------------------------- ### FSMBuilder: Adding Character Transitions in C# Source: https://github.com/b3b00/csly/wiki/GenericLexerExtension This C# code shows how to add a transition in the `FSMBuilder` that moves to a new state based on a specific character. The `Transition(char token, Func precondition)` method defines a transition triggered by a given character `token`. An optional `precondition` predicate can be provided to further refine the transition logic based on the already parsed string. ```csharp fsmBuilder.Transition('a', s => s.Length > 0); ``` -------------------------------- ### FSMBuilder: Adding Unconditional Transitions in C# Source: https://github.com/b3b00/csly/wiki/GenericLexerExtension This C# code shows how to add an unconditional transition using the `FSMBuilder`. The `AnyTransition(Func precondition)` method creates a transition that will always be taken regardless of the input character. An optional `precondition` predicate can be provided to conditionally execute this transition. ```csharp fsmBuilder.AnyTransition(s => true); ``` -------------------------------- ### C# Compact Expression Parser Attributes Source: https://context7.com/b3b00/csly/llms.txt Demonstrates using shorter, more concise attribute names for defining expression parsing rules in C#. This includes infix, prefix operations, and operands, aiming for cleaner code while maintaining the same functionality as the more verbose versions. This approach reduces boilerplate code for common parsing scenarios. ```csharp public class CompactExpressionParser { [Infix("PLUS", Associativity.Right, 10)] [Infix("MINUS", Associativity.Left, 10)] public int AddSubtract(int left, Token op, int right) { return op.TokenID == ExprToken.PLUS ? left + right : left - right; } [Infix("TIMES", Associativity.Right, 50)] [Infix("DIVIDE", Associativity.Left, 50)] public int MultiplyDivide(int left, Token op, int right) { return op.TokenID == ExprToken.TIMES ? left * right : left / right; } [Prefix("MINUS", Associativity.Right, 100)] public int Negate(Token op, int value) { return -value; } [Operand] [Production("operand : INT")] public int Operand(Token value) { return value.IntValue; } } ``` -------------------------------- ### Enable Broaden Token Window Optimization in CSLY Parser Source: https://github.com/b3b00/csly/wiki/optin-optimizations The BroadenTokenWindow attribute allows the parser to look ahead up to two tokens when choosing production rules, potentially limiting backtracking. Performance gains are grammar-dependent and may be outweighed by the UseMemoization optimization. ```C# [BroadenTokenWindow] public class MyParser { // parser definition } ``` -------------------------------- ### Parse an Addition Expression with CSLY in C# Source: https://github.com/b3b00/csly/wiki/Getting-started Demonstrates how to parse a simple addition expression string using a pre-built CSLY parser. It retrieves the parser, calls the Parse method, and checks for errors, printing the result or error messages. ```csharp public class SomeTest { public void TestCSLY() { string expression = "42 + 42"; var Parser = SomeClass.GetParser(); var r = Parser.Parse(expression); if (!r.IsError) { Console.WriteLine($"result of <{expression}> is {(int)r.Result}"); // outputs : result of <42 + 42> is 84" } else { if (r.Errors != null && r.Errors.Any()) { // display errors r.Errors.ForEach(error => Console.WriteLine(error.ErrorMessage)); } } } } ``` -------------------------------- ### Parse with Context Source: https://github.com/b3b00/csly/wiki/Implementing-a-BNF-parser Illustrates parsing source code with a symbolic context. The `Parser.ParseWithContext(source, context)` method is thread-safe and ensures that the correct context is applied during parsing, which is useful for state-dependent parsing scenarios like variable declarations. ```csharp // Assuming 'parser' is an initialized parser instance // and 'context' is an object representing the parsing context string source = "variable_declaration = 10;"; var context = new MyContext(); // Replace MyContext with your actual context type ParseResult result = parser.ParseWithContext(source, context); if (!result.IsError) { // Process the successful parse result Console.WriteLine("Parsing successful with context."); } else { // Handle parsing errors result.Errors.ForEach(error => Console.WriteLine(error.ErrorMessage)); } ``` -------------------------------- ### Define Arithmetic Expressions with C# FluentParserBuilder Source: https://github.com/b3b00/csly/wiki/parser-fluent-api This C# code snippet demonstrates building a parser for arithmetic expressions using FluentParserBuilder. It defines left and right associative infix operators like '+' and '-', and prefix operators like '-'. It also includes an operand rule for integer values and visitor callbacks to perform the actual calculations. The parser is then used to evaluate a sample expression. ```csharp var parserBuilder = FluentParserBuilder.NewBuilder(new ExprParser(), "root", "en"); ParseResult parserResult = parserBuilder .Production("root : ExprParser_expressions",) .Left("'-'",10(object[] args) => (int)args[0] - (int)args[2]) .Right("'+'",10 (object[] args) => (int)args[0] + (int)args[2]) .Left("'/'",50(object[] args) => (int)args[0] / (int)args[2]) .Right("'*'",50 (object[] args) => (int)args[0] * (int)args[2]) .Prefix("'-'",100 (Tobject[] args) => -(int)args[1]) .Operand("value : INT",(object[] args) => ((Token)args[0]).IntValue) .BuildParser(); if (parserResult.IsOk) { IParser parser = parserResult.Result; var result = parser.Parse("-1 + 2 * 3"); // result.IsOk should be true // result.Result should be equal to -1 + 2 * 3 = -1 + 6 = 5 } ``` -------------------------------- ### Configurable Lexeme Construction in Csly Source: https://github.com/b3b00/csly/wiki/GenericLexer Illustrates the construction of configurable lexemes in Csly, specifically for Keyword and SugarToken. These lexemes take two parameters: the mapped GenericToken and the specific value or pattern for the token. ```C# // Example for Keyword lexeme [Lexeme(GenericToken.KeyWord, "if")] // Example for SugarToken lexeme [Lexeme(GenericToken.SugarToken, "$")] ``` -------------------------------- ### Add Date Extension with FSM Transitions and Preconditions Source: https://github.com/b3b00/csly/wiki/GenericLexerExtension Implements a complete extension handler that adds date pattern matching to an existing lexer. Uses FSM builder to create transitions from the double state to a new date state with a precondition (CheckDate) that validates the 'dd.mm' format before proceeding. Includes a callback to mark matched dates and transitions for year digits with proper node marking. ```C# public static void AddExtension(Extensions token, LexemeAttribute lexem, GenericLexer lexer) { if (token == Extensions.DATE) { // precondition to check if starting date matches the dd.mm format Func checkDate = (string value) => { bool ok = false; if (value.Length==5) { ok = char.IsDigit(value[0]); ok = ok && char.IsDigit(value[1]); ok = ok && value[2] == '.'; ok = ok && char.IsDigit(value[3]); ok = ok && char.IsDigit(value[4]); } return ok; } // callback on end_date node NodeCallback callback = (FSMMatch match) => { // this store the token id the the FSMMatch object to be later returned by GenericLexer.Tokenize match.Properties[GenericLexer.DerivedToken] = Extensions.DATE; return match; }; var fsmBuilder = lexer.FSMBuilder; fsmBuilder.GoTo(GenericLexer.in_double) // start a in_double node .Transition('.', CheckDate) // add a transition on '.' with precondition .Mark("start_date") // set the node name .RangeTransition('0','9') // first year digit .Mark("y1") .RangeTransition('0','9') // second year digit .Mark("y2") .RangeTransition('0','9') // third year digit .Mark("y3") .RangeTransition('0','9') // fourth year digit .Mark("y4") .End(GenericToken.Extension) // mark as ending node .CallBack(callback); // set the ending callback } } ```