Fix issue 214. When a syntax error is reported, we report the source file name

and the line number within the source file where the error occurred.
This works even if the syntax error is within an included file.

The parse() function, and the places where it is called, have been changed
as necessary to enforce the convention that the second argument is the source
filename, and not the name of the parent directory containing the source file.
Previously, different calls to parse() made different assumptions about
the meaning of the second argument, which probably means include and import
were broken in some cases.
This commit is contained in:
Doug Moen 2016-05-03 21:48:51 -04:00
parent 5af6ce171c
commit a60ba41b77
6 changed files with 43 additions and 29 deletions

View file

@ -105,8 +105,8 @@ bool ModuleCache::evaluate(const std::string &filename, FileModule *&module)
FileModule *oldmodule = lib_mod;
std::string pathname = boosty::stringy(fs::path(filename).parent_path());
lib_mod = dynamic_cast<FileModule*>(parse(textbuf.str().c_str(), pathname.c_str(), false));
fs::path pathname = fs::path(filename);
lib_mod = dynamic_cast<FileModule*>(parse(textbuf.str().c_str(), pathname, false));
PRINTDB(" compiled module: %p", lib_mod);
// We defer deletion so we can ensure that the new module won't

View file

@ -54,7 +54,7 @@ int lexerget_lineno(void);
static void yyunput(int, char*) __attribute__((unused));
#endif
extern const char *parser_input_buffer;
extern std::string parser_source_path;
extern fs::path parser_sourcefile;
extern FileModule *rootmodule;
#define YY_INPUT(buf,result,max_size) { \
@ -79,8 +79,9 @@ extern FileModule *rootmodule;
void to_utf8(const char *, char *);
void includefile();
fs::path sourcepath();
std::vector<fs::path> path_stack;
fs::path sourcefile();
std::vector<fs::path> filename_stack;
std::vector<int> lineno_stack;
std::vector<FILE*> openfiles;
std::vector<std::string> openfilenames;
@ -121,7 +122,7 @@ use[ \t\r\n>]*"<" { BEGIN(cond_use); }
[^\t\r\n>]+ { filename = yytext; }
">" {
BEGIN(INITIAL);
fs::path fullpath = find_valid_path(sourcepath(), fs::path(filename), &openfilenames);
fs::path fullpath = find_valid_path(sourcefile().parent_path(), fs::path(filename), &openfilenames);
if (fullpath.empty()) {
PRINTB("WARNING: Can't open library '%s'.", filename);
parserlval.text = strdup(filename.c_str());
@ -134,7 +135,11 @@ use[ \t\r\n>]*"<" { BEGIN(cond_use); }
}
<<EOF>> {
if(!path_stack.empty()) path_stack.pop_back();
if (!filename_stack.empty()) filename_stack.pop_back();
if (!lineno_stack.empty()) {
yylineno = lineno_stack.back();
lineno_stack.pop_back();
}
if (yyin && yyin != stdin) {
assert(!openfiles.empty());
fclose(openfiles.back());
@ -237,11 +242,12 @@ void to_utf8(const char *str, char *out)
}
}
fs::path sourcepath()
// Filename of the source file currently being lexed.
fs::path sourcefile()
{
if (!path_stack.empty()) return path_stack.back();
if (!filename_stack.empty()) return filename_stack.back();
return fs::path(parser_source_path);
return parser_sourcefile;
}
/*
@ -249,12 +255,12 @@ fs::path sourcepath()
1) include <sourcepath/path/file>
2) include <librarydir/path/file>
Globals used: filepath, sourcepath, filename
Globals used: filepath, sourcefile, filename
*/
void includefile()
{
fs::path localpath = fs::path(filepath) / filename;
fs::path fullpath = find_valid_path(sourcepath(), localpath, &openfilenames);
fs::path fullpath = find_valid_path(sourcefile().parent_path(), localpath, &openfilenames);
if (!fullpath.empty()) {
rootmodule->registerInclude(boosty::stringy(localpath), boosty::stringy(fullpath));
}
@ -267,17 +273,19 @@ void includefile()
std::string fullname = boosty::stringy(fullpath);
filepath.clear();
path_stack.push_back(fullpath.parent_path());
filename_stack.push_back(fullpath);
handle_dep(fullname);
yyin = fopen(fullname.c_str(), "r");
if (!yyin) {
PRINTB("WARNING: Can't open include file '%s'.", boosty::stringy(localpath));
path_stack.pop_back();
filename_stack.pop_back();
return;
}
lineno_stack.push_back(yylineno);
yylineno = 1;
openfiles.push_back(yyin);
openfilenames.push_back(fullname);
filename.clear();
@ -294,5 +302,6 @@ void lexerdestroy()
for (auto f : openfiles) fclose(f);
openfiles.clear();
openfilenames.clear();
path_stack.clear();
filename_stack.clear();
lineno_stack.clear();
}

View file

@ -1695,9 +1695,10 @@ void MainWindow::compileTopLevelDocument()
delete this->root_module;
this->root_module = NULL;
this->root_module = parse(fulltext.c_str(),
this->fileName.isEmpty() ? "" :
QFileInfo(this->fileName).absolutePath().toLocal8Bit(), false);
fs::path fname = fs::path(
this->fileName.isEmpty() ? "" :
QFileInfo(this->fileName).absolutePath().toLocal8Bit());
this->root_module = parse(fulltext.c_str(), fname, false);
}
void MainWindow::checkAutoReload()

View file

@ -394,8 +394,7 @@ int cmdline(const char *deps_output_file, const std::string &filename, Camera &c
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);
root_module = parse(text.c_str(), abspath, false);
if (!root_module) {
PRINTB("Can't parse file '%s'!\n", filename.c_str());
return 1;

View file

@ -26,7 +26,10 @@
#pragma once
extern class FileModule *parse(const char *text, const char *path, int debug);
// forward declaration
namespace boost { namespace filesystem { class path; }};
extern class FileModule *parse(const char *text, const boost::filesystem::path &filename, int debug);
#include <string>
extern std::string commandline_commands;

View file

@ -56,6 +56,7 @@ int parserlex(void);
void yyerror(char const *s);
int lexerget_lineno(void);
fs::path sourcefile(void);
int lexerlex_destroy(void);
int lexerlex(void);
@ -66,7 +67,7 @@ extern void lexerdestroy();
extern FILE *lexerin;
extern const char *parser_input_buffer;
const char *parser_input_buffer;
std::string parser_source_path;
fs::path parser_sourcefile;
%}
@ -261,7 +262,7 @@ if_statement:
{
$<ifelse>$ = new IfElseModuleInstantiation();
$<ifelse>$->arguments.push_back(Assignment("", shared_ptr<Expression>($3)));
$<ifelse>$->setPath(parser_source_path);
$<ifelse>$->setPath(boosty::stringy(parser_sourcefile.parent_path()));
scope_stack.push(&$<ifelse>$->scope);
}
child_statement
@ -299,7 +300,7 @@ single_module_instantiation:
{
$$ = new ModuleInstantiation($1);
$$->arguments = *$3;
$$->setPath(parser_source_path);
$$->setPath(boosty::stringy(parser_sourcefile.parent_path()));
free($1);
delete $3;
}
@ -590,19 +591,20 @@ int parserlex(void)
void yyerror (char const *s)
{
// FIXME: We leak memory on parser errors...
PRINTB("ERROR: Parser error in line %d: %s\n", lexerget_lineno() % s);
// FIXME: We leak memory on parser errors...
PRINTB("ERROR: Parser error in file %s, line %d: %s\n",
sourcefile() % lexerget_lineno() % s);
}
FileModule *parse(const char *text, const char *path, int debug)
FileModule *parse(const char *text, const fs::path &filename, int debug)
{
lexerin = NULL;
parser_error_pos = -1;
parser_input_buffer = text;
parser_source_path = boosty::absolute(std::string(path)).string();
parser_sourcefile = boosty::absolute(filename);
rootmodule = new FileModule();
rootmodule->setModulePath(path);
rootmodule->setModulePath(boosty::stringy(filename.parent_path()));
scope_stack.push(&rootmodule->scope);
// PRINTB_NOCACHE("New module: %s %p", "root" % rootmodule);