about summary refs log tree commit diff
path: root/src/libexpr/parser.cc
blob: 167c34bd83e48222450f7d500d84a5e6af26b401 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include <sstream>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include "aterm.hh"
#include "parser.hh"


struct ParseData 
{
    Expr result;
    string basePath;
    string location;
    string error;
};

extern "C" {

#include "parser-tab.h"
#include "lexer-tab.h"
    
    /* Callbacks for getting from C to C++.  Due to a (small) bug in the
       GLR code of Bison we cannot currently compile the parser as C++
       code. */
   
    void setParseResult(ParseData * data, ATerm t)
    {
        data->result = t;
    }

    ATerm absParsedPath(ParseData * data, ATerm t)
    {
        return string2ATerm(absPath(aterm2String(t), data->basePath).c_str());
    }
    
    void parseError(ParseData * data, char * error, int line, int column)
    {
        data->error = (format("%1%, at line %2%, column %3%, of %4%")
            % error % line % column % data->location).str();
    }
        
    int yyparse(yyscan_t scanner, ParseData * data);
}


static Expr parse(const char * text, const string & location,
    const Path & basePath)
{
    yyscan_t scanner;
    ParseData data;
    data.basePath = basePath;
    data.location = location;

    yylex_init(&scanner);
    yy_scan_string(text, scanner);
    int res = yyparse(scanner, &data);
    yylex_destroy(scanner);
    
    if (res) throw Error(data.error);

    return data.result;
}


Expr parseExprFromFile(Path path)
{
    assert(path[0] == '/');

#if 0
    /* Perhaps this is already an imploded parse tree? */
    Expr e = ATreadFromNamedFile(path.c_str());
    if (e) return e;
#endif

    /* If `path' is a symlink, follow it.  This is so that relative
       path references work. */
    struct stat st;
    if (lstat(path.c_str(), &st))
        throw SysError(format("getting status of `%1%'") % path);
    if (S_ISLNK(st.st_mode)) path = absPath(readLink(path), dirOf(path));

    /* If `path' refers to a directory, append `/default.nix'. */
    if (stat(path.c_str(), &st))
        throw SysError(format("getting status of `%1%'") % path);
    if (S_ISDIR(st.st_mode))
        path = canonPath(path + "/default.nix");

    /* Read the input file.  We can't use SGparseFile() because it's
       broken, so we read the input ourselves and call
       SGparseString(). */
    AutoCloseFD fd = open(path.c_str(), O_RDONLY);
    if (fd == -1) throw SysError(format("opening `%1%'") % path);

    if (fstat(fd, &st) == -1)
        throw SysError(format("statting `%1%'") % path);

    char text[st.st_size + 1];
    readFull(fd, (unsigned char *) text, st.st_size);
    text[st.st_size] = 0;

    return parse(text, "`" + path + "'", dirOf(path));
}


Expr parseExprFromString(const string & s, const Path & basePath)
{
    return parse(s.c_str(), "(string)", basePath);
}