Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

13432 lines (12617 sloc) 365.639 kb
#! winxed
/***********************************************************************
Winxed stage 2 compiler
***********************************************************************/
namespace Winxed
{
namespace Compiler
{
const int VERSION_MAJOR = 1;
const int VERSION_MINOR = 10;
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;
}
function toModuleFilename(var tlist)
{
return "\"" + join("/", toIdentifierList(tlist)) + ".pbc\"";
}
//*********************************************
// 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])
{
__ASSERT__(name instanceof TokenIdentifier);
string sname = name.getidentifier();
var locals = self.locals;
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)
{
__ASSERT__(name instanceof TokenIdentifier);
string sname = name;
var locals = self.locals;
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])
{
__ASSERT__(name instanceof TokenIdentifier);
string sname = name.getidentifier();
var locals = self.locals;
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);
reg2 = aux;
}
e.emitsubto(reg, reg2);
break;
default:
e.emitsubto(reg, reg2);
}
if (lexpr instanceof LexicalVolatileExpr)
lexpr.emit_store(e, reg);
return reg;
}
}
//*********************************************
class OpMulToExpr : OpBaseAssignExpr
{
function clone(var owner)
{
return self.cloneassign(new OpMulToExpr, owner);
}
function emit_get(var e)
{
self.checkleft();
var lexpr = self.lexpr;
var rexpr = self.rexpr;
string lreg = lexpr.emit_get(e);
string rreg;
switch (lexpr.checkresult()) {
case REGstring:
rreg = rexpr.emit_getint(e);
self.annotate(e);
e.emitrepeat(lreg, lreg, rreg);
break;
default:
rreg = rexpr.emit_get(e);
self.annotate(e);
e.emitarg2("mul", lreg, rreg);
}
if (lexpr instanceof LexicalVolatileExpr)
lexpr.emit_store(e, lreg);
return lreg;
}
}
//*********************************************
class OpDivToExpr : OpBaseAssignExpr
{
function clone(var owner)
{
return self.cloneassign(new OpDivToExpr, owner);
}
function emit_get(var e)
{
self.checkleft();
var lexpr = self.lexpr;
string reg = lexpr.emit_get(e);
string reg2 = self.rexpr.emit_get(e);
self.annotate(e);
e.emitarg2("div", reg, reg2);
if (lexpr instanceof LexicalVolatileExpr)
lexpr.emit_store(e, reg);
return reg;
}
}
//*********************************************
class OpModToExpr : OpBaseAssignExpr
{
function clone(var owner)
{
return self.cloneassign(new OpModToExpr, owner);
}
function emit_get(var e)
{
self.checkleft();
var lexpr = self.lexpr;
string reg = lexpr.emit_get(e);
string reg2 = self.rexpr.emit_get(e);
self.annotate(e);
e.emitarg2("mod", reg, reg2);
if (lexpr instanceof LexicalVolatileExpr)
lexpr.emit_store(e, reg);
return reg;
}
}
//*********************************************
const int
COMPARATOR_DEFAULT = 0,
COMPARATOR_IF = 1,
COMPARATOR_ELSE = 2;
class ComparatorBaseExpr : OpBinaryExpr, ConditionFriendlyExpr
{
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.int_op(li, ri));
}
return self;
}
function emit_comparator(var e, string result, int doifelse[optional])
{
string rl = self.lexpr.checkresult();
string rr = self.rexpr.checkresult();
string regl = self.lexpr.emit_get(e);
string regr = self.rexpr.emit_get(e);
self.annotate(e);
string aux;
switch {
case rl == REGint && rr == REGfloat:
aux = self.tempreg(REGfloat);
e.emitset(aux, regl);
regl = aux;
break;
case rl == REGfloat && rr == REGint:
aux = self.tempreg(REGfloat);
e.emitset(aux, regr);
regr = aux;
break;
case rr == REGint && rl == REGvar:
aux = self.tempreg(REGint);
e.emitset( aux, regl);
regl = aux;
break;
case rr == REGvar && rl == REGint:
aux = self.tempreg(REGint);
e.emitset(aux, regr);
regr = aux;
break;
case rr == REGstring && rl == REGvar:
aux = self.tempreg(REGstring);
e.emitset(aux, regl);
regl = aux;
break;
case rr == REGvar && rl == REGstring:
aux = self.tempreg(REGstring);
e.emitset(aux, regr);
regr = aux;
break;
}
switch (doifelse) {
case COMPARATOR_DEFAULT:
self.emitop(e, result, regl, regr);
break;
case COMPARATOR_IF:
self.emitop_if(e, result, regl, regr);
break;
case COMPARATOR_ELSE:
self.emitop_else(e, result, regl, regr);
break;
}
}
function emit(var e, string result)
{
self.emit_comparator(e, result);
}
function emit_if(var e, string labeltrue)
{
self.emit_comparator(e, labeltrue, COMPARATOR_IF);
}
function emit_else(var e, string labelfalse)
{
self.emit_comparator(e, labelfalse, COMPARATOR_ELSE);
}
}
//*********************************************
class Negable
{
var positive;
function Negable(int positive)
{
self.positive = new ["Boolean"](positive);
}
function isnegable() { return true; }
function negated()
{
int positive = ! self.positive;
self.positive =: positive;
return self;
}
}
//*********************************************
// Null checkers, created during optimize of Equal and NotEqual
// to simplify its emit functions.
class CheckerExpr : Expr, Negable, ConditionFriendlyExpr
{
var expr;
function CheckerExpr(var base, var expr, int positive)
{
self.Expr(base.owner, base.start);
self.Negable(positive);
self.expr = expr;
}
function isnegable() { return true; }
function checkresult() { return REGint; }
}
class NullCheckerExpr : CheckerExpr
{
function NullCheckerExpr(var base, var expr, int checknull)
{
self.CheckerExpr(base, expr, checknull);
}
function emit(var e, string result)
{
string reg = self.expr.emit_get(e);
self.annotate(e);
e.emitarg2("isnull", result, reg);
if (! self.positive)
e.emitarg1("not", result);
}
function emit_if(var e, string labelif)
{
string reg = self.expr.emit_get(e);
self.annotate(e);
if (self.positive)
e.emitif_null(reg, labelif);
else
e.emitunless_null(reg, labelif);
}
function emit_else(var e, string labelelse)
{
string reg = self.expr.emit_get(e);
self.annotate(e);
if (self.positive)
e.emitunless_null(reg, labelelse);
else
e.emitif_null(reg, labelelse);
}
}
class ZeroCheckerExpr : CheckerExpr
{
function ZeroCheckerExpr(var base, var expr, int positive)
{
self.CheckerExpr(base, expr, positive);
}
function emit(var e, string result)
{
var expr = self.expr;
string reg = expr.emit_getint(e);
self.annotate(e);
if (self.positive)
e.emitarg3("iseq", result, reg, 0);
else
e.emitarg3("isne", result, reg, 0);
}
function emit_if(var e, string labelif)
{
var expr = self.expr;
string reg = expr.emit_getint(e);
self.annotate(e);
if (self.positive)
e.emitunless(reg, labelif);
else
e.emitif(reg, labelif);
}
function emit_else(var e, string labelelse)
{
var expr = self.expr;
string reg = expr.emit_getint(e);
self.annotate(e);
if (self.positive)
e.emitif(reg, labelelse);
else
e.emitunless(reg, labelelse);
}
}
//*********************************************
class OpEqualExpr : ComparatorBaseExpr, Negable
{
function OpEqualExpr(var owner, var start, var lexpr, var rexpr, int positive)
{
self.set(owner, start, lexpr, rexpr);
self.Negable(positive);
}
function clone(var owner)
{
var cloned = self.clonebinary(new OpEqualExpr, owner);
cloned.Negable(self.positive);
return cloned;
}
function isnegable() { return true; }
function optimize()
{
self.optimizearg();
var lexpr = self.lexpr;
var rexpr = self.rexpr;
int lnull = lexpr.isnull();
int rnull = rexpr.isnull();
switch {
case lnull && rnull:
return integerValue(self.owner, self.start, self.positive);
case lnull:
return new NullCheckerExpr(self, rexpr, self.positive);
case rnull:
return new NullCheckerExpr(self, lexpr, self.positive);
}
if (lexpr.isliteral() && rexpr.isliteral()) {
string ltype = lexpr.checkresult();
string rtype = rexpr.checkresult();
switch {
case ltype == REGint && rtype == REGint:
int li = lexpr.getIntegerValue();
int ri = rexpr.getIntegerValue();
int vi = self.positive ? li == ri : li != ri;
return integerValue(self.owner, self.start, vi);
case ltype == REGfloat && rtype == REGfloat:
case ltype == REGfloat && rtype == REGint:
case ltype == REGint && rtype == REGfloat:
float lf = lexpr.getFloatValue();
float rf = rexpr.getFloatValue();
int vf = self.positive ? lf == rf : lf != rf;
return integerValue(self.owner, self.start, vf);
case ltype == REGstring && rtype == REGstring:
string ls = (lexpr.strval).str;
string rs = (rexpr.strval).str;
int vs = self.positive ? ls == rs : ls != rs;
return integerValue(self.owner, self.start, vs);
}
}
if (rexpr.isintegerzero())
return new ZeroCheckerExpr(self, lexpr, self.positive);
if (lexpr.isintegerzero())
return new ZeroCheckerExpr(self, rexpr, self.positive);
return self;
}
function emitop(var e, string result, string regl, string regr)
{
self.annotate(e);
e.emitbinop(self.positive ? "iseq" : "isne", result, regl, regr);
}
function emit(var e, string result)
{
self.annotate(e);
self.emit_comparator(e, result);
}
function emitop_if(var e, string labeltrue, string regl, string regr)
{
self.annotate(e);
e.emitcompare(self.positive ? "eq" : "ne", regl, regr, labeltrue);
}
function emitop_else(var e, string labelelse, string regl, string regr)
{
self.annotate(e);
e.emitcompare(self.positive ? "ne" : "eq", regl, regr, labelelse);
}
}
//**********************************************************************
class OpSameExpr : ComparatorBaseExpr, Negable
{
var positive;
function OpSameExpr(var owner, var t, var lexpr, var rexpr, int positive)
{
self.initbinary(owner, t, lexpr, rexpr);
self.Negable(positive);
}
function clone(var owner)
{
var cloned = self.clonebinary(new OpSameExpr, owner);
cloned.Negable(self.positive);
return cloned;
}
function isnegable() { return true; }