diff options
Diffstat (limited to 'src/parse.cpp')
-rw-r--r-- | src/parse.cpp | 207 |
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; +} |