Extending the language syntax
Creating custom statements
Creating a custom statement can be summarized as follows:
- Create a (direct or indirect) subclass of the AddyScript.Ast.AstNode class (moreover, you will subclass AddyScript.Ast.Statements.Statement or AddyScript.Ast.Expressions.Expression).
- Override the AcceptTranslator method of your class; this may require the addition of a new method to the AddyScript.Translators.ITranslator interface (e.g.: if your class is called Unless, the body of its AcceptTranslator method could be something like
translator.TranslateUnless(this);thus requiring the addition of a TranslateUnless method in the ITranslator interface). - Create a stub for the eventually newly created method in any class implementing the ITranslator interface.
- If your statement introduces new terminal symbols in the language (like an unless keyword), add corresponding members to the AddyScript.Parsers.TokenID enumeration (e.g.: you could add a KW_Unless member representing the unless keyword). Update the AddyScript.Parsers.Token class to take your tokens into account in some of its method (like the static version of ToString). Change the NextToken method of the AddyScript.Parsers.Lexer class to recognize your tokens. For a keyword, you'll just have to add it to the keywords registry. To do so, open the AddyScript.Parsers.Keyword.cs source file, find the class initializer and add a line of code to it like the following one:
Register("unless", TokenID.KW_Unless);. - Update the AddyScript.Parsers.Parser class to recognize your statement (or the AddyScript.Parsers.ExpressionParser class if you are creating a new kind of expression). This is typically done in two stages: first, find the Statement method and add a case label for your statement (something like:
case TokenID.KW_Unless: return Unless();). secondly, define the method that recognizes your statement. - Recognizing a statement is somehow straightforward. Assuming that your statement is a sequence of terminal and non terminal symbols, you just have to ensure that each of those symbols appears at the right place. To ensure that a terminal symbol (a token) is where it should be, use the Match method of the parser. The TryMatch method does the same job except that it does not throw an exception when the expected token is not matched. Use it for optional tokens. There are also variants of Match and TryMatch (respectively MatchAny and TryMatchAny) which accept several tokens and try to match any of them. For a non-terminal symbol, use the method that recognizes that symbol; such a method generally has the same name than the corresponding symbol, expects no parameter and returns the recognized non-terminal symbol upon completion. Don't forget to set the location of your symbol in the source file upon completion. So recognizing the unless-statement could be done like this:
- The BasicParser class provides some helper methods to recognize a sequence of non-terminal symbols of the same kind or a non-null instance of some symbol.
- Once you've recognized your statement, the next stage is to interpret it. It's up to you to define the logic of your statement. But an unless-statement would probably be similar to an if-statement, so the TranslateUnless method of the Interpreter class could look like this:
| C# | |
|---|---|
That's all!
Going further
Well, to go further in AddyScript extension, just dive into the source code and discover how all the engine works. Don't forget to share your experience with us.