aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr/parser.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/libexpr/parser.cc')
-rw-r--r--src/libexpr/parser.cc164
1 files changed, 164 insertions, 0 deletions
diff --git a/src/libexpr/parser.cc b/src/libexpr/parser.cc
new file mode 100644
index 000000000..b2c74af33
--- /dev/null
+++ b/src/libexpr/parser.cc
@@ -0,0 +1,164 @@
+#include <sstream>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+extern "C" {
+#include <sglr.h>
+#include <asfix2.h>
+}
+
+#include "aterm.hh"
+#include "parser.hh"
+#include "shared.hh"
+#include "parse-table.h"
+
+
+/* Cleanup cleans up an imploded parse tree into an actual abstract
+ syntax tree that we can evaluate. It removes quotes around
+ strings, converts integer literals into actual integers, and
+ absolutises paths relative to the directory containing the input
+ file. */
+struct Cleanup : TermFun
+{
+ string basePath;
+
+ virtual ATerm operator () (ATerm e)
+ {
+ ATMatcher m;
+ string s;
+
+ if (atMatch(m, e) >> "Str" >> s) {
+ return ATmake("Str(<str>)",
+ string(s, 1, s.size() - 2).c_str());
+ }
+
+ if (atMatch(m, e) >> "Path" >> s) {
+ if (s[0] != '/')
+ s = basePath + "/" + s;
+ return ATmake("Path(<str>)", canonPath(s).c_str());
+ }
+
+ if (atMatch(m, e) >> "Int" >> s) {
+ istringstream s2(s);
+ int n;
+ s2 >> n;
+ return ATmake("Int(<int>)", n);
+ }
+
+ if (atMatch(m, e) >> "Bool" >> "true")
+ return ATmake("Bool(True)");
+
+ if (atMatch(m, e) >> "Bool" >> "false")
+ return ATmake("Bool(False)");
+
+ if (atMatch(m, e) >> "ExprNil")
+ return (ATerm) ATempty;
+
+ ATerm e1;
+ ATermList e2;
+ if (atMatch(m, e) >> "ExprCons" >> e1 >> e2)
+ return (ATerm) ATinsert(e2, e1);
+
+ return e;
+ }
+};
+
+
+Expr parseExprFromFile(Path path)
+{
+#if 0
+ /* Perhaps this is already an imploded parse tree? */
+ Expr e = ATreadFromNamedFile(path.c_str());
+ if (e) return e;
+#endif
+
+ /* If `path' refers to a directory, append `/default.nix'. */
+ struct stat st;
+ 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");
+
+ /* Initialise the SDF libraries. */
+ static bool initialised = false;
+ static ATerm parseTable = 0;
+ static language lang = 0;
+
+ if (!initialised) {
+ PT_initMEPTApi();
+ PT_initAsFix2Api();
+ SGinitParser(ATfalse);
+
+ ATprotect(&parseTable);
+ parseTable = ATreadFromBinaryString(
+ (char *) nixParseTable, sizeof nixParseTable);
+ if (!parseTable)
+ throw Error(format("cannot construct parse table term"));
+
+ ATprotect(&lang);
+ lang = ATmake("Nix");
+ if (!SGopenLanguageFromTerm(
+ (char *) programId.c_str(), lang, parseTable))
+ throw Error(format("cannot open language"));
+
+ SG_STARTSYMBOL_ON();
+ SG_OUTPUT_ON();
+ SG_ASFIX2ME_ON();
+ SG_AMBIGUITY_ERROR_ON();
+ SG_FILTER_OFF();
+
+ initialised = true;
+ }
+
+ /* 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;
+
+ /* Parse it. */
+ ATerm result = SGparseString(lang, "Expr", text);
+ if (!result)
+ throw SysError(format("parse failed in `%1%'") % path);
+ if (SGisParseError(result))
+ throw Error(format("parse error in `%1%': %2%")
+ % path % result);
+
+ /* Implode it. */
+ PT_ParseTree tree = PT_makeParseTreeFromTerm(result);
+ if (!tree)
+ throw Error(format("cannot create parse tree"));
+
+ ATerm imploded = PT_implodeParseTree(tree,
+ ATtrue,
+ ATtrue,
+ ATtrue,
+ ATtrue,
+ ATtrue,
+ ATtrue,
+ ATfalse,
+ ATtrue,
+ ATtrue,
+ ATtrue,
+ ATfalse);
+ if (!imploded)
+ throw Error(format("cannot implode parse tree"));
+
+ printMsg(lvlVomit, format("imploded parse tree of `%1%': %2%")
+ % path % imploded);
+
+ /* Finally, clean it up. */
+ Cleanup cleanup;
+ cleanup.basePath = dirOf(path);
+ return bottomupRewrite(cleanup, imploded);
+}