Open-source image
open-source
Hime logo

Table of content

Semantic Actions

Semantic actions are pieces of behaviors that are introduced in syntactic rules and executed when the parser encountered them. In the Hime Parser Generator, we strongly believe in the separation of concerns and we do not write the content of the semantic actions within the syntactic rules. Semantic actions are marked in the syntactic rules, but must be specified in the targeted language. For example:

variable -> A @MyAction B ;

Here, the @MyAction represents the semantic action. It is placed directly after the A element in the rule. The meaning of this rule is that when the parser finds itself in this rule and immediately after it encounters a A token, it will execute the @MyAction semantic action. The @MyAction semantic action is itself a piece of code that the user specifies and that will be called by the parser in this situation. In a sense, they are callbacks that users define and that are given to the parser. In C#, a semantic action is implemented as a method with the following signature:

void MyAction(Hime.Redist.Symbol head, Hime.Redist.SemanticBody body);

There should be no return value. The semantic action is given two parameters:

  • head is the symbol corresponding to the head of the rule in which the parser is in.
  • body is a handle giving access to the symbols found by the parser for the rule so far. Here, there will be only one, a A token.

Refer to the API documentation for complete details about the parameters. Note that if the semantic action is placed in the middle of the rule, the parser will only give in the body parameter the symbols that are before the semantic action. This is due to the fact that the parser is precisely at this point in the rule.

When at least one semantic action is defined in the grammar, the generator will output a dedicated inner class in the parser's class. This will take the form:

public class MyGrammarParser : LRkParser
{
	public class Actions
	{
		// by default, the semantic actions do nothing
		public virtual void MyAction(Symbol head, SemanticBody body) { }
	}

	// Initializes this parser with custom actions
	public MyGrammarParser(MyGrammarLexer lexer, Actions actions) { }
}

When custom actions should be implemented, the inner Actions class can be extended and the default actions overriden. Then an instance of the derived class can be passed to the parser's constructor. For example:

public class CustomActions : MyGrammarParser.Actions
{
	public override void MyAction(Symbol head, SemanticBody body)
	{
		Console.WriteLine("Executing MyAction with head {0} and {1} elements in the body", head.Value, body.Length);
	}
}

// then
MyGrammarLexer lexer = new MyGrammarLexer("some input");
// build the parser with the custom actions
MyGrammarParser parser = new MyGrammarParser(lexer, new CustomActions());
// parse
parser.Parse();