summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/MainWindow.h11
-rw-r--r--src/ModuleCache.cc105
-rw-r--r--src/ModuleCache.h23
-rw-r--r--src/context.cc1
-rw-r--r--src/lexer.l20
-rw-r--r--src/mainwin.cc305
-rw-r--r--src/module.cc33
-rw-r--r--src/module.h13
-rw-r--r--src/openscad.cc34
-rw-r--r--src/openscad.h2
-rw-r--r--src/parser.y121
11 files changed, 393 insertions, 275 deletions
diff --git a/src/MainWindow.h b/src/MainWindow.h
index 226689a..ed12f34 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -27,10 +27,10 @@ public:
double tval, fps, fsteps;
QTimer *autoReloadTimer;
- QString autoReloadInfo;
+ std::string autoReloadId;
Context root_ctx;
- AbstractModule *root_module; // Result of parsing
+ Module *root_module; // Result of parsing
ModuleInstantiation root_inst; // Top level instance
AbstractNode *absolute_root_node; // Result of tree evaluation
AbstractNode *root_node; // Root if the root modifier (!) is used
@@ -73,9 +73,12 @@ private slots:
private:
void openFile(const QString &filename);
- void load();
+ void refreshDocument();
AbstractNode *find_root_tag(AbstractNode *n);
- void compile(bool procevents);
+ void updateTemporalVariables();
+ bool fileChangedOnDisk();
+ bool compileTopLevelDocument(bool reload);
+ bool compile(bool reload, bool procevents);
void compileCSG(bool procevents);
bool maybeSave();
bool checkModified();
diff --git a/src/ModuleCache.cc b/src/ModuleCache.cc
new file mode 100644
index 0000000..e974a03
--- /dev/null
+++ b/src/ModuleCache.cc
@@ -0,0 +1,105 @@
+#include "ModuleCache.h"
+#include "module.h"
+#include "printutils.h"
+#include "openscad.h"
+
+#include "boosty.h"
+#include <boost/format.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/foreach.hpp>
+
+#include <stdio.h>
+#include <fstream>
+#include <sstream>
+#include <time.h>
+#include <sys/stat.h>
+
+/*!
+ FIXME: Implement an LRU scheme to avoid having an ever-growing module cache
+*/
+
+ModuleCache *ModuleCache::inst = NULL;
+
+static bool is_modified(const std::string &filename, const time_t &mtime)
+{
+ struct stat st;
+ memset(&st, 0, sizeof(struct stat));
+ stat(filename.c_str(), &st);
+ return (st.st_mtime > mtime);
+}
+
+Module *ModuleCache::evaluate(const std::string &filename)
+{
+ Module *lib_mod = NULL;
+
+ // Create cache ID
+ struct stat st;
+ memset(&st, 0, sizeof(struct stat));
+ stat(filename.c_str(), &st);
+
+ std::string cache_id = str(boost::format("%x.%x") % st.st_mtime % st.st_size);
+
+ // Lookup in cache
+ if (this->entries.find(filename) != this->entries.end() &&
+ this->entries[filename].cache_id == cache_id) {
+#ifdef DEBUG
+ PRINTB("Using cached library: %s (%s)", filename % cache_id);
+#endif
+ lib_mod = &(*this->entries[filename].module);
+
+ BOOST_FOREACH(const Module::IncludeContainer::value_type &item, lib_mod->includes) {
+ if (is_modified(item.first, item.second)) {
+ lib_mod = NULL;
+ break;
+ }
+ }
+ }
+
+ // If cache lookup failed (non-existing or old timestamp), compile module
+ if (!lib_mod) {
+#ifdef DEBUG
+ if (this->entries.find(filename) != this->entries.end()) {
+ PRINTB("Recompiling cached library: %s (%s)", filename % cache_id);
+ }
+ else {
+ PRINTB("Compiling library '%s'.", filename);
+ }
+#endif
+
+ std::ifstream ifs(filename.c_str());
+ if (!ifs.is_open()) {
+ PRINTB("WARNING: Can't open library file '%s'\n", filename);
+ return NULL;
+ }
+ std::string text((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
+
+ print_messages_push();
+
+ cache_entry e = { NULL, cache_id };
+ if (this->entries.find(filename) != this->entries.end()) {
+ delete this->entries[filename].module;
+ }
+ this->entries[filename] = e;
+
+ std::string pathname = boosty::stringy(fs::path(filename).parent_path());
+ lib_mod = dynamic_cast<Module*>(parse(text.c_str(), pathname.c_str(), 0));
+
+ if (lib_mod) {
+ this->entries[filename].module = lib_mod;
+ } else {
+ this->entries.erase(filename);
+ }
+
+ print_messages_pop();
+ }
+
+ if (lib_mod) lib_mod->handleDependencies();
+
+ return lib_mod;
+}
+
+void ModuleCache::clear()
+{
+ this->entries.clear();
+}
+
diff --git a/src/ModuleCache.h b/src/ModuleCache.h
new file mode 100644
index 0000000..1e6373d
--- /dev/null
+++ b/src/ModuleCache.h
@@ -0,0 +1,23 @@
+#include <string>
+#include <boost/unordered_map.hpp>
+
+class ModuleCache
+{
+public:
+ static ModuleCache *instance() { if (!inst) inst = new ModuleCache; return inst; }
+ class Module *evaluate(const std::string &filename);
+ size_t size() { return this->entries.size(); }
+ void clear();
+
+private:
+ ModuleCache() {}
+ ~ModuleCache() {}
+
+ static ModuleCache *inst;
+
+ struct cache_entry {
+ class Module *module;
+ std::string cache_id;
+ };
+ boost::unordered_map<std::string, cache_entry> entries;
+};
diff --git a/src/context.cc b/src/context.cc
index b9e685c..f96a45b 100644
--- a/src/context.cc
+++ b/src/context.cc
@@ -162,6 +162,7 @@ AbstractNode *Context::evaluate_module(const ModuleInstantiation &inst) const
}
if (this->usedlibs_p) {
BOOST_FOREACH(const ModuleContainer::value_type &m, *this->usedlibs_p) {
+ assert(m.second);
if (m.second->modules.find(inst.name()) != m.second->modules.end()) {
Context ctx(this->parent, m.second);
return m.second->modules[inst.name()]->evaluate(&ctx, &inst);
diff --git a/src/lexer.l b/src/lexer.l
index 5644ded..884275e 100644
--- a/src/lexer.l
+++ b/src/lexer.l
@@ -30,6 +30,7 @@
#include "printutils.h"
#include "parsersettings.h"
#include "parser_yacc.h"
+#include "module.h"
#include <assert.h>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
@@ -52,7 +53,8 @@ int lexerget_lineno(void);
static void yyunput(int, char*) __attribute__((unused));
#endif
extern const char *parser_input_buffer;
-extern const char *parser_source_path;
+extern std::string parser_source_path;
+extern Module *currmodule;
#define YY_INPUT(buf,result,max_size) { \
if (yyin && yyin != stdin) { \
@@ -108,6 +110,7 @@ use[ \t\r\n>]*"<" { BEGIN(cond_use); }
<cond_use>{
[^\t\r\n>]+ { filename = yytext; }
">" {
+ PRINTB("USE: %s", filename);
BEGIN(INITIAL);
fs::path usepath;
if (boosty::is_absolute(fs::path(filename))) {
@@ -119,9 +122,12 @@ use[ \t\r\n>]*"<" { BEGIN(cond_use); }
usepath = boosty::absolute(fs::path(get_librarydir()) / filename);
}
}
- handle_dep(usepath.string());
- parserlval.text = strdup(usepath.string().c_str());
- return TOK_USE;
+ /* Only accept regular files which exists */
+ if (usepath.has_parent_path() && fs::exists(usepath)) {
+ handle_dep(usepath.string());
+ parserlval.text = strdup(usepath.string().c_str());
+ return TOK_USE;
+ }
}
}
@@ -215,8 +221,10 @@ void includefile()
filepath.clear();
path_stack.push_back(finfo.parent_path());
- handle_dep(boosty::absolute(finfo).string());
- yyin = fopen(boosty::absolute(finfo).string().c_str(), "r");
+ std::string fullname = boosty::absolute(finfo).string();
+ handle_dep(fullname);
+ currmodule->registerInclude(fullname);
+ yyin = fopen(fullname.c_str(), "r");
if (!yyin) {
PRINTB("WARNING: Can't open input file '%s'.", filename);
path_stack.pop_back();
diff --git a/src/mainwin.cc b/src/mainwin.cc
index b371742..1057499 100644
--- a/src/mainwin.cc
+++ b/src/mainwin.cc
@@ -25,6 +25,7 @@
*/
#include "PolySetCache.h"
+#include "ModuleCache.h"
#include "MainWindow.h"
#include "openscad.h" // examplesdir
#include "parsersettings.h"
@@ -79,9 +80,8 @@
#include <algorithm>
#include <boost/version.hpp>
#include <boost/foreach.hpp>
-#include <boost/lambda/lambda.hpp>
-#include <boost/lambda/bind.hpp>
-using namespace boost::lambda;
+#include <boost/version.hpp>
+#include <sys/stat.h>
#ifdef ENABLE_CGAL
@@ -200,8 +200,8 @@ MainWindow::MainWindow(const QString &filename)
autoReloadTimer->setSingleShot(false);
connect(autoReloadTimer, SIGNAL(timeout()), this, SLOT(checkAutoReload()));
- connect(e_tval, SIGNAL(textChanged(QString)), this, SLOT(actionCompile()));
- connect(e_fps, SIGNAL(textChanged(QString)), this, SLOT(updatedFps()));
+ connect(this->e_tval, SIGNAL(textChanged(QString)), this, SLOT(actionCompile()));
+ connect(this->e_fps, SIGNAL(textChanged(QString)), this, SLOT(updatedFps()));
animate_panel->hide();
@@ -489,7 +489,7 @@ MainWindow::openFile(const QString &new_filename)
#endif
setFileName(new_filename);
- load();
+ refreshDocument();
updateRecentFiles();
}
@@ -545,11 +545,11 @@ void MainWindow::updateRecentFiles()
void MainWindow::updatedFps()
{
bool fps_ok;
- double fps = e_fps->text().toDouble(&fps_ok);
+ double fps = this->e_fps->text().toDouble(&fps_ok);
animate_timer->stop();
if (fps_ok && fps > 0) {
animate_timer->setSingleShot(false);
- animate_timer->setInterval(int(1000 / e_fps->text().toDouble()));
+ animate_timer->setInterval(int(1000 / this->e_fps->text().toDouble()));
animate_timer->start();
}
}
@@ -557,27 +557,28 @@ void MainWindow::updatedFps()
void MainWindow::updateTVal()
{
bool fps_ok;
- double fps = e_fps->text().toDouble(&fps_ok);
+ double fps = this->e_fps->text().toDouble(&fps_ok);
if (fps_ok) {
if (fps <= 0) {
actionCompile();
} else {
- double s = e_fsteps->text().toDouble();
- double t = e_tval->text().toDouble() + 1/s;
+ double s = this->e_fsteps->text().toDouble();
+ double t = this->e_tval->text().toDouble() + 1/s;
QString txt;
txt.sprintf("%.5f", t >= 1.0 ? 0.0 : t);
- e_tval->setText(txt);
+ this->e_tval->setText(txt);
}
}
}
-void MainWindow::load()
+void MainWindow::refreshDocument()
{
setCurrentOutput();
if (!this->fileName.isEmpty()) {
QFile file(this->fileName);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- PRINTB("Failed to open file: %s (%s)", this->fileName.toStdString() % file.errorString().toStdString());
+ PRINTB("Failed to open file %s: %s",
+ this->fileName.toStdString() % file.errorString().toStdString());
}
else {
QString text = QTextStream(&file).readAll();
@@ -599,42 +600,30 @@ AbstractNode *MainWindow::find_root_tag(AbstractNode *n)
/*!
Parse and evaluate the design => this->root_node
+
+ Returns true if something was compiled, false if nothing was changed
+ and the root_node was left untouched.
*/
-void MainWindow::compile(bool procevents)
+bool MainWindow::compile(bool reload, bool procevents)
{
- PRINT("Parsing design (AST generation)...");
- if (procevents)
- QApplication::processEvents();
+ if (!compileTopLevelDocument(reload)) return false;
// Invalidate renderers before we kill the CSG tree
this->glview->setRenderer(NULL);
- if (this->opencsgRenderer) {
- delete this->opencsgRenderer;
- this->opencsgRenderer = NULL;
- }
- if (this->thrownTogetherRenderer) {
- delete this->thrownTogetherRenderer;
- this->thrownTogetherRenderer = NULL;
- }
+ delete this->opencsgRenderer;
+ this->opencsgRenderer = NULL;
+ delete this->thrownTogetherRenderer;
+ this->thrownTogetherRenderer = NULL;
// Remove previous CSG tree
- if (this->root_module) {
- delete this->root_module;
- this->root_module = NULL;
- }
-
- if (this->absolute_root_node) {
- delete this->absolute_root_node;
- this->absolute_root_node = NULL;
- }
+ delete this->absolute_root_node;
+ this->absolute_root_node = NULL;
this->root_raw_term.reset();
this->root_norm_term.reset();
- if (this->root_chain) {
- delete this->root_chain;
- this->root_chain = NULL;
- }
+ delete this->root_chain;
+ this->root_chain = NULL;
this->highlight_terms.clear();
delete this->highlights_chain;
@@ -647,96 +636,43 @@ void MainWindow::compile(bool procevents)
this->root_node = NULL;
this->tree.setRoot(NULL);
- // Initialize special variables
- this->root_ctx.set_variable("$t", Value(e_tval->text().toDouble()));
-
- Value vpt;
- vpt.type = Value::VECTOR;
- vpt.append(new Value(-this->glview->object_trans_x));
- vpt.append(new Value(-this->glview->object_trans_y));
- vpt.append(new Value(-this->glview->object_trans_z));
- this->root_ctx.set_variable("$vpt", vpt);
-
- Value vpr;
- vpr.type = Value::VECTOR;
- vpr.append(new Value(fmodf(360 - this->glview->object_rot_x + 90, 360)));
- vpr.append(new Value(fmodf(360 - this->glview->object_rot_y, 360)));
- vpr.append(new Value(fmodf(360 - this->glview->object_rot_z, 360)));
- root_ctx.set_variable("$vpr", vpr);
-
- // Parse
- this->last_compiled_doc = editor->toPlainText();
- this->root_module = parse((this->last_compiled_doc + "\n" +
- QString::fromStdString(commandline_commands)).toAscii().data(),
- this->fileName.isEmpty() ?
- "" :
- QFileInfo(this->fileName).absolutePath().toLocal8Bit(),
- false);
-
- // Error highlighting
- if (this->highlighter) {
- delete this->highlighter;
- this->highlighter = NULL;
- }
- if (parser_error_pos >= 0) {
- this->highlighter = new Highlighter(editor->document());
- }
-
- if (!this->root_module) {
- if (!animate_panel->isVisible()) {
-#ifdef _QCODE_EDIT_
- QDocumentCursor cursor = editor->cursor();
- cursor.setPosition(parser_error_pos);
-#else
- QTextCursor cursor = editor->textCursor();
- cursor.setPosition(parser_error_pos);
- editor->setTextCursor(cursor);
-#endif
+ if (this->root_module) {
+ // Evaluate CSG tree
+ PRINT("Compiling design (CSG Tree generation)...");
+ if (procevents) QApplication::processEvents();
+
+ AbstractNode::resetIndexCounter();
+ this->root_inst = ModuleInstantiation();
+ this->absolute_root_node = this->root_module->evaluate(&this->root_ctx, &this->root_inst);
+
+ if (this->absolute_root_node) {
+ // Do we have an explicit root node (! modifier)?
+ if (!(this->root_node = find_root_tag(this->absolute_root_node))) {
+ this->root_node = this->absolute_root_node;
+ }
+ // FIXME: Consider giving away ownership of root_node to the Tree, or use reference counted pointers
+ this->tree.setRoot(this->root_node);
+ // Dump the tree (to initialize caches).
+ // FIXME: We shouldn't really need to do this explicitly..
+ this->tree.getString(*this->root_node);
}
- goto fail;
}
- // Evaluate CSG tree
- PRINT("Compiling design (CSG Tree generation)...");
- if (procevents)
- QApplication::processEvents();
-
- AbstractNode::resetIndexCounter();
- this->root_inst = ModuleInstantiation();
- this->absolute_root_node = this->root_module->evaluate(&this->root_ctx, &this->root_inst);
-
- if (!this->absolute_root_node)
- goto fail;
-
- // Do we have an explicit root node (! modifier)?
- if (!(this->root_node = find_root_tag(this->absolute_root_node))) {
- this->root_node = this->absolute_root_node;
- }
- // FIXME: Consider giving away ownership of root_node to the Tree, or use reference counted pointers
- this->tree.setRoot(this->root_node);
- // Dump the tree (to initialize caches).
- // FIXME: We shouldn't really need to do this explicitly..
- this->tree.getString(*this->root_node);
-
- if (1) {
- PRINT("Compilation finished.");
- if (procevents)
- QApplication::processEvents();
- } else {
-fail:
+ if (!this->root_node) {
if (parser_error_pos < 0) {
PRINT("ERROR: Compilation failed! (no top level object found)");
} else {
PRINT("ERROR: Compilation failed!");
}
- if (procevents)
- QApplication::processEvents();
+ if (procevents) QApplication::processEvents();
}
+
+ return true;
}
/*!
Generates CSG tree for OpenCSG evaluation.
- Assumes that the design has been parsed and evaluated
+ Assumes that the design has been parsed and evaluated (this->root_node is set)
*/
void MainWindow::compileCSG(bool procevents)
{
@@ -992,7 +928,7 @@ void MainWindow::actionSaveAs()
void MainWindow::actionReload()
{
- if (checkModified()) load();
+ if (checkModified()) refreshDocument();
}
void MainWindow::hideEditor()
@@ -1032,16 +968,105 @@ void MainWindow::pasteViewportRotation()
cursor.insertText(txt);
}
-void MainWindow::checkAutoReload()
+void MainWindow::updateTemporalVariables()
+{
+ this->root_ctx.set_variable("$t", Value(this->e_tval->text().toDouble()));
+
+ Value vpt;
+ vpt.type = Value::VECTOR;
+ vpt.append(new Value(-this->glview->object_trans_x));
+ vpt.append(new Value(-this->glview->object_trans_y));
+ vpt.append(new Value(-this->glview->object_trans_z));
+ this->root_ctx.set_variable("$vpt", vpt);
+
+ Value vpr;
+ vpr.type = Value::VECTOR;
+ vpr.append(new Value(fmodf(360 - this->glview->object_rot_x + 90, 360)));
+ vpr.append(new Value(fmodf(360 - this->glview->object_rot_y, 360)));
+ vpr.append(new Value(fmodf(360 - this->glview->object_rot_z, 360)));
+ root_ctx.set_variable("$vpr", vpr);
+}
+
+bool MainWindow::fileChangedOnDisk()
{
if (!this->fileName.isEmpty()) {
- QString new_stinfo;
- QFileInfo finfo(this->fileName);
- new_stinfo = QString::number(finfo.size()) + QString::number(finfo.lastModified().toTime_t());
- if (new_stinfo != autoReloadInfo)
- actionReloadCompile();
- autoReloadInfo = new_stinfo;
+ struct stat st;
+ memset(&st, 0, sizeof(struct stat));
+ stat(this->fileName.toLocal8Bit(), &st);
+ std::string newid = str(boost::format("%x.%x") % st.st_mtime % st.st_size);
+
+ if (newid != this->autoReloadId) {
+ this->autoReloadId = newid;
+ return true;
+ }
+ }
+ return false;
+}
+
+/*!
+ If reload is true, does a timestamp check on the document and tries to reload it.
+ Otherwise, just reparses the current document and any dependencies, updates the
+ GUI accordingly and populates this->root_module.
+
+ Returns true if anything was compiled.
+*/
+bool MainWindow::compileTopLevelDocument(bool reload)
+{
+ bool shouldcompiletoplevel = !reload;
+
+ if (reload && fileChangedOnDisk() && checkModified()) {
+ shouldcompiletoplevel = true;
+ refreshDocument();
+ }
+
+ if (shouldcompiletoplevel) {
+ updateTemporalVariables();
+
+ this->last_compiled_doc = editor->toPlainText();
+ std::string fulltext =
+ this->last_compiled_doc.toStdString() + "\n" + commandline_commands;
+
+ delete this->root_module;
+ this->root_module = NULL;
+
+ this->root_module = parse(fulltext.c_str(),
+ this->fileName.isEmpty() ?
+ "" :
+ QFileInfo(this->fileName).absolutePath().toLocal8Bit(),
+ false);
+
+ // Error highlighting
+ delete this->highlighter;
+ this->highlighter = NULL;
+
+ if (!this->root_module) {
+ this->highlighter = new Highlighter(editor->document());
+
+ if (!animate_panel->isVisible()) {
+#ifdef _QCODE_EDIT_
+ QDocumentCursor cursor = editor->cursor();
+ cursor.setPosition(parser_error_pos);
+#else
+ QTextCursor cursor = editor->textCursor();
+ cursor.setPosition(parser_error_pos);
+ editor->setTextCursor(cursor);
+#endif
+ }
+ }
+ }
+
+ bool changed = shouldcompiletoplevel;
+ if (this->root_module) {
+ changed |= this->root_module->handleDependencies();
+ if (changed) PRINTB("Module cache size: %d modules", ModuleCache::instance()->size());
}
+
+ return changed;
+}
+
+void MainWindow::checkAutoReload()
+{
+ if (!this->fileName.isEmpty()) actionReloadCompile();
}
void MainWindow::autoReloadSet(bool on)
@@ -1049,7 +1074,7 @@ void MainWindow::autoReloadSet(bool on)
QSettings settings;
settings.setValue("design/autoReload",designActionAutoReload->isChecked());
if (on) {
- autoReloadInfo = QString();
+ autoReloadId = "";
autoReloadTimer->start(200);
} else {
autoReloadTimer->stop();
@@ -1076,15 +1101,12 @@ void MainWindow::actionReloadCompile()
{
if (GuiLocker::isLocked()) return;
GuiLocker lock;
-
- if (!checkModified()) return;
-
+ setCurrentOutput();
console->clear();
- load();
-
- setCurrentOutput();
- compile(true);
+ // PRINT("Parsing design (AST generation)...");
+ // QApplication::processEvents();
+ if (!compile(true, true)) return;
if (this->root_node) compileCSG(true);
// Go to non-CGAL view mode
@@ -1106,11 +1128,12 @@ void MainWindow::actionCompile()
{
if (GuiLocker::isLocked()) return;
GuiLocker lock;
-
setCurrentOutput();
console->clear();
- compile(!viewActionAnimate->isChecked());
+ PRINT("Parsing design (AST generation)...");
+ QApplication::processEvents();
+ compile(false, !viewActionAnimate->isChecked());
if (this->root_node) compileCSG(!viewActionAnimate->isChecked());
// Go to non-CGAL view mode
@@ -1128,8 +1151,8 @@ void MainWindow::actionCompile()
if (viewActionAnimate->isChecked() && e_dump->isChecked()) {
QImage img = this->glview->grabFrameBuffer();
QString filename;
- double s = e_fsteps->text().toDouble();
- double t = e_tval->text().toDouble();
+ double s = this->e_fsteps->text().toDouble();
+ double t = this->e_tval->text().toDouble();
filename.sprintf("frame%05d.png", int(round(s*t)));
img.save(filename, "PNG");
}
@@ -1147,7 +1170,9 @@ void MainWindow::actionRenderCGAL()
setCurrentOutput();
console->clear();
- compile(true);
+ PRINT("Parsing design (AST generation)...");
+ QApplication::processEvents();
+ compile(false, true);
if (!this->root_module || !this->root_node) {
return;
@@ -1450,7 +1475,7 @@ void MainWindow::actionFlushCaches()
#endif
dxf_dim_cache.clear();
dxf_cross_cache.clear();
- Module::clear_library_cache();
+ ModuleCache::instance()->clear();
}
void MainWindow::viewModeActionsUncheck()
@@ -1557,7 +1582,7 @@ void MainWindow::animateUpdate()
{
if (animate_panel->isVisible()) {
bool fps_ok;
- double fps = e_fps->text().toDouble(&fps_ok);
+ double fps = this->e_fps->text().toDouble(&fps_ok);
if (fps_ok && fps <= 0 && !animate_timer->isActive()) {
animate_timer->stop();
animate_timer->setSingleShot(true);
@@ -1798,7 +1823,7 @@ void MainWindow::consoleOutput(const std::string &msg, void *userdata)
void MainWindow::setCurrentOutput()
{
- set_output_handler(&MainWindow::consoleOutput, this);
+// set_output_handler(&MainWindow::consoleOutput, this);
}
void MainWindow::clearCurrentOutput()
diff --git a/src/module.cc b/src/module.cc
index 6641ff7..69623dd 100644
--- a/src/module.cc
+++ b/src/module.cc
@@ -25,13 +25,16 @@
*/
#include "module.h"
+#include "ModuleCache.h"
#include "node.h"
#include "context.h"
#include "expression.h"
#include "function.h"
#include "printutils.h"
+
#include <boost/foreach.hpp>
#include <sstream>
+#include <sys/stat.h>
AbstractModule::~AbstractModule()
{
@@ -201,7 +204,33 @@ std::string Module::dump(const std::string &indent, const std::string &name) con
return dump.str();
}
-void Module::clear_library_cache()
+void Module::registerInclude(const std::string &filename)
+{
+ struct stat st;
+ memset(&st, 0, sizeof(struct stat));
+ stat(filename.c_str(), &st);
+ this->includes[filename] = st.st_mtime;
+}
+
+/*!
+ Check if any dependencies have been modified and recompile them.
+ Returns true if anything was recompiled.
+*/
+bool Module::handleDependencies()
{
- Module::libs_cache.clear();
+ bool changed = false;
+ // Iterating manually since we want to modify the container while iterating
+ Module::ModuleContainer::iterator iter = this->usedlibs.begin();
+ while (iter != this->usedlibs.end()) {
+ Module::ModuleContainer::iterator curr = iter++;
+ Module *oldmodule = curr->second;
+ curr->second = ModuleCache::instance()->evaluate(curr->first);
+ if (curr->second != oldmodule) changed = true;
+ PRINTB_NOCACHE(" %s: %p", curr->first % curr->second);
+ if (!curr->second) {
+ PRINTB_NOCACHE("WARNING: Failed to compile library '%s'.", curr->first);
+ this->usedlibs.erase(curr);
+ }
+ }
+ return changed;
}
diff --git a/src/module.h b/src/module.h
index 6c6529b..cd25287 100644
--- a/src/module.h
+++ b/src/module.h
@@ -3,6 +3,7 @@
#include <string>
#include <vector>
+#include <list>
#include <boost/unordered_map.hpp>
#include "value.h"
@@ -65,11 +66,12 @@ public:
void addChild(ModuleInstantiation *ch) { this->children.push_back(ch); }
- static Module *compile_library(const std::string &filename);
- static void clear_library_cache();
-
typedef boost::unordered_map<std::string, class Module*> ModuleContainer;
ModuleContainer usedlibs;
+ void registerInclude(const std::string &filename);
+ typedef boost::unordered_map<std::string, time_t> IncludeContainer;
+ IncludeContainer includes;
+ bool handleDependencies();
std::vector<std::string> assignments_var;
std::vector<Expression*> assignments_expr;
@@ -87,11 +89,6 @@ public:
protected:
private:
- struct libs_cache_ent {
- Module *mod;
- std::string cache_id, msg;
- };
- static boost::unordered_map<std::string, libs_cache_ent> libs_cache;
};
#endif
diff --git a/src/openscad.cc b/src/openscad.cc
index 11dad7c..f45f81a 100644
--- a/src/openscad.cc
+++ b/src/openscad.cc
@@ -255,34 +255,28 @@ int main(int argc, char **argv)
Context root_ctx;
register_builtin(root_ctx);
- AbstractModule *root_module;
+ Module *root_module;
ModuleInstantiation root_inst;
AbstractNode *root_node;
handle_dep(filename);
- FILE *fp = fopen(filename, "rt");
- if (!fp) {
- fprintf(stderr, "Can't open input file `%s'!\n", filename);
+
+ std::ifstream ifs(filename);
+ if (!ifs.is_open()) {
+ fprintf(stderr, "Can't open input file '%s'!\n", filename);
exit(1);
- } else {
- std::stringstream text;
- char buffer[513];
- int ret;
- while ((ret = fread(buffer, 1, 512, fp)) > 0) {
- buffer[ret] = 0;
- text << buffer;
- }
- fclose(fp);
- text << "\n" << commandline_commands;
- fs::path abspath = boosty::absolute( filename );
- std::string fpath = boosty::stringy(abspath.parent_path());
- root_module = parse(text.str().c_str(), fpath.c_str(), false);
- if (!root_module) exit(1);
}
+ std::string text((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
+ text += "\n" + commandline_commands;
+ fs::path abspath = boosty::absolute(filename);
+ std::string parentpath = boosty::stringy(abspath.parent_path());
+ root_module = parse(text.c_str(), parentpath.c_str(), false);
+ if (!root_module) exit(1);
+ root_module->handleDependencies();
- fs::path fpath = boosty::absolute( fs::path(filename) );
+ fs::path fpath = boosty::absolute(fs::path(filename));
fs::path fparent = fpath.parent_path();
- fs::current_path( fparent );
+ fs::current_path(fparent);
AbstractNode::resetIndexCounter();
root_node = root_module->evaluate(&root_ctx, &root_inst);
diff --git a/src/openscad.h b/src/openscad.h
index dab14cd..8b49ba2 100644
--- a/src/openscad.h
+++ b/src/openscad.h
@@ -27,7 +27,7 @@
#ifndef OPENSCAD_H
#define OPENSCAD_H
-extern class AbstractModule *parse(const char *text, const char *path, int debug);
+extern class Module *parse(const char *text, const char *path, int debug);
extern int get_fragments_from_r(double r, double fn, double fs, double fa);
#include <string>
diff --git a/src/parser.y b/src/parser.y
index 15a754b..2f4379a 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -43,7 +43,7 @@
#include <boost/foreach.hpp>
#include <boost/filesystem.hpp>
-using namespace boost::filesystem;
+namespace fs = boost::filesystem;
#include "boosty.h"
int parser_error_pos = -1;
@@ -56,7 +56,7 @@ int lexerlex_destroy(void);
int lexerlex(void);
std::vector<Module*> module_stack;
-Module *module;
+Module *currmodule;
class ArgContainer {
public:
@@ -133,7 +133,7 @@ public:
input:
/* empty */ |
- TOK_USE { module->usedlibs[$1] = NULL; } input |
+ TOK_USE { currmodule->usedlibs[$1] = NULL; } input |
statement input ;
inner_input:
@@ -145,37 +145,38 @@ statement:
'{' inner_input '}' |
module_instantiation {
if ($1) {
- module->addChild($1);
+ currmodule->addChild($1);
} else {
delete $1;
}
} |
TOK_ID '=' expr ';' {
bool add_new_assignment = true;
- for (size_t i = 0; i < module->assignments_var.size(); i++) {
- if (module->assignments_var[i] != $1)
+ for (size_t i = 0; i < currmodule->assignments_var.size(); i++) {
+ if (currmodule->assignments_var[i] != $1)
continue;
- delete module->assignments_expr[i];
- module->assignments_expr[i] = $3;
+ delete currmodule->assignments_expr[i];
+ currmodule->assignments_expr[i] = $3;
add_new_assignment = false;
}
if (add_new_assignment) {
- module->assignments_var.push_back($1);
- module->assignments_expr.push_back($3);
+ currmodule->assignments_var.push_back($1);
+ currmodule->assignments_expr.push_back($3);
free($1);
}
} |
TOK_MODULE TOK_ID '(' arguments_decl optional_commas ')' {
- Module *p = module;
- module_stack.push_back(module);
- module = new Module();
- p->modules[$2] = module;
- module->argnames = $4->argnames;
- module->argexpr = $4->argexpr;
+ Module *p = currmodule;
+ module_stack.push_back(currmodule);
+ currmodule = new Module();
+ PRINTB_NOCACHE("New module: %s %p", $2 % currmodule);
+ p->modules[$2] = currmodule;
+ currmodule->argnames = $4->argnames;
+ currmodule->argexpr = $4->argexpr;
free($2);
delete $4;
} statement {
- module = module_stack.back();
+ currmodule = module_stack.back();
module_stack.pop_back();
} |
TOK_FUNCTION TOK_ID '(' arguments_decl optional_commas ')' '=' expr {
@@ -183,7 +184,7 @@ statement:
func->argnames = $4->argnames;
func->argexpr = $4->argexpr;
func->expr = $8;
- module->functions[$2] = func;
+ currmodule->functions[$2] = func;
free($2);
delete $4;
} ';' ;
@@ -560,101 +561,33 @@ void yyerror (char const *s)
{
// FIXME: We leak memory on parser errors...
PRINTB("Parser error in line %d: %s\n", lexerget_lineno() % s);
- module = NULL;
+ currmodule = NULL;
}
extern void lexerdestroy();
extern FILE *lexerin;
extern const char *parser_input_buffer;
const char *parser_input_buffer;
-const char *parser_source_path;
+std::string parser_source_path;
-AbstractModule *parse(const char *text, const char *path, int debug)
+Module *parse(const char *text, const char *path, int debug)
{
lexerin = NULL;
parser_error_pos = -1;
parser_input_buffer = text;
- parser_source_path = path;
+ parser_source_path = std::string(path);
module_stack.clear();
- module = new Module();
+ Module *rootmodule = currmodule = new Module();
+ // PRINTB_NOCACHE("New module: %s %p", "root" % rootmodule);
parserdebug = debug;
parserparse();
lexerdestroy();
lexerlex_destroy();
- if (!module)
- return NULL;
-
- // Iterating manually since we want to modify the container while iterating
- Module::ModuleContainer::iterator iter = module->usedlibs.begin();
- while (iter != module->usedlibs.end()) {
- Module::ModuleContainer::iterator curr = iter++;
- curr->second = Module::compile_library(curr->first);
- if (!curr->second) {
- PRINTB("WARNING: Failed to compile library '%s'.", curr->first);
- module->usedlibs.erase(curr);
- }
- }
+ if (!rootmodule) return NULL;
parser_error_pos = -1;
- return module;
-}
-
-boost::unordered_map<std::string, Module::libs_cache_ent> Module::libs_cache;
-
-Module *Module::compile_library(const std::string &filename)
-{
- struct stat st;
- memset(&st, 0, sizeof(struct stat));
- stat(filename.c_str(), &st);
-
- std::stringstream idstream;
- idstream << std::hex << st.st_mtime << "." << st.st_size;
- std::string cache_id = idstream.str();
-
- if (libs_cache.find(filename) != libs_cache.end() && libs_cache[filename].cache_id == cache_id) {
- PRINTB("%s", libs_cache[filename].msg);
- return &(*libs_cache[filename].mod);
- }
-
- FILE *fp = fopen(filename.c_str(), "rt");
- if (!fp) {
- fprintf(stderr, "WARNING: Can't open library file '%s'\n", filename.c_str());
- return NULL;
- }
- std::stringstream text;
- char buffer[513];
- int ret;
- while ((ret = fread(buffer, 1, 512, fp)) > 0) {
- buffer[ret] = 0;
- text << buffer;
- }
- fclose(fp);
-
- print_messages_push();
-
- PRINTB("Compiling library '%s'.", filename);
- libs_cache_ent e = { NULL, cache_id, std::string("WARNING: Library `") + filename + "' tries to recursively use itself!" };
- if (libs_cache.find(filename) != libs_cache.end())
- delete libs_cache[filename].mod;
- libs_cache[filename] = e;
-
- Module *backup_mod = module;
- std::string pathname = boosty::stringy( fs::path(filename).parent_path() );
- Module *lib_mod = dynamic_cast<Module*>(parse(text.str().c_str(), pathname.c_str(), 0));
- module = backup_mod;
-
- if (lib_mod) {
- libs_cache[filename].mod = lib_mod;
- libs_cache[filename].msg = print_messages_stack.back();
- } else {
- libs_cache.erase(filename);
- }
-
- print_messages_pop();
-
- return lib_mod;
+ return rootmodule;
}
-
contact: Jan Huwald // Impressum