Access to the Compiler and Interpreter (SDK)
The compiler in MAXScript can be invoked to compile source in files or strings into executable code. You gain access to it by creating an instance of the Parser class with the following constructor:
Parser(CharStream* errout);
Debugging and error output is sent to the given 'errout' charstream. Typically, you would specify the current standard output for this, which defaults to the Listener window, for example:
Parser* reader = new Parser (thread_local(current_stdout));
Any number of Parsers can be instantiated and active; they each encapsulate a separate compile environment including lexical scoping tables and source stream tracking.
The following Parser member functions are used to compile source code in a CharStream instance, either a FileStream or a StringStream (see the Stream I/O topic for details on constructing these streams).
Value* compile(CharStream* stream);
Compile the next complete expression in the given stream. The stream pointer is left at the end of the expression compiled, so you can repeatedly call compile() to step through source an expression at a time. Remember that MAXScript is expression-based, so 'expression' here means any MAXScript construct including all the control structures, function definitions, blocks, utility definitions, etc.
Value* compile_factor(CharStream* stream);
Compile the next MAXScript <operand> in the source stream. See the MAXScript Grammar topic for the exact definition of <operand>. The MAXScript function readValue() uses compile_factor() to get the next value in a text file, so it is capable of reading global variable references, array indexes, property references, pathnames, etc. as well as simple literals.
Value* compile_all(CharStream* stream);
Compile the whole stream into one piece of executable code.
The compile functions all return a MAXScript value containing the executable code, usually a CodeTree instance, but possibly a Number or String if the expression you give it to compile is a simple literal. The code is executed by invoking the Value virtual function eval() on it. For example,
Value* code = reader->compile(source); // compile an expr
result = code->eval(); // execute it
In the case of simple values, this is a no-op and just returns the value. The code returned from the compiler is always a first-class MAXScript value that you can store for later execution or pass back to running scripts.
Here is the low-level file_in() function in MAXScript, showing how the compiler and interpreter are used.
Value* file_in(CharStream* source, int quiet)
{
three_typed_value_locals(Parser* parser, Value* code, Value* result);
CharStream* out = thread_local(current_stdout);
vl.parser = new Parser (out);
if (!quiet)
source->log_to(out);
// loop through file compiling and evaluating all expressions
try
{
source->flush_whitespace();
while (!source->at_eos())
{
vl.code = vl.parser->compile(source);
vl.result = vl.code->eval();
if (!quiet)
vl.result->print();
source->flush_whitespace();
}
source->close();
}
catch (...)
{
// catch any errors and tell what file we are in if any
out->puts("Error occurred during fileIn: ");
source->sprin1(out);
out->puts("\n");
throw;
}
// return last expression in stream as result
if (vl.result == NULL)
vl.result = &ok;
return_value(vl.result);
}