Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

10837 lines (10111 sloc) 287.686 kB
#! winxed
/***********************************************************************
Winxed stage 1 compiler
***********************************************************************/
namespace Winxed
{
namespace 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 >= 0) 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, args[slurpy])
{
var builder = new ['StringBuilder'];
builder.append_format(format, args:[flat]);
return string(builder);
}
// Algorithms
function for_each(src, func)
{
for (var item in src)
func(item);
}
function transform(src, dest, func)
{
for (var item in src)
push(dest, func(item));
return dest;
}
function find_same(src, 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);
}
//*********************************************
// Token
//*********************************************
class Token
{
var file;
var line;
function Token(string file, int line)
{
self.file = file;
self.line = line;
}
function getstart() { return self; }
function iseof() { return false; }
function iscomment() { return false; }
function isidentifier() { return false; }
function isint() { return false; }
function isfloat() { return false; }
function isstring() { return false; }
function issinglequoted() { return false; }
function getintvalue()
{
InternalError('Not a literal int', self);
}
function rawstring()
{
InternalError('Not a literal string', self);
}
function getidentifier()
{
ExpectedIdentifier(self);
}
function iskeyword(string name) { return false; }
function checkkeyword() { return false; }
function isop(string name) { return false; }
function checkop() { return ''; }
function viewable() { return '(unknown)'; }
function filename() { return self.file; }
function linenum() { return self.line; }
function show()
{
string r = self.viewable();
return r + ' at ' + string(self.file) + ' line ' + string(self.line);
}
}
class TokenEof : Token
{
function TokenEof(string file)
{
self.Token(file, 0);
}
function iseof() { return true; }
function viewable() { return '(End of file)'; }
}
class TokenWithVal : Token
{
var str;
function TokenWithVal(string file, int line, string str)
{
self.Token(file, line);
self.str = str;
}
function get_string[vtable]() { return self.str; }
function viewable()
{
return self.str;
}
}
class TokenComment : TokenWithVal
{
function TokenComment(string file, int line, string str)
{
self.TokenWithVal(file, line, str);
}
function iscomment() { return true; }
}
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(self.str) + '"';
}
function viewable()
{
return '"' + string(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 = escape(strunesc);
string encoding = need_encoding ? "utf8:" : "";
return encoding + '"' + str + '"';
}
}
class TokenSingleQuoted : TokenString
{
function TokenSingleQuoted(string file, int line, string str)
{
self.TokenWithVal(file, line, str);
}
function issinglequoted() { return true; }
function get_string[vtable]()
{
return "'" + string(self.str) + "'";
}
function viewable()
{
return "'" + string(self.str) + "'";
}
function getasquoted()
{
string s = '';
for (string c in self.str) {
switch (c) {
case '"':
case '\':
case "'":
c = '\' + c;
break;
}
s = s + c;
}
return s;
}
function getPirString()
{
string str = self.str;
string quote = "'";
int need_escape = false;
for (int code in str) {
if (code < 32 || code > 127) {
need_escape = true;
break;
}
}
int need_encoding = false;
if (need_escape) {
quote = '"';
string result = '';
for (string c in str) {
if (c == '\')
result = result + '\\';
else
{
int n = ord(c);
if (n < 32 || n > 127) {
if (n > 127)
need_encoding = true;
string h = n.get_as_base(16);
result = result + '\x{' + h + '}';
}
else
result = result + c;
}
}
str = result;
}
str = quote + str + quote;
if (need_encoding)
str = 'utf8:' + str;
return str;
}
}
class TokenInteger : TokenWithVal
{
function TokenInteger(string file, int line, string str)
{
self.TokenWithVal(file, line, str);
}
function isint() { return true; }
function getintvalue()
{
return int(self.str);
}
}
class TokenFloat : TokenWithVal
{
function TokenFloat(string file, int line, string str)
{
self.TokenWithVal(file, line, str);
}
function isfloat() { return true; }
function getfloatvalue()
{
// Cast to float fails in stage 1, use an aux var instead.
float value = self.str;
return value;
}
}
//*********************************************
// Tokenizer auxiliar functions
//*********************************************
function TokenError(string msg, 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(tk, int line)
{
TokenError('Unterminated string', tk, line);
}
function UnterminatedHeredoc(tk, int line)
{
TokenError('Unterminated heredoc', tk, line);
}
function UnclosedComment(tk, int line)
{
TokenError("Unclosed comment", tk, line);
}
function getquoted(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 getsinglequoted(tk, string start, int line)
{
string s = '';
string c;
for (c = tk.getchar(); c != "'"; c = tk.getchar()) {
if (c == "" || c == "\n")
UnterminatedString(tk, line);
s = s + c;
}
return new TokenSingleQuoted(tk.filename, line, s);
}
function getheredoc(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(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(tk, string start, int line)
{
string s;
string c = start;
do {
s = s+ c;
c = tk.getchar();
} while (isdigit(c));
if (s == '0' && (c == 'x' || c == 'X')) {
int hexval = 0, h;
for (c = tk.getchar(); (h = hexdigit(c)) >= 0; c = tk.getchar()) {
hexval = hexval * 16 + h;
s = s + c;
}
tk.ungetchar(c);
s = hexval;
return new TokenInteger(tk.filename, line, s);
}
if (c == '.') {
do {
s = s + c;
c = tk.getchar();
} while (isdigit(c));
if (c == 'e' || c == 'E') {
s = s + 'E';
if ((c = tk.getchar()) == '+' || c == '-') {
s = s + c;
c = tk.getchar();
}
for ( ; isdigit(c); c = tk.getchar())
s = s + c;
}
tk.ungetchar(c);
return new TokenFloat(tk.filename, line, s);
}
else {
if (c == 'e' || c == 'E') {
s = s + 'E';
if ((c = tk.getchar()) == '+' || c == '-') {
s = s + c;
c = tk.getchar();
}
for ( ; isdigit(c); c = tk.getchar())
s = s + c;
tk.ungetchar(c);
return new TokenFloat(tk.filename, line, s);
}
else {
tk.ungetchar(c);
return new TokenInteger(tk.filename, line, s);
}
}
}
function getlinecomment(tk, string start, int line)
{
string s = start;
for (string c = tk.getchar(); c != '' && c != "\n"; c = tk.getchar())
s = s + c;
return new TokenComment(tk.filename, line, s);
}
function getcomment(tk, string start, int line)
{
string s = start;
string c = tk.getchar();
do {
for ( ; c != '' && c != '*'; c = tk.getchar())
s = s + c;
if (c == '')
UnclosedComment(tk, line);
s = s + c;
c = tk.getchar();
if (c == '')
UnclosedComment(tk, line);
} while (c != '/');
s = s + '/';
return new TokenComment(tk.filename, line, s);
}
function getop(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(handle, string filename, int nowarn)
{
self.warnings = new ["Boolean"](! nowarn);
self.h = handle;
self.pending = '';
self.stacked = [];
self.filename = filename;
self.line = 1;
var select = {
'$': { '': getident, '{': getop },
'"': getquoted,
"'": getsinglequoted,
'=': {
'=': { '': getop, '=': getop },
':': getop
},
'+': { '+': getop, '=': getop },
'-': { '-': getop, '=': getop },
'*': { '=': getop },
'|': { '|': getop },
'&': { '&': getop },
'<': {
'<': { '': getop, ':': getheredoc },
'=': getop
},
'>': {
'>': { '': getop, '>': getop },
'=': getop
},
'!': {
'=': { '': getop, '=': getop }
},
'%': { '%': getop, '=': getop },
'/': { '=': getop, '/': getlinecomment, '*': getcomment },
'#': getlinecomment
};
self.select = select;
}
function warn(string msg, var pos [optional])
{
if (self.warnings)
Warn(msg, pos);
}
function getchar()
{
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(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 emitrepeat(string dst, string src1, string src2)
{
self.say(INDENT + "repeat ", 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 floatValue(var owner, var start, float value)
{
var t = new TokenFloat(start.file, start.line, value);
return new FloatLiteral(owner, t);
}
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;
}
function floatAsString(float n)
/*
Make sure that a float value is recognized as such by the
PIR compiler, adding a dot if needed.
*/
{
// Use assignment instead of initializations to work around
// stage 1 limitations.
string aux; aux = n;
if (aux.is_integer(aux))
aux = aux + '.0';
return aux;
}
// Predefined constants
// Fake filename and line number for the tokens.
const string PREDEFCONST_FILENAME = '__predefconst__';
const int PREDEFCONST_LINENUM = 0;
function createPredefConstInt(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(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(arg)
{
string value;
switch {
case arg.isintegerliteral():
value = arg.numval;
break;
case arg.isfloatliteral():
value = arg.getFloatValue();
break;
case arg.isstringliteral():
value = arg.get_value();
break;
default:
InternalError('wrong call to string_from_literal', arg);
}
return value;
}
function int_from_literal(arg)
{
int value;
switch {
case arg.isintegerliteral():
value = arg.numval;
break;
case arg.isfloatliteral():
value = arg.getFloatValue();
break;
case arg.isstringliteral():
value = arg.get_value();
break;
default:
InternalError('wrong call to int_from_literal', arg);
}
return value;
}
function float_from_literal(arg)
{
float value;
switch {
case arg.isintegerliteral():
case arg.isfloatliteral():
value = arg.getFloatValue();
break;
case arg.isstringliteral():
value = arg.get_value();
break;
default:
InternalError('wrong call to float_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](e, owner, start, string result, 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, 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, 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, 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, evalfun, 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](e, owner, start, string result, 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(owner, start, args)
{
var arg = args[0].arg;
return stringQuotedValue(owner, start, string_from_literal(arg));
}
function builtineval_intcast(owner, start, args)
{
var arg = args[0].arg;
int value = int_from_literal(arg);
return integerValue(owner, start, value);
}
function Builtin_varcast(e, owner, start, string result, args)
{
if (elements(args) != 1)
InternalError("Invalid var cast");
var arg = args[0];
string argtype = arg.checkresult();
if (argtype == REGvar) {
string name;
var sym;
switch {
case arg instanceof IdentifierExpr:
name = arg.getName();
string id = arg.checkIdentifier();
var desc = arg.checkVar();
if (id != '' && desc != null && ! desc.issubid()) {
// The cast is redundant in this case
arg.emit(e, result);
}
else {
sym = arg.scopesearch([name], 0);
if (sym != null && sym instanceof FunctionStatement) {
var path = sym.owner.getpath();
e.emitget_hll_global(result, name, getparrotkey(path));
}
}
break;
case arg instanceof MemberExpr:
// If it's a known function avoid using it bt subid,
// else, look up in the hll namespace at runtime.
string key[];
arg.buildkey(key);
sym = owner.scopesearch(key, 0);
if (sym != null && sym instanceof FunctionStatement) {
var path = sym.owner.getpath();
e.annotate(start);
e.emitget_hll_global(result, name, getparrotkey(path));
}
name = key.pop();
e.annotate(start);
e.emitget_hll_global(result, name, getparrotkey(key));
break;
default:
e.annotate(start);
arg.emit(e, result);
}
}
else {
string reg = arg.emit_get(e);
e.annotate(start);
if (reg == NULL)
e.emitnull(result);
else
e.emitbox(result, reg);
}
}
function Builtin_say(e, owner, start, string result, 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(e, owner, start, string result, args)
{
e.annotate(start);
e.say(sformat(
<<:
getstderr $P0
%0
print $P0, "\n"
:>>
, join("\n", transform(args, [], emit_printP0))));
}
function Builtin_print(e, owner, start, string result, args)
{
e.annotate(start);
for (string arg in args)
e.emitprint(arg);
}
function Builtin_ASSERT(e, owner, start, string result, 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(owner, start, args)
{
var arg = args[0].arg;
string s = string_from_literal(arg);
return integerValue(owner, start, length(s));
}
function builtineval_bytelength(owner, start, args)
{
var arg = args[0].arg;
string s = string_from_literal(arg);
return integerValue(owner, start, bytelength(s));
}
function builtineval_ord(owner, start, 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(owner, start, args)
{
var arg = args[0].arg;
return stringQuotedValue(owner, start, chr(int_from_literal(arg)));
}
function builtineval_substr(owner, start, 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 (args > 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(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('var',
Builtin_varcast,
REGvar, 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('pop_var',
'pop %0, %1',
REGvar, REGvar
));
builder.add(new BuiltinFunction('shift_var',
'shift %0, %1',
REGvar, REGvar
));
builder.add(new BuiltinFunction('pop_int',
'pop %0, %1',
REGint, REGvar
));
builder.add(new BuiltinFunction('shift_int',
'shift %0, %1',
REGint, REGvar
));
builder.add(new BuiltinFunction('pop_float',
'pop %0, %1',
REGfloat, REGvar
));
builder.add(new BuiltinFunction('shift_float',
'shift %0, %1',
REGfloat, REGvar
));
builder.add(new BuiltinFunction('pop_string',
'pop %0, %1',
REGstring, REGvar
));
builder.add(new BuiltinFunction('shift_string',
'shift %0, %1',
REGstring, REGvar
));
builder.add(new BuiltinFunction('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(e, arr)
{
for (var item in arr)
item.emit(e);
}
function parseDotted(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(tk, owner, 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;
}
// Helper to parse identifier lists using parseList
function parseIdentifier(tk, 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(start, owner)
{
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(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(fn)
{
return self.owner.addlocalfunction(fn);
}
function scopesearch(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(owner, 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(tk, 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(t, tk, 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(start, tk, 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(tk, 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(tk, 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();
if (! open.isop('('))
Unexpected("':'", t);
return parseSig(t, tk, owner);
}
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(start, owner)
{
self.initbase(start, owner);
}
function isempty() { return false; }
function allowtailcall()
{
return self.owner.allowtailcall();
}
function createreg(string type)
{
return self.owner.createreg(type);
}
function tempreg(string type)
{
return self.owner.tempreg(type);
}
function freetemps()
{
self.owner.freetemps();
}
function genlabel()
{
return self.owner.genlabel();
}
function createconst(name, string type, int flags [optional])
{
return self.owner.createconst(name, type, flags);
}
function createvar(name, string type, int flags[optional])
{
return self.owner.createvar(name, type, flags);
}
function createvarused(name, data)
{
return self.owner.createvarused(name, data);
}
function createvarnamed(name, string type, string pirname)
{
return self.owner.createvarnamed(name, type, pirname);
}
function getvar(name)
{
return self.owner.getvar(name);
}
function checkclass(string name)
{
return self.owner.checkclass(name);
}
function usenamespace(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(pos)
{
return self.owner.getcontinuelabel(pos);
}
function getbreaklabel(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(e)
{
InternalError('Attempt to annotate empty statement');
}
function optimize() { return self; }
function emit(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(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(start, tk, 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(e)
{
self.annotate(e);
e.say(INDENT, "load_bytecode '", join('/', self.path), ".pbc'");
}
}
//*********************************************
// UsingStatement
//*********************************************
class UsingStatement : Statement
{
var path;
var subid;
function UsingStatement(start, tk, 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(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(start, tk, 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(start, tk, owner)
{
self.Statement(tk, owner);
usingNamespace(start, tk, owner);
ExpectOp(';', tk);
}
function optimize()
{
return self;
}
function emit(e)
{
}
}
//*********************************************
// ExprStatement
//*********************************************
class ExprStatement : Statement
{
var expr;
function ExprStatement(start, tk, 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(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, reg, 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, name, scope, int flags)
{
self.VarData(type, new ConstantInternalFail(name), scope, flags);
}
function isconst() { return true; }
function setvalue(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)
SyntaxError("Redeclared '" + sname + "'", name);
string reg = self.createreg(type);
var data = new VarData(type, reg, self, flags);
locals[sname] = data;
return data;
}
function createvarused(name, data)
{
var locals = self.locals;
string sname = name;
var exist = locals[sname];
if (exist != null)
SyntaxError("Redeclared '" + sname + "'", 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)
SyntaxError("Redeclared '" + sname + "'", 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)
SyntaxError("Redeclared '" + sname + "'", 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(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(start, owner)
{
self.Statement(start, owner);
self.VarContainer();
}
}
//*********************************************
// Expr
//*********************************************
class Expr : CommonBase
{
function Expr(owner, start)
{
self.initbase(start, owner);
}
function issimple() { return false; }
function isliteral() { return false; }
function isintegerliteral() { return false; }
function isintegerzero() { return false; }
function isfloatliteral() { return false; }
function isstringliteral() { return false; }
function isidentifier() { return false; }
function isnull() { return false; }
function hascompilevalue() { return false; }
function isnegable() { return false; }
function tempreg(type)
{
return self.owner.tempreg(type);
}
function genlabel()
{
return self.owner.genlabel();
}
function optimize()
{
// By default return same expression unchanged
return self;
}
function cantailcall() { return false; }
function emit_init(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(e)
{
string reg = self.tempreg(self.checkresult());
self.emit(e, reg);
return reg;
}
function emit_void(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(e)
{
// Must be overriden by any possible null
return self.emit_get(e);
}
function emit_getint(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(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(e, 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(tk, owner, 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(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(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(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(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(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(e) { }
}
//*********************************************
class StringLiteral : Literal
{
var strval;
function StringLiteral(owner, t)
{
self.Expr(owner, t);
self.strval = t;
}
function isstringliteral() { return true; }
function checkresult() { return REGstring; }
function getPirString()
{
var strtok = self.strval;
string str = strtok.getPirString();
return str;
}
function get_value()
{
var strtok = self.strval;
string str = strtok.str;
if (strtok instanceof TokenQuoted)
str = unescape(str);
return str;
}
function emit(e, string result)
{
e.emitset(result, self.getPirString());
}
function emit_get(e)
{
return self.getPirString();
}
}
//*********************************************
class IntegerLiteral : Literal
{
var pos;
var numval;
function IntegerLiteral(owner, t, value)
{
self.Expr(owner, t);
self.pos = t;
int n = value;
self.numval = n;
}
function isintegerliteral() { return true; }
function isintegerzero()
{
return int(self.numval) == 0;
}
function checkresult() { return REGint; }
function getIntegerValue()
{
return self.numval;
}
function getFloatValue()
{
float value = self.getIntegerValue();
return value;
}
function emit(e, string result)
{
int value = self.getIntegerValue();
if (value == 0)
e.emitnull(result);
else
e.emitset(result, value);
}
function emit_get(e)
{
return self.getIntegerValue();
}
function emit_getint(e)
{
// Just a shortcut
return self.getIntegerValue();
}
}
//*********************************************
class FloatLiteral : Literal
{
var numval;
function FloatLiteral(owner, t)
{
self.Expr(owner, t);
self.numval = t;
}
function isfloatliteral() { return true; }
function checkresult() { return REGfloat; }
function getFloatValue()
{
float value = self.numval.getfloatvalue();
return value;
}
function emit(e, string result)
{
string n = self.emit_get(e);
e.emitset(result, n);
}
function emit_get(e)
{
float value = self.getFloatValue();
return floatAsString(value);
}
}
//*********************************************
function concat_literal(lexpr, rexpr)
{
__ASSERT__(lexpr.isstringliteral());
__ASSERT__(rexpr.isstringliteral());
var etok = lexpr.strval;
var rtok = rexpr.strval;
// If both are single quoted, result is single quoted.
// If one is double quoted, result is double quoted.
var t = etok.issinglequoted() && rtok.issinglequoted()
?
new TokenSingleQuoted(etok.file, etok.line,
string(etok.str) + string(rtok.str))
:
new TokenQuoted(etok.file, etok.line,
string(etok.getasquoted()) + string(rtok.getasquoted()));
return new StringLiteral(lexpr.owner, t);
}
//*********************************************
class FunctionId : FinalExpr
{
var subid;
function FunctionId(owner, start, name, string id)
{
self.Expr(owner, start);
self.subid = id;
}
function checkresult() { return REGvar; }
function emit_get(e)
{
return self.subid;
}
function emit(e, string result)
{
self.annotate(e);
e.emitset(result, self.subid);
}
}
class FunctionRef : FinalExpr
{
var sym;
function FunctionRef(owner, start, sym)
{
self.Expr(owner, start);
self.sym = sym;
}
function checkresult() { return REGvar; }
function emit(e, string result)
{
var sym = self.sym;
var path = sym.owner.getpath();
self.annotate(e);
path.emit_get_global(e, self.owner, result, sym.name);
}
}
class IdentifierExpr : SimpleExpr
{
var name;
function isidentifier() { return true; }
function IdentifierExpr(owner, t)
{
self.Expr(owner, t);
self.name = t;
}
function isnull()
{
var name = self.name;
if (self.owner.getvar(name) != null)
return false;
return name.iskeyword(NULL);
}
function emit_get_nonull(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 desc = self.owner.getvar(name);
if (desc != null) {
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(self.owner, self.start, name, reg);
}
}
else {
var sym = self.scopesearch([name], 0);
switch {
case sym == null:
break;
case sym instanceof FunctionStatement:
if (!sym.ismulti()) {
string id = sym.makesubid();
self.usesubid(id);
self.owner.createvarnamed(name, REGvar, id);
return new FunctionId(self.owner, self.start, name, id);
}
else
return new FunctionRef(self.owner, self.start, sym);
break;
case sym instanceof FunctionExtern:
return new FunctionRef(self.owner, self.start, sym);
}
}
return self;
}
function emit(e, string result)
{
string reg = self.emit_get(e);
self.annotate(e);
e.emitset(result, reg);
}
function emit_void() { }
function emit_get(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(e, 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(idexpr, desc)
{
self.Expr(idexpr.owner, idexpr.start);
self.desc = desc;
}
function checkresult()
{
return self.desc.gettype();
}
function emit_get(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(e, string result)
{
self.annotate(e);
string reg = self.emit_get(e);
e.emitset(result, reg);
}
function emit_assign_get(e, 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(e, reg)
{
e.emitstore_lex(self.desc.getlex(), reg);
}
}
//*********************************************
class OpExpr : Expr
{
function initop(owner, start)
{
self.Expr(owner, start);
}
}
//*********************************************
class OpNamespaceExpr : OpExpr
{
var key;
function OpNamespaceExpr(tk, owner, start)
{
self.initop(owner, start);
self.key = parseDotted(tk);
if (elements(self.key) == 0)
Expected('namespace identifier', start);
}
function checkresult() { return REGvar; }
function emit(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(tk, owner, 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(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(owner, start, 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(owner, start, lexpr, rexpr)
{
self.initop(owner, start);
self.lexpr = lexpr;
self.rexpr = rexpr;
}
function set(owner, t, lexpr, rexpr)
{
self.initbinary(owner, t, lexpr, rexpr);
return self;
}
function setfrom(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(e)
{
return self.lexpr.emit_getint(e);
}
function emit_intright(e)
{
return self.rexpr.emit_getint(e);
}
}
//*********************************************
class OpBinaryIntExpr : OpBinaryExpr
{
function checkresult()
{
return REGint;
}
function optimize()
{
self.optimizearg();
var lexpr = self.lexpr;
var rexpr = self.rexpr;
return self;
}
}
//*********************************************
class OpDelExBase : OpUnaryExpr
{
function checkresult()
{
return REGint;
}
}
class OpDeleteExpr : OpDelExBase
{
function OpDeleteExpr(owner, start, 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(e)
{
var expr = self.subexpr;
expr.emit_prep(e);
self.annotate(e);
e.print(INDENT + 'delete ');
expr.emit_aux(e);
e.say();
}
function emit(e, string result)
{
self.emit_void(e);
e.emitset(result, '1');
}
}
class OpExistsExpr : OpDelExBase
{
function OpExistsExpr(owner, start, subexpr)
{
self.OpUnaryExpr(owner, start, subexpr);
}
function emit(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(owner, start, subexpr)
{
self.OpUnaryExpr(owner, start, subexpr);
}
function checkresult()
{
return self.subexpr.checkresult();
}
function set(owner, t, 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(e, string result)
{
string reg = self.subexpr.emit_get(e);
self.annotate(e);
e.emitarg2('neg', result, reg);
}
}
//*********************************************
class OpNotExpr : OpUnaryExpr
{
function OpNotExpr(owner, start, subexpr)
{
self.OpUnaryExpr(owner, start, subexpr);
}
function isnegable() { return true; }
function checkresult()
{
return REGint;
}
function set(owner, t, 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(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(owner, start, subexpr)
{
self.OpUnaryExpr(owner, start, subexpr);
}
function checkresult()
{
return REGint;
}
function set(owner, t, 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 emit(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(e, reg)
{
var expr = self.subexpr;
if (expr instanceof LexicalVolatileExpr)
expr.emit_store(e, reg);
}
}
//*********************************************
class OpPreIncDec : OpIncDec
{
function emit(e, string result)
{
string reg = self.emit_get(e);
e.emitset(result, reg);
}
function emit_void(e)
{
self.emit_get(e);
}
}
class OpPreIncExpr : OpPreIncDec
{
function OpPreIncExpr(owner, start, subexpr)
{
self.OpUnaryExpr(owner, start, subexpr);
}
function emit_get(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(owner, start, subexpr)
{
self.OpUnaryExpr(owner, start, subexpr);
}
function emit_get(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(owner, start, subexpr)
{
self.OpUnaryExpr(owner, start, subexpr);
}
function emit(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(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(e)
{
string reg = self.subexpr.emit_get(e);
self.annotate(e);
e.emitinc(reg);
self.iflexical(e, reg);
}
}
class OpPostDecExpr : OpIncDec
{
function OpPostDecExpr(owner, start, subexpr)
{
self.OpUnaryExpr(owner, start, subexpr);
}
function emit(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(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(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(owner, start, lexpr, 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(e, string result)
{
string reg = self.emit_get(e);
self.annotate(e);
e.emitset(result, reg);
}
function emit_void(e)
{
self.emit_get(e);
}
}
//*********************************************
class OpAssignExpr : OpBaseAssignExpr
{
function emit_get(e)
{
self.annotate(e);
var lexpr = self.lexpr;
return lexpr.emit_assign_get(e, self.rexpr);
}
function emit_void(e)
{
self.annotate(e);
var lexpr = self.lexpr;
lexpr.emit_assign_get(e, self.rexpr);
}
}
//*********************************************
class OpAssignToExpr : OpBaseAssignExpr
{
function emit(e, string result)
{
self.annotate(e);
string reg = self.emit_get(e);
e.emitassign(result, reg);
}
function emit_get(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(e)
{
self.annotate(e);
string reg = self.emit_get(e);
}
}
//*********************************************
class OpAddToExpr : OpBaseAssignExpr
{
function emit_get(e)
{
self.checkleft();
var lexpr = self.lexpr;
var rexpr = self.rexpr;
string ltype = lexpr.checkresult();
string rtype = rexpr.checkresult();
string reg = lexpr.emit_get(e);
if (ltype == REGstring && (rexpr instanceof ConcatString))
rexpr.emit_concat_to(e, reg);
else {
string reg2 = rexpr.emit_get(e);
string aux;
self.annotate(e);
switch (ltype) {
case REGstring:
if (rtype != REGstring) {
aux = self.tempreg(REGstring);
e.emitset(aux, reg2);
reg2 = aux;
}
e.emitconcat1(reg, reg2);
break;
case REGint:
case REGfloat:
if (ltype != rtype) {
aux = self.tempreg(ltype);
e.emitset(aux, reg2);
reg2 = aux;
}
e.emitaddto(reg, reg2);
break;
default:
e.emitaddto(reg, reg2);
}
}
if (lexpr instanceof LexicalVolatileExpr)
lexpr.emit_store(e, reg);
return reg;
}
}
//*********************************************
class OpSubToExpr : OpBaseAssignExpr
{
function emit_get(e)
{
self.checkleft();
var lexpr = self.lexpr;
var rexpr = self.rexpr;
string ltype = lexpr.checkresult();
string rtype = rexpr.checkresult();
string reg = lexpr.emit_get(e);
string reg2 = rexpr.emit_get(e);
string aux;
self.annotate(e);
switch (ltype) {
case REGstring:
SyntaxError("-= can't be applied to string", self);
case REGint:
case REGfloat:
if (ltype != rtype) {
aux = self.tempreg(ltype);
e.emitset(aux, reg2);
reg2 = aux;
}
e.emitsubto(reg, reg2);
break;
default:
e.emitsubto(reg, reg2);
}
if (lexpr instanceof LexicalVolatileExpr)
lexpr.emit_store(e, reg);
return reg;
}
}
//*********************************************
class OpMulToExpr : OpBaseAssignExpr
{
function emit_get(e)
{
self.checkleft();
var lexpr = self.lexpr;
var rexpr = self.rexpr;
string lreg = lexpr.emit_get(e);
string rreg;
switch (lexpr.checkresult()) {
case REGstring:
rreg = rexpr.emit_getint(e);
self.annotate(e);
e.emitrepeat(lreg, lreg, rreg);
break;
default:
rreg = rexpr.emit_get(e);
self.annotate(e);
e.emitarg2('mul', lreg, rreg);
}
if (lexpr instanceof LexicalVolatileExpr)
lexpr.emit_store(e, lreg);
return lreg;
}
}
//*********************************************
class OpDivToExpr : OpBaseAssignExpr
{
function emit_get(e)
{
self.checkleft();
var lexpr = self.lexpr;
string reg = lexpr.emit_get(e);
string reg2 = self.rexpr.emit_get(e);
self.annotate(e);
e.emitarg2('div', reg, reg2);
if (lexpr instanceof LexicalVolatileExpr)
lexpr.emit_store(e, reg);
return reg;
}
}
//*********************************************
class OpModToExpr : OpBaseAssignExpr
{
function emit_get(e)
{
self.checkleft();
var lexpr = self.lexpr;
string reg = lexpr.emit_get(e);
string reg2 = self.rexpr.emit_get(e);
self.annotate(e);
e.emitarg2('mod', reg, reg2);
if (lexpr instanceof LexicalVolatileExpr)
lexpr.emit_store(e, reg);
return reg;
}
}
//*********************************************
const int
COMPARATOR_DEFAULT = 0,
COMPARATOR_IF = 1,
COMPARATOR_ELSE = 2;
class ComparatorBaseExpr : OpBinaryExpr
{
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(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(e, string result)
{
self.emit_comparator(e, result);
}
function emit_if(e, string labeltrue)
{
self.emit_comparator(e, labeltrue, COMPARATOR_IF);
}
function emit_else(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(base, 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(base, expr, int checknull)
{
self.CheckerExpr(base, expr, checknull);
}
function emit(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(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(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(base, expr, int positive)
{
self.CheckerExpr(base, expr, positive);
}
function emit(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(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(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(owner, start, lexpr, 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(e, string result, string regl, string regr)
{
self.annotate(e);
e.emitbinop(self.positive ? 'iseq' : 'isne', result, regl, regr);
}
function emit(e, string result)
{
self.annotate(e);
self.emit_comparator(e, result);
}
function emitop_if(e, string labeltrue, string regl, string regr)
{
self.annotate(e);
e.emitcompare(self.positive ? 'eq' : 'ne', regl, regr, labeltrue);
}
function emitop_else(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(owner, t, lexpr, 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(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(e, string result)
{
self.emit_comparator(e, result);
}
function emitop_if(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(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(e, string result, string regl, string regr)
{
e.emitbinop('islt', result, regl, regr);
}
function emitop_if(e, string labeltrue, string regl, string regr)
{
e.emitcompare('lt', regl, regr, labeltrue);
}
function emitop_else(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(e, string result, string regl, string regr)
{
e.emitbinop('isgt', result, regl, regr);
}
function emitop_if(e, string labeltrue, string regl, string regr)
{
e.emitcompare('gt', regl, regr, labeltrue);
}
function emitop_else(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(e, string result, string regl, string regr)
{
e.emitbinop('isle', result, regl, regr);
}
function emitop_if(e, string labeltrue, string regl, string regr)
{
e.emitcompare('le', regl, regr, labeltrue);
}
function emitop_else(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(e, string result, string regl, string regr)
{
e.emitbinop('isge', result, regl, regr);
}
function emitop_if(e, string labeltrue, string regl, string regr)
{
e.emitcompare('ge', regl, regr, labeltrue);
}
function emitop_else(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(owner, start, lexpr, 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(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(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(owner, start, lexpr, 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(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);