Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

13411 lines (12597 sloc) 364.814 kB
#! winxed
/***********************************************************************
Winxed stage 2 compiler
***********************************************************************/
namespace Winxed
{
namespace Compiler
{
const int VERSION_MAJOR = 1;
const int VERSION_MINOR = 9;
const int VERSION_BUILD = 1;
function getVersion [anon] ()
{
int version[3] = [ VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD ];
return version;
}
function getVersionString [anon] ()
{
return "Winxed " + string(join(".", getVersion()));
}
//*********************************************
// Character test functions
//*********************************************
inline isspace(string c) return int
{
return c == " " || c == "\n" || c == "\t" || c == "\r";
}
inline isdigit(string c) return int
{
return indexof("0123456789", c) > -1;
}
inline hexdigit(string c) return int
{
int i = indexof("0123456789abcdef0123456789ABCDEF", c);
if (i >= 0) i = i % 16;
return i;
}
inline isidentstart(string c) return int
{
return indexof(
"abcdefghijklmnopqrstuvwxyz" +
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "_",
c) > -1;
}
inline isident(string c) return int
{
return indexof(
"abcdefghijklmnopqrstuvwxyz" +
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "_$" +
"0123456789",
c) > -1;
}
function sformat(string format, var args[slurpy])
{
var builder = new ["StringBuilder"];
builder.append_format(format, args:[flat]);
return string(builder);
}
inline quoted(string str) return string
{
return "\"" + str + "\"";
}
// Algorithms
inline for_each(var src, var func)
{
for (var item in src)
func(item);
}
inline transform(var src, var dest, var func) return var
{
for (var item in src)
push(dest, func(item));
return dest;
}
inline find_same(var src, var value) return var
{
for (var item in src)
if (item === value)
return item;
return null;
}
inline find_if(var src, var fun) return var
{
for (var item in src)
if (fun(item))
return item;
return null;
}
function bindfirst(var fun, var argfirst[slurpy])
{
return function(var arglast[slurpy])
{
return fun(argfirst:[flat], arglast:[flat]);
};
}
function bindlast(var fun, var arglast[slurpy])
{
return function(var argfirst[slurpy])
{
return fun(argfirst:[flat], arglast:[flat]);
};
}
function bindmethod(string name)
{
return function(var obj, var args[slurpy])
{
return obj.*name(args:[flat]);
};
}
function method_fun(var obj, string methodname)
{
var method = find_method(obj, methodname);
return function (var args[slurpy])
{
return obj.*method(args:[flat]);
};
}
function addprefix(string prefix)
{
return function (string str) { return prefix + str; };
}
inline clone_array(var src, var owner) return var
{
return transform(src, [], bindlast(bindmethod("clone"), owner));
}
//*********************************************
// Error handling exceptions
//*********************************************
const string
ERR_INTERNAL = "internal",
ERR_TOKEN = "tokenizer",
ERR_PARSER = "parser";
class WinxedCompilerError
{
// Payload for compiler exceptions.
var type; // internal, tokenizer, parser
var filename;
var line;
var message;
function WinxedCompilerError(
string type,
string message,
string filename[optional],
int line[optional])
{
self.type = type;
self.message = message;
self.filename = filename;
self.line = line;
}
}
function Warn(string msg, var pos [optional])
{
var stderr = getstderr();
stderr.print("WARNING: ");
stderr.print(msg);
if (pos != null) {
stderr.print(" near ");
stderr.print(pos.show());
}
stderr.print("\n");
}
function InternalError(string msg, var pos [optional], int has_pos [opt_flag])
{
if (has_pos) {
pos = pos.getstart();
string desc = pos.show();
msg = msg + " near " + desc;
}
var payload = has_pos ?
new WinxedCompilerError(ERR_INTERNAL, msg,
pos.filename(), pos.linenum()) :
new WinxedCompilerError(ERR_INTERNAL, msg);
throw Error(msg, 2, __WINXED_ERROR__, payload);
}
function SyntaxError(string msg, var pos)
{
if (pos != null)
pos = pos.getstart();
int line = pos != null ? pos.linenum() : -1;
string file = pos != null ? pos.filename() : "UNKNOWN";
string desc = " near " + string(pos.viewable());
throw Error(msg + " in " + file + " line " + string(line) + desc,
2, __WINXED_ERROR__,
new WinxedCompilerError(ERR_PARSER, msg + desc,
file, line));
}
function NoLeftSide(var pos)
{
SyntaxError("Not a left-side expression", pos);
}
function Expected(string msg, var t)
{
SyntaxError("Expected " + msg, t);
}
function Unexpected(string msg, var t)
{
SyntaxError("Unexpected " + msg, t);
}
function ExpectedIdentifier(var t)
{
SyntaxError("Expected identifier", t);
}
function ExpectedOp(string msg, var t)
{
SyntaxError("Expected '" + msg + "'", t);
}
function RequireOp(string name, var t)
{
if (! t.isop(name))
ExpectedOp(name, t);
}
function RequireKeyword(string name, var t)
{
if (! t.iskeyword(name))
ExpectedOp(name, t);
}
function RequireIdentifier(var t)
{
if (! t.isidentifier())
ExpectedIdentifier(t);
}
function ExpectOp(string name, var tk)
{
var t = tk.get();
RequireOp(name, t);
}
function ExpectKeyword(string name, var tk)
{
var t = tk.get();
RequireKeyword(name, t);
}
function UndefinedVariable(string name, var t)
{
SyntaxError("Variable '" + name + "' is not defined", t);
}
function Redeclared(var t)
{
SyntaxError("Redeclared '" + string(t.getidentifier()) + "'", t);
}
//*********************************************
// Token
//*********************************************
class Token
{
var file;
var line;
function Token(string file, int line)
{
self.file = file;
self.line = line;
}
function getstart() { return self; }
function iseof() { return false; }
function iscomment() { return false; }
function isidentifier() { return false; }
function isint() { return false; }
function isfloat() { return false; }
function isstring() { return false; }
function issinglequoted() { return false; }
function getintvalue()
{
InternalError("Not a literal int", self);
}
function rawstring()
{
InternalError("Not a literal string", self);
}
function getidentifier()
{
ExpectedIdentifier(self);
}
function iskeyword(string name) { return false; }
function checkkeyword() { return false; }
function isop(string name) { return false; }
function checkop() { return ""; }
function viewable() { return "(unknown)"; }
function filename() { return self.file; }
function linenum() { return self.line; }
function show()
{
string r = self.viewable();
return r + " at " + string(self.file) + " line " + string(self.line);
}
}
class TokenEof : Token
{
function TokenEof(string file)
{
self.Token(file, 0);
}
function iseof() { return true; }
function viewable() { return "(End of file)"; }
}
class TokenWithVal : Token
{
var str;
function TokenWithVal(string file, int line, string str)
{
self.Token(file, line);
self.str = str;
}
function get_string[vtable]() { return self.str; }
function viewable()
{
return self.str;
}
}
class TokenComment : Token
{
function TokenComment(string file, int line)
{
self.Token(file, line);
}
function iscomment() { return true; }
function viewable() { return "(comment)"; }
}
class TokenOp : TokenWithVal
{
function TokenOp(string file, int line, string str)
{
self.TokenWithVal(file, line, str);
}
function isop(string name)
{
return self.str == name;
}
function checkop()
{
return string(self.str);
}
}
class TokenIdentifier : TokenWithVal
{
function TokenIdentifier(string file, int line, string str)
{
self.TokenWithVal(file, line, str);
}
function isidentifier() { return true; }
function getidentifier()
{
return self.str;
}
function checkkeyword()
{
return string(self.str);
}
function iskeyword(string name)
{
return self.str == name;
}
}
class TokenString : TokenWithVal
{
function isstring() { return true; }
function rawstring()
{
return self.str;
}
}
class TokenQuoted : TokenString
{
function TokenQuoted(string file, int line, string str)
{
self.TokenWithVal(file, line, str);
}
function get_string[vtable]()
{
return quoted(self.str);
}
function viewable()
{
return quoted(self.str);
}
function getasquoted()
{
return self.str;
}
function getPirString()
{
string str = self.str;
string strunesc;
try {
strunesc = unescape(str);
}
catch () {
SyntaxError("Invalid escape sequence in literal string", self);
}
int need_encoding = false;
for (int code in strunesc) {
if (code > 127) {
need_encoding = true;
break;
}
}
str = quoted(escape(strunesc));
string encoding = need_encoding ? "utf8:" : "";
return encoding + str;
}
}
class TokenSingleQuoted : TokenString
{
function TokenSingleQuoted(string file, int line, string str)
{
self.TokenWithVal(file, line, str);
}
function issinglequoted() { return true; }
function get_string[vtable]()
{
return "'" + string(self.str) + "'";
}
function viewable()
{
return "'" + string(self.str) + "'";
}
function getasquoted()
{
string s = "";
for (string c in self.str) {
switch (c) {
case "\"":
case "\\":
case "'":
c = "\\" + c;
break;
}
s += c;
}
return s;
}
function getPirString()
{
string str = self.str;
string quote = "'";
int need_escape = false;
for (int code in str) {
if (code < 32 || code > 127) {
need_escape = true;
break;
}
}
int need_encoding = false;
if (need_escape) {
quote = "\"";
string result = "";
for (string c in str) {
if (c == "\\")
result += "\\\\";
else
{
int n = ord(c);
if (n < 32 || n > 127) {
if (n > 127)
need_encoding = true;
string h = n.get_as_base(16);
result += "\\x{" + h + "}";
}
else
result += c;
}
}
str = result;
}
str = quote + str + quote;
if (need_encoding)
str = "utf8:" + str;
return str;
}
}
class TokenInteger : TokenWithVal
{
function TokenInteger(string file, int line, string str)
{
self.TokenWithVal(file, line, str);
}
function isint() { return true; }
function getintvalue()
{
return int(self.str);
}
}
class TokenFloat : TokenWithVal
{
function TokenFloat(string file, int line, string str)
{
self.TokenWithVal(file, line, str);
}
function isfloat() { return true; }
function getfloatvalue()
{
return float(self.str);
}
}
//*********************************************
// Tokenizer auxiliar functions
//*********************************************
function TokenError(string msg, var tk, int line)
{
throw Error(msg + " in " + string(tk.filename) + " line " + string(line),
2, __WINXED_ERROR__,
new WinxedCompilerError(ERR_TOKEN, msg, tk.filename, line));
}
function UnterminatedString(var tk, int line)
{
TokenError("Unterminated string", tk, line);
}
function UnterminatedHeredoc(var tk, int line)
{
TokenError("Unterminated heredoc", tk, line);
}
function UnclosedComment(var tk, int line)
{
TokenError("Unclosed comment", tk, line);
}
function getquoted(var tk, string start, int line)
{
string s = "";
string c;
for (c = tk.getchar(); c != "\""; c = tk.getchar()) {
switch (c) {
case "":
case "\n":
UnterminatedString(tk, line);
case "\\":
// Quick fix for escaped double quotes.
string c2 = tk.getchar();
if (c2 == "" || c2 == "\n")
UnterminatedString(tk, line);
s += c + c2;
break;
default:
s += c;
}
}
return new TokenQuoted(tk.filename, line, s);
}
function getsinglequoted(var tk, string start, int line)
{
string s = "";
string c;
for (c = tk.getchar(); c != "'"; c = tk.getchar()) {
if (c == "" || c == "\n")
UnterminatedString(tk, line);
s += c;
}
return new TokenSingleQuoted(tk.filename, line, s);
}
function getheredoc(var tk, string start, int linenum)
{
string mark = "";
string c;
for (c = tk.getchar(); c != "\n"; c = tk.getchar()) {
switch (c) {
case "":
UnterminatedHeredoc(tk, linenum);
case "\r":
break; // Quick fix for windows line-ending text files
case "\"":
case "\\":
// Encode the mark the same way as the heredoc content
// to simplify its detection
mark += "\\" + c;
break;
default:
mark += c;
}
}
mark = mark + ":>>";
string content = "";
string line;
do {
line = "";
for (c = tk.getchar(); c != "\n"; c = tk.getchar()) {
switch (c) {
case "":
UnterminatedHeredoc(tk, linenum);
case "\r":
break; // Quick fix for windows line-ending text files
case "\"":
case "\\":
line += "\\" + c;
break;
default:
line += c;
}
}
if (line != mark)
content += line + "\\n";
} while (line != mark);
return new TokenQuoted(tk.filename, linenum, content);
}
function getident(var tk, string start, int line)
{
string s = start;
string c;
for (c = tk.getchar(); isident(c); c = tk.getchar())
s += c;
tk.ungetchar(c);
return new TokenIdentifier(tk.filename, line, s);
}
function getnumber(var tk, string start, int line)
{
var filename = tk.filename;
string c = tk.getchar();
if (start == "0" && (c == "x" || c == "X")) {
int hexval = 0, h;
for (c = tk.getchar(); (h = hexdigit(c)) >= 0; c = tk.getchar())
hexval = hexval * 16 + h;
tk.ungetchar(c);
return new TokenInteger(filename, line, hexval);
}
string s = start;
for ( ; isdigit(c); c = tk.getchar())
s += c;
int isfloat = false;
if (c == ".") {
isfloat = true;
do {
s += c;
c = tk.getchar();
} while (isdigit(c));
}
if (c == "e" || c == "E") {
isfloat = true;
s += "E";
if ((c = tk.getchar()) == "+" || c == "-") {
s += c;
c = tk.getchar();
}
for ( ; isdigit(c); c = tk.getchar())
s += c;
}
tk.ungetchar(c);
if (isfloat)
return new TokenFloat(filename, line, s);
else
return new TokenInteger(filename, line, s);
}
function getlinecomment(var tk, string start, int line)
{
string c;
do
c = tk.getchar();
while (c != "" && c != "\n");
return new TokenComment(tk.filename, line);
}
function getcomment(var tk, string start, int line)
{
string c = tk.getchar();
do {
while (c != "" && c != "*")
c = tk.getchar();
if (c == "")
UnclosedComment(tk, line);
c = tk.getchar();
if (c == "")
UnclosedComment(tk, line);
} while (c != "/");
return new TokenComment(tk.filename, line);
}
function getop(var tk, string start, int line)
{
string s = start;
return new TokenOp(tk.filename, line, s);
}
//*********************************************
// Tokenizer
//*********************************************
class Tokenizer
{
var warnings;
var h;
var pending;
var select;
var stacked;
var filename;
var line;
function Tokenizer(var handle, string filename, int nowarn)
{
self.warnings = new ["Boolean"](! nowarn);
self.h = handle;
self.pending = "";
self.stacked = [];
self.filename = filename;
self.line = 1;
var select = {
"$": { "": getident, "{": getop },
"\"": getquoted,
"'": getsinglequoted,
"=": {
"=": { "": getop, "=": getop },
":": getop
},
"+": { "+": getop, "=": getop },
"-": { "-": getop, "=": getop },
"*": { "=": getop },
"|": { "|": getop },
"&": { "&": getop },
"<": {
"<": { "": getop, ":": getheredoc },
"=": getop
},
">": {
">": { "": getop, ">": getop },
"=": getop
},
"!": {
"=": { "": getop, "=": getop }
},
"%": { "%": getop, "=": getop },
"/": { "=": getop, "/": getlinecomment, "*": getcomment },
"#": getlinecomment
};
self.select = select;
}
function warn(string msg, var pos [optional])
{
if (self.warnings)
Warn(msg, pos);
}
function getchar()
{
var pending = self.pending;
string c = pending;
if (c != "")
pending =: "";
else {
var h = self.h;
c = h.read(1);
if (c == "\n")
++self.line;
}
return c;
}
function ungetchar(string c)
{
self.pending =: c;
}
function get_token()
{
if (self.stacked)
return self.stacked.pop();
string c = self.getchar();
while (isspace(c))
c = self.getchar();
int line = self.line;
if (c == "")
return new TokenEof(self.filename);
if (isidentstart(c))
return getident(self, c, line);
if (isdigit(c))
return getnumber(self, c, line);
string op = c;
var select = self.select;
var current = select[c];
while (current != null && (current instanceof "Hash")) {
c = self.getchar();
select = current;
current = select[c];
if (current == null) {
self.ungetchar(c);
current = select[""];
}
else
op += c;
}
return current != null ?
current(self, op, line) :
getop(self, op, line);
}
function get(int withcomments [optional])
{
var t = self.get_token();
while (!t.iseof() && !withcomments && t.iscomment())
t = self.get_token();
return t;
}
function unget(var t)
{
push(self.stacked, t);
}
}
//*********************************************
// PIR generation values
//*********************************************
// Register types
const string
REGint = "I",
REGfloat = "N",
REGstring = "S",
REGvar = "P",
// Pseudotypes for builtins
REGarglist = "*",
REGany = "?",
REGsame = ":", // same as argument
REGs_v = "p", // string or var
REGraw1 = "!", // raw mode one arg
REGnone = "v"; // void return
// Register reserved for temporal and dicardable results
const string DISCARD_IREG = "$I0";
const string DISCARD_NREG = "$N0";
const string DISCARD_SREG = "$S0";
const string DISCARD_PREG = "$P0";
function typetoregcheck(string type)
{
switch (type) {
case "int": return REGint;
case "float": return REGfloat;
case "string": return REGstring;
case "var": return REGvar;
default: return "";
}
}
function typetopirname(string regtype)
{
switch (regtype) {
case REGint: return "int";
case REGfloat: return "num";
case REGstring: return "string";
case REGvar: return "pmc";
default: InternalError("Invalid reg type");
}
}
//*********************************************
// Emit
//*********************************************
const string INDENT = " ";
const string INDENTLABEL = " ";
class Emit
{
var handle;
var file;
var line;
var pendingf;
var pendingl;
var debug;
var noan;
var warnings;
function Emit(var handle, int nowarn)
{
self.handle = handle;
self.file = "";
self.line = 0;
self.pendingf = false;
self.pendingl = false;
self.warnings = new ["Boolean"](! nowarn);
}
function setDebug()
{
self.debug = true;
}
function getDebug()
{
return self.debug != null;
}
function disable_annotations()
{
self.noan = true;
}
function close()
{
self.handle = null;
}
function warn(string msg, var pos [optional])
{
if (self.warnings)
Warn(msg, pos);
}
function updateannot()
{
if (self.pendingf) {
self.handle.print(join("", [
".annotate 'file', '",
self.file,
"'\n" ]
));
self.pendingf =: false;
}
if (self.pendingl) {
self.handle.print(join("", [
".annotate 'line', ",
self.line,
"\n" ]
));
self.pendingl =: false;
}
}
function vprint(var args)
{
for_each(args, method_fun(self.handle, "print"));
}
function print(var args [slurpy])
{
self.updateannot();
self.vprint(args);
}
function say(var args [slurpy])
{
self.updateannot();
self.vprint(args);
self.handle.print("\n");
}
function annotate(var t)
{
if (self.noan == null)
{
var file = self.file;
var line = self.line;
string tfile = t.file;
int tline = t.line;
if (file != tfile) {
file =: tfile;
self.pendingf =: true;
line =: 0;
}
if (line != tline) {
line =: tline;
self.pendingl =: true;
}
}
}
function comment(var args [slurpy])
{
self.updateannot();
self.handle.print("# " + join("", args) + "\n");
}
function emitlabel(string label, string comment [optional])
{
var handle = self.handle;
handle.print(INDENTLABEL);
handle.print(label);
handle.print(":");
if (comment != null)
handle.print(" # " + comment);
handle.print("\n");
}
function emitgoto(string label, string comment [optional])
{
var handle = self.handle;
handle.print(INDENT + "goto ");
handle.print(label);
if (comment != null)
handle.print(" # " + comment);
handle.print("\n");
}
function emitarg1(string op, string arg1)
{
self.say(INDENT, op, " ", arg1);
}
function emitarg2(string op, string arg1, string arg2)
{
self.say(INDENT, op, " ", arg1, ", ", arg2);
}
function emitarg3(string op, string arg1, string arg2, string arg3)
{
self.say(INDENT, op, " ", arg1, ", ", arg2, ", ", arg3);
}
function emitcompare(string op, string arg1, string arg2, string label)
{
self.say(INDENT, op, " ", arg1, ", ", arg2, ", ", label);
}
function emitif(string value, string label)
{
self.say(INDENT + "if ", value, " goto ", label);
}
function emitunless(string value, string label)
{
self.say(INDENT + "unless ", value, " goto ", label);
}
function emitif_null(string value, string label)
{
self.say(INDENT + "if_null ", value, ", ", label);
}
function emitunless_null(string value, string label)
{
self.say(INDENT + "unless_null ", value, ", ", label);
}
function emitnull(string dst)
{
self.say(INDENT + "null ", dst);
}
function emitinc(string arg)
{
self.say(INDENT + "inc ", arg);
}
function emitdec(string arg)
{
self.say(INDENT + "dec ", arg);
}
function emitset(string dst, string src)
{
self.say(INDENT + "set ", dst, ", ", src);
}
function emitassign(string dst, string src)
{
self.say(INDENT + "assign ", dst, ", ", src);
}
function emitbox(string dst, string src)
{
self.say(INDENT + "box ", dst, ", ", src);
}
function emitunbox(string dst, string src)
{
self.say(INDENT + "unbox ", dst, ", ", src);
}
function emitbinop(string op, string res, string dst, string src)
{
self.say(INDENT, op, " ", res, ", ", dst, ", ", src);
}
function emitaddto(string dst, string src)
{
self.say(INDENT + "add ", dst, ", ", src);
}
function emitsubto(string dst, string src)
{
self.say(INDENT + "sub ", dst, ", ", src);
}
function emitadd(string dst, string src1, string src2)
{
self.say(INDENT + "add ", dst, ", ", src1, ", ", src2);
}
function emitsub(string dst, string src1, string src2)
{
self.say(INDENT + "sub ", dst, ", ", src1, ", ", src2);
}
function emitmul(string dst, string src1, string src2)
{
self.say(INDENT + "mul ", dst, ", ", src1, ", ", src2);
}
function emitdiv(string dst, string src1, string src2)
{
self.say(INDENT + "div ", dst, ", ", src1, ", ", src2);
}
function emitconcat1(string dst, string src)
{
self.say(INDENT + "concat ", dst, ", ", dst, ", ", src);
}
function emitconcat(string dst, string src1, string src2)
{
self.say(INDENT + "concat ", dst, ", ", src1, ", ", src2);
}
function emitprint(string arg)
{
self.say(INDENT + "print ", arg);
}
function emitsay(string arg)
{
self.say(INDENT + "say ", arg);
}
function emitget_hll_namespace(string reg, string key)
{
self.say(INDENT + "get_hll_namespace ", reg, ", " + key);
}
function emitget_root_namespace(string reg, string key)
{
self.say(INDENT + "get_root_namespace ", reg, ", " + key);
}
function emitget_hll_global(string reg, string name, string key[optional])
{
self.print(INDENT + "get_hll_global ", reg);
if (key != null)
self.print(", ", key);
self.say(", '", name, "'");
}
function emitget_root_global(string reg, string name, string key[optional])
{
self.print(INDENT + "get_root_global ", reg);
if (key != null)
self.print(", ", key);
self.say(", '", name, "'");
}
function emitfind_lex(string reg, string name)
{
self.say(INDENT + "find_lex ", reg, ", ", name);
}
function emitstore_lex(string name, string reg)
{
self.say(INDENT + "store_lex ", name, ", ", reg);
}
function emitrepeat(string dst, string src1, string src2)
{
self.say(INDENT + "repeat ", dst, ", ", src1, ", ", src2);
}
}
//*********************************************
// Helper functions and constants
//*********************************************
// Scope search flags
const int
SEARCH_NAMESPACE = 1,
SEARCH_CLASS = 2;
// Var flags
const int
VAR_is_volatile = 1, // volatile qualifier
VAR_is_lexical = 2, // lexicalized local variable
VAR_is_extern = 4; // externaly defined constant, do not export
const string
NULL = "null",
SELF = "self";
function integerValue(var owner, var start, int value)
{
return new IntegerLiteral(owner, start, value);
}
function floatValue(var owner, var start, float value)
{
:TokenFloat t(start.file, start.line, value);
return new FloatLiteral(owner, t);
}
function stringQuotedValue(var owner, var start, string value)
{
:TokenQuoted t(start.file, start.line, value);
return new StringLiteral(owner, t);
}
function floatAsString(float n)
/*
Make sure that a float value is recognized as such by the
PIR compiler, adding a dot if needed.
*/
{
string aux = n;
if (aux.is_integer(aux))
aux += ".0";
return aux;
}
function floatresult(string r1, string r2)
/*
Check if the passed types can give a float result in arithmetic
operations.
*/
{
int result = (r1 == REGfloat && (r2 == REGfloat || r2 == REGint)) ||
(r2 == REGfloat && (r1 == REGfloat || r1 == REGint));
return result;
}
// Predefined constants
// Fake filename and line number for the tokens.
const string PREDEFCONST_FILENAME = "__predefconst__";
const int PREDEFCONST_LINENUM = 0;
function createPredefConstInt(var scope, string name, int value)
{
:TokenIdentifier tid(PREDEFCONST_FILENAME, PREDEFCONST_LINENUM,
name);
var cst = scope.createconst(tid, REGint, VAR_is_extern);
cst.setvalue(integerValue(scope, tid, value));
}
function createPredefConstString(var scope, string name, string value)
{
:TokenIdentifier tid(PREDEFCONST_FILENAME, PREDEFCONST_LINENUM,
name);
var cst = scope.createconst(tid, REGstring, VAR_is_extern);
cst.setvalue(new StringLiteral(scope,
new TokenQuoted(PREDEFCONST_FILENAME, PREDEFCONST_LINENUM, value)));
}
//*********************************************
// Builtins
//*********************************************
const int
BULTIN_arglist = -1,
BULTIN_raw1 = -2;
// Helpers for compile time evaluation
inline int_from_literal(var arg) return int
{
__ASSERT__(arg instanceof Literal);
int value = arg.getIntegerValue();
return value;
}
inline float_from_literal(var arg) return float
{
__ASSERT__(arg instanceof Literal);
float value = arg.getFloatValue();
return value;
}
inline string_from_literal(var arg) return string
{
__ASSERT__(arg instanceof Literal);
string value = arg.getStringValue();
return value;
}
/*
Get a literal from a builtin with just one param.
The argument list validity should be checked before, so
check here only in debug builds.
*/
function int_from_literal_arg1(var args)
{
if (__DEBUG__) {
if (elements(args) != 1)
InternalError("call to " + __FUNCTION__ + " with invalid args");
}
return int_from_literal(args[0].arg);
}
function string_from_literal_arg1(var args)
{
if (__DEBUG__) {
if (elements(args) != 1)
InternalError("call to " + __FUNCTION__ + " with invalid args");
}
return string_from_literal(args[0].arg);
}
// Invokable object generated from a string body
class Builtin_frombody
{
const string resultmark = "%0";
var body;
var typeresult;
function Builtin_frombody(string typeresult, string bodystr)
{
if (__DEBUG__) {
// Minimal sanity check
int pos = indexof(bodystr, resultmark);
if (typeresult == REGnone && pos != -1)
InternalError("void builtin with " + resultmark);
if (typeresult != REGnone && pos == -1)
InternalError("non void builtin without " + resultmark);
switch (typeresult) {
case REGint: case REGfloat: case REGstring:
case REGvar: case REGnone:
break;
case REGsame:
// TODO: must check it has one argument
break;
default:
InternalError("Invalid result type in builtin");
}
}
// Predecorate output
int l = length(bodystr) - 1;
if (substr(bodystr, l, 1) == "\n")
bodystr = substr(bodystr, 0, l);
bodystr = INDENT + join("\n" + INDENT, split("\n", bodystr));
self.body = bodystr;
self.typeresult = typeresult;
}
function invoke [vtable](var e, var owner, var start, string result, var args)
{
string typeresult = self.typeresult;
if (typeresult == REGnone) {
if (result != null && result != "")
SyntaxError("using return value from void builtin", start);
}
else {
if (result == null || result == "")
InternalError("Bad result in non void builtin");
}
e.annotate(start);
e.say(sformat(self.body, result, args:[flat]));
}
}
class BuiltinBase
{
var name;
var body;
var typeresult;
var type0;
var type1;
var type2;
var type3;
var nparams;
function BuiltinBase(string name, var body,
string typeresult,
string type0,
string type1,
string type2,
string type3)
{
self.name = name;
if (body != null && body instanceof "String")
self.body = new Builtin_frombody(typeresult, body);
else
self.body = body;
self.typeresult = typeresult;
int n = 0;
if (type0 != null) {
self.type0 = type0;
switch (type0) {
case REGarglist:
n = BULTIN_arglist;
break;
case REGraw1:
n = BULTIN_raw1;
if (type1 != null)
InternalError("Invalid builtin '" + name + "'");
break;
default:
n = 1;
if (type1 != null) {
self.type1 = type1;
++n;
}
if (type2 != null) {
self.type2 = type2;
++n;
}
if (type3 != null) {
self.type3 = type3;
++n;
}
}
}
self.nparams = n;
}
function isreplaceexpr() { return false; }
function iscompileevaluable() { return false; }
function name()
{
return string(self.name);
}
function result()
{
return self.typeresult;
}
function params() { return self.nparams; }
function paramtype(int i)
{
string type;
switch (i) {
case 0: type = self.type0; break;
case 1: type = self.type1; break;
case 2: type = self.type2; break;
case 3: type = self.type3; break;
default:
InternalError("Invalid builtin arg");
}
return type;
}
function expand(var e, var owner, var start, string result, var args)
{
string name = self.name;
if (e.getDebug()) {
// Special case for a now.
if (name != "__ASSERT__")
e.comment("builtin ", name);
}
string typeresult = self.typeresult;
if (typeresult != REGnone && result == "")
result = owner.tempreg(typeresult);
var fun = self.body;
fun(e, owner, start, result, args);
}
}
class BuiltinFunction : BuiltinBase
{
function BuiltinFunction(string name, var body,
string typeresult,
string type0[optional],
string type1[optional],
string type2[optional],
string type3[optional])
{
self.BuiltinBase(name, body, typeresult, type0, type1, type2, type3);
}
}
class BuiltinFunctionEval : BuiltinBase
{
var evalfun;
function BuiltinFunctionEval(string name, var evalfun, var body,
string typeresult,
string type0[optional],
string type1[optional],
string type2[optional],
string type3[optional])
{
self.BuiltinBase(name, body, typeresult, type0, type1, type2, type3);
self.evalfun = evalfun;
}
function iscompileevaluable() { return true; }
}
class BuiltinExpr : BuiltinBase
{
var exprfun;
function BuiltinExpr(string name, var exprfun,
string typeresult,
string type0[optional],
string type1[optional],
string type2[optional],
string type3[optional])
{
self.BuiltinBase(name, null, typeresult, type0, type1, type2, type3);
self.exprfun = exprfun;
}
function isreplaceexpr() { return true; }
function replaceexpr(var owner, var start, var args)
{
var exprfun = self.exprfun;
return exprfun(owner, start, args);
}
}
function Builtin_say(var e, var owner, var start, string result, var args)
{
e.annotate(start);
int n = elements(args) - 1;
if (n >= 0) {
for (int i = 0; i < n; ++i)
e.emitprint(args[i]);
e.emitsay(args[n]);
}
else
e.emitsay("''");
}
function Builtin_cry(var e, var owner, var start, string result, var args)
{
e.annotate(start);
e.say(sformat(
<<:
getstderr $P0
%0
print $P0, "\n"
:>>
, join("\n", transform(args, [], addprefix(INDENT + "print $P0, ")))));
}
function Builtin_print(var e, var owner, var start, string result, var args)
{
e.annotate(start);
for_each(args, method_fun(e, "emitprint"));
}
function Builtin_abs(var e, var owner, var start, string result, var args)
{
var arg = args[0];
string argreg = arg.emit_get(e);
if (arg.checkresult() == REGstring) {
string aux = owner.tempreg(REGfloat);
e.annotate(start);
e.emitset(aux, argreg);
e.emitarg2("abs", aux, aux);
e.emitset(result, aux);
}
else {
e.annotate(start);
e.emitarg2("abs", result, argreg);
}
}
function Builtin_ASSERT(var e, var owner, var start, string result, var args)
{
__ASSERT__(elements(args) == 1); // Best place to use it
if (e.getDebug()) {
e.annotate(start);
string label = owner.genlabel();
var arg = args[0];
var reg = arg.emit_getint(e);
e.emitif(reg, label);
e.print(sformat(
<<:
getstderr $P0
print $P0, "Assertion failed at '%0' line "
print $P0, %1
print $P0, "\n"
exit 1
:>>
, start.filename(), start.linenum()));
e.emitlabel(label);
}
}
// invoke is a quick & dirty way to allow use of multiple return values.
// It's a wrap around the call that gives the signature object returned.
function Builtin_invoke(var e, var owner, var start, string result, var args)
{
var arg = args[0];
if (! (arg instanceof CallExpr))
SyntaxError("invoke argument must be callable", start);
arg.emit(e, "(" + result + " :call_sig)");
}
function builtineval_length(var owner, var start, var args)
{
string s = string_from_literal_arg1(args);
return integerValue(owner, start, length(s));
}
function builtineval_bytelength(var owner, var start, var args)
{
string s = string_from_literal_arg1(args);
return integerValue(owner, start, bytelength(s));
}
function builtineval_ord(var owner, var start, var args)
{
int nargs = elements(args);
var arg = args[0].arg;
string s = string_from_literal(arg);
int pos = 0;
if (nargs > 1) {
var argpos = args[1].arg;
pos = int_from_literal(argpos);
}
return integerValue(owner, start, ord(s, pos));
}
function builtineval_chr(var owner, var start, var args)
{
return stringQuotedValue(owner, start, chr(int_from_literal_arg1(args)));
}
function builtineval_substr(var owner, var start, var args)
{
int nargs = elements(args);
var argstr = args[0].arg;
var argpos = args[1].arg;
string str = string_from_literal(argstr);
int pos = int_from_literal(argpos);
string result;
if (nargs > 2) {
var arglen = args[2].arg;
int len = int_from_literal(arglen);
result = substr(str, pos, len);
}
else
result = substr(str, pos);
return stringQuotedValue(owner, start, result);
}
function builtineval_indexof(var owner, var start, var args)
{
var argstrfrom = args[0].arg;
var argstrsearch = args[1].arg;
string strfrom = string_from_literal(argstrfrom);
string strsearch = string_from_literal(argstrsearch);
return integerValue(owner, start, indexof(strfrom, strsearch));
}
function builtineval_indexof_pos(var owner, var start, var args)
{
var argstrfrom = args[0].arg;
var argstrsearch = args[1].arg;
var argpos = args[2].arg;
string strfrom = string_from_literal(argstrfrom);
string strsearch = string_from_literal(argstrsearch);
int pos = int_from_literal(args[2].arg);
return integerValue(owner, start, indexof(strfrom, strsearch, pos));
}
function builtineval_upcase(var owner, var start, var args)
{
return stringQuotedValue(owner, start,
upcase(string_from_literal_arg1(args)));
}
function builtineval_downcase(var owner, var start, var args)
{
return stringQuotedValue(owner, start,
downcase(string_from_literal_arg1(args)));
}
function builtineval_escape(var owner, var start, var args)
{
// This is a unescaped string. To get a escaped string
// in the output we need to escape twice.
return stringQuotedValue(owner, start,
escape(escape(string_from_literal_arg1(args))));
}
function builtin_sleep(var e, var owner, var start, string result, var args)
{
var expr = args[0];
string reg;
string type = expr.checkresult();
switch (type) {
case REGint:
reg = expr.emit_getint(e);
break;
case REGfloat:
reg = expr.emit_get(e);
break;
default:
if (expr.isnull())
reg = "0";
else {
string aux = expr.emit_get(e);
reg = DISCARD_NREG;
e.annotate(start);
e.emitset(reg, aux);
}
}
e.annotate(start);
e.emitarg1("sleep", reg);
}
function getbuiltins(var builder)
{
builder.add(new BuiltinExpr("int",
builtinexpr_intcast,
REGstring, REGraw1
));
builder.add(new BuiltinExpr("float",
builtinexpr_floatcast,
REGstring, REGraw1
));
builder.add(new BuiltinExpr("string",
builtinexpr_stringcast,
REGstring, REGraw1
));
builder.add(new BuiltinExpr("var",
builtinexpr_varcast,
REGvar, REGraw1
));
builder.add(new BuiltinFunction("die",
"die %1",
REGnone, REGstring
));
builder.add(new BuiltinFunction("exit",
"exit %1",
REGnone, REGint
));
builder.add(new BuiltinFunction("time",
"time %0",
REGint
));
builder.add(new BuiltinFunction("floattime",
"time %0",
REGfloat
));
builder.add(new BuiltinFunction("sleep",
builtin_sleep,
REGnone, REGraw1
));
builder.add(new BuiltinFunction("spawnw",
"spawnw %0, %1",
REGint, REGvar
));
builder.add(new BuiltinFunction("getstdin",
"getstdin %0",
REGvar
));
builder.add(new BuiltinFunction("getstdout",
"getstdout %0",
REGvar
));
builder.add(new BuiltinFunction("getstderr",
"getstderr %0",
REGvar
));
builder.add(new BuiltinFunction("open",
<<:
root_new %0, ["parrot";"FileHandle"]
%0."open"(%1)
:>>
, REGvar, REGstring
));
builder.add(new BuiltinFunction("open",
<<:
root_new %0, ["parrot";"FileHandle"]
%0."open"(%1,%2)
:>>
, REGvar, REGstring, REGstring
));
builder.add(new BuiltinFunction("Error",
<<:
root_new %0, ["parrot";"Exception"]
%0["message"] = %1
:>>
, REGvar, REGstring
));
builder.add(new BuiltinFunction("Error",
<<:
root_new %0, ["parrot";"Exception"]
%0["message"] = %1
%0["severity"] = %2
:>>
, REGvar, REGstring, REGint
));
builder.add(new BuiltinFunction("Error",
<<:
root_new %0, ["parrot";"Exception"]
%0["message"] = %1
%0["severity"] = %2
%0["type"] = %3
:>>
, REGvar, REGstring, REGint, REGint
));
builder.add(new BuiltinFunction("Error",
<<:
root_new %0, ["parrot";"Exception"]
%0["message"] = %1
%0["severity"] = %2
%0["type"] = %3
%0["payload"] = %4
:>>
, REGvar, REGstring, REGint, REGint, REGvar
));
builder.add(new BuiltinFunction("elements",
"elements %0, %1",
REGint, REGvar
));
builder.add(new BuiltinFunctionEval("length",
builtineval_length,
"length %0, %1",
REGint, REGstring
));
builder.add(new BuiltinFunctionEval("bytelength",
builtineval_bytelength,
"bytelength %0, %1",
REGint, REGstring
));
builder.add(new BuiltinFunctionEval("chr",
builtineval_chr,
<<:
chr $S0, %1
find_encoding $I0, "utf8"
trans_encoding %0, $S0, $I0
:>>
, REGstring, REGint
));
builder.add(new BuiltinFunctionEval("ord",
builtineval_ord,
"ord %0, %1",
REGint, REGstring
));
builder.add(new BuiltinFunctionEval("ord",
builtineval_ord,
"ord %0, %1, %2",
REGint, REGstring, REGint
));
builder.add(new BuiltinFunctionEval("substr",
builtineval_substr,
"substr %0, %1, %2",
REGstring, REGstring, REGint
));
builder.add(new BuiltinFunctionEval("substr",
builtineval_substr,
"substr %0, %1, %2, %3",
REGstring, REGstring, REGint, REGint
));
builder.add(new BuiltinFunction("replace",
"replace %0, %1, %2, %3, %4",
REGstring, REGstring, REGint, REGint, REGstring
));
builder.add(new BuiltinFunctionEval("indexof",
builtineval_indexof,
"index %0, %1, %2",
REGint, REGstring, REGstring
));
builder.add(new BuiltinFunctionEval("indexof",
builtineval_indexof_pos,
"index %0, %1, %2, %3",
REGint, REGstring, REGstring, REGint
));
builder.add(new BuiltinFunction("join",
"join %0, %1, %2",
REGstring, REGstring, REGvar
));
builder.add(new BuiltinFunctionEval("escape",
builtineval_escape,
"escape %0, %1",
REGstring, REGstring
));
builder.add(new BuiltinFunction("unescape",
<<:
$P0 = new ["String"]
$P0 = %1
%0 = $P0."unescape"("utf8")
:>>
, REGstring, REGstring
));
builder.add(new BuiltinFunction("unescape",
<<:
$P0 = new ["String"]
$P0 = %1
%0 = $P0."unescape"(%2)
:>>
, REGstring, REGstring, REGstring
));
builder.add(new BuiltinFunction("trans_encoding",
<<:
find_encoding $I0, %2
trans_encoding %0, %1, $I0
:>>
, REGstring, REGstring, REGstring
));
builder.add(new BuiltinFunction("encoding_name",
<<:
encoding $I0, %1
encodingname %0, $I0
:>>
, REGstring, REGstring
));
builder.add(new BuiltinFunctionEval("upcase",
builtineval_upcase,
"upcase %0, %1",
REGstring, REGstring
));
builder.add(new BuiltinFunctionEval("downcase",
builtineval_downcase,
"downcase %0, %1",
REGstring, REGstring
));
builder.add(new BuiltinFunction("titlecase",
"titlecase %0, %1",
REGstring, REGstring
));
builder.add(new BuiltinFunction("split",
"split %0, %1, %2",
REGvar, REGstring, REGstring
));
builder.add(new BuiltinFunction("chomp",
<<:
$P0 = get_root_global ["parrot";"String";"Utils"], "chomp"
%0 = $P0(%1)
:>>
, REGstring, REGstring
));
builder.add(new BuiltinFunction("chomp",
<<:
$P0 = get_root_global ["parrot";"String";"Utils"], "chomp"
%0 = $P0(%1, %2)
:>>
, REGstring, REGstring, REGstring
));
builder.add(new BuiltinFunction("push",
"push %1, %2",
REGnone, REGvar, REGany
));
builder.add(new BuiltinFunction("unshift",
"unshift %1, %2",
REGnone, REGvar, REGany
));
builder.add(new BuiltinFunction("pop_var",
"pop %0, %1",
REGvar, REGvar
));
builder.add(new BuiltinFunction("shift_var",
"shift %0, %1",
REGvar, REGvar
));
builder.add(new BuiltinFunction("pop_int",
"pop %0, %1",
REGint, REGvar
));
builder.add(new BuiltinFunction("shift_int",
"shift %0, %1",
REGint, REGvar
));
builder.add(new BuiltinFunction("pop_float",
"pop %0, %1",
REGfloat, REGvar
));
builder.add(new BuiltinFunction("shift_float",
"shift %0, %1",
REGfloat, REGvar
));
builder.add(new BuiltinFunction("pop_string",
"pop %0, %1",
REGstring, REGvar
));
builder.add(new BuiltinFunction("shift_string",
"shift %0, %1",
REGstring, REGvar
));
builder.add(new BuiltinFunction("abs",
Builtin_abs,
REGsame, REGraw1
));
builder.add(new BuiltinFunction("sqrt",
"sqrt %0, %1",
REGfloat, REGfloat
));
builder.add(new BuiltinFunction("pow",
"pow %0, %1, %2",
REGfloat, REGfloat, REGfloat
));
builder.add(new BuiltinFunction("exp",
"exp %0, %1",
REGfloat, REGfloat
));
builder.add(new BuiltinFunction("ln",
"ln %0, %1",
REGfloat, REGfloat
));
builder.add(new BuiltinFunction("sin",
"sin %0, %1",
REGfloat, REGfloat
));
builder.add(new BuiltinFunction("cos",
"cos %0, %1",
REGfloat, REGfloat
));
builder.add(new BuiltinFunction("tan",
"tan %0, %1",
REGfloat, REGfloat
));
builder.add(new BuiltinFunction("asin",
"asin %0, %1",
REGfloat, REGfloat
));
builder.add(new BuiltinFunction("acos",
"acos %0, %1",
REGfloat, REGfloat
));
builder.add(new BuiltinFunction("atan",
"atan %0, %1",
REGfloat, REGfloat
));
builder.add(new BuiltinFunction("atan",
"atan %0, %1, %2",
REGfloat, REGfloat, REGfloat
));
builder.add(new BuiltinFunction("sinh",
"sinh %0, %1",
REGfloat, REGfloat
));
builder.add(new BuiltinFunction("cosh",
"cosh %0, %1",
REGfloat, REGfloat
));
builder.add(new BuiltinFunction("tanh",
"tanh %0, %1",
REGfloat, REGfloat
));
builder.add(new BuiltinFunction("getinterp",
"getinterp %0",
REGvar
));
builder.add(new BuiltinFunction("getcontext",
/*
DEPRECATED: for compatibility only, use get_context instead.
*/
"get_context %0",
REGvar
));
builder.add(new BuiltinFunction("get_context",
"get_context %0",
REGvar
));
builder.add(new BuiltinFunction("get_class",
"get_class %0, %1",
REGvar, REGs_v
));
builder.add(new BuiltinFunction("typeof",
"typeof %0, %1",
REGvar, REGvar
));
builder.add(new BuiltinFunction("getattribute",
"getattribute %0, %1, %2",
REGvar, REGvar, REGstring
));
builder.add(new BuiltinFunction("getattribute",
"getattribute %0, %1, %2, %3",
REGvar, REGvar, REGvar, REGstring
));
builder.add(new BuiltinFunction("setattribute",
"setattribute %1, %2, %3, %4",
REGnone, REGvar, REGvar, REGstring, REGvar
));
builder.add(new BuiltinFunction("find_method",
"find_method %0, %1, %2",
REGvar, REGvar, REGstring
));
builder.add(new BuiltinFunction("callmethodwithargs",
"%0 = %1.%2(%3 :flat)",
REGvar, REGvar, REGvar, REGvar
));
builder.add(new BuiltinFunction("clone",
"clone %0, %1",
REGvar, REGvar
));
builder.add(new BuiltinFunction("compreg",
"compreg %0, %1",
REGvar, REGstring
));
builder.add(new BuiltinFunction("compreg",
"compreg %1, %2",
REGnone, REGstring, REGvar
));
builder.add(new BuiltinFunction("load_language",
<<:
load_language %1
compreg %0, %1
:>>
, REGvar, REGstring
));
builder.add(new BuiltinFunction("load_language",
<<:
load_language %1
compreg %0, %2
:>>
, REGvar, REGstring, REGstring
));
builder.add(new BuiltinFunction("loadlib",
"loadlib %0, %1",
REGvar, REGstring
));
builder.add(new BuiltinFunction("load_bytecode",
"load_bytecode %1",
REGnone, REGstring
));
builder.add(new BuiltinFunction("load_packfile",
"load_bytecode %0, %1",
REGvar, REGstring
));
builder.add(new BuiltinFunction("dlfunc",
"dlfunc %0, %1, %2, %3",
REGvar, REGvar, REGstring, REGstring
));
builder.add(new BuiltinFunction("sprintf",
"sprintf %0, %1, %2",
REGstring, REGstring, REGvar
));
builder.add(new BuiltinFunction("print",
Builtin_print,
REGnone, REGarglist
));
builder.add(new BuiltinFunction("say",
Builtin_say,
REGnone, REGarglist
));
builder.add(new BuiltinFunction("cry",
Builtin_cry,
REGnone, REGarglist
));
builder.add(new BuiltinFunction("__ASSERT__",
Builtin_ASSERT,
REGnone, REGraw1
));
builder.add(new BuiltinFunction("invoke",
Builtin_invoke,
REGvar, REGraw1
));
}
//*********************************************
// Auxiliar functions
//*********************************************
inline optimize_array(var arr)
{
int n = arr != null ? elements(arr) : 0;
for (int i = 0; i < n; ++i)
arr[i] = arr[i].optimize();
}
inline emit_array(var e, var arr)
{
for_each(arr, bindlast(bindmethod("emit"), e));
}
function parseDotted(var tk)
{
var list = [];
var t = tk.get();
if (t.isidentifier()) {
push(list, t);
while ((t = tk.get()).isop(".")) {
t = tk.get();
push(list, t);
}
}
tk.unget(t);
return list;
}
function parseList(var tk, var owner, var fn, string oper_end[optional])
/*
Parse a comma separated list of items.
Calls the passed fn to parse each item.
*/
{
var list = [];
var t;
do {
var value = fn(tk, owner);
push(list, value);
} while ((t = tk.get()).isop(","));
if (oper_end == null)
tk.unget(t);
else
if (! t.isop(oper_end))
SyntaxError("Unfinished argument list", t);
return list;
}
function parseListOrEmpty(var tk, var owner, var fn, string oper_end)
/*
Parse a comma separated list of items.
Calls the passed fn to parse each item.
Return null if empty.
*/
{
var t = tk.get();
if (t.isop(oper_end))
return null;
tk.unget(t);
var list = [];
do {
var value = fn(tk, owner);
push(list, value);
} while ((t = tk.get()).isop(","));
if (! t.isop(oper_end))
SyntaxError("Unfinished argument list", t);
return list;
}
// Helper to parse identifier lists using parseList
function parseIdentifier(var tk, var unused)
{
var t = tk.get();
if (!t.isidentifier())
ExpectedIdentifier(t);
return t;
}
function toIdentifierList(var tlist)
{
string list[];
transform(tlist, list, bindmethod("getidentifier"));
return list;
}
//*********************************************
// CommonBase
//*********************************************
class CommonBase
{
var start;
var owner;
function initbase(var start, var owner)
{
__ASSERT__(start instanceof Token);
__ASSERT__((owner instanceof Statement) ||
(owner instanceof ClassStatement) ||
(owner instanceof NamespaceBase));
self.start = start;
self.owner = owner;
}
function clone(var owner)
{
SyntaxError("Cannot use " + string(typeof(self)) + " in inline (yet)",
self);
}
function getstart()
{
return self.start;
}
function viewable()
{
if (self.start != null)
return self.start.viewable();
else
return "";
}
function annotate(var e)
{
e.annotate(self.start);
}
function getpath()
{
return self.owner.getpath();
}
function use_builtin(string name)
{
self.owner.use_builtin(name);
}
function generatesubid()
{
return self.owner.generatesubid();
}
function usesubid(string id)
{
self.owner.usesubid(id);
}
function addlocalfunction(var fn)
{
return self.owner.addlocalfunction(fn);
}
function scopesearch(var key, int flags)
{
return self.owner.scopesearch(key, flags);
}
function dowarnings()
{
return self.owner.dowarnings();
}
}
//*********************************************
// CollectValues
// Emit an get registers for a collection of
// values avoiding redundant setting of null
// registers.
//*********************************************
class CollectValues
{
var owner;
var e;
var pnull;
function CollectValues(var owner, var e)
{
self.owner = owner;
self.e = e;
}
function add(var value)
{
__ASSERT__(value instanceof Expr);
var e = self.e;
string valuereg;
if (value.isnull()) {
var pnull = self.pnull;
if (pnull == null) {
string regnull = self.owner.tempreg(REGvar);
e.emitnull(regnull);
self.pnull = pnull = regnull;
}
valuereg = pnull;
}
else
valuereg = value.emit_get(e);
return valuereg;
}
}
//*********************************************
// SimpleArgList
//*********************************************
// A list of arguments without modifiers.
class SimpleArgList
{
var args;
function SimpleArgList(var tk, var owner, string oper_end)
{
self.args = parseList(tk, owner, parseExpr, oper_end);
}
function clone(var owner)
{
:SimpleArgList cloned;
cloned.args = clone_array(self.args, owner);
return cloned;
}
function numargs()
{
return elements(self.args);
}
function getarg(int i)
{
var args = self.args;
return args[i];
}
function optimizeargs()
{
optimize_array(self.args);
}
function getargvalues(var e)
{
return transform(self.args, [], bindlast(bindmethod("emit_get"), e));
}
function emitargs(var e)
{
e.print(join(", ", self.getargvalues(e)));
}
}
//*********************************************
// Modifiers
//*********************************************
class Modifier
{
var name;
var args;
function clone(var owner)
{
var args = self.args;
return new Modifier(self.name,
args != null ? args.clone(owner) : null);
}
function getname() { return self.name; }
function numargs()
{
var args = self.args;
int nargs = args == null ? 0 : args.numargs();
return nargs;
}
function getarg(int argnum)
{
var args = self.args;
if (argnum >= args.numargs())
InternalError("Wrong modifier arg number");
return args.getarg(argnum);
}
function Modifier(string name, var args)
{
self.name = name;
if (args != null)
self.args = args;
}
function optimize()
{
if (self.args != null)
self.args.optimizeargs();
return self;
}
}
function parseModifier(var tk, var owner)
{
var t = tk.get();
string name = t.getidentifier();
t = tk.get();
var args;
if (t.isop("("))
args = new SimpleArgList(tk, owner, ")");
else
tk.unget(t);
return new Modifier(name, args);
}
class ModifierList
{
var list;
function ModifierList(var tk, var owner)
{
self.list = parseList(tk, owner, parseModifier, "]");
}
function clonemodifiers(var owner)
{
return clone_array(self.list, owner);
}
function optimize()
{
optimize_array(self.list);
}
function getlist() { return self.list; }
function pick(string name)
{
return find_if(self.list,
function (var mod) { return mod.getname() == name; }
);
}
}
//*********************************************
// Auxiliary classes and functions
//*********************************************
inline getparrotkey(var path) return string
{
string r;
if (elements(path) != 0)
r = "[ '" + string(join("'; '", path)) + "' ]";
return r;
}
function parseUsing(var t, var tk, var owner)
{
var taux = tk.get();
switch {
case taux.iskeyword("extern"):
return new ExternStatement(t, tk, owner);
case taux.iskeyword("static"):
return new StaticStatement(t, tk, owner);
case taux.iskeyword("namespace"):
return new UsingNamespaceStatement(taux, tk, owner);
default:
tk.unget(taux);
return new UsingStatement(t, tk, owner);
}
}
function parseSig(var start, var tk, var owner)
{
:SigParameterList params(tk, owner);
var t = tk.get();
if (! t.isop("="))
Expected("'='", t);
var expr = parseExpr(tk, owner);
return new MultiAssignStatement(start, owner, params, expr);
}
function parseClassSpecifier(var tk, var owner)
{
var t = tk.get();
if (t.isstring())
return new ClassSpecifierStr(owner, t);
if (t.isop("["))
return new ClassSpecifierParrotKey(tk, owner, t);
if (t.isidentifier())
return new ClassSpecifierId(tk, owner, t);
SyntaxError("Invalid class", t);
}
function parseStatement(var tk, var owner)
{
var t = tk.get();
var t2;
if (t.isop(";"))
return new EmptyStatement;
if (t.isop("{"))
return new CompoundStatement(t, tk, owner);
if (t.isop("${"))
return new PiropStatement(t, tk, owner);
if (t.isop(":")) {
var open = tk.get();
switch {
case open.isop("("):
return parseSig(t, tk, owner);
case open.isidentifier():
// EXPERIMENTAL
var cl = new ClassSpecifierId(tk, owner, open);
var tname = tk.get();
RequireIdentifier(tname);
// Violating encapsulation for fun and profit!
var varst = new VarStatement;
varst.initvarbase(tname, owner, tname, 0);
var newexpr = new NewQualifiedExpr;
newexpr.Expr(varst, tname);
newexpr.nskey = cl;
t = tk.get();
if (t.isop("(")) {
newexpr.parseinitializer(tk);
t = tk.get();
}
RequireOp(";", t);
varst.init = newexpr;
return varst;
default:
Unexpected("':'", t);
}
}
switch (t.checkkeyword()) {
case "using":
return parseUsing(t, tk, owner);
case "const":
return parseConst(t, tk, owner);
break;
case "volatile":
return parseVolatile(t, tk, owner);
break;
case "var":
t2 = tk.get();
tk.unget(t2);
if (! t2.isop("("))
return parseVar(t, tk, owner);
break;
case "string":
t2 = tk.get();
tk.unget(t2);
if (! t2.isop("("))
return parseString(t, tk, owner);
break;
case "int":
t2 = tk.get();
tk.unget(t2);
if (! t2.isop("("))
return parseInt(t, tk, owner);
break;
case "float":
t2 = tk.get();
tk.unget(t2);
if (! t2.isop("("))
return parseFloat(t, tk, owner);
break;
case "return":
return parseReturn(t, tk, owner);
case "yield":
return new YieldStatement(t, tk, owner);
case "goto":
return parseGoto(t, tk, owner);
case "if":
return new IfStatement(t, tk, owner);
case "while":
return new WhileStatement(t, tk, owner);
case "do":
return new DoStatement(t, tk, owner);
case "continue":
return new ContinueStatement(t, tk, owner);
case "break":
return new BreakStatement(t, tk, owner);
case "switch":
return parseSwitch(t, tk, owner);
case "for":
return parseFor(t, tk, owner);
case "throw":
return new ThrowStatement(t, tk, owner);
case "try":
return new TryStatement(t, tk, owner);
case "inline":
:InlineStatement inl(t, tk, owner);
owner.addinline(inl);
return new EmptyStatement;
}
if (t.isidentifier()) {
var t2 = tk.get();
if (t2.isop(":"))
return new LabelStatement(t, owner);
tk.unget(t2);
}
tk.unget(t);
return new ExprStatement(t, tk, owner);
}
//*********************************************
// Statement
//*********************************************
class Statement : CommonBase
{
function Statement(var start, var owner)
{
self.initbase(start, owner);
}
function isempty() { return false; }
function allowtailcall()
{
return self.owner.allowtailcall();
}
function createreg(string type)
{
return self.owner.createreg(type);
}
function tempreg(string type)
{
return self.owner.tempreg(type);
}
function freetemps()
{
self.owner.freetemps();
}
function genlabel()
{
return self.owner.genlabel();
}
function getlabel(string name)
{
return self.owner.getlabel(name);
}
function createlabel(var name)
{
return self.owner.createlabel(name);
}
function createconst(var name, string type, int flags [optional])
{
return self.owner.createconst(name, type, flags);
}
function createvar(var name, string type, int flags[optional])
{
return self.owner.createvar(name, type, flags);
}
function createvarused(var name, var data)
{
return self.owner.createvarused(name, data);
}
function createvarnamed(var name, string type, string pirname)
{
return self.owner.createvarnamed(name, type, pirname);
}
function getvar(var name)
{
return self.owner.getvar(name);
}
function checkclass(string name)
{
return self.owner.checkclass(name);
}
function usenamespace(var ns)
{
self.owner.usenamespace(ns);
}
function getouter()
{
// Returns the nearest outer scope. In this base case,
// just propagate it. Deriveds that are appropiate
// scopes will override.
return self.owner.getouter();
}
function getlexicalouter()
{
// Same as getouter but for outer able to contains lexicals,
// skiping inline blocks.
return self.owner.getlexicalouter();
}
function getcontinuelabel(var pos)
{
return self.owner.getcontinuelabel(pos);
}
function getbreaklabel(var pos)
{
return self.owner.getbreaklabel(pos);
}
function optimize()
{
InternalError("**checking**", self.start);
// Return unchanged by default
return self;
}
}
class EmptyStatement : Statement
{
function isempty() { return true; }
function clone(var owner) { return self; }
function annotate(var e)
{
InternalError("Attempt to annotate empty statement");
}
function optimize() { return self; }
function emit(var e)
{
// Do nothing
}
}
class MultiStatementBase
{
var statements;
function optimize()
{
var statements = self.statements;
int n = elements(statements);
int empty = true;
for (int i = 0; i < n; ++i) {
var st = statements[i].optimize();
if (empty && ! st.isempty())
empty = false;
statements[i] = st;
}
return empty ? new EmptyStatement : self;
}
}
class MultiStatement : MultiStatementBase
{
function MultiStatement(var st1, var st2)
{
self.statements = [st1, st2];
}
function clone(var owner)
{
:MultiStatement cloned;
cloned.statements = clone_array(self.statements, owner);
return cloned;
}
function isempty() { return false; }
function push(var statement)
{
push(self.statements, statement);
return self;
}
function emit(var e)
{
emit_array(e, self.statements);
}
}
inline addtomulti(var multi, var newst) return var
{
switch {
case multi == null:
return newst;
case multi instanceof MultiStatement:
return multi.push(newst);
default:
return new MultiStatement(multi, newst);
}
}
//*********************************************
// PiropStatement
//*********************************************
function parsePiropArg(var tk, var owner)
{
var arg;
var t = tk.get();
if (t.isop(":")) {
t = tk.get();
if (! t.isidentifier())
SyntaxError("Label expected", t);
arg = new Reflabel(owner, t);
}
else {
tk.unget(t);
arg = parseExpr(tk, owner);
}
return arg;
}
class PiropStatement : Statement
{
var opname;
var args;
function PiropStatement(var start, var tk, var owner)
{
self.Statement(start, owner);
var t = tk.get();
int dotted = t.isop(".");
if (dotted)
t = tk.get();
string opname = t.getidentifier();
self.opname = (dotted ? "." : "") + opname;
self.args = parseListOrEmpty(tk, owner, parsePiropArg, "}");
ExpectOp(";", tk);
}
function clone(var owner)
{
:PiropStatement cloned;
cloned.Statement(self.start, owner);
cloned.opname = self.opname;
cloned.args = clone_array(self.args, owner);
return cloned;
}
function optimize()
{
optimize_array(self.args);
return self;
}
function emit(var e)
{
string opname = self.opname;
self.annotate(e);
if (e.getDebug())
e.comment("pirop ", opname);
var args = self.args;
e.print(INDENT);
if (args == null)
e.say(opname);
else
e.say(opname, " ", join(", ",
transform(args, [], bindlast(bindmethod("emit_get"), e))
));
}
}
//*********************************************
// ExternStatement
//*********************************************
class ExternStatement : Statement
{
var path;
function ExternStatement(var start, var tk, var owner)
{
self.Statement(start, owner);
var path = parseDotted(tk);
if (elements(path) == 0)
ExpectedIdentifier(tk.get());
ExpectOp(";", tk);
self.path = toIdentifierList(path);
return self;
}
function optimize() { return self; }
function emit(var e)
{
self.annotate(e);
e.say(INDENT, "load_bytecode '", join("/", self.path), ".pbc'");
}
}
//*********************************************
// StaticStatement
//*********************************************
class StaticStatement : Statement
{
var names;
function StaticStatement(var start, var tk, var owner)
{
self.Statement(start, owner);
var names = parseList(tk, null, parseIdentifier, ";");
for (var name in names) {
string id = self.generatesubid();
self.createvarnamed(name, REGvar, id);
}
self.names = names;
}
function optimize()
{
return self;
}
function emit(var e)
{
self.annotate(e);
for (var name in self.names)
e.say(".const 'Sub' ", self.getvar(name).getreg(),
" = '", name, "'");
}
}
//*********************************************
// UsingStatement
//*********************************************
class UsingStatement : Statement
{
var path;
var subid;
function UsingStatement(var start, var tk, var owner)
{
self.Statement(start, owner);
var path = parseDotted(tk);
if (elements(path) == 0)
ExpectedIdentifier(tk.get());
ExpectOp(";", tk);
self.path = path;
}
function optimize()
{
var path = self.path;
var name = path[-1];
var symbol = self.scopesearch(path, 0);
switch {
case symbol == null:
if (elements(path) != 0) {
var p = clone(path);
p.pop();
var ns = self.scopesearch(p, SEARCH_NAMESPACE);
if (ns != null) {
symbol = ns.getvar(name);
if (symbol != null) {
self.createvarused(name, symbol);
return new EmptyStatement;
}
}
}
break;
case symbol instanceof FunctionStatement:
string subid = symbol.makesubid();
self.createvarnamed(name, REGvar, subid);
self.subid = subid;
self.usesubid(subid);
return self;
}
self.createvar(name, REGvar);
return self;
}
function emit(var e)
{
var path = self.path;
var name = path[-1];
var vdata = self.getvar(name);
if (self.subid == null) {
self.annotate(e);
string key;
if (elements(path) > 1) {
path.pop();
key = getparrotkey(path);
}
e.emitget_hll_global(vdata.getreg(), name.getidentifier(), key);
}
}
}
//*********************************************
// UsingNamespaceStatement
//*********************************************
// Common part of using namespace from different kinds of scopes.
function usingNamespace(var start, var tk, var owner)
{
var nskey = parseDotted(tk);
if (elements(nskey) == 0)
Expected("namespace identifier", start);
var nssym = owner.scopesearch(nskey, SEARCH_NAMESPACE);
if (nssym == null)
SyntaxError("unknow namespace", start);
owner.usenamespace(nssym);
}
class UsingNamespaceStatement : Statement
{
function UsingNamespaceStatement(var start, var tk, var owner)
{
self.Statement(start, owner);
usingNamespace(start, tk, owner);
ExpectOp(";", tk);
}
function optimize()
{
return self;
}
function emit(var e)
{
}
}
//*********************************************
// ExprStatement
//*********************************************
class ExprStatement : Statement
{
var expr;
function ExprStatement(var start, var tk, var owner)
{
self.Statement(start, owner);
self.expr = parseList(tk, self, parseExpr, ";");
}
function clone(var owner)
{
:ExprStatement cloned;
cloned.Statement(self.start, owner);
cloned.expr = clone_array(self.expr, owner);
return cloned;
}
function optimize()
{
optimize_array(self.expr);
return self;
}
function emit(var e)
{
for_each(self.expr, bindlast(bindmethod("emit_void"), e));
}
}
//*********************************************
// VarContainer
//*********************************************
class VarData
{
var type;
var reg;
var scope;
var flags;
var lexname;
function VarData(string type, var reg, var scope, int flags)
{
// Sanity check
__ASSERT__(type == REGint || type == REGfloat || type == REGstring ||
type == REGvar);
self.type = type;
self.reg = reg;
self.scope = scope;
self.flags = flags;
}
function setlex(string name)
{
self.lexname = name;
}
function createlex(string lexname)
{
// Set the lexname and make the variable a lexical
// in its outer.
self.setlex(lexname);
self.getscope().getouter().setlex(lexname, self.reg);
}
function gettype() { return self.type; }
function getreg() { return self.reg; }
function getscope() { return self.scope; }
function getvalue() { return self.value; }
function isconst() { return false; }
function getlex()
{
var lexname = self.lexname;
return lexname != null ? string(lexname) : null;
}
function getflags() { return self.flags; }
function issubid()
{
var reg = self.reg;
return reg != null && substr(reg, 0, 7) == "WSubId_";
}
}
class ConstantInternalFail
{
var name;
function ConstantInternalFail(var name)
{
self.name = name;
}
function get_string[vtable]()
{
InternalError("Attempt to use unexpanded constant!!!", self.name);
}
}
class VarData_const : VarData
{
var value;
function VarData_const(string type, var name, var scope, int flags)
{
self.VarData(type, new ConstantInternalFail(name), scope, flags);
}
function isconst() { return true; }
function setvalue(var value)
{
if (self.value != null)
InternalError("Attempt change value of constant!!!");
self.value = value;
}
}
class VarContainer
{
var locals;
var usednamespaces;
function VarContainer()
{
self.locals = {};
}
function createvar(var name, string type, int flags[optional])
{
var locals = self.locals;
string sname = name;
var exist = locals[sname];
if (exist != null)
Redeclared(name);
string reg = self.createreg(type);
:VarData data(type, reg, self, flags);
locals[sname] = data;
return data;
}
function createvarused(var name, var data)
{
var locals = self.locals;
string sname = name;
var exist = locals[sname];
if (exist != null)
Redeclared(name);
locals[sname] = data;
}
function createvarnamed(var name, string type, string pirname)
{
var locals = self.locals;
string sname = name;
var exist = locals[sname];
if (exist != null)
Redeclared(name);
locals[sname] = new VarData(type, pirname, self, 0);
}
function createconst(var name, string type, int flags[optional])
{
var locals = self.locals;
string sname = name;
var exist = locals[sname];
if (exist != null)
Redeclared(name);
:VarData_const data(type, name, self, flags);
locals[sname] = data;
return data;
}
function getlocalvar(var name)
{
var locals = self.locals;
return locals[string(name)];
}
function getusedvar(var name)
{
var sym;
for (var ns in self.usednamespaces) {
if ((sym = ns.getlocalvar(name)) != null)
return sym;
}
return null;
}
function getvar(var name)
{
__ASSERT__(name instanceof TokenIdentifier);
var sym;
if ((sym = self.getlocalvar(name)) != null)
return sym;
if ((sym = self.getusedvar(name)) != null)
return sym;
var owner = self.owner;
if (owner != null)
return owner.getvar(name);
return null;
}
function makelexical(var vardesc)
{
var lexowner = self.getlexicalouter();
string lexname = lexowner.createlex(vardesc);
return lexname;
}
function makelexicalself()
{
const string lexname = "'__WLEX_self'";
self.setlex(lexname, SELF);
return lexname;
}
}
//*********************************************
// BlockStatement
//*********************************************
// A BlockStatement is a Statement that can have local variables.
class BlockStatement : VarContainer, Statement
{
var inlines;
function BlockStatement(var start, var owner)
{
self.Statement(start, owner);
self.VarContainer();
}
function addinline(var inl)
{
__ASSERT__(inl instanceof InlineStatement);
string name = inl.name;
var inlines = self.inlines;
if (inlines != null)
inlines[name] = inl;
else
self.inlines = { name : inl };
}
function scopesearch(var key, int flags)
{
if (flags == 0 && elements(key) == 1) {
var inlines = self.inlines;
if (inlines != null) {
var inl = inlines[string(key[0])];
if (inl != null)
return inl;
}
}
return self.owner.scopesearch(key, flags);
}
}
//*********************************************
// Expr
//*********************************************
class Expr : CommonBase
{
function Expr(var owner, var start)
{
self.initbase(start, owner);
}
function issimple() { return false; }
function isliteral() { return false; }
function isintegerliteral() { return false; }
function isintegerzero() { return false; }
function isfloatliteral() { return false; }
function isstringliteral() { return false; }
function isidentifier() { return false; }
function isnull() { return false; }
function hascompilevalue() { return false; }
function isnegable() { return false; }
function tempreg(string type)
{
return self.owner.tempreg(type);
}
function genlabel()
{
return self.owner.genlabel();
}
function optimize()
{
// By default return same expression unchanged
return self;
}
function cantailcall() { return false; }
function emit_init(var e, string result)
{
// By default does the same as plane emit, some expressions
// can override it for optimization.
self.emit(e, result);
}
function emit_get(var e)
{
string reg = self.tempreg(self.checkresult());
self.emit(e, reg);
return reg;
}
function emit_void(var e)
{
string type = self.checkresult();
string reg;
switch (type) {
case REGint: reg = DISCARD_IREG; break;
case REGfloat: reg = DISCARD_NREG; break;
case REGstring: reg = DISCARD_SREG; break;
case REGvar: reg = DISCARD_PREG; break;
default:
InternalError("Unexcpected emit_void with type '" + type + "'", self.start);
}
self.emit(e, reg);
}
function emit_get_nonull(var e)
{
// Must be overriden by any possible null
return self.emit_get(e);
}
function emit_getint(var e)
{
string reg = self.emit_get_nonull(e);
if (self.checkresult() != REGint) {
string aux = self.tempreg(REGint);
self.annotate(e);
e.emitset(aux, reg);
reg = aux;
}
return reg;
}
function emit_getvar(var e)
{
string type = self.checkresult();
string reg = self.emit_get(e);
if (type != REGvar) {
string auxreg = reg;
reg = self.tempreg(REGvar);
e.emitbox(reg, auxreg);
}
return reg;
}
function emit_assign_get(var e, var expr)
{
NoLeftSide(self);
}
}
class SimpleExpr : Expr
{
function issimple() { return true; }
}
class FinalExpr : Expr
// An expr crated during optimize, never parsed
{
function optimize()
{
InternalError("misuse of " + string(typeof(self)), self);
}
}
//**********************************************************************
class FunctionExpr : Expr
{
var fn;
function FunctionExpr(var tk, var owner, var start)
{
self.Expr(owner, start);
var t = tk.get();
if (!t.isop("("))
Expected("anonymous function", t);
self.fn = new LocalFunctionStatement(start, tk, owner);
}
function clone(var owner)
{
:FunctionExpr cloned;
cloned.Expr(owner, self.start);
cloned.fn = self.fn.clone(owner);
return cloned;
}
function checkresult() { return REGvar; }
function optimize()
{
self.fn = self.fn.optimize();
self.usesubid(self.fn.getsubid());
return self;
}
function emit(var e, string result)
{
self.annotate(e);
var fn = self.fn;
string subid = fn.getsubid();
if (fn.needclosure())
e.emitarg2("newclosure", result, subid);
else
e.emitset(result, subid);
}
function emit_void(var e)
{
}
}
//*********************************************
// Condition class, contains and works with expressions used in
// conditional operators and statements.
const int CONDisruntime = 0, CONDistrue = 1, CONDisfalse = 2;
// Abstract base classes for expressions with specialized code generation
// for conditions.
class ConditionFriendlyIf
{
function emit_if(var e, string labeltrue)
{
InternalError(__FUNCTION__ + " not overriden", self);
}
}
class ConditionFriendlyElse
{
function emit_else(var e, string labelfalse)
{
InternalError(__FUNCTION__ + " not overriden", self);
}
}
class ConditionFriendlyExpr : ConditionFriendlyIf, ConditionFriendlyElse
{
}
class Condition
{
var condexpr;
function set(var expr)
{
self.condexpr = expr;
return self;
}
function optimize_condition()
{
self.condexpr = self.condexpr.optimize();
}
function optimize()
{
self.condexpr = self.condexpr.optimize();
return self;
}
function getvalue()
{
var condexpr = self.condexpr;
if (condexpr.isintegerliteral())
return condexpr.isintegerzero() ? CONDisfalse : CONDistrue;
return CONDisruntime;
}
function emit_if(var e, string labeltrue, string labelfalse)
{
var condexpr = self.condexpr;
if (condexpr instanceof ConditionFriendlyIf)
condexpr.emit_if(e, labeltrue);
else {
string reg = condexpr.emit_get(e);
switch (condexpr.checkresult()) {
case REGvar:
e.emitif_null(reg, labelfalse);
case REGstring:
case REGint:
case REGfloat:
e.emitif(reg, labeltrue);
break;
default:
InternalError("Invalid if condition");
}
}
}
function emit_else(var e, string labelfalse)
{
var condexpr = self.condexpr;
if (condexpr instanceof ConditionFriendlyElse)
condexpr.emit_else(e, labelfalse);
else {
string reg = condexpr.emit_get(e);
switch (condexpr.checkresult()) {
case REGvar:
e.emitif_null(reg, labelfalse);
case REGstring:
case REGint:
case REGfloat:
e.emitunless(reg, labelfalse);
break;
default:
InternalError("Invalid if condition");
}
}
}
}
//*********************************************
class Literal : SimpleExpr
{
function isliteral() { return true; }
function hascompilevalue() { return true; }
function emit_void(var e) { }
}
//*********************************************
class IntegerLiteral : Literal
{
var intval;
function IntegerLiteral(var owner, var start, int value)
{
self.Expr(owner, start);
self.intval = value;
}
function clone(var owner)
{
return new IntegerLiteral(owner, self.start, self.intval);
}
function isintegerliteral() { return true; }
function isintegerzero()
{
return int(self.intval) == 0;
}
function checkresult() { return REGint; }
function getIntegerValue()
{
return int(self.intval);
}
function getFloatValue()
{
return float(self.intval);
}
function getStringValue()
{
return string(self.intval);
}
function getLiteralInteger()
{
return self;
}
function getLiteralFloat()
{
return floatValue(self.owner, self.start, self.intval);
}
function getLiteralString()
{
return stringQuotedValue(self.owner, self.start, self.intval);
}
function emit(var e, string result)
{
int value = self.getIntegerValue();
if (value == 0)
e.emitnull(result);
else
e.emitset(result, value);
}
function emit_get(var e)
{
return self.getIntegerValue();
}
function emit_getint(var e)
{
// Just a shortcut
return self.getIntegerValue();
}
}
//*********************************************
class FloatLiteral : Literal
{
var numval;
function FloatLiteral(var owner, var start)
{
self.Expr(owner, start);
self.numval = start;
}
function clone(var owner)
{
return new FloatLiteral(owner, self.numval);
}
function isfloatliteral() { return true; }
function checkresult() { return REGfloat; }
function getIntegerValue()
{
int ival = self.getFloatValue();
return ival;
}
function getFloatValue()
{
float value = self.numval.getfloatvalue();
return value;
}
function getStringValue()
{
string str = self.getFloatValue();
return str;
}
function getLiteralInteger()
{
return integerValue(self.owner, self.start, self.getFloatValue());
}
function getLiteralFloat()
{
return self;
}
function getLiteralString()
{
return stringQuotedValue(self.owner, self.start, self.getFloatValue());
}
function emit(var e, string result)
{
string n = self.emit_get(e);
e.emitset(result, n);
}
function emit_get(var e)
{
float value = self.getFloatValue();
return floatAsString(value);
}
}
//*********************************************
class StringLiteral : Literal
{
var strval;
function StringLiteral(var owner, var start)
{
self.Expr(owner, start);
self.strval = start;
}
function clone(var owner)
{
return new StringLiteral(owner, self.strval);
}
function isstringliteral() { return true; }
function checkresult() { return REGstring; }
function getPirString()
{
var strtok = self.strval;
string str = strtok.getPirString();
return str;
}
function getIntegerValue()
{
int ival = self.getStringValue();
return ival;
}
function getFloatValue()
{
float nval = self.getStringValue();
return nval;
}
function getStringValue()
{
var strtok = self.strval;
string str = strtok.str;
if (strtok instanceof TokenQuoted)
str = unescape(str);
return str;
}
function getLiteralInteger()
{
return integerValue(self.owner, self.start, self.getStringValue());
}
function getLiteralFloat()
{
return floatValue(self.owner, self.start, self.getStringValue());
}
function getLiteralString()
{
return self;
}
function emit(var e, string result)
{
e.emitset(result, self.getPirString());
}
function emit_get(var e)
{
return self.getPirString();
}
}
//*********************************************
function concat_literal(var lexpr, var rexpr)
{
__ASSERT__(lexpr.isstringliteral());
__ASSERT__(rexpr.isstringliteral());
var etok = lexpr.strval;
var rtok = rexpr.strval;
// If both are single quoted, result is single quoted.
// If one is double quoted, result is double quoted.
var t = etok.issinglequoted() && rtok.issinglequoted()
?
new TokenSingleQuoted(etok.file, etok.line,
string(etok.str) + string(rtok.str))
:
new TokenQuoted(etok.file, etok.line,
string(etok.getasquoted()) + string(rtok.getasquoted()));
return new StringLiteral(lexpr.owner, t);
}
//*********************************************
class FunctionId : FinalExpr
{
var subid;
function FunctionId(var owner, var name, string id)
{
self.Expr(owner, name);
self.subid = id;
}
function checkresult() { return REGvar; }
function emitvar(var e, string result)
{
var name = self.start;
var sym = self.scopesearch([name], 0);
__ASSERT__(sym != null && (sym instanceof FunctionStatement));
var path = sym.owner.getpath();
self.annotate(e);
e.emitget_hll_global(result, name, path.getparrotkey());
}
function emit_get(var e)
{
return self.subid;
}
function emit(var e, string result)
{
self.annotate(e);
e.emitset(result, self.subid);
}
}
class FunctionRef : FinalExpr
{
var sym;
function FunctionRef(var owner, var start, var sym)
{
self.Expr(owner, start);
self.sym = sym;
}
function checkresult() { return REGvar; }
function emit(var e, string result)
{
var sym = self.sym;
var path = sym.owner.getpath();
self.annotate(e);
path.emit_get_global(e, self.owner, result, sym.name);
}
}
class NullExpr : FinalExpr
{
function NullExpr(var owner, var start)
{
self.Expr(owner, start);
}
function isnull() { return true; }
function emit_get_nonull(var e)
{
SyntaxError("Invalid 'null' usage", self);
}
function checkresult() { return REGvar; }
function emit_void(var e) { }
function emit(var e, string result)
{
self.annotate(e);
e.emitnull(result);
}
}
class IdentifierExpr : SimpleExpr
{
var name;
function isidentifier() { return true; }
function IdentifierExpr(var owner, var name)
{
__ASSERT__(name instanceof TokenIdentifier);
self.Expr(owner, name);
self.name = name;
}
function clone(var owner)
{
return new IdentifierExpr(owner, self.name);
}
function isnull()
{
var name = self.name;
if (self.owner.getvar(name) != null)
return false;
return name.iskeyword(NULL);
}
function emit_get_nonull(var e)
{
if (self.isnull())
SyntaxError("Invalid 'null' usage", self);
return self.emit_get(e);
}
function checkresult()
{
var name = self.name;
var desc = self.owner.getvar(name);
if (desc != null)
return desc.gettype();
else {
switch (name) {
case SELF:
case NULL:
return REGvar;
default:
return "";
}
}
}
function getName()
{
string s = self.name;
return s;
}
function checkVar()
{
return self.owner.getvar(string(self.name));
}
function checkIdentifier()
{
var name = self.name;
if (name == null)
InternalError("Bad thing");
var desc = self.owner.getvar(name);
string s;
if (desc == null) {
switch (name) {
case SELF:
case NULL:
s = name; break;
default:
s = "";
}
}
else
s = desc.getreg();
return s;
}
function getIdentifier()
{
var value = self.checkIdentifier();
if (value == "")
UndefinedVariable(self.name, self);
return value;
}
function optimize()
{
var name = self.name;
var owner = self.owner;
var desc = owner.getvar(name);
if (desc != null) {
__ASSERT__(desc instanceof VarData);
if (desc.isconst())
return desc.getvalue();
int flags = desc.getflags();
if (flags & VAR_is_volatile) {
if (flags & VAR_is_lexical)
return new LexicalVolatileExpr(self, desc);
}
else {
// const Sub previous definition may be in a code path
// not taken. Better allow redundant definitions than
// leave undefined depending on runtime conditions.
// TODO: improve this.
var reg = desc.getreg();
if (reg != null && substr(reg, 0, 7) == "WSubId_")
return new FunctionId(owner, name, reg);
}
}
else {
var sym = self.scopesearch([name], 0);
switch {
case sym == null:
if (name.iskeyword(NULL))
return new NullExpr(self.owner, name);
break;
case sym instanceof FunctionStatement:
if (!sym.ismulti()) {
string id = sym.makesubid();
self.usesubid(id);
owner.createvarnamed(name, REGvar, id);
return new FunctionId(owner, name, id);
}
else
return new FunctionRef(owner, name, sym);
break;
case sym instanceof FunctionExtern:
return new FunctionRef(owner, name, sym);
case sym instanceof InlineStatement:
return new InlineRef(owner, name, sym);
}
}
return self;
}
function emit(var e, string result)
{
string reg = self.emit_get(e);
self.annotate(e);
e.emitset(result, reg);
}
function emit_void(var e) { }
function emit_get(var e)
{
string reg;
reg = self.getIdentifier();
var desc = self.owner.getvar(self.name);
int flags = desc == null ? 0 : desc.getflags();
if (flags & VAR_is_volatile) {
if (flags & VAR_is_lexical) {
string lexname = desc.getlex();
if (lexname != null)
self.annotate(e);
e.emitfind_lex(reg, lexname);
}
}
return reg;
}
function emit_assign_get(var e, var expr)
{
self.annotate(e);
if (self.isnull())
SyntaxError("Cannot assign to null", self);
string typeleft = self.checkresult();
string lreg = self.getIdentifier();
if (expr.isnull()) {
switch (typeleft) {
case REGstring:
case REGvar:
e.emitnull(lreg);
break;
default:
SyntaxError("Can't assign null to that type", self);
}
}
else if (expr instanceof IndexExpr)
expr.emit(e, lreg);
else {
string typeright = expr.checkresult();
if (typeright == REGnone)
SyntaxError("Can't assign from void expression", self);
if (typeleft == typeright) {
expr.emit(e, lreg);
}
else {
string rreg = expr.emit_get(e);
self.annotate(e);
if (typeleft == REGvar && typeright != REGvar)
e.emitbox(lreg, rreg);
else
e.emitset(lreg, rreg);
}
}
return lreg;
}
}
//*********************************************
class LexicalVolatileExpr : FinalExpr
{
var desc;
function LexicalVolatileExpr(var idexpr, var desc)
{
self.Expr(idexpr.owner, idexpr.start);
self.desc = desc;
}
function checkresult()
{
return self.desc.gettype();
}
function emit_get(var e)
{
var desc = self.desc;
string lexname = desc.getlex();
string reg = self.owner.tempreg(desc.gettype());
e.emitfind_lex(reg, lexname);
return reg;
}
function emit(var e, string result)
{
self.annotate(e);
string reg = self.emit_get(e);
e.emitset(result, reg);
}
function emit_assign_get(var e, var expr)
{
var owner = self.owner;
var desc = self.desc;
string typelex = desc.gettype();
string lreg;
switch {
case expr.isnull():
lreg = owner.tempreg(typelex);
e.emitnull(lreg);
break;
case expr instanceof IndexExpr:
lreg = owner.tempreg(typelex);
expr.emit(e, lreg);
break;
default:
if (typelex == REGvar)
lreg = expr.emit_getvar(e);
else if (typelex == expr.checkresult())
lreg = expr.emit_get(e);
else {
lreg = owner.tempreg(typelex);
expr.emit(e, lreg);
}
}
e.emitstore_lex(desc.getlex(), lreg);
return lreg;
}
function emit_store(var e, string reg)
{
e.emitstore_lex(self.desc.getlex(), reg);
}
}
//*********************************************
class OpExpr : Expr
{
function initop(var owner, var start)
{
self.Expr(owner, start);
}
}
//*********************************************
class OpNamespaceExpr : OpExpr
{
var key;
function OpNamespaceExpr(var tk, var owner, var start)
{
self.initop(owner, start);
self.key = parseDotted(tk);
if (elements(self.key) == 0)
Expected("namespace identifier", start);
}
function checkresult() { return REGvar; }
function emit(var e, string result)
{
var owner = self.owner;
var sym = owner.scopesearch(self.key, SEARCH_NAMESPACE);
if (sym == null)
SyntaxError("unknown namespace", self);
var path = sym.getpath();
path.emit_get_namespace(e, owner, result);
}
}
//*********************************************
class OpClassExpr : OpExpr
{
var clspec;
function OpClassExpr(var tk, var owner, var start)
{
self.initop(owner, start);
self.clspec = parseClassSpecifier(tk, owner);
}
function checkresult() { return REGvar; }
function get_class_raw_key()
{
var owner = self.owner;
var clspec = self.clspec;
var clkey;
// TODO: cover more class specifiers
switch {
case clspec instanceof ClassSpecifierId:
clkey = clspec.checknskey(owner);
return clkey != null ? clkey.path : null;
case clspec instanceof ClassSpecifierParrotKey:
clkey = clspec.checknskey(owner);
return clkey != null ? clkey.path : null;
case clspec instanceof ClassSpecifier:
SyntaxError(string(typeof(clspec)) + " not supported yet here",
clspec.start);
}
InternalError("Unexpected class key", clspec.start);
}
function emit(var e, string result)
{
var owner = self.owner;
var clspec = self.clspec;
// TODO: cover more class specifiers
switch {
case clspec instanceof ClassSpecifierId:
var clkey = clspec.checknskey(owner);
if (clkey != null) {
clkey.emit_get_class(e, owner, result);
return;
}
break;
}
e.print(INDENT + "get_class ", result, ", ");
clspec.emit(e, self.owner);
e.say();
}
}
//*********************************************
class OpUnaryExpr : OpExpr
{
var subexpr;
function OpUnaryExpr(var owner, var start, var subexpr)
{
self.initop(owner, start);
self.subexpr = subexpr;
}
function cloneunary(var cloned, var owner)
{
cloned.OpUnaryExpr(owner, self.start, self.subexpr.clone(owner));
return cloned;
}
function optimizearg()
{
self.subexpr = self.subexpr.optimize();
}
function optimize()
{
self.optimizearg();
return self;
}
}
//*********************************************
class OpBinaryExpr : OpExpr
{
var lexpr;
var rexpr;
function initbinary(var owner, var start, var lexpr, var rexpr)
{
self.initop(owner, start);
self.lexpr = lexpr;
self.rexpr = rexpr;
}
function set(var owner, var t, var lexpr, var rexpr)
{
self.initbinary(owner, t, lexpr, rexpr);
return self;
}
function clonebinary(var cloned, var owner)
{
cloned.initbinary(owner, self.start,
self.lexpr.clone(owner), self.rexpr.clone(owner));
return cloned;
}
function setfrom(var from)
{
return self.set(from.owner, from.start, from.lexpr, from.rexpr);
}
function optimizearg()
{
self.lexpr = self.lexpr.optimize();
self.rexpr = self.rexpr.optimize();
}
function optimize()
{
self.optimizearg();
return self;
}
function emit_intleft(var e)
{
return self.lexpr.emit_getint(e);
}
function emit_intright(var e)
{
return self.rexpr.emit_getint(e);
}
}
//*********************************************
class OpBinaryIntExpr : OpBinaryExpr
{
function checkresult()
{
return REGint;
}
function optimize()
{
self.optimizearg();
var lexpr = self.lexpr;
var rexpr = self.rexpr;
if (lexpr.isintegerliteral() && rexpr.isintegerliteral()) {
int li = lexpr.getIntegerValue();
int ri = rexpr.getIntegerValue();
return integerValue(self.owner, self.start, self.do_op(li, ri));
}
return self;
}
}
//*********************************************
class OpDelExBase : OpUnaryExpr
{
function checkresult()
{
return REGint;
}
function optimize()
{
self.optimizearg();
var expr = self.subexpr;
if ((!(expr instanceof IndexExpr)) || expr.checkresult() == REGstring)
SyntaxError("invalid operand", self);
return self;
}
}
class OpDeleteExpr : OpDelExBase
{
function OpDeleteExpr(var owner, var start, var subexpr)
{
self.OpUnaryExpr(owner, start, subexpr);
}
function emit_void(var e)
{
var expr = self.subexpr;
expr.emit_prep(e);
self.annotate(e);
e.print(INDENT + "delete ");
expr.emit_aux(e);
e.say();
}
function emit(var e, string result)
{
self.emit_void(e);
e.emitset(result, "1");
}
}
class OpExistsExpr : OpDelExBase
{
function OpExistsExpr(var owner, var start, var subexpr)
{
self.OpUnaryExpr(owner, start, subexpr);
}
function emit(var e, string result)
{
var expr = self.subexpr;
expr.emit_prep(e);
self.annotate(e);
e.print(INDENT + "exists ", result, ", ");
expr.emit_aux(e);
e.say();
}
}
class OpDefinedExpr : OpDelExBase
{
function OpDefinedExpr(var owner, var start, var subexpr)
{
self.OpUnaryExpr(owner, start, subexpr);
}
function optimize()
{
self.optimizearg();
var expr = self.subexpr;
if (expr.checkresult() != REGvar || expr.isnull())
SyntaxError("invalid operand", self);
return self;
}
function emit(var e, string result)
{
var expr = self.subexpr;
if (expr instanceof IndexExpr) {
expr.emit_prep(e);
self.annotate(e);
e.print(INDENT + "defined ", result, ", ");
expr.emit_aux(e);
e.say();
}
else {
string reg = expr.emit_get(e);
e.say(INDENT + " defined ", result, ", ", reg);
}
}
}
//*********************************************
class OpUnaryMinusExpr : OpUnaryExpr
{
function OpUnaryMinusExpr(var owner, var start, var subexpr)
{
self.OpUnaryExpr(owner, start, subexpr);
}
function clone(var owner)
{
return self.cloneunary(new OpUnaryMinusExpr, owner);
}
function checkresult()
{
return self.subexpr.checkresult();
}
function set(var owner, var t, var subexpr)
{
self.OpUnaryExpr(owner, t, subexpr);
return self;
}
function optimize()
{
var subexpr = self.subexpr;
self.optimizearg();
if (subexpr.isintegerliteral()) {
int i = subexpr.getIntegerValue();
return integerValue(self.owner, subexpr.start, -i);
}
if (subexpr.isfloatliteral()) {
float n = subexpr.getFloatValue();
return floatValue(self.owner, subexpr.start, -n);
}
return self;
}
function emit(var e, string result)
{
string reg = self.subexpr.emit_get(e);
self.annotate(e);
e.emitarg2("neg", result, reg);
}
}
//*********************************************
class OpNotExpr : OpUnaryExpr, ConditionFriendlyExpr
{
function OpNotExpr(var owner, var start, var subexpr)
{
self.OpUnaryExpr(owner, start, subexpr);
}
function clone(var owner)
{
return self.cloneunary(new OpNotExpr, owner);
}
function isnegable() { return true; }
function checkresult()
{
return REGint;
}
function set(var owner, var t, var subexpr)
{
self.OpUnaryExpr(owner, t, subexpr);
return self;
}
function optimize()
{
self.optimizearg();
var subexpr = self.subexpr;
if (subexpr.isintegerliteral()) {
int n = subexpr.getIntegerValue();
return integerValue(self.owner, subexpr.start, ! n);
}
if (subexpr.isnegable())
return subexpr.negated();
return self;
}
function negated()
{
return self.subexpr;
}
function emit(var e, string result)
{
var subexpr = self.subexpr;
string reg = subexpr.emit_get(e);
self.annotate(e);
switch (subexpr.checkresult()) {
case REGint:
e.emitarg2("not", result, reg);
break;
case REGstring:
// No appropiate op for string. Do it the hard way.
e.emitset(result, 0);
string label = self.genlabel();
e.emitif(reg, label);
e.emitinc(result);
e.emitlabel(label);
break;
case REGvar:
e.emitarg2("isfalse", result, reg);
break;
default:
e.emitarg2("isfalse", result, reg);
}
}
function emit_if(var e, string labelif)
{
string reg = self.subexpr.emit_get(e);
self.annotate(e);
e.emitunless(reg, labelif);
}
function emit_else(var e, string labelelse)
{
string reg = self.subexpr.emit_get(e);
self.annotate(e);
e.emitif(reg, labelelse);
}
}
//*********************************************
class OpBinNotExpr : OpUnaryExpr
{
function OpBinNotExpr(var owner, var start, var subexpr)
{
self.OpUnaryExpr(owner, start, subexpr);
}
function clone(var owner)
{
return self.cloneunary(new OpBinNotExpr, owner);
}
function checkresult()
{
return REGint;
}
function set(var owner, var t, var subexpr)
{
self.OpUnaryExpr(owner, t, subexpr);
return self;
}
function optimize()
{
self.optimizearg();
var subexpr = self.subexpr;
if (subexpr.isintegerliteral()) {
int n = subexpr.getIntegerValue();
return integerValue(self.owner, subexpr.start, ~ n);
}
return self;
}
function emit(var e, string result)
{
var subexpr = self.subexpr;
string reg = subexpr.emit_getint(e);
self.annotate(e);
e.emitarg3("bxor", result, reg, -1);
}
}
//*********************************************
class OpIncDec : OpUnaryExpr
{
function checkresult()
{
return self.subexpr.checkresult();
}
function iflexical(var e, string reg)
{
var expr = self.subexpr;
if (expr instanceof LexicalVolatileExpr)
expr.emit_store(e, reg);
}
}
//*********************************************
class OpPreIncDec : OpIncDec
{
function emit(var e, string result)
{
string reg = self.emit_get(e);
e.emitset(result, reg);
}
function emit_void(var e)
{
self.emit_get(e);
}
}
class OpPreIncExpr : OpPreIncDec
{
function OpPreIncExpr(var owner, var start, var subexpr)
{
self.OpUnaryExpr(owner, start, subexpr);
}
function clone(var owner)
{
return self.cloneunary(new OpPreIncExpr, owner);
}
function emit_get(var e)
{
string reg = self.subexpr.emit_get(e);
self.annotate(e);
e.emitinc(reg);
self.iflexical(e, reg);
return reg;
}
}
class OpPreDecExpr : OpPreIncDec
{
function OpPreDecExpr(var owner, var start, var subexpr)
{
self.OpUnaryExpr(owner, start, subexpr);
}
function clone(var owner)
{
return self.cloneunary(new OpPreDecExpr, owner);
}
function emit_get(var e)
{
string reg = self.subexpr.emit_get(e);
self.annotate(e);
e.emitdec(reg);
self.iflexical(e, reg);
return reg;
}
}
//*********************************************
class OpPostIncExpr : OpIncDec
{
function OpPostIncExpr(var owner, var start, var subexpr)
{
self.OpUnaryExpr(owner, start, subexpr);
}
function clone(var owner)
{
return self.cloneunary(new OpPostIncExpr, owner);
}
function emit(var e, string result)
{
string reg = self.subexpr.emit_get(e);
self.annotate(e);
if (self.checkresult() == REGvar) {
string aux = self.tempreg(REGvar);
e.emitarg2("clone", aux, reg);
e.emitset(result, aux);
}
else
e.emitset(result, reg);
e.emitinc(reg);
self.iflexical(e, reg);
}
function emit_get(var e)
{
string reg = self.subexpr.emit_get(e);
self.annotate(e);
string result = self.tempreg(self.checkresult());
if (self.checkresult() == REGvar)
e.emitarg2("clone", result, reg);
else
e.emitset(result, reg);
e.emitinc(reg);
self.iflexical(e, reg);
return result;
}
function emit_void(var e)
{
string reg = self.subexpr.emit_get(e);
self.annotate(e);
e.emitinc(reg);
self.iflexical(e, reg);
}
}
class OpPostDecExpr : OpIncDec
{
function OpPostDecExpr(var owner, var start, var subexpr)
{
self.OpUnaryExpr(owner, start, subexpr);
}
function clone(var owner)
{
return self.cloneunary(new OpPostDecExpr, owner);
}
function emit(var e, string result)
{
string reg = self.subexpr.emit_get(e);
self.annotate(e);
if (self.checkresult() == REGvar) {
string aux = self.tempreg(REGvar);
e.emitarg2("clone", aux, reg);
e.emitset(result, aux);
}
else
e.emitset(result, reg);
e.emitdec(reg);
self.iflexical(e, reg);
return reg;
}
function emit_get(var e)
{
string reg = self.subexpr.emit_get(e);
self.annotate(e);
string result = self.tempreg(self.checkresult());
if (self.checkresult() == REGvar)
e.emitarg2("clone", result, reg);
else
e.emitset(result, reg);
e.emitdec(reg);
self.iflexical(e, reg);
return result;
}
function emit_void(var e)
{
string reg = self.subexpr.emit_get(e);
self.annotate(e);
e.emitdec(reg);
self.iflexical(e, reg);
}
}
//*********************************************
class OpBaseAssignExpr : Expr
{
var lexpr;
var rexpr;
function set(var owner, var start, var lexpr, var rexpr)
{
self.Expr(owner, start);
self.lexpr = lexpr;
self.rexpr = rexpr;
return self;
}
function cloneassign(var cloned, var owner)
{
return cloned.set(owner, self.start,
self.lexpr.clone(owner), self.rexpr.clone(owner));
}
function checkresult()
{
return self.lexpr.checkresult();
}
function optimize_base()
{
self.lexpr = self.lexpr.optimize();
self.rexpr = self.rexpr.optimize();
return self;
}
function optimize()
{
return self.optimize_base();
}
function checkleft()
{
var lexpr = self.lexpr;
if (lexpr.isnull() || lexpr.isliteral())
NoLeftSide(lexpr);
}
function emit(var e, string result)
{
string reg = self.emit_get(e);
self.annotate(e);
e.emitset(result, reg);
}
function emit_void(var e)
{
self.emit_get(e);
}
}
//*********************************************
class OpAssignExpr : OpBaseAssignExpr
{
function clone(var owner)
{
return self.cloneassign(new OpAssignExpr, owner);
}
function emit_get(var e)
{
self.annotate(e);
var lexpr = self.lexpr;
return lexpr.emit_assign_get(e, self.rexpr);
}
function emit_void(var e)
{
self.annotate(e);
var lexpr = self.lexpr;
lexpr.emit_assign_get(e, self.rexpr);
}
}
//*********************************************
class OpAssignToExpr : OpBaseAssignExpr
{
function clone(var owner)
{
return self.cloneassign(new OpAssignToExpr, owner);
}
function emit(var e, string result)
{
self.annotate(e);
string reg = self.emit_get(e);
e.emitassign(result, reg);
}
function emit_get(var e)
{
self.checkleft();
var lexpr = self.lexpr;
if (lexpr.checkresult() != REGvar)
SyntaxError("Wrong dest type in =:", lexpr);
string reg = lexpr.emit_get(e);
string reg2 = self.rexpr.emit_get(e);
self.annotate(e);
e.emitassign(reg, reg2);
return reg;
}
function emit_void(var e)
{
self.annotate(e);
string reg = self.emit_get(e);
}
}
//*********************************************
class OpAddToExpr : OpBaseAssignExpr
{
function clone(var owner)
{
return self.cloneassign(new OpAddToExpr, owner);
}
function emit_get(var e)
{
self.checkleft();
var lexpr = self.lexpr;
var rexpr = self.rexpr;
string ltype = lexpr.checkresult();
string rtype = rexpr.checkresult();
string reg = lexpr.emit_get(e);
if (ltype == REGstring && (rexpr instanceof ConcatString))
rexpr.emit_concat_to(e, reg);
else {
string reg2 = rexpr.emit_get(e);
string aux;
self.annotate(e);
switch (ltype) {
case REGstring:
if (rtype != REGstring) {
aux = self.tempreg(REGstring);
e.emitset(aux, reg2);
reg2 = aux;
}
e.emitconcat1(reg, reg2);
break;
case REGint:
case REGfloat:
if (ltype != rtype) {
aux = self.tempreg(ltype);
e.emitset(aux, reg2);
reg2 = aux;
}
e.emitaddto(reg, reg2);
break;
default:
e.emitaddto(reg, reg2);
}
}
if (lexpr instanceof LexicalVolatileExpr)
lexpr.emit_store(e, reg);
return reg;
}
}
//*********************************************
class OpSubToExpr : OpBaseAssignExpr
{
function clone(var owner)
{
return self.cloneassign(new OpSubToExpr, owner);
}
function emit_get(var e)
{
self.checkleft();
var lexpr = self.lexpr;
var rexpr = self.rexpr;
string ltype = lexpr.checkresult();
string rtype = rexpr.checkresult();
string reg = lexpr.emit_get(e);
string reg2 = rexpr.emit_get(e);
string aux;
self.annotate(e);
switch (ltype) {
case REGstring:
SyntaxError("-= can't be applied to string", self);
case REGint:
case REGfloat:
if (ltype != rtype) {
aux = self.tempreg(ltype);
e.emitset(aux, reg2);