/* Resp: A programming language * Copyright (C) 2008-2009 David Robillard * * 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 . */ /** @file * @brief Parsing (build an AST from text) */ #include #include #include #include "resp.hpp" using namespace std; static inline int read_char(Cursor& cur, istream& in) { switch (in.peek()) { case '\n': ++cur.line; cur.col = 0; break; default: ++cur.col; } return in.get(); } 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); THROW_IF(c != character, cur, (format("expected `%1%'") % character).str()); } static const AST* read_string(Cursor& cur, istream& in) { const Cursor loc = cur; string str; char c; eat_char(cur, in, '"'); while ((c = read_char(cur, in)) != '"') { switch (c) { case '\\': 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 + "'"); } default: str.push_back(c); } } return new AString(loc, str); } static void read_line_comment(Cursor& cur, istream& in) { while (read_char(cur, in) != '\n') {} } static const AST* read_list(PEnv& penv, Cursor& cur, istream& in) { const Cursor loc = cur; List list; eat_char(cur, in, '('); while (true) { skip_space(cur, in); if (in.peek() == ')') { eat_char(cur, in, ')'); list.head->loc = loc; return list.head; } else { list.push_back(penv.parse(cur, in)); } } assert(false); } static const 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(T_BOOL, true, cur); case 'f': eat_char(cur, in, 'f'); return new ALiteral(T_BOOL, false, cur); default: throw Error(cur, (format("unknown special lexeme `%1%'") % in.peek()).str()); } assert(false); } static const AST* read_number(Cursor& cur, istream& in) { const Cursor loc = cur; string str; char c; while ((c = in.peek()) != EOF) { if (isdigit(c) || c == '.') str += read_char(cur, in); else break; } if (str.find('.') == string::npos) return new ALiteral(T_INT32, strtol(str.c_str(), NULL, 10), loc); else return new ALiteral(T_FLOAT, strtod(str.c_str(), NULL), loc); } static const AST* read_symbol(PEnv& penv, Cursor& cur, istream& in) { const Cursor loc = cur; string str; char c; 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, loc); } /// Read an expression from @a in const AST* PEnv::parse(Cursor& cur, istream& in) throw(Error) { 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(*this, cur, in); case ')': throw Error(cur, "unexpected `)'"); case '#': { const 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(*this, cur, in); } default: if (isdigit(c)) return read_number(cur, in); else return read_symbol(*this, cur, in); } } return NULL; }