mirror of
https://github.com/Sei-Lisa/LSL-PyOptimizer
synced 2025-07-01 23:58:20 +00:00
401 lines
24 KiB
HTML
401 lines
24 KiB
HTML
<!DOCTYPE html>
|
|
<html><head>
|
|
<title>LSL PyOptimizer project</title>
|
|
</head><body>
|
|
|
|
<h1><a id="lsl-pyoptimizer"></a>LSL PyOptimizer</h1>
|
|
|
|
<h2><a id="introduction"></a>Introduction</h2>
|
|
|
|
<p>LSL PyOptimizer is a LSL2 script optimizer written for Python 2. Currently it only supports code memory optimization (no speed optimization), only for Mono (no LSO), and only for the Second Life flavour of LSL (no OpenSim etc.).</p>
|
|
|
|
<p>The original LSL compiler does not do any optimizations whatsoever. Either the programmer does the optimization, or the scripts take more memory than necessary when writing something as simple as <code>a = -1;</code> (yes, the sign takes code memory!).</p>
|
|
|
|
<p>Given the 64K memory limit that applies to Mono scripts, this becomes a problem, leading to either difficult to read scripts, if optimized by hand, or in case of large scripts, to code taking valuable memory that could be used to embed more code or data.</p>
|
|
|
|
<p>The aim of this program is to act as a filter that performs the optimizations automatically, letting the programmer focus on writing readable code.</p>
|
|
|
|
<p>It also implements several syntax extensions to help improving the readability of scripts and the productivity of the programmer. It works well when combined with a C preprocessor such as <i>Boost::Wave</i> (the one embedded in Firestorm) or <code>cpp</code>.</p>
|
|
|
|
<p>Firestorm does already incorporate an optimizer. However it is limited to removing unused global variables and functions, and does so by simple string analysis, not by syntactic analysis (e.g. if a local variable with the same name as a global is defined, the global isn't optimized out even if not used). In contrast, the program presented here does full syntax analysis and implements many more optimizations, including removing unused locals, simplifying many expressions, removing dead code, and more.</p>
|
|
|
|
<h2><a id="status"></a>Status</h2>
|
|
|
|
<p>It is still considered alpha code. Command line options are still subject to change, and new features may be added without notice. Please help by reporting any bugs you find, using the <a href="https://github.com/Sei-Lisa/LSL-PyOptimizer/issues">project's issue tracker at GitHub</a>.</p>
|
|
|
|
<h2><a id="features"></a>Features</h2>
|
|
|
|
<p><a href="#syntax-extensions">Syntax extensions supported:</a></p>
|
|
|
|
<ul>
|
|
<li><a href="#extensions-break-and-continue"><code>break</code> and <code>continue</code> statements.</a></li>
|
|
<li><a href="#extensions-expressions-in-globals">Expressions in globals.</a></li>
|
|
<li><a href="#extensions-extended-assignment-operators">Extended assignment operators</a> <code>&=</code>, <code>|=</code>, <code>^=</code>, <code><<=</code>, <code>>>=</code>.</li>
|
|
<li><a href="#extensions-concatenation-of-key-and-string">Concatenation of key and string.</a></li>
|
|
<li><a href="#extensions-c-like-string-juxtaposition">C-like string juxtaposition</a>: "str1" "str2" produces "str1str2".</li>
|
|
<li><a href="#extensions-type-cast-extension">Allow type-cast of unary expressions that are not postfix</a>, e.g. <code>(string)++x</code>.</li>
|
|
<li><a href="#extensions-multiple-labels-with-the-same-name">Allow multiple labels with the same name in the same function.</a></li>
|
|
<li><a href="#extensions-switch-statements"><code>switch()</code> statements</a>, for compatibility with Firestorm.</li>
|
|
<li><a href="#extensions-lazy-lists">Lazy list syntax</a> (<code>identifier[index]</code>), for compatibility with Firestorm.</li>
|
|
</ul>
|
|
|
|
<p><a href="#optimizations">Optimizations supported:</a></p>
|
|
|
|
<ul>
|
|
<li><a href="#optimizations-constant-folding-and-expression-simplification">Constant folding, expression and statement simplification</a>.</li>
|
|
<li><a href="#optimizations-dead-code-removal">Dead code removal</a>, including unused global and local variables.</li>
|
|
<li><a href="#optimizations-shrinking-identifiers">Shrinking identifiers.</a></li>
|
|
<li><a href="#optimizations-floats">Floats to integers where possible.</a></li>
|
|
<li><a href="#optimizations-signs">Signs in numbers.</a></li>
|
|
<li><a href="#optimizations-string-constant-folding">String constant concatenation.</a>
|
|
</ul>
|
|
|
|
<p>The next sections explain these features in detail.</p>
|
|
|
|
<h2><a id="Download"></a>Download</h2>
|
|
|
|
<p>The project is hosted on GitHub. The latest version is available at: <a href="https://github.com/Sei-Lisa/LSL-PyOptimizer">https://github.com/Sei-Lisa/LSL-PyOptimizer</a></p>
|
|
|
|
<p>In order to run the program, you need <a href="https://www.python.org/downloads/">Python 2</a> installed. <b>Python 3 will not work. Download and install Python 2</b> if you don't have it already.</p>
|
|
|
|
<h2><a id="also-on-this-page"></a>Also on this page</h2>
|
|
|
|
<ul>
|
|
<li><a href="#using-the-program">Using the program</a></li>
|
|
<li><a href="#other-future-plans">Other future plans</a></li>
|
|
<li><a href="#license">License</a></li>
|
|
<li><a href="#author">Author</a></li>
|
|
</ul>
|
|
|
|
<hr>
|
|
|
|
<h2><a id="syntax-extensions"></a>Syntax extensions</h2>
|
|
|
|
<h3><a id="extensions-break-and-continue"></a>
|
|
<code>break</code> and <code>continue</code>
|
|
</h3>
|
|
|
|
<p>Support for <code>break</code> and <code>continue</code> statements, working as their C equivalents. It also supports <code>break n;</code> and <code>continue n;</code> for a constant integer <code>n</code> which indicates the number of nested loops to exit from (in the case of <code>break</code>) or to <code>continue</code> at. The default <code>n</code> when not specified is 1, meaning to break or continue at the current loop.</p>
|
|
|
|
<h3><a id="extensions-expressions-in-globals"></a>Expressions in globals</h3>
|
|
|
|
<p>Allow arbitrary expressions in globals, as long as they resolve to a single constant. The optimizer will evaluate the expression and substitute the result in the final script. For example, you can have a line like:</p>
|
|
|
|
<pre><code> float a = llPow(llSin(40*DEG_TO_RAD), 2);
|
|
</code></pre>
|
|
|
|
<p>in the globals section, and the optimizer will output the line:</p>
|
|
|
|
<pre><code> float a = 0.41317588;
|
|
</code></pre>
|
|
|
|
<p>instead. Needs constant folding optimization to be enabled. If the expression does not resolve to a constant, a warning will be emitted, and the compilation will fail at that line.</p>
|
|
|
|
<h3><a id="extensions-extended-assignment-operators"></a>Extended assignment operators</h3>
|
|
|
|
<p>C-like <code>&=</code>, <code>|=</code>, <code>^=</code>, <code><<=</code>, <code>>>=</code> assignment operator support.</p>
|
|
|
|
<h3><a id="extensions-concatenation-of-key-and-string"></a>Concatenation of key and string</h3>
|
|
|
|
<p>In LSL, keys are automatically cast to string in most cases. For example:</p>
|
|
|
|
<pre><code> llOwnerSay(llGetOwner());
|
|
</code></pre>
|
|
|
|
<p>works perfectly. However, one prominent case where that does not happen automatically is in string concatenation. Confusingly, while the above works, this doesn't:</p>
|
|
|
|
<pre><code> llOwnerSay("Your key is: " + llGetOwner());
|
|
</code></pre>
|
|
|
|
<p>and instead produces a type mismatch error. This syntax extension allows you to concatenate strings and keys, making the type cast transparent.</p>
|
|
|
|
<h3><a id="extensions-c-like-string-juxtaposition"></a>C-like string juxtaposition.</h3>
|
|
|
|
<p>For example <code>"a" "b"</code> resolves to the string <code>"ab"</code>. Very useful when combined with preprocessor macros, for not needing to add strings together to form a larger string. For example:</p>
|
|
|
|
<pre><code>#define VERSION "1.13"
|
|
#define REVISION "b"
|
|
...
|
|
llOwnerSay("Program version " VERSION
|
|
", revision " REVISION);
|
|
</code></pre>
|
|
|
|
<p>or even</p>
|
|
|
|
<pre><code>#define VERSION 1.13
|
|
#define REVISION b
|
|
#define VERBATIM_STRINGIFY(x) #x
|
|
#define STRINGIFY(x) VERBATIM_STRINGIFY(x)
|
|
...
|
|
llOwnerSay("Program version " STRINGIFY(VERSION)
|
|
", revision " STRINGIFY(REVISION));
|
|
</code></pre>
|
|
|
|
<p>will resolve to:</p>
|
|
|
|
<pre><code>llOwnerSay("Program version 1.13, revision b");
|
|
</code></pre>
|
|
|
|
<p>instead of something like</p>
|
|
|
|
<pre><code>llOwnerSay("Program version " + "1.13" + ", revision " + "b");
|
|
</code></pre>
|
|
|
|
<p>The latter can also be resolved by the optimizer, but by default that optimization is disabled as it can be counter-productive.</p>
|
|
|
|
<h3><a id="extensions-type-cast-extension"></a>Type-cast extension</h3>
|
|
|
|
<p>For some reason, LSL only allows postfix expressions after a type cast. This extends the syntax to allow prefix expressions as well. This means that you can write e.g. <code>(string)(integer)a</code> instead of <code>(string)((integer)a)</code>, or <code>(string)++x</code> instead of <code>(string)(++x)</code>, sparing you from having to enter the extra parentheses.</p>
|
|
|
|
<h3><a id="extensions-multiple-labels-with-the-same-name"></a>Multiple labels with the same name</h3>
|
|
|
|
<p>Allows duplicate labels obeying the LSL scope rules. This one is tricky to understand. In LSL, syntax-wise, label identifiers obey the same scope rules as variables: when opening a new block, the labels defined inside it are invisible to any of the outer blocks. This means that you can have labels with the same name in different blocks, just like you can have variables with the same name in different blocks.</p>
|
|
|
|
<p>For example, you can have:</p>
|
|
|
|
<pre><code>default
|
|
{
|
|
state_entry()
|
|
{
|
|
{
|
|
integer i;
|
|
@label1;
|
|
}
|
|
{
|
|
integer i;
|
|
@label1;
|
|
}
|
|
}
|
|
}
|
|
</code></pre>
|
|
|
|
<p>Just as the variables, the labels are visible only within their own blocks or any nested block inside them. Unlike variables, labels are also visible if they are declared <em>after</em> the <code>jump</code> instruction that uses them, as long as it's within a block that encloses the label.</p>
|
|
|
|
<p>However, that's only the theory, The compiler is prepared to work like that, but when there are more than one label with the same name, the underlying assembler only jumps to the last label (in the case of LSO) or fails and doesn't let the script to be saved (in the case of Mono).</p>
|
|
|
|
<p>This syntax extension fixes that situation by renaming the labels in the output, to give each a different name and allow Mono scripts to be saved, making the compiler work as intended.</p>
|
|
|
|
<p>Again, one use is in preprocessor macros. Consider this example:</p>
|
|
|
|
<pre><code>#define DO {@dowhile;
|
|
#define WHILE(expr) if (expr) jump dowhile;}
|
|
</code></pre>
|
|
|
|
<p>Without this option, you would only be able to have one <code>DO...WHILE()</code> loop per function.</p>
|
|
|
|
<hr>
|
|
|
|
<h2><a id="compatibility-syntax-extensions"></a>Compatibility Syntax extensions</h2>
|
|
|
|
<p>These extensions are implemented for compatibility with the syntax extensions originally integrated in Emerald and currently in Firestorm. Their use is discouraged and they are considered legacy extensions.</p>
|
|
|
|
<h3><a id="extensions-switch-statements"></a>
|
|
<code>switch()</code> statements</h3>
|
|
|
|
<p>Enables use of C-like <code>switch</code> statements. These produce very awkward code, hard to optimize, and the argument is evaluated multiple times (as many as <code>case</code> labels are present).</p>
|
|
|
|
<p>The syntax of the <code>switch</code> statement as implemented, has two restrictions over its C counterpart:</p>
|
|
|
|
<ol>
|
|
<li>
|
|
<code>case</code> labels can't appear in nested blocks. That's because they are replaced by LSL labels, and as discussed in the <i>Multiple labels with the same name</i> section above, label scope rules prevent their visibility in an outer block, so once converted to labels, the corresponding <code>jump</code> instructions would not be able to find them. This limitation means that <a href="https://en.wikipedia.org/wiki/Duff's_device">Duff's device</a> or similar constructs can't be implemented with this optimizer.</li>
|
|
<li>
|
|
<code><div>switch()</code> needs to be followed by a block, not by a single statement. For example, whiile this works in C, it won't work in this optimizer:<div>
|
|
<pre><code> switch(1) case 1: break;</code></pre>
|
|
<div>The reason is that <code>case</code> is treated by this parser as a statement, rather than as a label prefix, making <code>break</code> be outside the <code>switch</code>. This limitation is probably only of theoretical importance and will not have any practical implication, since single-statement <code>switch</code> clauses are of little no practical use (known to the author). Of course it works perfectly when enclosed in braces:</div>
|
|
<pre><code> switch(1) { case 1: break; }</code></pre></li>
|
|
</ol>
|
|
|
|
<p>As an extension, and for compatibility with Firestorm, if there is a block beginning right after a <code>case</code> or <code>default</code> statement, the colon is optional. For example, all these are valid:</p>
|
|
|
|
<pre><code> switch (x) { case 1: ; default: ; }
|
|
switch (x) { case 1 {} default {} }
|
|
switch (x) { case 1: {} default: {} }
|
|
</code></pre>
|
|
|
|
<h3><a id="extensions-lazy-lists"></a>Lazy lists</h3>
|
|
|
|
<p>That's how Firestorm calls an extended syntax for subindex values after individual list elements.</p>
|
|
|
|
<h4><a id="lazy-lists-assignment"></a>Assignment</h4>
|
|
|
|
<p>The syntax for assignment is:</p>
|
|
|
|
<pre><code>mylist[index] = value;
|
|
</code></pre>
|
|
|
|
<p>which is designed to be roughly a shortcut for this:</p>
|
|
|
|
<pre><code>mylist = llListReplaceList(mylist, (list)value, index, index);
|
|
</code></pre>
|
|
|
|
<p>However, the implementation includes creating a function that performs the replacement. The function is called <code>lazy_list_set</code>. It can be user-overriden. If you define a function with this prototype:</p>
|
|
|
|
<pre><code>list lazy_list_set(list target, integer index, list value)
|
|
</code></pre>
|
|
|
|
<p>which returns the list with the element replaced, then the optimizer will use yours rather than defining it twice.</p>
|
|
|
|
<p>For compatibility with Firestorm, when the index is greater than the number of elements in the list, the intermediate values are filled with integer zeros. If you don't want that, you may have a reason to override it.</p>
|
|
|
|
<p>Note that the value of the assignment as an expression is the whole list, not the element. For example, this will fail because it's assigning a list to an integer:</p>
|
|
|
|
<pre><code> list a; integer b = a[5] = 4;
|
|
</code></pre>
|
|
|
|
<p>To see why, look what that is expanded to:</p>
|
|
|
|
<pre><code> list a; integer b = a = lazy_list_set(a, 5, (list)4);
|
|
</code></pre>
|
|
|
|
<p>which will obviously fail. But this will work:</p>
|
|
|
|
<pre><code> list a; integer b; a[5] = b = 4;
|
|
</code></pre>
|
|
|
|
<h4>
|
|
<a id="lazy-lists-reading"></a>Reading</h4>
|
|
|
|
<p>The syntax for reading an element is the same as for assigning, but it returns no type, therefore a type cast is mandatory. For example:</p>
|
|
|
|
<pre><code> list a; integer b = (integer)a[3];
|
|
</code></pre>
|
|
|
|
<p>That is converted at parsing time to:</p>
|
|
|
|
<pre><code> list a; integer b = llList2Integer(a, 3);
|
|
</code></pre>
|
|
|
|
<p>If the type it's cast to is list, it needs two parameters (starting and ending index), not one:</p>
|
|
|
|
<pre><code> list a; a = (list)a[3, 3];
|
|
</code></pre>
|
|
|
|
<p>That is a requirement of the underlying <code>llList2List</code> function used in this case.</p>
|
|
|
|
<hr>
|
|
|
|
<h2><a id="optimizations"></a>Optimizations</h2>
|
|
|
|
<h3><a id="optimizations-constant-folding-and-expression-simplification"></a>Constant folding and expression simplification</h3>
|
|
|
|
<p>The optimizer simplifies expressions as much as it knows, which is a fair amount, though there's still room for improvement in this area. Expressions that evaluate to a constant will be replaced with that constant. Other expressions such as a+3+1 are replaced with a+4, and so on. Note, however, that for floats, <code>(a+b)+c</code> may not equal <code>a+(b+c)</code>, so that optimization is not always done for floats. Also, as of this writing this optimization is only partial, so some expressions may not be optimized, e.g. <code>2+a+b+3</code> is not optimized to <code>a+b+5</code>. Many boolean expressions are simplified too (more are on the way). For example, <code>(TRUE&&(expression))</code> is simplified to <code>(expression)</code>, and <code>(FALSE&&(expression))</code> is simplified to <code>(FALSE)</code> provided the expression has no side effects. The famous <code>if (llListFindList(...)!=-1)</code> to <code>if (~llListFindList(...))</code> replacement is also performed automatically.</p>
|
|
|
|
<p>The constant folding optimizer is also responsible for simplifying certain statements, e.g. <code>if (FALSE) { statements; }</code> is completely removed, and <code>if (TRUE) { statements1; } else { statements2; }</code> is replaced with just <code>{ statements1; }</code>, removing <code>{ statements2; }</code>. Similarly, <code>do...while(constant)</code> loops and other loops are optimized too.</p>
|
|
|
|
<p>This enables using many preprocessor tricks, like creating an <code>assert()</code> macro similar to that in C:</p>
|
|
|
|
<pre><code>#define assert(x) do { if (DEBUG && !(x)) \
|
|
llOwnerSay("ASSERTION FAILED: " #x); } while (0)
|
|
</code></pre>
|
|
|
|
<p>without worrying about the extra memory that it will take in production code once DEBUG is switched off, or about the loop taking actual code memory.</p>
|
|
|
|
<h3><a id="optimizations-dead-code-removal"></a>Dead code removal</h3>
|
|
|
|
<p>This part of the optimizer tracks execution and usage of statements and variables, removing the ones that aren't used. It performs a function similar to that of the Firestorm optimizer, but it can remove also unused locals, and isn't confused by having a global and a local with the same name.</p>
|
|
|
|
<p>It also replaces references to integer variables that are not written to (global or local), with their value. For example:</p>
|
|
|
|
<pre><code>integer ANSWER = 42;
|
|
default
|
|
{
|
|
state_entry()
|
|
{
|
|
llOwnerSay((string)X);
|
|
}
|
|
}
|
|
</code></pre>
|
|
|
|
<p>will produce:</p>
|
|
|
|
<pre><code>default
|
|
{
|
|
state_entry()
|
|
{
|
|
llOwnerSay("42");
|
|
}
|
|
}
|
|
</code></pre>
|
|
|
|
<p>after DCR and constant folding. This optimization has one of the largest impacts, as variables and parameters in general seem to take a lot of memory in Mono, and removing as much of them as possible produces good savings.</p>
|
|
|
|
<h3><a id="optimizations-shrinking-identifiers"></a>Shrinking Identifiers</h3>
|
|
|
|
<p>Long variable and parameter names are nice and readable, but when used as part of the globals or as function parameters, each character in the identifier takes at least one byte of code memory. In large programs, this can add up to a significant amount. This option replaces global and parameter identifiers with the shortest possible ones, also reusing as many as it can. The savings from this alone can be very significant in programs with a large number of globals or states.</p>
|
|
|
|
<h3><a id="optimizations-floats"></a>Floats</h3>
|
|
|
|
<p>Floats under Mono are internally double precision, so float constants take four more bytes than integers. On the other hand, type cast from integer to float takes only one byte. This optimization substitutes floats with integers where possible, for an overall saving of three bytes per number.</p>
|
|
|
|
<h3><a id="optimizations-signs"></a>Signs</h3>
|
|
|
|
<p>The sign at the beginning of a number (except in globals) takes one byte of code, unless prefixed by a type cast (which does not, under Mono, take code memory in itself if the destination type is the same). Small saving, but it adds up to the overall. Numbers are thus output with a type cast and surrounded by parentheses, e.g. <code>((float)-1.5)</code> instead of <code>-1.5</code>.</p>
|
|
|
|
<h3><a id="optimizations-string-constant-folding"></a>String constant folding</h3>
|
|
|
|
<p>This optimization is turned off by default, as it can be counter-productive. It enables concatenating constants together. However, consider this situation:</p>
|
|
|
|
<pre><code>string a = "A very long string that appears in this script once" + ", or not";
|
|
string b = "A very long string that appears in this script once" + " or twice";
|
|
</code></pre>
|
|
|
|
<p>Since Mono keeps only one copy of each constant string in the code, making the optimizer concatenate them would be counter-productive, generating two long strings that take more code than the original string plus the shorter ones.</p>
|
|
|
|
<hr>
|
|
|
|
<h2><a id="using-the-program"></a>Using the program</h2>
|
|
|
|
<p>This program is designed to work as a filter. It can read from standard input if the file name argument is "-"; in any case it currently outputs the result to standard output and any errors to standard error.</p>
|
|
|
|
<p>Running it by hand to optimize your scripts can be cumbersome. The intention is for it to act as a transparent filter; however, as of this writing there's no support for any viewer or IDE, as it has just been released. Run it without parameters to see the invocation help, for example with <code>python main.py</code>. Redirect the output to a file if you want to store the result, possibly to open it with an editor and copy it to the clipboard. Or under <i>X Window</i>, you can pipe the output directly to <code>xclip -quiet -selection clipboard</code> to copy it to the clipboard, rather than using a file, so you can paste it directly into the viewer. Examples:</p>
|
|
|
|
<pre><code>python main.py myscript.lsl | xclip -quiet -selection clipboard
|
|
</code></pre>
|
|
|
|
<p>will, under <i>X Window</i>, read <code>myscript.lsl</code>, optimize it, and copy the optimized result to the clipboard, ready to be pasted into the viewer.</p>
|
|
|
|
<pre><code>python main.py myscript.lsl > temp.opt
|
|
notepad temp.opt
|
|
</code></pre>
|
|
|
|
<p>will, under any system which has an editor called <code>notepad</code>, read <code>myscript.lsl</code>, optimize it, and write the optimized result to <code>temp.opt</code>, then open it in the editor, enabling you to copy it and paste it into the viewer. Under <i>Windows</i> version <i>Vista</i> and above, apparently there's a command line application called <code>clip</code> that does the same as <code>xclip</code> does for <i>X Window</i>, enabling you to use this:</p>
|
|
|
|
<pre><code>python main.py myscript.lsl | clip
|
|
</code></pre>
|
|
|
|
<p>to copy the optimized output to the clipboard. Under OS X, it seems <code>pbcopy</code> does the same as <code>xclip</code> and <code>clip</code>.</p>
|
|
|
|
<p>Future plans include writing a patch for Firestorm to enable it to run external script filtering programs instead of the internal preprocessor and optimizer. That would allow this program to be run using Firestorm's integrated machinery, making usage pretty transparent to the programmer.</p>
|
|
|
|
<p>Support for other IDEs like Eclipse is not planned, but the author encourages others to write it. Please notify Sei Lisa if you write one, so that it can be listed in this page.</p>
|
|
|
|
<p>The program uses two external files. One is <code>builtins.txt</code>, which is in the same format as needed by <a href="https://github.com/pclewis/lslint">lslint</a>, and an up-to-date copy can be obtained from the <i>kwdb</i> project: <a href="https://bitbucket.org/Sei_Lisa/kwdb/raw/tip/outputs/builtins.txt">https://bitbucket.org/Sei_Lisa/kwdb/raw/tip/outputs/builtins.txt</a>. The other is <code>seftable.txt</code>, which is a list of functions that have no side effects (SEF stands for Side-Effect Free), allowing them to be removed (optimized out) if nothing else has side effects in an expression and the result of the expression is not used.</p>
|
|
|
|
<hr>
|
|
|
|
<h2><a id="other-future-plans"></a>Other future plans</h2>
|
|
|
|
<p>Making the optimizer smarter is one primary objective. Conversion to <a href="https://en.wikipedia.org/wiki/Static_single_assignment_form">SSA</a> form is currently not performed, and would be nice to have, for one, to enable more optimizations where values are not used. There are a number of TODO items in the code about optimizations pending to implement.</p>
|
|
|
|
<p>Another goal is to add the possibility to link each output line with its source counterpart, so that in case of a server side compilation error, the source line corresponding to the actual line where the error happened can be displayed. Currently, server-side compilation errors should be rare (unless there are bugs in the optimizer) as there will be few situations in which they won't be caught by the optimizer's parser first. This would also allow warnings output by the optimizer (e.g. an expression in globals not resolving to a constant) to be related to a source line. Currently those warnings don't tell where the error occurred.</p>
|
|
|
|
<p>Lastly, implementation of some kind of machine-readable way to inform the invoker about the available options, is also in the plans. That would allow the plugin or viewer or whatever to present a dialog with the options for invoking the optimizer.</p>
|
|
|
|
<hr>
|
|
|
|
<h2><a id="license"></a>License</h2>
|
|
|
|
<p>This program is distributed under the terms of the GNU General Public License (GPL) version 3.</p>
|
|
|
|
<p>(C) Copyright 2015 Sei Lisa. All rights reserved.</p>
|
|
|
|
<h2><a id="author"></a>Author</h2>
|
|
|
|
<p>Written by Sei Lisa. Sei Lisa is the author's pseudonym and user name in the Second Life virtual world.</p>
|
|
|
|
<hr>
|
|
|
|
<p>Happy coding!</p>
|
|
|
|
</body></html>
|