NLT generator — macro expansions

As moving towards “grand finale” the generator becomes more mature accidentally I could say. I didn’t anticipate such scenario until I met one — consider such lexer rule:

GRAMMAR "{" 
{ 
  $token = TokenEnum.LBRACE;
  lexer.PushState(StateEnum.CODE);
  str_buf = new StringBuilder();
};
CODE "{"
{
  str_buf.Append($text);
  lexer.PushState(StateEnum.CODE);
};

When the lexer sees left brace in grammar it should switch to CODE mode and create string buffer. When it sees the same brace in CODE mode it should simply add it to the buffer and push CODE state again — it helps unwinding states when right braces are met.

This works nicely because once in CODE mode we treat all nested code blocks as one code body. Nothing wrong with that but when we need to differentiate between nested blocks such approach will fail — I was struggling a little when writing rules for scanning macros in NLT. Macro is written as:

$(variable : expression : expression)

Each expression can be macro as well, and of course within expression regular parentheses can be used. There is no problem whether lexer should be in MACRO or CODE state when closing parenthesis is found, the problem is whether state should be of this or that MACRO (inner or outer).

So I came up with idea of associating nesting counter with state — here instead of pushing MACRO when left parenthesis is found, I increase the counter, and I decrease it on right parenthesis. So when counter hits zero I know for sure it is inner MACRO boundary.

Yes, you could do it by yourself in your code, but why do it every time, when it can be done once in the framework.

The real change though is macro support. Previously I had to write:

formal_param -> attr:param_attr? colon:COLON? ...
{ 
  new FunctionParameter(
    currCoords(), 
    colon!=null ? NamedEnum.Yes : NamedEnum.No,
    attr!=null ? attr.Value : AttrEnum.Constant,
    ...

With macro expansion I can be more concise:

formal_param -> attr:param_attr? colon:COLON? ...
{ 
  new FunctionParameter(
    currCoords(), 
    $(colon : NamedEnum.Yes : NamedEnum.No),
    $(attr.Value : AttrEnum.Constant),
    ...

The created code is shorter too, because NLT generator extracts only needed part from the macro — as the side effect the productions are optimized as well.

Macro can be written in three ways:

$(variable)
$(variable : expression)
$(variable : expression1 : expression2)

In the first form generator produces “true” if the variable is present, “false” otherwise. In the second — it creates variable itself if it is present, or given expression if not. And in the third form it puts the first or the second expression depending if the variable is present.

There is one shortcut when the variable is a compound object — instead of writing:

$(var : var.property : expression2)

which is perfectly legal just long, one can use shorter syntax:

$(var.property : expression2)

Calling an object’s method is also OK, but a function is not:

$(foo(var) : expression2) // WRONG!

There is one optimization left — detecting if the given variable was used at all in the code and if it is a case not passing it. I will handle it and other speed ups after making NLT generator a true generator.

Tagged , , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: