Game Development Community

CMDscan.l and CMDgram.y - help please!

by Konrad Kiss · in Torque Game Engine · 03/14/2009 (1:05 pm) · 6 replies

I'd like Torque to be able to accept inline function definitions as parameters of another function.

Right now, we use callbacks like this:

function testFunction() {
   %retval = myFuncCall(%variable, "callbackFunction");
}

function callbackFunction(%someparam) {
   echo(%someparam);
}

What I'm looking for is this:

function testFunction() {
   %retval = myFuncCall(%variable, function(%someparam) {
      echo(%someparam);
   });
}

How hard would it be to modify the existing parser to allow this? Does anyone have a good solution to achieve what I'm looking for? Did anyone ever find a solution to this?

I need this very badly to not lose control over my script sources. I am about to deal with a great number of callbacks, so this would mean a lot of help to me.

I'd be very thankful to anyone who'd help me solve this! Thanks in advance, any reply is greatly appreciated!

#1
03/15/2009 (10:32 am)
Well, I dived into some Flex / Bison and eventually learned a few things about CMDscan.l and CMDgram.y.

I've managed to make the parser accept an unnamed function definition as an expression. It gets a generated name that's based on an incremented U32. I can pass the name back to the function call in which the callback is defined inline. So when I call the example above, myFuncCall would actually receive the generated function name as the second parameter, so it is able to use that name - possibly in an eval.

Right now, my problem is that I still need to register the function. I saw two solutions for this:

a) I call FunctionDeclStmtNode::alloc with the generated function name, a NULL namespace, and the parameters and statements that are passed. The problem with this approach is that $$ must be a statement, while $$ is an expression where this is being parsed. So FunctionDeclStmtNode::alloc should return an expression, but it looks as if statements and expressions here are not really compatible.

b) I pass the whole function declaration to newCodeBlock->compileExec as a char *. But I have no idea how to get the currently matching nodes as a string.

| rwDEFINE '(' var_list_decl ')' '{' statement_list '}'
      { 
		autoFuncID++;

		char * bfr = "autoFN_";
		char uib[22] = "";
		_itoa(autoFuncID, uib, 22);
		dStrcat(bfr, uib);
		StringTableEntry autoFnName = StringTable->insert(bfr);

		CodeBlock *newCodeBlock = new CodeBlock();
		StringTableEntry name   = StringTable->insert(autoFnName);
		
		char *scrpt = "function ";
		dStrcat(scrpt, bfr);
		dStrcat(scrpt, "(");
		dStrcat(scrpt, );      // this needs a string represenation
		dStrcat(scrpt, ") {");
		dStrcat(scrpt, );      // so does this one
		dStrcat(scrpt, "}n");
		
		newCodeBlock->compileExec(name, scrpt, false, 0);

		delete [] scrpt;

		$$ = ConstantNode::alloc(bfr);

		delete [] bfr;
	  }

If anyone could be of help here, I'd be extremely thankful.

If someone can point me in the right direction, I can throw in Cliff Construction Kit licenses as compensation.

Thanks a lot in advance,
--Konrad
#2
03/18/2009 (7:30 pm)
Can't you add them to a list, then at the end of parsing, before you move to bytecode generation, append them to the AST? This would probably be outside the parser.
#3
03/19/2009 (2:29 am)
Ben, thanks a lot for your help. To tell the truth, I'm not 100% sure I get what you mean, but I did my best to understand :)

My biggest problem is that I'm unable to see the parsing plan as a whole. I am now familiar with the CMDscan and CMDgram files, but I don't quite get it how these are integrated into Torque. Because of this, I am not able to tell what the scopes of the different variables are, and that made me stand in one place for a while now.

I see where the bitcode is generated, what I'm looking for is CodeBlock::compileExec, right?

I've found the parsing code in CodeBlock that will make the ConsoleParser parse the code:

smCurrentParser->setScanBuffer(string, fileName);
	smCurrentParser->restart(NULL);
	smCurrentParser->parse();

(btw, really cool solution to let people use any parser for Torque)

Anyway, so that code creates my statementList. Are you saying that I should create another list like statementList, and use that to define embedded functions through appending any such code - something like this? :

| rwDEFINE '(' var_list_decl ')' '{' statement_list '}'
      { 
		$$ = ConstantNode::alloc(generated_name);

		inlineFnList->append(the whole function);
	  }

And so in compileExec, I compile the new list after the normal statementList. Plus I have to modify the function definition to allow for noname functions which get registered with a generated name. So that way I don't have to ever poke the strings.

Well, at first I was gasping for air after your hint, but it did eventually lead me here, so many thanks. If I misunderstood you please let me know. I have serious doubts that I will be able to get through this, so any further hints are very welcome. Until then, I will give this a try, and see where this takes me.

Many thanks!
#4
03/19/2009 (5:20 am)
Looks like it works! Thanks Ben, I'll make a resource of this soon.
#5
03/19/2009 (12:52 pm)
Hahaha.... Awesome! I'm glad it worked out! Having anonymous functions is really useful, it's really cool to see it in Torque. Nice work, Konrad! :)

(Tom Bampton did the multiparser support, one of many cool features he has added to the engine.)
#6
03/20/2009 (1:57 pm)
:) Wasn't very likely, huh?

The resource: http://www.garagegames.com/community/blogs/view/16723

Thanks again Ben.