void Compiler::compileModule(VM& vm, ErrorReporter& reporter, gc<ModuleAst> ast, Module* module) { Compiler compiler(vm, reporter); for (int i = 0; i < ast->body()->expressions().count(); i++) { compiler.declareTopLevel(ast->body()->expressions()[i], module); } gc<Chunk> code = ExprCompiler(compiler).compileBody(module, ast->body()); module->setBody(code); }
gc<String> dir(gc<String> path) { // Find the last directory separator. int lastSeparator; for (lastSeparator = path->length() - 1; lastSeparator >= 0; lastSeparator--) { if ((*path)[lastSeparator] == separator()) break; } // If there are no directory separators, just return the original path. if (lastSeparator == -1) return path; return path->substring(0, lastSeparator); }
void ExprCompiler::compileParamField(PatternCompiler& compiler, gc<Pattern> param, int slot) { VariablePattern* variable = param->asVariablePattern(); if (variable != NULL) { // It's a variable, so compile its inner pattern. We don't worry about // the variable itself because the calling convention ensures its value // is already in the right slot. compiler.compile(variable->pattern(), slot); // If we closed over the parameter, then we don't want in a local slot, // we want it in the upvar, so create it and copy the value up. if (*variable->name() != "_" && variable->resolved()->scope() == NAME_CLOSURE) { write(variable->pos(), OP_SET_UPVAR, variable->resolved()->index(), slot, 1); } } else { // Not a variable, so just compile it normally. compiler.compile(param, slot); } }
gc<FunctionObject> FunctionObject::create(gc<Chunk> chunk) { // Allocate enough memory for the object and its upvars. void* mem = Memory::allocate(sizeof(FunctionObject) + sizeof(gc<Upvar>) * (chunk->numUpvars() - 1)); // Construct it by calling global placement new. return ::new(mem) FunctionObject(chunk); }
gc<Object> RecordObject::create(gc<RecordType> type, const Array<gc<Object> >& stack, int startIndex) { // Allocate enough memory for the record and its fields. void* mem = Memory::allocate(sizeof(RecordObject) + sizeof(gc<Object>) * (type->numFields() - 1)); // Construct it by calling global placement new. gc<RecordObject> record = ::new(mem) RecordObject(type); // Initialize the fields. for (int i = 0; i < type->numFields(); i++) { record->fields_[i] = stack[startIndex + i]; } return record; }
gc<DynamicObject> DynamicObject::create(gc<ClassObject> classObj) { ASSERT(classObj->numFields() == 0, "Class cannot have fields."); // Allocate enough memory for the object. void* mem = Memory::allocate(sizeof(DynamicObject)); // Construct it by calling global placement new. return ::new(mem) DynamicObject(classObj); }
void Resolver::destructureParam(gc<Pattern> pattern) { // No parameter so do nothing. if (pattern.isNull()) return; RecordPattern* record = pattern->asRecordPattern(); if (record != NULL) { // Resolve each field. for (int i = 0; i < record->fields().count(); i++) { resolveParam(record->fields()[i].value); } } else { // If we got here, the pattern isn't a record, so its a single slot. resolveParam(pattern); } }
void Resolver::allocateSlotsForParam(gc<Pattern> pattern) { // No parameter so do nothing. if (pattern.isNull()) return; RecordPattern* record = pattern->asRecordPattern(); if (record != NULL) { // Allocate each field. for (int i = 0; i < record->fields().count(); i++) { makeParamSlot(record->fields()[i].value); } } else { // If we got here, the pattern isn't a record, so it's a single slot. makeParamSlot(pattern); } }
void Resolver::makeParamSlot(gc<Pattern> param) { VariablePattern* variable = param->asVariablePattern(); if (variable != NULL && *variable->name() != "_") { // It's a variable, so create a named local for it and resolve the // variable. variable->setResolved(makeLocal(param->pos(), variable->name())); // Note that we do *not* resolve the variable's inner pattern here. We // do that after all param slots are resolved so that we can ensure the // param slots are contiguous. } else { // We don't have a variable for this parameter, but the argument // will still be on the stack, so make an unnamed slot for it. makeLocal(param->pos(), String::format("(%d)", unnamedSlotId_++)); } }
void FileObject::open(gc<Fiber> fiber, gc<String> path) { FSTask* task = new FSTask(fiber); // TODO(bob): Make this configurable. int flags = O_RDONLY; // TODO(bob): Make this configurable when creating a file. int mode = 0; uv_fs_open(task->loop(), task->request(), path->cString(), flags, mode, openFileCallback); }
void ExprCompiler::compileParam(PatternCompiler& compiler, gc<Pattern> param, int& slot) { // No parameter so do nothing. if (param.isNull()) return; RecordPattern* record = param->asRecordPattern(); if (record != NULL) { // Compile each field. for (int i = 0; i < record->fields().count(); i++) { compileParamField(compiler, record->fields()[i].value, slot++); } } else { // If we got here, the pattern isn't a record, so it's a single slot. compileParamField(compiler, param, slot++); } }
int ExprCompiler::compileArg(gc<Expr> arg) { // No arg so do nothing. if (arg.isNull()) return 0; RecordExpr* record = arg->asRecordExpr(); if (record != NULL) { // Compile each field. for (int i = 0; i < record->fields().count(); i++) { compile(record->fields()[i].value, makeTemp()); } return record->fields().count(); } // If we got here, the arg isn't a record, so its a single value. compile(arg, makeTemp()); return 1; }
void ChannelObject::send(gc<Fiber> sender, gc<Object> value) { // TODO(bob): What if the channel is closed? // If we have a receiver, give it the value. if (receivers_.count() > 0) { gc<Fiber> receiver = receivers_.removeAt(0); receiver->storeReturn(value); receiver->ready(); // Add the sender back to the scheduler too since it isn't blocked. sender->ready(); return; } // Otherwise, stuff the value and suspend. sender->waitToSend(value); senders_.add(sender); return; }
void Compiler::declareTopLevel(gc<Expr> expr, Module* module) { DefExpr* def = expr->asDefExpr(); if (def != NULL) { declareMultimethod(SignatureBuilder::build(*def)); return; } DefClassExpr* defClass = expr->asDefClassExpr(); if (defClass != NULL) { declareClass(*defClass, module); return; } VariableExpr* var = expr->asVariableExpr(); if (var != NULL) { declareVariables(var->pattern(), module); return; } }
void Compiler::declareVariable(gc<SourcePos> pos, gc<String> name, Module* module) { // Make sure there isn't already a top-level variable with that name. int existing = module->findVariable(name); if (existing != -1) { reporter_.error(pos, "There is already a variable '%s' defined in this module.", name->cString()); } module->addVariable(name, gc<Object>()); }
void Compiler::declareVariables(gc<Pattern> pattern, Module* module) { RecordPattern* record = pattern->asRecordPattern(); if (record != NULL) { for (int i = 0; i < record->fields().count(); i++) { declareVariables(record->fields()[i].value, module); } return; } VariablePattern* variable = pattern->asVariablePattern(); if (variable != NULL) { declareVariable(variable->pos(), variable->name(), module); if (!variable->pattern().isNull()) { declareVariables(variable->pattern(), module); } } }
PrintTask::PrintTask(gc<Fiber> fiber, gc<Object> value, int numBuffers) : Task(fiber), value_(value) { request_.data = this; buffers_[0].base = const_cast<char*>(asString(value)->cString()); buffers_[0].len = asString(value)->length(); buffers_[1].base = const_cast<char*>("\n"); buffers_[1].len = 1; uv_write(&request_, reinterpret_cast<uv_stream_t*>(fiber->scheduler().tty()), buffers_, 2, printCallback); }
void Resolver::resolveParam(gc<Pattern> param) { VariablePattern* variable = param->asVariablePattern(); if (variable != NULL) { // It's a variable, so resolve its inner pattern. if (!variable->pattern().isNull()) { scope_->resolve(*variable->pattern()); } } else { // Not a variable, so just resolve it normally. scope_->resolve(*param); } }
void SignatureBuilder::writeParam(gc<Pattern> pattern) { // If it's a record, destructure it into the signature. RecordPattern* record = pattern->asRecordPattern(); if (record != NULL) { for (int i = 0; i < record->fields().count(); i++) { add(record->fields()[i].name); add(":"); } return; } // Any other pattern is implicitly a single-field record. add("0:"); }
void SignatureBuilder::writeArg(gc<Expr> expr) { // If it's a record, destructure it into the signature. RecordExpr* record = expr->asRecordExpr(); if (record != NULL) { for (int i = 0; i < record->fields().count(); i++) { add(record->fields()[i].name); add(":"); } return; } // Right now, all other exprs mean "some arg goes here". add("0:"); }
gc<ResolvedName> Resolver::makeLocal(gc<SourcePos> pos, gc<String> name) { // Make sure there isn't already a local variable with this name in the // current scope. for (int i = scope_->startSlot(); i < locals_.count(); i++) { if (locals_[i].name() == name) { compiler_.reporter().error(pos, "There is already a variable '%s' defined in this scope.", name->cString()); } } gc<ResolvedName> resolved = new ResolvedName(locals_.count()); locals_.add(Local(name, resolved)); if (locals_.count() > maxLocals_) { maxLocals_ = locals_.count(); } return resolved; }
// Reads a file from the given path into a String. gc<String> readFile(gc<String> path) { // TODO(bob): Use platform-native API for this? using namespace std; ifstream stream(path->cString()); if (stream.fail()) return gc<String>(); // From: http://stackoverflow.com/questions/2602013/read-whole-ascii-file-into-c-stdstring. string str; // Allocate a std::string big enough for the file. stream.seekg(0, ios::end); str.reserve(stream.tellg()); stream.seekg(0, ios::beg); // Read it in. str.assign((istreambuf_iterator<char>(stream)), istreambuf_iterator<char>()); return String::create(str.c_str()); }
void ExprCompiler::compile(Module* module, int maxLocals, gc<Pattern> leftParam, gc<Pattern> rightParam, gc<Pattern> valueParam, gc<Expr> body) { currentFile_ = chunk_->addFile(module->source()); module_ = module; // Reserve slots up front for all of the locals. This ensures that // temps will always be after locals. // TODO(bob): Using max here isn't optimal. Ideally a given temp only // needs to be after the locals that are in scope during the duration // of that temp. But calculating that is a bit hairy. For now, until we // have a more advanced compiler, this is a simple solution. numLocals_ = maxLocals; maxSlots_ = MAX(maxSlots_, numLocals_); PatternCompiler compiler(*this, true); // Track the slots used for the arguments and result. This code here // must be kept carefully in sync with the similar prelude code in // Resolver. int numParamSlots = 0; // Evaluate the method's parameter patterns. compileParam(compiler, leftParam, numParamSlots); compileParam(compiler, rightParam, numParamSlots); compileParam(compiler, valueParam, numParamSlots); // The result slot is just after the param slots. compile(body, numParamSlots); write(body->pos(), OP_RETURN, numParamSlots); ASSERT(numTemps_ == 0, "Should not have any temps left."); compiler.endJumps(); }
bool ChannelObject::close(VM& vm, gc<Fiber> sender) { if (!isOpen_) return false; isOpen_ = false; // If nothing is going to receive the "done". Just ignore it and close // immediately. if (receivers_.count() == 0) return false; // Send "done" to all of the receivers. for (int i = 0; i < receivers_.count(); i++) { receivers_[i]->storeReturn(vm.getAtom(ATOM_DONE)); receivers_[i]->ready(); } receivers_.clear(); // Add the sender back to the scheduler after the receiver so it can // continue. sender->ready(); return true; }
void ExprCompiler::compileAssignment(gc<SourcePos> pos, gc<ResolvedName> resolved, int value, bool isCreate) { ASSERT(resolved->isResolved(), "Must resolve before compiling."); switch (resolved->scope()) { case NAME_LOCAL: // Copy the value into the new variable. write(pos, OP_MOVE, value, resolved->index()); break; case NAME_CLOSURE: write(pos, OP_SET_UPVAR, resolved->index(), value, isCreate ? 1 : 0); break; case NAME_MODULE: // Assign to the top-level variable. write(pos, OP_SET_VAR, resolved->module(), resolved->index(), value); break; } }
void PrintTask::reach() { Task::reach(); value_.reach(); }
void ExprCompiler::compile(gc<Expr> expr, int dest) { expr->accept(*this, dest); }
void ExprCompiler::write(gc<SourcePos> pos, OpCode op, int a, int b, int c) { write(pos->startLine(), op, a, b, c); }
void PatternCompiler::compile(gc<Pattern> pattern, int slot) { if (pattern.isNull()) return; pattern->accept(*this, slot); }
int ExprCompiler::startJump(gc<SourcePos> pos) { // Just write a dummy op to leave a space for the jump instruction. write(pos->startLine(), OP_MOVE); return chunk_->count() - 1; }