aboutsummaryrefslogtreecommitdiffstats
path: root/src/parse.cpp
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2010-12-07 23:26:35 +0000
committerDavid Robillard <d@drobilla.net>2010-12-07 23:26:35 +0000
commitb38ff5a30986514a26bd800d113b40d6fcfef0db (patch)
treef36df58c0358a83c8439bf44ff9785049a16f2d3 /src/parse.cpp
parent375bed3c2e94f44fc3587f3b0cd036b97df8f72d (diff)
downloadresp-b38ff5a30986514a26bd800d113b40d6fcfef0db.tar.gz
resp-b38ff5a30986514a26bd800d113b40d6fcfef0db.tar.bz2
resp-b38ff5a30986514a26bd800d113b40d6fcfef0db.zip
Rename 'lex' to the now more appropriate 'parse'.
git-svn-id: http://svn.drobilla.net/resp/resp@308 ad02d1e2-f140-0410-9f75-f8b11f17cedd
Diffstat (limited to 'src/parse.cpp')
-rw-r--r--src/parse.cpp207
1 files changed, 207 insertions, 0 deletions
diff --git a/src/parse.cpp b/src/parse.cpp
new file mode 100644
index 0000000..2ac838f
--- /dev/null
+++ b/src/parse.cpp
@@ -0,0 +1,207 @@
+/* Resp: A programming language
+ * Copyright (C) 2008-2009 David Robillard <dave@drobilla.net>
+ *
+ * Resp is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Resp is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Resp. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file
+ * @brief Parsing (build an AST from text)
+ */
+
+#include <stdio.h>
+#include <stack>
+#include "resp.hpp"
+
+using namespace std;
+
+static inline int
+read_char(Cursor& cur, istream& in)
+{
+ int ch = in.get();
+ switch (ch) {
+ case '\n': ++cur.line; cur.col = 0; break;
+ default: ++cur.col;
+ }
+ return ch;
+}
+
+static inline void
+skip_space(Cursor& cur, istream& in)
+{
+ while (isspace(in.peek()))
+ read_char(cur, in);
+}
+
+static inline void
+eat_char(Cursor& cur, istream& in, const char character)
+{
+ const char c = read_char(cur, in);
+ assert(c == character);
+ return;
+}
+
+static AST*
+read_string(Cursor& cur, istream& in)
+{
+ string str;
+ char c;
+ Cursor loc = cur;
+ eat_char(cur, in, '"');
+ while ((c = read_char(cur, in)) != '"') {
+ if (c == '\\') { // string escape
+ switch (c = read_char(cur, in)) {
+ case '"':
+ str.push_back('"');
+ break;
+ case '\\':
+ str.push_back('\\');
+ break;
+ default:
+ cin.putback(c);
+ throw Error(cur, string("unknown string escape `\\") + (char)c + "'");
+ }
+ } else { // any other character
+ str.push_back(c);
+ }
+ }
+ return new AString(loc, str);
+}
+
+static AST*
+read_line_comment(Cursor& cur, istream& in)
+{
+ char c;
+ while ((c = read_char(cur, in)) != '\n') {}
+ return NULL;
+}
+
+static AST*
+read_list(PEnv& penv, Cursor& cur, istream& in)
+{
+ List<ATuple, AST> list;
+
+ eat_char(cur, in, '(');
+ while (true) {
+ skip_space(cur, in);
+ if (in.peek() == ')') {
+ eat_char(cur, in, ')');
+ return list.head;
+ }
+
+ list.push_back(read_expression(penv, cur, in));
+ }
+ assert(false);
+}
+
+static AST*
+read_special(Cursor& cur, istream& in)
+{
+ eat_char(cur, in, '#');
+ switch (in.peek()) {
+ case '|':
+ while (!(read_char(cur, in) == '|' && read_char(cur, in) == '#')) {}
+ return NULL;
+ case 't':
+ eat_char(cur, in, 't');
+ return new ALiteral<bool>(T_BOOL, true, cur);
+ case 'f':
+ return new ALiteral<bool>(T_BOOL, false, cur);
+ default:
+ throw Error(cur, (format("unknown special lexeme `%1%'") % in.peek()).str());
+ }
+ assert(false);
+ return NULL;
+}
+
+static AST*
+read_number(Cursor& cur, istream& in)
+{
+ string str;
+ char c;
+ Cursor loc = cur;
+ while ((c = in.peek()) != EOF) {
+ if (isdigit(c) || c == '.')
+ str += read_char(cur, in);
+ else
+ break;
+ }
+
+ if (str.find('.') == string::npos)
+ return new ALiteral<int32_t>(T_INT32, strtol(str.c_str(), NULL, 10), loc);
+ else
+ return new ALiteral<float>(T_FLOAT, strtod(str.c_str(), NULL), loc);
+}
+
+static AST*
+read_symbol(PEnv& penv, Cursor& cur, istream& in)
+{
+ string str;
+ char c;
+ Cursor loc = cur;
+ while ((c = in.peek()) != EOF) {
+ if (!isspace(c) && c != ')' && c != '(' && c != EOF && c != -1) {
+ str += read_char(cur, in);
+ } else {
+ break;
+ }
+ }
+
+ return penv.sym(str);
+}
+
+/// Read an expression from @a in
+AST*
+read_expression(PEnv& penv, Cursor& cur, istream& in)
+{
+ while (!cin.eof()) {
+ skip_space(cur, in);
+ const char c = in.peek();
+ switch (c) {
+ case EOF:
+ return NULL;
+ case ';':
+ read_line_comment(cur, in);
+ break;
+ case '"':
+ return read_string(cur, in);
+ case '(':
+ return read_list(penv, cur, in);
+ case ')':
+ throw Error(cur, "unexpected `)'");
+ case '#':
+ {
+ AST* ret = read_special(cur, in);
+ if (ret)
+ return ret;
+ break;
+ }
+ case '-':
+ case '+':
+ read_char(cur, in);
+ if (isdigit(in.peek())) {
+ in.putback(c);
+ return read_number(cur, in);
+ } else {
+ in.putback(c);
+ return read_symbol(penv, cur, in);
+ }
+ default:
+ if (isdigit(c))
+ return read_number(cur, in);
+ else
+ return read_symbol(penv, cur, in);
+ }
+ }
+ return NULL;
+}