📄 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 Sem::field(char *id)
{
trace("field not implemented\n"); // not implemented
return true;
}
bool Sem::aref()
{
return emit(OPCODE_AREF);
}
bool Sem::if_true()
{
// emit a test to branch over true part
must(emit(OPCODE_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 Sem::if_else()
{
// this is the target for the if:
long target = if_stack->pop(mach);
// emit a jump around else part
must(emit(OPCODE_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 Sem::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 Sem::while_begin()
{
// remember where to branch back to
looper_stack->push(next_instr, mach);
return true;
}
bool Sem::while_true()
{
// emit a test to branch over true part
must(emit(OPCODE_JUMPIFNOT));
// save PC for the branch location
looper_stack->push(next_instr, mach);
// emit zero as a placeholder for branch location
return emit(0);
}
bool Sem::while_end()
{
// top of stack is branch out of loop
long end_of_looper_branch = looper_stack->pop(mach);
// now emit loop branch
must(emit(OPCODE_JUMP));
must(emit(looper_stack->pop(mach) - next_instr));
// fix up previous branch to next instr (after loop)
return emit_at(end_of_looper_branch, next_instr - end_of_looper_branch);
}
bool Sem::return_end()
{
return emit(OPCODE_RETURN);
}
bool Sem::load_end()
{
return emit(OPCODE_LOAD);
}
bool Sem::print_expr()
{
return emit(OPCODE_PRINT);
}
bool Sem::print_comma()
{
return emit(OPCODE_SPACE);
}
bool Sem::print_end()
{
return emit(OPCODE_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 Sem::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 Sem::rval2lval()
{
// first get previous instruction
short op = unemit();
short newop = OPCODE_NOOP;
// now compute the assignment version of op
short typ = instr_type(op);
short arg = instr_arg(op);
if (typ == instr_op) {
if (op == OPCODE_AREF) newop = OPCODE_SETAREF;
else if (op == OPCODE_FIELD) newop = OPCODE_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 != OPCODE_NOOP) {
assign_stack->push(newop, mach);
return true;
}
return false;
}
bool Sem::unary_minus()
{
return emit(OPCODE_MINUS);
}
bool Sem::not()
{
return emit(OPCODE_NULL);
}
bool Sem::lognot()
{
return emit(OPCODE_LOGNOT);
}
bool Sem::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::make(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 Sem::push_double(double value)
{
FVal lit = FDouble::make(value, mach);
return push_fval(lit);
}
bool Sem::push_string(char *s)
{
FVal lit = FString::make(s, mach);
return push_fval(lit);
}
bool Sem::push_symbol(char *s)
{
FVal lit = Symbol::make(s, mach);
return push_fval(lit);
}
bool Sem::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 Sem::var_decl(char * id)
{
if (method_started) { // local variable in method
assert(method);
Symbol * s = Symbol::make(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 * s = Symbol::make(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 Sem::array_start()
{
array_stack->push(array_index, mach);
array_index = 0;
array_stack->push(next_instr, mach);
return emit(0) && emit(OPCODE_NEWARRAY);
}
bool Sem::dict_start()
{
array_stack->push(array_index, mach);
array_index = 0;
array_stack->push(next_instr, mach);
return emit(0) && emit(OPCODE_NEWDICT);
}
bool Sem::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(OPCODE_DUP) && emit(instr);
}
bool Sem::before_dict_key()
{
array_index++;
return emit(OPCODE_DUP);
}
bool Sem::before_dict_val()
{
return true;
}
bool Sem::array_expr()
{
return emit(OPCODE_SETAREF);
}
bool Sem::dict_key()
{
return true;
}
bool Sem::dict_val()
{
return emit(OPCODE_SETAREF);
}
bool Sem::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 Sem::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 Sem::actual()
{
if (opcode == 0) return emit(OPCODE_PARAM);
if (actuals == 0) {
comp->report_error("too many parameters");
return false;
}
actuals--;
return true;
}
bool Sem::keyword(long index)
{
assert(index < 0x10000);
if (opcode == 0) {
return emit(OPCODE_KEYPARAM) && emit((short) index);
} else {
comp->report_error("unexpected keyword parameter");
return false;
}
}
bool Sem::actual_end()
{
// if optional start, stop parameters are missing, fill
// them in now
if (opcode == instr_compose(instr_op, OPCODE_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 OPCODE_GET, if get(key, default) use OPCODE_GET2:
} else if (opcode == instr_compose(instr_op, OPCODE_GET)) {
if (actuals == 1) {
actuals--; // OPCODE_GET really takes one parameter
} else if (actuals == 0) {
// 2 parameters, so use OPCODE_GET2:
opcode = instr_compose(instr_op, OPCODE_GET2);
}
}
if (actuals > 0) {
comp->report_error("too few parameters");
return false;
}
if (opcode != 0) emit(opcode);
else emit(instr_compose(instr_op, OPCODE_CALL));
actuals = call_stack->pop(mach); // restore actuals
opcode = call_stack->pop(mach); // restore opcode
return true;
}
bool Sem::end_id_expr()
{
emit(instr_compose(instr_op, OPCODE_POP));
return true;
}
int Sem::insert_symbol(char *str)
{
Symbol * s = Symbol::make(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 Sem::relop(char *op)
{
if (streql(op, "<")) return emit(OPCODE_LESS);
if (streql(op, ">")) return emit(OPCODE_GTR);
if (streql(op, "<=")) return emit(OPCODE_LESSEQL);
if (streql(op, ">=")) return emit(OPCODE_GTREQL);
if (streql(op, "==")) return emit(OPCODE_ISEQUAL);
if (streql(op, "!=")) return emit(OPCODE_NOTEQUAL);
if (streql(op, "is")) return emit(OPCODE_IS);
if (streql(op, "isnot")) return emit(OPCODE_ISNOT);
if (streql(op, "in")) return emit(OPCODE_MEMBER);
if (streql(op, "notin")) return emit(OPCODE_NOTMEMBER);
return false;
}
bool Sem::addop(char *op)
{
if (streql(op, "+")) return emit(OPCODE_ADD);
if (streql(op, "-")) return emit(OPCODE_SUB);
return false;
}
bool Sem::mulop(char *op)
{
if (streql(op, "*")) return emit(OPCODE_MUL);
if (streql(op, "/")) return emit(OPCODE_DIV);
if (streql(op, "%")) return emit(OPCODE_REM);
return false;
}
bool Sem::power()
{
return emit(OPCODE_POWER);
}
bool Sem::begin_and(char *op)
{
if (op[0] == '&') return true; // logical and
must(emit(OPCODE_CNDAND));
if_stack->push(next_instr, mach);
// emit zero as a placeholder for branch location
return emit(0);
}
bool Sem::end_and(char *op)
{
if (op[0] == '&') return emit(OPCODE_LOGAND);
long target = if_stack->pop(mach);
return emit_at(target, next_instr - target);
// emit a jump around 2nd operand to here
}
bool Sem::begin_or(char *op)
{
if (op[0] == '|' ||
op[0] == '^') return true; // logical or
must(emit(OPCODE_CNDOR));
if_stack->push(next_instr, mach);
// emit zero as a placeholder for branch location
return emit(0);
}
bool Sem::end_or(char *op)
{
if (op[0] == '|') return emit(OPCODE_LOGIOR);
if (op[0] == '^') return emit(OPCODE_LOGXOR);
if (op[0] == '<') return emit(OPCODE_LSHIFT);
if (op[0] == '>') return emit(OPCODE_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 + -