dvander@195: /* dvander@195: * Copyright (C) 2012 David Anderson dvander@195: * dvander@195: * This file is part of JITCraft. dvander@195: * dvander@195: * JITCraft is free software: you can redistribute it and/or modify it under dvander@195: * the terms of the GNU General Public License as published by the Free dvander@195: * Software Foundation, either version 3 of the License, or (at your option) dvander@195: * any later version. dvander@195: * dvander@195: * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY dvander@195: * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS dvander@195: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. dvander@195: * dvander@195: * You should have received a copy of the GNU General Public License along with dvander@195: * JITCraft. If not, see http://www.gnu.org/licenses/. dvander@195: */ dvander@195: #include dvander@195: #include "../Zone.h" dvander@195: #include "../Interpreter.h" dvander@195: #include "Scanner.h" dvander@195: #include "CompileContext.h" dvander@195: #include "Parser.h" dvander@195: #include "NameBinding.h" dvander@195: #include "SemanticAnalysis.h" dvander@195: #include "../Spaces-inl.h" dvander@195: #include "../Heap-inl.h" dvander@195: dvander@195: using namespace ke; dvander@195: dvander@195: CompileContext::CompileContext(Zone *zone, const char *file) dvander@195: : zone_(zone), dvander@195: failures_(0), dvander@195: file_(file) dvander@195: { dvander@195: } dvander@195: dvander@195: CompileContext::~CompileContext() dvander@195: { dvander@195: if (failures_) dvander@195: zone_->endCompilationErrors(); dvander@195: } dvander@195: dvander@195: bool dvander@195: CompileContext::addTranslationUnit(TranslationUnit *unit) dvander@195: { dvander@195: if (!units_.append(unit)) dvander@195: return false; dvander@195: dvander@195: if (!unparsed_.append(unit)) dvander@195: return false; dvander@195: dvander@195: return true; dvander@195: } dvander@195: dvander@195: char * dvander@195: CompileContext::loadSource(const char *file, size_t *lengthp) dvander@195: { dvander@195: char *bytes = NULL; dvander@195: size_t length; dvander@195: dvander@195: FILE *fp = fopen(file, "rb"); dvander@195: if (!fp) { dvander@195: reportError(SourcePosition(), Message_CantOpenFile); dvander@195: return NULL; dvander@195: } dvander@195: dvander@195: if (fseek(fp, 0, SEEK_END)) { dvander@195: reportError(SourcePosition(), Message_ErrorSeekingFile); dvander@195: fclose(fp); dvander@195: return NULL; dvander@195: } dvander@195: long size = ftell(fp); dvander@195: if (size < 0) { dvander@195: reportError(SourcePosition(), Message_ErrorSeekingFile); dvander@195: fclose(fp); dvander@195: return NULL; dvander@195: } dvander@195: rewind(fp); dvander@195: length = size_t(size); dvander@195: bytes = new char[length + 1]; dvander@195: if (fread(bytes, 1, length, fp) != length) { dvander@195: reportError(SourcePosition(), Message_ErrorReadingFile); dvander@195: fclose(fp); dvander@195: delete [] bytes; dvander@195: return NULL; dvander@195: } dvander@195: fclose(fp); dvander@195: dvander@195: *lengthp = length; dvander@195: return bytes; dvander@195: } dvander@195: dvander@195: bool dvander@195: CompileContext::parse(TranslationUnit *unit) dvander@195: { dvander@195: assert(!unit->tree_); dvander@195: dvander@195: size_t length; dvander@196: char *source = loadSource(unit->path_, &length); dvander@195: if (!source) dvander@195: return false; dvander@195: dvander@195: Parser parser(ZONE(), *this, source, length); dvander@195: if ((unit->tree_ = parser.parse()) == NULL) dvander@195: return false; dvander@195: dvander@195: return true; dvander@195: } dvander@195: dvander@195: bool dvander@195: CompileContext::evaluateImports(TranslationUnit *unit) dvander@195: { dvander@195: if (units_.length() > 1) dvander@195: return true; dvander@195: dvander@195: #if 0 dvander@195: // For now we hardcode this... dvander@195: TranslationUnit *u2 = new (ZONE()->pool()) TranslationUnit(); dvander@195: u2->path_ = "C:\\Users\\dvander\\jitcraft\\jitcraft-2\\src\\Debug\\wizard.sp"; dvander@195: dvander@195: ImportStatement *stmt = unit->tree()->imports()->at(0)->toImportStatement(); dvander@195: stmt->setImportSource(u2); dvander@195: dvander@195: if (!u2->dependents_.append(unit)) dvander@195: return false; dvander@195: dvander@195: if (!addTranslationUnit(u2)) dvander@195: return false; dvander@195: #endif dvander@195: dvander@195: return true; dvander@195: } dvander@195: dvander@195: bool dvander@195: CompileContext::propagateFailure() dvander@195: { dvander@195: Vector worklist; dvander@195: for (size_t i = 0; i < units_.length(); i++) { dvander@195: if (units_[i]->failed() && !worklist.append(units_[i])) dvander@195: return false; dvander@195: } dvander@195: dvander@195: while (!worklist.empty()) { dvander@195: TranslationUnit *unit = worklist.popCopy(); dvander@195: for (size_t i = 0; i < unit->dependents().length(); i++) { dvander@195: TranslationUnit *other = unit->dependents().at(i); dvander@195: if (other->failed()) dvander@195: continue; dvander@195: dvander@195: other->failed_ = true; dvander@195: if (!worklist.append(other)) dvander@195: return false; dvander@195: } dvander@195: } dvander@195: dvander@195: return true; dvander@195: } dvander@195: dvander@195: bool dvander@195: CompileContext::compile() dvander@195: { dvander@195: // Phase 1. Parse all modules we're importing. This process may load new dvander@195: // modules off disk, and add them to the parse queue. dvander@195: while (!unparsed_.empty()) { dvander@195: TranslationUnit *unit = unparsed_.popCopy(); dvander@195: dvander@195: if (!parse(unit)) dvander@195: unit->failed_ = true; dvander@195: dvander@195: if (!evaluateImports(unit)) dvander@195: unit->failed_ = true; dvander@195: } dvander@195: dvander@195: // Phase 2. Create scopes and symbol tables for every lexical region with dvander@195: // a declaration. This phase excludes declarations created via imports. dvander@195: for (size_t i = 0; i < units_.length(); i++) { dvander@195: TranslationUnit *unit = units_[i]; dvander@195: if (unit->failed()) dvander@195: continue; dvander@195: dvander@195: if (!PopulateNamesAndTypes(zone_, *this, unit)) dvander@195: unit->failed_ = true; dvander@195: } dvander@195: dvander@195: // Phase 3. Complete imports and bind all names. dvander@195: for (size_t i = 0; i < units_.length(); i++) { dvander@195: TranslationUnit *unit = units_[i]; dvander@195: if (unit->failed()) dvander@195: continue; dvander@195: dvander@195: if (!BindNamesAndTypes(zone_, *this, unit)) dvander@195: unit->failed_ = true; dvander@195: } dvander@195: dvander@195: // Phase 4. Assign types to every public name in the module. dvander@195: for (size_t i = 0; i < units_.length(); i++) { dvander@195: TranslationUnit *unit = units_[i]; dvander@195: if (unit->failed()) dvander@195: continue; dvander@195: dvander@195: } dvander@195: dvander@195: // Intermediate phase. Decide which modules we should not perform dvander@195: // semantic analysis on. dvander@195: if (!propagateFailure()) dvander@195: return false; dvander@195: dvander@195: // Phase 4. Perform full semantic analysis on each module. dvander@195: for (size_t i = 0; i < units_.length(); i++) { dvander@195: TranslationUnit *unit = units_[i]; dvander@195: if (unit->failed()) dvander@195: continue; dvander@195: dvander@195: dvander@195: } dvander@195: dvander@195: // Intermediate phase. Propagate failure from semantic analysis. dvander@195: if (!propagateFailure()) dvander@195: return false; dvander@195: dvander@195: // Phase 5. Now we can finally perform bytecode compilation. dvander@195: for (size_t i = 0; i < units_.length(); i++) { dvander@195: TranslationUnit *unit = units_[i]; dvander@195: if (unit->failed()) dvander@195: continue; dvander@195: dvander@195: SemanticAnalysis sema(zone_, *this, unit); dvander@195: sema.analyze(); dvander@195: } dvander@195: dvander@195: // Phase 6. Run modules. dvander@195: for (size_t i = 0; i < units_.length(); i++) { dvander@195: TranslationUnit *unit = units_[i]; dvander@195: if (unit->failed()) dvander@195: continue; dvander@195: dvander@195: InvokeArgs args; dvander@195: if (!zone_->stack().pushModuleFrame(unit->module_, args)) dvander@195: assert(false); dvander@195: dvander@232: if (failures_) dvander@232: return false; dvander@232: dvander@195: ke::LocalSlot slot; dvander@195: if (!Interpret(zone_, NULL, &slot)) dvander@195: assert(false); dvander@195: } dvander@195: dvander@195: return true; dvander@195: } dvander@195: dvander@195: void dvander@195: CompileContext::reportErrorVa(const SourcePosition &pos, Message msg, va_list ap) dvander@195: { dvander@195: if (!failures_) dvander@195: zone_->beginCompilationErrors(file_); dvander@195: dvander@195: zone_->reportCompilationError(msg, pos.line, pos.col, ap); dvander@195: dvander@195: failures_++; dvander@195: } dvander@195: dvander@195: void dvander@195: CompileContext::reportError(const SourcePosition &pos, Message msg, ...) dvander@195: { dvander@195: va_list ap; dvander@195: va_start(ap, msg); dvander@195: reportErrorVa(pos, msg, ap); dvander@195: va_end(ap); dvander@195: }