From 314482b95ff83f1f88e41d45fadd05a7e625bb0c Mon Sep 17 00:00:00 2001 From: David Robillard Date: Mon, 29 Jun 2009 00:28:50 +0000 Subject: Split code up further. git-svn-id: http://svn.drobilla.net/resp/tuplr@165 ad02d1e2-f140-0410-9f75-f8b11f17cedd --- Makefile | 2 + src/lex.cpp | 95 ++++++++++++++++++++++++++++++ src/parse.cpp | 131 ++++++++++++++++++++++++++++++++++++++++++ src/tuplr.cpp | 182 ---------------------------------------------------------- src/tuplr.hpp | 7 ++- tuplr.dox | 3 +- 6 files changed, 234 insertions(+), 186 deletions(-) create mode 100644 src/lex.cpp create mode 100644 src/parse.cpp diff --git a/Makefile b/Makefile index f91718e..9388f95 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,9 @@ OBJECTS = \ build/cps.o \ build/gc.o \ build/gclib.so \ + build/lex.o \ build/llvm.so \ + build/parse.o \ build/pprint.o \ build/tuplr.o \ build/unify.o diff --git a/src/lex.cpp b/src/lex.cpp new file mode 100644 index 0000000..81dbe19 --- /dev/null +++ b/src/lex.cpp @@ -0,0 +1,95 @@ +/* Tuplr: A programming language + * Copyright (C) 2008-2009 David Robillard + * + * Tuplr 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. + * + * Tuplr 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 Tuplr. If not, see . + */ + +/** @file + + * @brief Lexing (build a SExp from a string) + */ + +#include +#include "tuplr.hpp" + +using namespace std; + +inline int +readChar(Cursor& cur, istream& in) +{ + int ch = in.get(); + switch (ch) { + case '\n': ++cur.line; cur.col = 0; break; + default: ++cur.col; + } + return ch; +} + +SExp +readExpression(Cursor& cur, istream& in) +{ +#define PUSH(s, t) { if (t != "") { s.top().push_back(SExp(loc, t)); t = ""; } } +#define YIELD(s, t) { if (s.empty()) { return SExp(loc, t); } else PUSH(s, t) } + stack stk; + string tok; + Cursor loc; // start of tok + while (int c = readChar(cur, in)) { + switch (c) { + case EOF: + THROW_IF(!stk.empty(), cur, "unexpected end of file") + return SExp(cur); + case ';': + while ((c = readChar(cur, in)) != '\n') {} + case '\n': case ' ': case '\t': + if (tok != "") YIELD(stk, tok); + break; + case '"': + loc = cur; + do { tok.push_back(c); } while ((c = readChar(cur, in)) != '"'); + YIELD(stk, tok + '"'); + break; + case '(': + stk.push(SExp(cur)); + break; + case ')': + switch (stk.size()) { + case 0: + throw Error(cur, "unexpected `)'"); + case 1: + PUSH(stk, tok); + return stk.top(); + default: + PUSH(stk, tok); + SExp l = stk.top(); + stk.pop(); + stk.top().push_back(l); + } + break; + case '#': + if (in.peek() == '|') { + while (!(readChar(cur, in) == '|' && readChar(cur, in) == '#')) {} + break; + } + default: + if (tok == "") loc = cur; + tok += c; + } + } + switch (stk.size()) { + case 0: return SExp(loc, tok); + case 1: return stk.top(); + default: throw Error(cur, "missing `)'"); + } + return SExp(cur); +} diff --git a/src/parse.cpp b/src/parse.cpp new file mode 100644 index 0000000..ae34778 --- /dev/null +++ b/src/parse.cpp @@ -0,0 +1,131 @@ +/* Tuplr: A programming language + * Copyright (C) 2008-2009 David Robillard + * + * Tuplr 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. + * + * Tuplr 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 Tuplr. If not, see . + */ + +/** @file + * @brief Parsing (build an AST from a SExp) + */ + +#include "tuplr.hpp" + +using namespace std; + + +/*************************************************************************** + * Macro Functions * + ***************************************************************************/ + +inline SExp +macDef(PEnv& penv, const SExp& exp) +{ + THROW_IF(exp.size() < 3, exp.loc, "[MAC] `def' requires at least 2 arguments") + if (exp.at(1).type == SExp::ATOM) { + return exp; + } else { + // (def (f x) y) => (def f (fn (x) y)) + SExp argsExp(exp.loc); + for (size_t i = 1; i < exp.at(1).size(); ++i) + argsExp.push_back(exp.at(1).at(i)); + SExp fnExp(exp.at(2).loc); + fnExp.push_back(SExp(exp.at(2).loc, "fn")); + fnExp.push_back(argsExp); + for (size_t i = 2; i < exp.size(); ++i) + fnExp.push_back(exp.at(i)); + SExp ret(exp.loc); + ret.push_back(exp.at(0)); + ret.push_back(exp.at(1).at(0)); + ret.push_back(fnExp); + return ret; + } +} + + +/*************************************************************************** + * Parser Functions * + ***************************************************************************/ + +template +inline AST* +parseCall(PEnv& penv, const SExp& exp, void* arg) +{ + return new C(exp, penv.parseTuple(exp)); +} + +template +inline AST* +parseLiteral(PEnv& penv, const SExp& exp, void* arg) +{ + return new ALiteral(*reinterpret_cast(arg), exp.loc); +} + +inline AST* +parseFn(PEnv& penv, const SExp& exp, void* arg) +{ + if (exp.size() < 2) + throw Error(exp.loc, "Missing function parameters and body"); + else if (exp.size() < 3) + throw Error(exp.loc, "Missing function body"); + SExp::const_iterator a = exp.begin(); ++a; + AFn* ret = tup(exp.loc, penv.sym("fn"), new ATuple(penv.parseTuple(*a++)), 0); + while (a != exp.end()) + ret->push_back(penv.parse(*a++)); + return ret; +} + + +/*************************************************************************** + * Language Definition * + ***************************************************************************/ + +void +initLang(PEnv& penv, TEnv& tenv) +{ + // Types + tenv.def(penv.sym("Nothing"), make_pair((AST*)0, new AType(penv.sym("Nothing")))); + tenv.def(penv.sym("Bool"), make_pair((AST*)0, new AType(penv.sym("Bool")))); + tenv.def(penv.sym("Int"), make_pair((AST*)0, new AType(penv.sym("Int")))); + tenv.def(penv.sym("Float"), make_pair((AST*)0, new AType(penv.sym("Float")))); + + // Literals + static bool trueVal = true; + static bool falseVal = false; + penv.reg(false, "#t", PEnv::Handler(parseLiteral, &trueVal)); + penv.reg(false, "#f", PEnv::Handler(parseLiteral, &falseVal)); + + // Macros + penv.defmac("def", macDef); + + // Special forms + penv.reg(true, "fn", PEnv::Handler(parseFn)); + penv.reg(true, "if", PEnv::Handler(parseCall)); + penv.reg(true, "def", PEnv::Handler(parseCall)); + + // Numeric primitives + penv.reg(true, "+", PEnv::Handler(parseCall)); + penv.reg(true, "-", PEnv::Handler(parseCall)); + penv.reg(true, "*", PEnv::Handler(parseCall)); + penv.reg(true, "/", PEnv::Handler(parseCall)); + penv.reg(true, "%", PEnv::Handler(parseCall)); + penv.reg(true, "and", PEnv::Handler(parseCall)); + penv.reg(true, "or", PEnv::Handler(parseCall)); + penv.reg(true, "xor", PEnv::Handler(parseCall)); + penv.reg(true, "=", PEnv::Handler(parseCall)); + penv.reg(true, "!=", PEnv::Handler(parseCall)); + penv.reg(true, ">", PEnv::Handler(parseCall)); + penv.reg(true, ">=", PEnv::Handler(parseCall)); + penv.reg(true, "<", PEnv::Handler(parseCall)); + penv.reg(true, "<=", PEnv::Handler(parseCall)); +} diff --git a/src/tuplr.cpp b/src/tuplr.cpp index 7ff9f52..a0c001d 100644 --- a/src/tuplr.cpp +++ b/src/tuplr.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include "tuplr.hpp" using namespace std; @@ -51,187 +50,6 @@ operator<<(ostream& out, const Exp& exp) } -/*************************************************************************** - * Lexer * - ***************************************************************************/ - -inline int -readChar(Cursor& cur, istream& in) -{ - int ch = in.get(); - switch (ch) { - case '\n': ++cur.line; cur.col = 0; break; - default: ++cur.col; - } - return ch; -} - -SExp -readExpression(Cursor& cur, istream& in) -{ -#define PUSH(s, t) { if (t != "") { s.top().push_back(SExp(loc, t)); t = ""; } } -#define YIELD(s, t) { if (s.empty()) { return SExp(loc, t); } else PUSH(s, t) } - stack stk; - string tok; - Cursor loc; // start of tok - while (int c = readChar(cur, in)) { - switch (c) { - case EOF: - THROW_IF(!stk.empty(), cur, "unexpected end of file") - return SExp(cur); - case ';': - while ((c = readChar(cur, in)) != '\n') {} - case '\n': case ' ': case '\t': - if (tok != "") YIELD(stk, tok); - break; - case '"': - loc = cur; - do { tok.push_back(c); } while ((c = readChar(cur, in)) != '"'); - YIELD(stk, tok + '"'); - break; - case '(': - stk.push(SExp(cur)); - break; - case ')': - switch (stk.size()) { - case 0: - throw Error(cur, "unexpected `)'"); - case 1: - PUSH(stk, tok); - return stk.top(); - default: - PUSH(stk, tok); - SExp l = stk.top(); - stk.pop(); - stk.top().push_back(l); - } - break; - case '#': - if (in.peek() == '|') { - while (!(readChar(cur, in) == '|' && readChar(cur, in) == '#')) {} - break; - } - default: - if (tok == "") loc = cur; - tok += c; - } - } - switch (stk.size()) { - case 0: return SExp(loc, tok); - case 1: return stk.top(); - default: throw Error(cur, "missing `)'"); - } - return SExp(cur); -} - - -/*************************************************************************** - * Macro Functions * - ***************************************************************************/ - -inline SExp -macDef(PEnv& penv, const SExp& exp) -{ - THROW_IF(exp.size() < 3, exp.loc, "[MAC] `def' requires at least 2 arguments") - if (exp.at(1).type == SExp::ATOM) { - return exp; - } else { - // (def (f x) y) => (def f (fn (x) y)) - SExp argsExp(exp.loc); - for (size_t i = 1; i < exp.at(1).size(); ++i) - argsExp.push_back(exp.at(1).at(i)); - SExp fnExp(exp.at(2).loc); - fnExp.push_back(SExp(exp.at(2).loc, "fn")); - fnExp.push_back(argsExp); - for (size_t i = 2; i < exp.size(); ++i) - fnExp.push_back(exp.at(i)); - SExp ret(exp.loc); - ret.push_back(exp.at(0)); - ret.push_back(exp.at(1).at(0)); - ret.push_back(fnExp); - return ret; - } -} - - -/*************************************************************************** - * Parser Functions * - ***************************************************************************/ - -template -inline AST* -parseCall(PEnv& penv, const SExp& exp, void* arg) -{ - return new C(exp, penv.parseTuple(exp)); -} - -template -inline AST* -parseLiteral(PEnv& penv, const SExp& exp, void* arg) -{ - return new ALiteral(*reinterpret_cast(arg), exp.loc); -} - -inline AST* -parseFn(PEnv& penv, const SExp& exp, void* arg) -{ - if (exp.size() < 2) - throw Error(exp.loc, "Missing function parameters and body"); - else if (exp.size() < 3) - throw Error(exp.loc, "Missing function body"); - SExp::const_iterator a = exp.begin(); ++a; - AFn* ret = tup(exp.loc, penv.sym("fn"), new ATuple(penv.parseTuple(*a++)), 0); - while (a != exp.end()) - ret->push_back(penv.parse(*a++)); - return ret; -} - - -/*************************************************************************** - * Standard Definitions * - ***************************************************************************/ - -void -initLang(PEnv& penv, TEnv& tenv) -{ - // Types - tenv.def(penv.sym("Nothing"), make_pair((AST*)0, new AType(penv.sym("Nothing")))); - tenv.def(penv.sym("Bool"), make_pair((AST*)0, new AType(penv.sym("Bool")))); - tenv.def(penv.sym("Int"), make_pair((AST*)0, new AType(penv.sym("Int")))); - tenv.def(penv.sym("Float"), make_pair((AST*)0, new AType(penv.sym("Float")))); - - // Literals - static bool trueVal = true; - static bool falseVal = false; - penv.reg(false, "#t", PEnv::Handler(parseLiteral, &trueVal)); - penv.reg(false, "#f", PEnv::Handler(parseLiteral, &falseVal)); - - // Macros - penv.defmac("def", macDef); - - // Special forms - penv.reg(true, "fn", PEnv::Handler(parseFn)); - penv.reg(true, "if", PEnv::Handler(parseCall)); - penv.reg(true, "def", PEnv::Handler(parseCall)); - - // Numeric primitives - penv.reg(true, "+", PEnv::Handler(parseCall)); - penv.reg(true, "-", PEnv::Handler(parseCall)); - penv.reg(true, "*", PEnv::Handler(parseCall)); - penv.reg(true, "/", PEnv::Handler(parseCall)); - penv.reg(true, "%", PEnv::Handler(parseCall)); - penv.reg(true, "and", PEnv::Handler(parseCall)); - penv.reg(true, "or", PEnv::Handler(parseCall)); - penv.reg(true, "xor", PEnv::Handler(parseCall)); - penv.reg(true, "=", PEnv::Handler(parseCall)); - penv.reg(true, "!=", PEnv::Handler(parseCall)); - penv.reg(true, ">", PEnv::Handler(parseCall)); - penv.reg(true, ">=", PEnv::Handler(parseCall)); - penv.reg(true, "<", PEnv::Handler(parseCall)); - penv.reg(true, "<=", PEnv::Handler(parseCall)); -} - - /*************************************************************************** * EVAL/REPL * ***************************************************************************/ diff --git a/src/tuplr.hpp b/src/tuplr.hpp index 613be0d..4948b82 100644 --- a/src/tuplr.hpp +++ b/src/tuplr.hpp @@ -138,11 +138,12 @@ typedef void* CFunction; ///< Compiled function (opaque) /*************************************************************************** - * Garbage Collector * + * Garbage Collection * ***************************************************************************/ -struct Object; ///< Object (AST nodes and runtime data) +struct Object; +/// Garbage collector struct GC { enum Tag { TAG_AST = 2, ///< Abstract syntax tree node @@ -160,7 +161,7 @@ private: Roots _roots; }; -/// Dynamic (garbage-collected) object +/// Garbage collected object (including AST and runtime data) struct Object { struct Header { uint8_t mark; diff --git a/tuplr.dox b/tuplr.dox index 01e4390..456d159 100644 --- a/tuplr.dox +++ b/tuplr.dox @@ -297,7 +297,7 @@ SYMBOL_CACHE_SIZE = 0 # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES -EXTRACT_ALL = NO +EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. @@ -568,6 +568,7 @@ INPUT = src/constrain.cpp \ src/cps.cpp \ src/gc.cpp \ src/gclib.cpp \ + src/lex.cpp \ src/llvm.cpp \ src/pprint.cpp \ src/tuplr.cpp \ -- cgit v1.2.1