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

9992 lines (9304 sloc) 266.384 kb
#! winxed
/***********************************************************************
Winxed stage 1 compiler
***********************************************************************/
//*********************************************
// Character test functions
//*********************************************
function isspace(string c)
{
return c == " " || c == "\n" || c == "\t";
}
function isdigit(string c)
{
return indexof("0123456789", c) > -1;
}
function hexdigit(string c)
{
int i = indexof("0123456789abcdef0123456789ABCDEF", c);
if (i >= 16) i = i - 16;
return i;
}
function isidentstart(string c)
{
return indexof(
"abcdefghijklmnopqrstuvwxyz" +
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "_",
c) > -1;
}
function isident(string c)
{
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);
}
function quoted(string str)
{
return "\"" + str + "\"";
}
// Algorithms
function for_each(var src, var func)
{
for (var item in src)
func(item);
}
function transform(var src, var dest, var func)
{
for (var item in src)
push(dest, func(item));
return dest;
}
function find_same(var src, var value)
{
for (var item in src)
if (item === value)
return item;
return null;
}
//*********************************************
// 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 isstring() { 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 string(quoted(self.str));
}
function viewable()
{
return string(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 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);
}
}
//*********************************************
// 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 = s + c + c2;
break;
default:
s = s + c;
}
}
return new TokenQuoted(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 "\"":
case "\\":
// Encode the mark the same way as the heredoc content
// to simplify its detection
c = "\\" + c;
break;
}
mark = 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 "\"":
case "\\":
c = "\\" + c;
break;
}
line = line + c;
}
if (line != mark)
content = 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 = 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 = s + c;
if (c == "." || c == "e" || c == "E")
TokenError("float literals not allowed in stage 1", tk, line);
tk.ungetchar(c);
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,
"=": {
"=": { "": getop, "=": getop },
":": getop
},
"+": { "+": getop, "=": getop },
"-": { "-": getop, "=": getop },
"|": { "|": getop },
"&": { "&": getop },
"<": {
"<": { "": getop, ":": getheredoc },
"=": 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()
{
string c = self.pending;
if (c != "")
self.pending = "";
else {
var h = self.h;
c = h.read(1);
if (c == "\n")
self.line = self.line + 1;
}
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 = 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 = "?",
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 (var s in args)
self.handle.print(s);
}
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)
{
string file = self.file;
int line = self.line;
string tfile = t.file;
int tline = t.line;
if (file != tfile) {
self.file = tfile;
self.pendingf = true;
line = 0;
}
if (line != tline) {
self.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);
}
}
//*********************************************
// 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 stringQuotedValue(var owner, var start, string value)
{
var t = new TokenQuoted(start.file, start.line, value);
return new StringLiteral(owner, t);
}
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)
{
var tid = new TokenIdentifier(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)
{
var tid = new TokenIdentifier(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
function string_from_literal(var arg)
{
string value;
switch {
case arg.isintegerliteral():
value = arg.getIntegerValue();
break;
case arg.isstringliteral():
value = arg.getStringValue();
break;
default:
InternalError("wrong call to string_from_literal", arg);
}
return value;
}
function int_from_literal(var arg)
{
int value;
switch {
case arg.isintegerliteral():
value = arg.getIntegerValue();
break;
case arg.isstringliteral():
value = arg.getStringValue();
break;
default:
InternalError("wrong call to int_from_literal", arg);
}
return value;
}
// 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)
{
// 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;
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 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 = n + 1;
}
if (type2 != null) {
self.type2 = type2;
n = n + 1;
}
if (type3 != null) {
self.type3 = type3;
n = n + 1;
}
}
}
self.nparams = n;
}
function iscompileevaluable() { return false; }
function name()
{
string name = self.name;
return 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;
// 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 Builtin_typecast
{
var type;
function Builtin_typecast(string type)
{
self.type = type;
}
function invoke [vtable](var e, var owner, var start, string result, var args)
{
string type = self.type;
if (elements(args) != 1)
InternalError("Invalid Builtin_typecast.invoke call");
var rawarg = args[0];
string argtype = rawarg.checkresult();
switch {
case argtype == type:
case rawarg instanceof IndexExpr:
rawarg.emit(e, result);
break;
default:
string arg = rawarg.emit_get(e);
e.annotate(start);
// Special case
if (arg == NULL)
e.emitnull(result);
else
e.emitset(result, arg);
}
}
}
function builtineval_stringcast(var owner, var start, var args)
{
var arg = args[0].arg;
return stringQuotedValue(owner, start, string_from_literal(arg));
}
function builtineval_intcast(var owner, var start, var args)
{
var arg = args[0].arg;
int value = int_from_literal(arg);
return integerValue(owner, start, value);
}
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 = i + 1)
e.emitprint(args[i]);
e.emitsay(args[n]);
}
else
e.emitsay("''");
}
function emit_printP0(string arg)
{
return INDENT + "print $P0, " + arg;
}
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, [], emit_printP0))));
}
function Builtin_print(var e, var owner, var start, string result, var args)
{
e.annotate(start);
for (string arg in args)
e.emitprint(arg);
}
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);
}
}
function builtineval_length(var owner, var start, var args)
{
var arg = args[0].arg;
string s = string_from_literal(arg);
return integerValue(owner, start, length(s));
}
function builtineval_bytelength(var owner, var start, var args)
{
var arg = args[0].arg;
string s = string_from_literal(arg);
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)
{
var arg = args[0].arg;
return stringQuotedValue(owner, start, chr(int_from_literal(arg)));
}
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 getbuiltins(var builder)
{
builder.add(new BuiltinFunctionEval("int",
builtineval_intcast,
new Builtin_typecast(REGint),
REGint, REGraw1
));
builder.add(new BuiltinFunction("float",
new Builtin_typecast(REGfloat),
REGfloat, REGraw1
));
builder.add(new BuiltinFunctionEval("string",
builtineval_stringcast,
new Builtin_typecast(REGstring),
REGstring, REGraw1
));
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("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 BuiltinFunction("indexof",
"index %0, %1, %2",
REGint, REGstring, REGstring
));
builder.add(new BuiltinFunction("indexof",
"index %0, %1, %2, %3",
REGint, REGstring, REGstring, REGint
));
builder.add(new BuiltinFunction("join",
"join %0, %1, %2",
REGstring, REGstring, REGvar
));
builder.add(new BuiltinFunction("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 BuiltinFunction("upcase",
"upcase %0, %1",
REGstring, REGstring
));
builder.add(new BuiltinFunction("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("push",
"push %1, %2",
REGnone, REGvar, REGany
));
builder.add(new BuiltinFunction("unshift",
"unshift %1, %2",
REGnone, REGvar, REGany
));
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("getinterp",
"getinterp %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("find_method",
"find_method %0, %1, %2",
REGvar, REGvar, REGstring
));
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("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
));
}
//*********************************************
// Auxiliar functions
//*********************************************
function optimize_array(var arr)
{
int n = arr != null ? elements(arr) : 0;
for (int i = 0; i < n; i = i + 1)
arr[i] = arr[i].optimize();
}
function emit_array(var e, var arr)
{
for (var item in arr)
item.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[];
for (var t in tlist)
push(list, t.getidentifier());
return list;
}
//*********************************************
// CommonBase
//*********************************************
class CommonBase
{
var start;
var owner;
function initbase(var start, var owner)
{
__ASSERT__(start instanceof Token);
__ASSERT__((owner instanceof Statement) ||
(owner instanceof ClassStatement) ||
(owner instanceof NamespaceBase));
self.start = start;
self.owner = owner;
}
function 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 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)
{
var argreg = [];
for (var a in self.args)
push(argreg, a.emit_get(e));
return argreg;
}
function emitargs(var e)
{
e.print(join(", ", self.getargvalues(e)));
}
}
//*********************************************
// Modifiers
//*********************************************
class Modifier
{
var name;
var args;
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 optimize()
{
optimize_array(self.list);
}
function getlist() { return self.list; }
function pick(string name)
{
for (var mod in self.list) {
if (mod.getname() == name)
return mod;
}
return null;
}
}
//*********************************************
// Auxiliary classes and functions
//*********************************************
function getparrotkey(var path)
{
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("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)
{
var params = new SigParameterList(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(":")) {
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 new ReturnStatement(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);
}
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 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 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 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 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 = i + 1) {
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)
{
var statements = [st1, st2];
self.statements = statements;
}
function isempty() { return false; }
function push(var statement)
{
push(self.statements, statement);
return self;
}
function emit(var e)
{
emit_array(e, self.statements);
}
}
function addtomulti(var multi, var newst)
{
switch {
case multi == null:
return newst;
case multi instanceof MultiStatement:
return multi.push(newst);
default:
return new MultiStatement(multi, newst);
}
}
//*********************************************
// 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'");
}
}
//*********************************************
// 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;
string 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, 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);
var expr = [];
var t;
do {
var newexpr = parseExpr(tk, self);
expr.push(newexpr);
} while((t = tk.get()).isop(","));
RequireOp(";", t);
self.expr = expr;
}
function optimize()
{
optimize_array(self.expr);
return self;
}
function emit(var e)
{
for (var item in self.expr)
item.emit_void(e);
}
}
//*********************************************
// VarContainer
//*********************************************
class VarData
{
var type;
var reg;
var scope;
var flags;
var lexname;
function VarData(string type, var reg, var scope, int flags)
{
// Sanity check
__ASSERT__(type == REGint || type == REGfloat || type == REGstring ||
type == REGvar);
self.type = type;
self.reg = reg;
self.scope = scope;
self.flags = flags;
}
function setlex(string name)
{
self.lexname = name;
}
function createlex(string lexname)
{
// Set the lexname and make the variable a lexical
// in its outer.
self.setlex(lexname);
self.getscope().getouter().setlex(lexname, self.reg);
}
function gettype() { return self.type; }
function getreg() { return self.reg; }
function getscope() { return self.scope; }
function getvalue() { return self.value; }
function isconst() { return false; }
function getlex()
{
var lexname = self.lexname;
return lexname != null ? string(lexname) : null;
}
function getflags() { return self.flags; }
function issubid()
{
var reg = self.reg;
return reg != null && substr(reg, 0, 7) == "WSubId_";
}
}
class ConstantInternalFail
{
var name;
function ConstantInternalFail(var name)
{
self.name = name;
}
function get_string[vtable]()
{
InternalError("Attempt to use unexpanded constant!!!", self.name);
}
}
class VarData_const : VarData
{
var value;
function VarData_const(string type, var name, var scope, int flags)
{
self.VarData(type, new ConstantInternalFail(name), scope, flags);
}
function isconst() { return true; }
function setvalue(var value)
{
if (self.value != null)
InternalError("Attempt change value of constant!!!");
self.value = value;
}
}
class VarContainer
{
var locals;
var usednamespaces;
function VarContainer()
{
self.locals = {};
}
function createvar(var name, string type, int flags[optional])
{
var locals = self.locals;
string sname = name;
var exist = locals[sname];
if (exist != null)
Redeclared(name);
string reg = self.createreg(type);
var data = new VarData(type, reg, self, flags);
locals[sname] = data;
return data;
}
function createvarused(var name, var data)
{
var locals = self.locals;
string sname = name;
var exist = locals[sname];
if (exist != null)
Redeclared(name);
locals[sname] = data;
}
function createvarnamed(var name, string type, string pirname)
{
var locals = self.locals;
string sname = name;
var exist = locals[sname];
if (exist != null)
Redeclared(name);
locals[sname] = new VarData(type, pirname, self, 0);
}
function createconst(var name, string type, int flags[optional])
{
var locals = self.locals;
string sname = name;
var exist = locals[sname];
if (exist != null)
Redeclared(name);
var data = new VarData_const(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)
{
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.getouter();
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
{
function BlockStatement(var start, var owner)
{
self.Statement(start, owner);
self.VarContainer();
}
}
//*********************************************
// 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 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 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 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;
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 ComparatorBaseExpr) ||
(condexpr instanceof CheckerExpr))
condexpr.emit_if(e, labeltrue);
else {
string reg = condexpr.emit_get(e);
switch (condexpr.checkresult()) {
case REGstring:
case REGvar:
e.emitif_null(reg, labelfalse);
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 ComparatorBaseExpr) ||
(condexpr instanceof CheckerExpr))
condexpr.emit_else(e, labelfalse);
else {
string reg = condexpr.emit_get(e);
switch (condexpr.checkresult()) {
case REGstring:
case REGvar:
e.emitif_null(reg, labelfalse);
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 isintegerliteral() { return true; }
function isintegerzero()
{
return int(self.intval) == 0;
}
function checkresult() { return REGint; }
function getIntegerValue()
{
return int(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 StringLiteral : Literal
{
var strval;
function StringLiteral(var owner, var start)
{
self.Expr(owner, start);
self.strval = start;
}
function isstringliteral() { return true; }
function checkresult() { return REGstring; }
function getPirString()
{
var strtok = self.strval;
string str = strtok.getPirString();
return str;
}
function getStringValue()
{
var strtok = self.strval;
string str = strtok.str;
if (strtok instanceof TokenQuoted)
str = unescape(str);
return str;
}
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;
var t = 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 emit_get(var e)
{
return self.subid;
}
function emit(var e, string result)
{
self.annotate(e);
e.emitset(result, self.subid);
}
}
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 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:
break;
case sym instanceof FunctionStatement:
string id = sym.makesubid();
self.usesubid(id);
owner.createvarnamed(name, REGvar, id);
return new FunctionId(owner, name, id);
break;
}
}
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 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 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();
return self;
}
}
//*********************************************
class OpDelExBase : OpUnaryExpr
{
function checkresult()
{
return REGint;
}
}
class OpDeleteExpr : OpDelExBase
{
function OpDeleteExpr(var owner, var start, var subexpr)
{
self.OpUnaryExpr(owner, start, subexpr);
}
function optimize()
{
self.optimizearg();
var expr = self.subexpr;
if ((!(expr instanceof IndexExpr)) || expr.checkresult() == REGstring)
SyntaxError("delete with invalid operator", self);
return self;
}
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;
if (expr instanceof IndexExpr && expr.checkresult() != REGstring) {
expr.emit_prep(e);
self.annotate(e);
e.print(INDENT + "exists ", result, ", ");
expr.emit_aux(e);
e.say();
}
else
SyntaxError("exists with invalid operator", self);
}
}
//*********************************************
class OpUnaryMinusExpr : OpUnaryExpr
{
function OpUnaryMinusExpr(var owner, var start, var subexpr)
{
self.OpUnaryExpr(owner, start, subexpr);
}
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);
}
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
{
function OpNotExpr(var owner, var start, var subexpr)
{
self.OpUnaryExpr(owner, start, subexpr);
}
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 REGvar:
e.emitarg2("isfalse", result, reg);
break;
default:
e.emitarg2("isfalse", result, reg);
}
}
}
//*********************************************
class OpBinNotExpr : OpUnaryExpr
{
function OpBinNotExpr(var owner, var start, var subexpr)
{
self.OpUnaryExpr(owner, start, subexpr);
}
function checkresult()
{
return REGint;
}
function set(var owner, var t, var subexpr)
{
self.OpUnaryExpr(owner, t, subexpr);
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 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 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 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 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 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 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 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 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:
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 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;
}
}
//*********************************************
const int
COMPARATOR_DEFAULT = 0,
COMPARATOR_IF = 1,
COMPARATOR_ELSE = 2;
class ComparatorBaseExpr : 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.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
{
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 isnegable() { return true; }
function optimize()
{
self.optimizearg();
var lexpr = self.lexpr;
var rexpr = self.rexpr;
int lnull = lexpr.isnull();
int rnull = rexpr.isnull();
if (lnull) {
if (rnull)
return integerValue(self.owner, self.start, self.positive);
else
return new NullCheckerExpr(self, rexpr, self.positive);
}
if (rnull)
return new NullCheckerExpr(self, lexpr, self.positive);
string ltype = lexpr.checkresult();
string rtype = rexpr.checkresult();
if (lexpr.isliteral() && rexpr.isliteral()) {
if (ltype == rtype) {
switch (ltype) {
case 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 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 isnegable() { return true; }
function int_op(int left, int right)
{
return self.positive ? (left == right) : (left != right);
}
function emitop(var e, string result, string regl, string regr)
{
self.annotate(e);
int positive = self.positive;
string op = positive ? "issame" : "isntsame";
e.emitbinop(op, result, regl, regr);
}
function emit(var e, string result)
{
self.emit_comparator(e, result);
}
function emitop_if(var e, string labeltrue, string regl, string regr)
{
self.annotate(e);
int positive = self.positive;
string op = positive ? "eq_addr" : "ne_addr";
e.emitcompare(op, regl, regr, labeltrue);
}
function emitop_else(var e, string labelelse, string regl, string regr)
{
self.annotate(e);
int positive = self.positive;
string op = positive ? "ne_addr" : "eq_addr";
e.emitcompare(op, regl, regr, labelelse);
}
}
//*********************************************
class OpLessExpr : ComparatorBaseExpr
{
function isnegable() { return true; }
function negated()
{
return (new OpGreaterEqualExpr).setfrom(self);
}
function int_op(int left, int right)
{
return left < right;
}
function emitop(var e, string result, string regl, string regr)
{
e.emitbinop("islt", result, regl, regr);
}
function emitop_if(var e, string labeltrue, string regl, string regr)
{
e.emitcompare("lt", regl, regr, labeltrue);
}
function emitop_else(var e, string labelelse, string regl, string regr)
{
e.emitcompare("ge", regl, regr, labelelse);
}
}
//*********************************************
class OpGreaterExpr : ComparatorBaseExpr
{
function isnegable() { return true; }
function negated()
{
return (new OpLessEqualExpr).setfrom(self);
}
function int_op(int left, int right)
{
return left > right;
}
function emitop(var e, string result, string regl, string regr)
{
e.emitbinop("isgt", result, regl, regr);
}
function emitop_if(var e, string labeltrue, string regl, string regr)
{
e.emitcompare("gt", regl, regr, labeltrue);
}
function emitop_else(var e, string labelelse, string regl, string regr)
{
e.emitcompare("le", regl, regr, labelelse);
}
}
//*********************************************
class OpLessEqualExpr : ComparatorBaseExpr
{
function isnegable() { return true; }
function negated()
{
return (new OpGreaterExpr).setfrom(self);
}
function int_op(int left, int right)
{
return left <= right;
}
function emitop(var e, string result, string regl, string regr)
{
e.emitbinop("isle", result, regl, regr);
}
function emitop_if(var e, string labeltrue, string regl, string regr)
{
e.emitcompare("le", regl, regr, labeltrue);
}
function emitop_else(var e, string labelelse, string regl, string regr)
{
e.emitcompare("gt", regl, regr, labelelse);
}
}
//*********************************************
class OpGreaterEqualExpr : ComparatorBaseExpr
{
function isnegable() { return true; }
function negated()
{
return (new OpLessExpr).setfrom(self);
}
function int_op(int left, int right)
{
return left >= right;
}
function emitop(var e, string result, string regl, string regr)
{
e.emitbinop("isge", result, regl, regr);
}
function emitop_if(var e, string labeltrue, string regl, string regr)
{
e.emitcompare("ge", regl, regr, labeltrue);
}
function emitop_else(var e, string labelelse, string regl, string regr)
{
e.emitcompare("lt", regl, regr, labelelse);
}
}
//*********************************************
class OpBaseBoolExpr : OpBinaryExpr
{
function checkresult()
{
return self.lexpr.checkresult() == REGint && self.rexpr.checkresult() == REGint ?
REGint : REGvar;
}
}
//*********************************************
class OpBoolAndExpr : OpBaseBoolExpr
{
function OpBoolAndExpr(var owner, var start, var lexpr, var rexpr)
{
self.set(owner, start, lexpr, rexpr);
}
function optimize()
{
self.optimizearg();
if (self.lexpr.isintegerliteral() ) {
int ln = self.lexpr.getIntegerValue();
return ln != 0 ?
self.rexpr :
integerValue(self.owner, self.start, ln);
}
return self;
}
function emit(var e, string result)
{
var lexpr = self.lexpr;
var rexpr = self.rexpr;
string type = self.checkresult();
if (type == REGint && lexpr.issimple() && rexpr.issimple()) {
string lreg = self.emit_intleft(e);
string rreg = self.emit_intright(e);
e.emitbinop("and", result, lreg, rreg);
}
else {
string done = self.genlabel();
if (type == REGvar && lexpr.checkresult() != REGvar) {
string lres = lexpr.emit_get(e);
e.emitbox(result, lres);
}
else
lexpr.emit(e, result);
e.emitunless(result, done);
if (type == REGvar && rexpr.checkresult() != REGvar) {
string rres = rexpr.emit_get(e);
e.emitbox(result, rres);
}
else
rexpr.emit(e, result);
e.emitlabel(done);
}
}
function emit_void(var e)
{
var lexpr = self.lexpr;
var rexpr = self.rexpr;
string type = self.checkresult();
if (type == REGint && lexpr.issimple() && rexpr.issimple()) {
string lreg = self.emit_intleft(e);
string rreg = self.emit_intright(e);
e.emitbinop("and", DISCARD_IREG, lreg, rreg);
}
else {
string done = self.genlabel();
string result = lexpr.emit_get(e);
e.emitunless(result, done);
rexpr.emit_void(e);
e.emitlabel(done);
}
}
}
//*********************************************
class OpBoolOrExpr : OpBaseBoolExpr
{
function OpBoolOrExpr(var owner, var start, var lexpr, var rexpr)
{
self.set(owner, start, lexpr, rexpr);
}
function optimize()
{
self.optimizearg();
if (self.lexpr.isintegerliteral() ) {
int ln = self.lexpr.getIntegerValue();
return ln == 0 ?
self.rexpr :
integerValue(self.owner, self.start, ln);
}
return self;
}
function emit(var e, string result)
{
var lexpr = self.lexpr;
var rexpr = self.rexpr;
string type = self.checkresult();
if (type == REGint && lexpr.issimple() && rexpr.issimple()) {
string lreg = self.emit_intleft(e);
string rreg = self.emit_intright(e);
e.emitbinop("or", result, lreg, rreg);
}
else {
string done = self.genlabel();
if (type == REGvar && lexpr.checkresult() != REGvar) {
string lres = lexpr.emit_get(e);
e.emitbox(result, lres);
}
else
lexpr.emit(e, result);
e.emitif(result, done);
if (type == REGvar && rexpr.checkresult() != REGvar) {
string rres = rexpr.emit_get(e);
e.emitbox(result, rres);
}
else
rexpr.emit(e, result);
e.emitlabel(done);
}
}
function emit_void(var e)
{
var lexpr = self.lexpr;
var rexpr = self.rexpr;
string type = self.checkresult();
if (type == REGint && lexpr.issimple() && rexpr.issimple()) {
string lreg = self.emit_intleft(e);
string rreg = self.emit_intright(e);
e.emitbinop("or", DISCARD_IREG, lreg, rreg);
}
else {
string done = self.genlabel();
string result = lexpr.emit_get(e);
e.emitif(result, done);
rexpr.emit_void(e);
e.emitlabel(done);
}
}
}
//*********************************************
class OpBaseBinExpr : OpBinaryIntExpr
{
}
//*********************************************
class OpBinAndExpr : OpBaseBinExpr
{
function OpBinAndExpr(var owner, var start, var lexpr, var rexpr)
{
self.set(owner, start, lexpr, rexpr);
}
function emit(var e, string result)
{
string lreg = self.emit_intleft(e);
string rreg = self.emit_intright(e);
self.annotate(e);
e.emitbinop("band", result, lreg, rreg);
}
}
//*********************************************
class OpBinOrExpr : OpBaseBinExpr
{
function OpBinOrExpr(var owner, var start, var lexpr, var rexpr)
{
self.set(owner, start, lexpr, rexpr);
}
function emit(var e, string result)
{
string lreg = self.emit_intleft(e);
string rreg = self.emit_intright(e);
self.annotate(e);
e.emitbinop("bor", result, lreg, rreg);
}
}
//*********************************************
class OpBinXorExpr : OpBaseBinExpr
{
function OpBinXorExpr(var owner, var start, var lexpr, var rexpr)
{
self.set(owner, start, lexpr, rexpr);
}
function emit(var e, string result)
{
string lreg = self.emit_intleft(e);
string rreg = self.emit_intright(e);
self.annotate(e);
e.emitbinop("bxor", result, lreg, rreg);
}
}
//*********************************************
class OpAddExpr : OpBinaryExpr
{
function OpAddExpr(var owner, var start, var lexpr, var rexpr)
{
self.set(owner, start, lexpr, rexpr);
}
function optimize()
{
self.optimizearg();
var lexpr = self.lexpr;
var rexpr = self.rexpr;
string ltype = lexpr.checkresult();
string rtype = rexpr.checkresult();
if (lexpr.isliteral() && rexpr.isliteral()) {
if (ltype == REGstring && rtype == REGstring)
return concat_literal(lexpr, rexpr);
if (ltype == REGint && rtype == REGint) {
int ln = lexpr.getIntegerValue();
int rn = rexpr.getIntegerValue();
return integerValue(self.owner, self.start, ln + rn);
}
}
return self;
}
function checkresult()
{
string rl = self.lexpr.checkresult();
string rr = self.rexpr.checkresult();
if (rl == rr)
return rl;
if (rl == REGint && rr == REGstring)
return REGstring;
if (rl == REGstring && rr == REGint)
return REGstring;
if (floatresult(rl, rr))
return REGfloat;
return REGint;
}
function emit(var e, string result)
{
var lexpr = self.lexpr;
var rexpr = self.rexpr;
string restype = self.checkresult();
string ltype = lexpr.checkresult();
string rtype = rexpr.checkresult();
string rleft = lexpr.emit_get(e);
string rright = rexpr.emit_get(e);
if (restype == REGstring) {
if (ltype != REGstring || rtype != REGstring) {
string aux = self.tempreg(REGstring);
if (ltype != REGstring) {
e.emitset(aux, rleft);
rleft = aux;
}
else {
e.emitset(aux, rright);
rright = aux;
}
}
e.emitconcat(result, rleft, rright);
}
else {
if (restype == REGint && (ltype != REGint || rtype != REGint)) {
string l;
if (ltype == REGint) l = rleft;
else {
l = self.tempreg(REGint);
e.emitset(l, rleft);
}
string r;
if (rtype == REGint) r = rright;
else {
r = self.tempreg(REGint);
e.emitset(r, rright);
}
e.emitadd(result, l, r);
}
else
e.emitadd(result, rleft, rright);
}
}
}
//*********************************************
class OpSubExpr : OpBinaryExpr
{
function OpSubExpr(var owner, var start, var lexpr, var rexpr)
{
self.set(owner, start, lexpr, rexpr);
}
function optimize()
{
self.optimizearg();
var lexpr = self.lexpr;
var rexpr = self.rexpr;
if (lexpr.isliteral() && rexpr.isliteral()) {
string ltype = lexpr.checkresult();
string rtype = rexpr.checkresult();
if (ltype == REGint && rtype == REGint) {
int ln = lexpr.getIntegerValue();
int rn = rexpr.getIntegerValue();
return integerValue(self.owner, self.start, ln - rn);
}
}
return self;
}
function checkresult()
{
string ltype = self.lexpr.checkresult();
string rtype = self.rexpr.checkresult();
switch {
case ltype == rtype:
return ltype;
case ltype == REGvar || rtype == REGvar:
return REGvar;
case ltype == REGint && rtype == REGfloat:
return REGfloat;
case ltype == REGfloat && rtype == REGint:
return REGfloat;
default:
return REGint;
}
}
function emit(var e, string result)
{
string type = self.checkresult();
var lexpr = self.lexpr;
var rexpr = self.rexpr;
string ltype = lexpr.checkresult();
string rtype = rexpr.checkresult();
string lreg = lexpr.emit_get(e);
string rreg = rexpr.emit_get(e);
string aux;
if (ltype != type) {
aux = self.tempreg(type);
if (type == REGvar)
e.emitbox(aux, lreg);
else
e.emitset(aux, lreg);
lreg = aux;
}
if (rtype != type) {
aux = self.tempreg(type);
if (type == REGvar)
e.emitbox(aux, rreg);
else
e.emitset(aux, rreg);
rreg = aux;
}
e.emitsub(result, lreg, rreg);
}
}
//*********************************************
class OpMulExpr : OpBinaryExpr
{
function OpMulExpr(var owner, var start, var lexpr, var rexpr)
{
self.set(owner, start, lexpr, rexpr);
}
function checkresult()
{
var lexpr = self.lexpr;
string rl = self.lexpr.checkresult();
string rr = self.rexpr.checkresult();
if (rl == rr)
return rl;
return REGfloat;
}
function emit(var e, string result)
{
var lexpr = self.lexpr;
var rexpr = self.rexpr;
string ltype = lexpr.checkresult();
string rtype = rexpr.checkresult();
string lreg, rreg;
if (ltype == rtype && (ltype == REGint || ltype == REGfloat || ltype == REGvar)) {
lreg = lexpr.emit_get(e);
rreg = rexpr.emit_get(e);
e.emitmul(result, lreg, rreg);
return;
}
// Quick fix for some float mul
// TODO: rewrite this mess
if (ltype == REGfloat) {
lreg = lexpr.emit_get(e);
rreg = rexpr.emit_get(e);
string rval;
switch (rtype) {
case REGint:
rval = self.tempreg(REGfloat);
e.emitset(rval, rreg);
rval = rreg;
break;
case REGfloat:
rval = rreg;
break;
default:
rval = self.tempreg(REGfloat);
e.emitset(rval, rreg);
}
self.annotate(e);
e.emitmul(result, lreg, rval);
return;
}
int nleft, nright;
if ((!lexpr.issimple()) || lexpr.isidentifier()) {
lreg = self.tempreg(self.checkresult());
lexpr.emit(e, lreg);
}
else {
nleft = lexpr.getIntegerValue();
lreg = nleft;
}
if ((!rexpr.issimple()) || rexpr.isidentifier()) {
rreg = self.tempreg(self.checkresult());
rexpr.emit(e, rreg);
}
else {
switch (rtype) {
case REGstring:
rreg = self.tempreg(self.checkresult());
rexpr.emit(e, rreg);
break;
case REGfloat:
rreg = rexpr.emit_get(e);
break;
case REGint:
default:
nright = rexpr.getIntegerValue();
rreg = nright;
break;
}
}
self.annotate(e);
e.emitmul(result, lreg, rreg);
}
}
//*********************************************
class OpDivExpr : OpBinaryExpr
{
function OpDivExpr(var owner, var start, var lexpr, var rexpr)
{
self.set(owner, start, lexpr, rexpr);
}
function checkresult()
{
return REGfloat;
}
function emit(var e, string result)
{
var lexpr = self.lexpr;
var aux;
var lreg = lexpr.emit_get(e);
if (lexpr.checkresult() != REGfloat) {
aux = self.tempreg(REGfloat);
e.emitset(aux, lreg);
lreg = aux;
}
var rexpr = self.rexpr;
var rreg = rexpr.emit_get(e);
if (rexpr.checkresult() != REGfloat) {
aux = self.tempreg(REGfloat);
e.emitset(aux, rreg);
rreg = aux;
}
self.annotate(e);
e.emitdiv(result, lreg, rreg);
}
}
//*********************************************
class OpModExpr : OpBinaryIntExpr
{
function OpModExpr(var owner, var start, var lexpr, var rexpr)
{
self.set(owner, start, lexpr, rexpr);