📄 semantics.cpp
字号:
mach->push(s);
// is this a local? look in method symbol table
if (method->get_localtable()->lookup(s, &index)) {
must(emit(instr_compose(instr_store_local, (short) index)));
} else if (fclass &&
fclass->get_symboltable()->lookup(s, &mp, &index) &&
mp == NULL) {
trace("assign_id got instance index of %d\n", index);
must(emit(instr_compose(instr_store_instvar, (short) index)));
} else { // load value from a global
index = insert_symbol(id);
must(emit(instr_compose(instr_store_global, (short) index)));
}
mach->pop();
return true;
}
bool Semantics::field(char *id)
{
trace("field not implemented\n"); // not implemented
return true;
}
bool Semantics::aref()
{
return emit(OP_AREF);
}
bool Semantics::if_true()
{
// emit a test to branch over true part
must(emit(OP_JUMPIFNOT));
// save PC for the branch location
if_stack->push(next_instr, mach);
// emit zero as a placeholder for branch location
return emit(0);
}
bool Semantics::if_else()
{
// this is the target for the if:
long target = if_stack->pop(mach);
// emit a jump around else part
must(emit(OP_JUMP));
// save PC for the branch location
if_stack->push(next_instr, mach);
// emit zero as a placeholder for branch location
emit(0);
return emit_at(target, next_instr - target);
}
bool Semantics::if_end()
{
// this is the target for whatever is on the stack
long target = if_stack->pop(mach);
return emit_at(target, next_instr - target);
}
bool Semantics::while_begin()
{
// remember where to branch back to
loop_stack->push(next_instr, mach);
return true;
}
bool Semantics::while_true()
{
// emit a test to branch over true part
must(emit(OP_JUMPIFNOT));
// save PC for the branch location
loop_stack->push(next_instr, mach);
// emit zero as a placeholder for branch location
return emit(0);
}
bool Semantics::while_end()
{
// top of stack is branch out of loop
long end_of_loop_branch = loop_stack->pop(mach);
// now emit loop branch
must(emit(OP_JUMP));
must(emit(loop_stack->pop(mach) - next_instr));
// fix up previous branch to next instr (after loop)
return emit_at(end_of_loop_branch, next_instr - end_of_loop_branch);
}
bool Semantics::return_end()
{
return emit(OP_RETURN);
}
bool Semantics::load_end()
{
return emit(OP_LOAD);
}
bool Semantics::print_expr()
{
return emit(OP_PRINT);
}
bool Semantics::print_comma()
{
return emit(OP_SPACE);
}
bool Semantics::print_end()
{
return emit(OP_ENDL);
}
// assign_end -- called after rval is on stack
//
// emit pushed instruction to finish the job:
// possibilities are:
// assign to array: stack has array, index, value
// assign to head: stack has node, value
// assign to tail: stack has node, value
// assign to variable: stack has value
// assign to field of object: stack has object, index, value
//
bool Semantics::assign_end()
{
short op = assign_stack->pop(mach);
return emit(op);
}
// rval2lval -- prepare for an assignment statement
//
// when you encounter an '=', the expression on the left has
// just emitted an instruction to load the value to the stack
// The method "undoes" the emit, and converts the instruction
// from one that loads a value to one that stores a value.
// Error is returned if the instruction cannot be converted.
// E.g. if the instruction is an array reference, convert to
// a store. If the instruction is addition, return false.
//
bool Semantics::rval2lval()
{
// first get previous instruction
short op = unemit();
short newop = OP_NOOP;
// now compute the assignment version of op
short typ = instr_type(op);
short arg = instr_arg(op);
if (typ == instr_op) {
if (op == OP_AREF) newop = OP_SETAREF;
else if (op == OP_FIELD) newop = OP_SETFIELD;
} else if (typ == instr_load_global) {
newop = instr_compose(instr_store_global, arg);
} else if (typ == instr_load_instvar) {
newop = instr_compose(instr_store_instvar, arg);
} else if (typ == instr_load_local) {
newop = instr_compose(instr_store_local, arg);
}
if (newop != OP_NOOP) {
assign_stack->push(newop, mach);
return true;
}
return false;
}
bool Semantics::unary_minus()
{
return emit(OP_MINUS);
}
bool Semantics::not()
{
return emit(OP_NULL);
}
bool Semantics::lognot()
{
return emit(OP_LOGNOT);
}
bool Semantics::push_long(int64 value)
{
if (mach->arg_sign_extend(instr_arg((long) value)) == value) {
return emit(instr_compose(instr_load_int, instr_arg((short) value)));
} else {
FVal lit = FLong::create(value, mach);
mach->push(lit);
assert(method);
short arg = method->literal_add(lit, mach);
mach->pop();
return emit(instr_compose(instr_load_lit, arg));
}
}
bool Semantics::push_double(double value)
{
FVal lit = FDouble::create(value, mach);
return push_fval(lit);
}
bool Semantics::push_string(char *s)
{
FVal lit = FString::create(s, mach);
return push_fval(lit);
}
bool Semantics::push_symbol(char *s)
{
FVal lit = Symbol::create(s, mach);
return push_fval(lit);
}
bool Semantics::push_fval(FVal v)
{
mach->push(v);
assert(method);
short arg = method->literal_add(v, mach);
mach->pop();
return emit(instr_compose(instr_load_lit, arg));
}
bool Semantics::var_decl(char * id)
{
if (method_started) { // local variable in method
assert(method);
Symbol_ptr s = Symbol::create(id, mach);
assert(s);
mach->push(s);
trace("var_decl: before new method variable, locals are:\n");
method->get_localtable()->trace_locals();
if (!method->get_localtable()->
insert_var(s, local_offset++, mach)) {
comp->report_error("local variable already declared");
mach->pop();
return false;
}
method->set_stack_offset(1 + method->get_stack_offset());
mach->pop();
} else if (class_started) {
assert(fclass);
Symbol_ptr s = Symbol::create(id, mach);
mach->push(s);
trace("instance var at index %d\n", member_offset);
if (!fclass->get_symboltable()->
insert_var(s, member_offset++, mach)) {
comp->report_error("instance variable already declared");
mach->pop();
return false;
}
mach->pop();
fclass->set_inst_slots(member_offset);
}
// undeclared variables are globals, the value is stored on the
// symbol itself, so we do not need to enter it into a table
// show current state of things
if (method_started) {
trace("current locals are:\n");
method->get_localtable()->trace_locals();
} else if (class_started) {
trace("current members are:\n");
fclass->get_symboltable()->trace_members();
} else trace("global variable declared\n");
return true;
}
// Here's how array code generation works:
// the current array_index is pushed on array_stack
// and array_index is set to 0
// (in case we are dealing with nested arrays)
// emit placeholder for the size of the array
// (so there is an upper bound on the size of a
// literal array: the maximum literal integer,
// which is 2047)
// the location of the literal is pushed on array_stack
// the create array instruction is emitted
// for each expression, we duplicate the top of stack,
// emit a literal index, and emit a store into array op
// at array_end, we know the size of the array, so we
// pop the stack and emit the size at that location
// finally, pop the previous array_index value
bool Semantics::array_start()
{
array_stack->push(array_index, mach);
array_index = 0;
array_stack->push(next_instr, mach);
return emit(0) && emit(OP_NEWARRAY);
}
bool Semantics::dict_start()
{
array_stack->push(array_index, mach);
array_index = 0;
array_stack->push(next_instr, mach);
return emit(0) && emit(OP_NEWDICT);
}
bool Semantics::before_array_expr()
{
short instr = instr_compose(instr_load_int, array_index);
if (instr_arg(instr) != array_index) {
comp->report_error("array expression too long");
return false;
}
array_index++;
return emit(OP_DUP) && emit(instr);
}
bool Semantics::before_dict_key()
{
array_index++;
return emit(OP_DUP);
}
bool Semantics::before_dict_val()
{
return true;
}
bool Semantics::array_expr()
{
return emit(OP_SETAREF);
}
bool Semantics::dict_key()
{
return true;
}
bool Semantics::dict_val()
{
return emit(OP_SETAREF);
}
bool Semantics::array_end()
{
must(emit_at(array_stack->pop(mach),
instr_compose(instr_load_int, array_index)));
array_index = array_stack->pop(mach);
return true;
}
bool Semantics::dict_end()
{
must(emit_at(array_stack->pop(mach),
instr_compose(instr_load_int, array_index)));
array_index = array_stack->pop(mach);
return true;
}
// here's how calls work:
// there are many built-in functions that compile to instructions
// which must be handled here rather than at run-time. When call()
// or method_call() is called, we look up the function to see if it
// is built-in. If so, we push the corresponding opcode and then
// the required number of parameters onto the call_stack (a
// compile-time structure). If the function is not built-in, we
// push zero and -1. Whenever we get an actual, we look at the top
// of stack. If opcode = 0 (not built-in), we generate a param opcode,
// otherwise we decrement
// the top of stack. (At runtime the parameter will be left on the
// expression stack, not copied to a frame.) If the top of stack
// would go to -1, we return an error (too many parameters).
// When we get an actual_end() we look at the opcode:
// If non-zero, emit it. If zero, emit the
// call opcode. If actuals is greater than zero, return an error
// (too few actuals).
// FOR CONVENIENCE, we will keep the top of the stack in member vars,
// opcode and actuals.
bool Semantics::actual()
{
if (opcode == 0) return emit(OP_PARAM);
if (actuals == 0) {
comp->report_error("too many parameters");
return false;
}
actuals--;
return true;
}
bool Semantics::keyword(long index)
{
assert(index < 0x10000);
if (opcode == 0) {
return emit(OP_KEYPARAM) && emit((short) index);
} else {
comp->report_error("unexpected keyword parameter");
return false;
}
}
bool Semantics::actual_end()
{
// if optional start, stop parameters are missing, fill
// them in now
if (opcode == instr_compose(instr_op, OP_FIND)) {
if (actuals == 2) {
push_long(0);
actuals--;
}
if (actuals == 1) {
push_long(-1);
actuals--;
}
// this is another style for variable argument lists
// if get(key) use OP_GET, if get(key, default) use OP_GET2:
} else if (opcode == instr_compose(instr_op, OP_GET)) {
if (actuals == 1) {
actuals--; // OP_GET really takes one parameter
} else if (actuals == 0) {
// 2 parameters, so use OP_GET2:
opcode = instr_compose(instr_op, OP_GET2);
}
}
if (actuals > 0) {
comp->report_error("too few parameters");
return false;
}
if (opcode != 0) emit(opcode);
else emit(instr_compose(instr_op, OP_CALL));
actuals = call_stack->pop(mach); // restore actuals
opcode = call_stack->pop(mach); // restore opcode
return true;
}
bool Semantics::end_id_expr()
{
emit(instr_compose(instr_op, OP_POP));
return true;
}
int Semantics::insert_symbol(char *str)
{
Symbol_ptr s = Symbol::create(str, mach);
mach->push(s);
long len = symbols->get_array_len();
for (int i = 0; i < len; i++) {
if (symbols->fastref(i).symbol == s) {
mach->pop();
return i;
}
}
symbols->append(s, mach);
mach->pop();
return i;
}
bool Semantics::relop(char *op)
{
if (streql(op, "<")) return emit(OP_LESS);
if (streql(op, ">")) return emit(OP_GTR);
if (streql(op, "<=")) return emit(OP_LESSEQL);
if (streql(op, ">=")) return emit(OP_GTREQL);
if (streql(op, "==")) return emit(OP_ISEQUAL);
if (streql(op, "!=")) return emit(OP_NOTEQUAL);
if (streql(op, "is")) return emit(OP_IS);
if (streql(op, "isnot")) return emit(OP_ISNOT);
if (streql(op, "in")) return emit(OP_MEMBER);
if (streql(op, "notin")) return emit(OP_NOTMEMBER);
return false;
}
bool Semantics::addop(char *op)
{
if (streql(op, "+")) return emit(OP_ADD);
if (streql(op, "-")) return emit(OP_SUB);
return false;
}
bool Semantics::mulop(char *op)
{
if (streql(op, "*")) return emit(OP_MUL);
if (streql(op, "/")) return emit(OP_DIV);
if (streql(op, "%")) return emit(OP_REM);
return false;
}
bool Semantics::power()
{
return emit(OP_POWER);
}
bool Semantics::begin_and(char *op)
{
if (op[0] == '&') return true; // logical and
must(emit(OP_CNDAND));
if_stack->push(next_instr, mach);
// emit zero as a placeholder for branch location
return emit(0);
}
bool Semantics::end_and(char *op)
{
if (op[0] == '&') return emit(OP_LOGAND);
long target = if_stack->pop(mach);
return emit_at(target, next_instr - target);
// emit a jump around 2nd operand to here
}
bool Semantics::begin_or(char *op)
{
if (op[0] == '|' ||
op[0] == '^') return true; // logical or
must(emit(OP_CNDOR));
if_stack->push(next_instr, mach);
// emit zero as a placeholder for branch location
return emit(0);
}
bool Semantics::end_or(char *op)
{
if (op[0] == '|') return emit(OP_LOGIOR);
if (op[0] == '^') return emit(OP_LOGXOR);
if (op[0] == '<') return emit(OP_LSHIFT);
if (op[0] == '>') return emit(OP_RSHIFT);
long target = if_stack->pop(mach);
return emit_at(target, next_instr - target);
// emit a jump around 2nd operand to here
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -