From cea95f2cc443a5bee5cb69476ae73fd99d32fc0d Mon Sep 17 00:00:00 2001 From: Gabriel Dos Reis Date: Sun, 15 Jan 2017 09:14:39 -0800 Subject: Add s-expression parsing to libopen-axiom-core.a --- config/var-def.mk | 5 +- src/lib/Makefile.in | 3 +- src/rt/Makefile.am | 6 +- src/rt/Makefile.in | 10 +- src/syntax/sexpr.cc | 670 -------------------------------------------------- src/syntax/sexpr.cxx | 670 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/utils/Makefile.am | 3 +- src/utils/Makefile.in | 25 +- src/utils/storage.cc | 314 ----------------------- src/utils/storage.cxx | 314 +++++++++++++++++++++++ 10 files changed, 1007 insertions(+), 1013 deletions(-) delete mode 100644 src/syntax/sexpr.cc create mode 100644 src/syntax/sexpr.cxx delete mode 100644 src/utils/storage.cc create mode 100644 src/utils/storage.cxx diff --git a/config/var-def.mk b/config/var-def.mk index 7f567f58..99f5c649 100644 --- a/config/var-def.mk +++ b/config/var-def.mk @@ -193,7 +193,10 @@ oa_use_x = @oa_use_x@ AXIOM_X11_CFLAGS = @X_CFLAGS@ AXIOM_X11_LDFLAGS = @X_LIBS@ @X_PRE_LIBS@ -lX11 @X_EXTRA_LIBS@ -oa_includes = -I$(oa_src_srcdir)/include -I$(oa_configdir) +oa_includes = \ + -I$(oa_src_srcdir)/include \ + -I$(oa_configdir) \ + -I$(oa_target_includedir) ## Where the staging build directory is found AXIOM = $(top_builddir)/$(target) diff --git a/src/lib/Makefile.in b/src/lib/Makefile.in index aeecfabf..fecb62bd 100644 --- a/src/lib/Makefile.in +++ b/src/lib/Makefile.in @@ -30,14 +30,15 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - core_SOURCES = \ bsdsignal.cxx \ cfuns-c.cxx \ sockio-c.cxx \ + ../utils/storage.cxx \ ../io/std-streams.cxx \ ../io/InputFragment.cxx \ ../syntax/token.cxx \ + ../syntax/sexpr.cxx \ ../syntax/Parser.cxx terminal_io_SOURCES = cursor.c edin.c fnct_key.c openpty.c prt.c wct.c diff --git a/src/rt/Makefile.am b/src/rt/Makefile.am index 8c3fdc14..90083687 100644 --- a/src/rt/Makefile.am +++ b/src/rt/Makefile.am @@ -1,4 +1,4 @@ -# Copyright (C) 2013-2014, Gabriel Dos Reis. +# Copyright (C) 2013-2017, Gabriel Dos Reis. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -32,7 +32,9 @@ noinst_PROGRAMS = lisp lisp_SOURCES = driver.cc -lisp_LDADD = $(oa_target_libdir)/libOpenAxiom.a +lisp_LDADD = \ + $(oa_target_libdir)/libOpenAxiom.a \ + $(oa_target_libdir)/libopen-axiom-core.a AM_CXXFLAGS = \ -I$(top_srcdir)/src/include \ diff --git a/src/rt/Makefile.in b/src/rt/Makefile.in index 2f7147a0..3a4cce43 100644 --- a/src/rt/Makefile.in +++ b/src/rt/Makefile.in @@ -14,7 +14,7 @@ @SET_MAKE@ -# Copyright (C) 2013-2014, Gabriel Dos Reis. +# Copyright (C) 2013-2017, Gabriel Dos Reis. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -140,7 +140,8 @@ CONFIG_CLEAN_VPATH_FILES = PROGRAMS = $(noinst_PROGRAMS) am_lisp_OBJECTS = driver.$(OBJEXT) lisp_OBJECTS = $(am_lisp_OBJECTS) -lisp_DEPENDENCIES = $(oa_target_libdir)/libOpenAxiom.a +lisp_DEPENDENCIES = $(oa_target_libdir)/libOpenAxiom.a \ + $(oa_target_libdir)/libopen-axiom-core.a AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent @@ -401,7 +402,10 @@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ void_type = @void_type@ lisp_SOURCES = driver.cc -lisp_LDADD = $(oa_target_libdir)/libOpenAxiom.a +lisp_LDADD = \ + $(oa_target_libdir)/libOpenAxiom.a \ + $(oa_target_libdir)/libopen-axiom-core.a + AM_CXXFLAGS = \ -I$(top_srcdir)/src/include \ -I$(oa_target_includedir) \ diff --git a/src/syntax/sexpr.cc b/src/syntax/sexpr.cc deleted file mode 100644 index f9d76825..00000000 --- a/src/syntax/sexpr.cc +++ /dev/null @@ -1,670 +0,0 @@ -// Copyright (C) 2010-2013, Gabriel Dos Reis. -// All rights reserved. -// Written by Gabriel Dos Reis. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// - Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// - Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in -// the documentation and/or other materials provided with the -// distribution. -// -// - Neither the name of The Numerical Algorithms Group Ltd. nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER -// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// --% Author: Gabriel Dos Reis. - -#include -#include -#include -#include -#include -#include -#include - -namespace OpenAxiom { - namespace Sexpr { - static void - invalid_character(Reader::State& s) { - auto line = std::to_string(s.bytes.lineno); - auto column = std::to_string(s.cur - s.line); - auto msg = "invalid character on line " + line + - " and column " + column; - if (isprint(*s.cur)) - throw Diagnostics::BasicError(msg + ": " + std::string(1, *s.cur)); - throw Diagnostics::BasicError(msg + " with code " + std::to_string(*s.cur)); - } - - static void - syntax_error(const std::string& s) { - throw Diagnostics::BasicError(s); - } - - // Return true if character `c' introduces a blank. - static bool - is_blank(char c) { - return c == ' ' or c == '\t' or c == '\v' - or c == '\n' or c == '\f' or c == '\r'; - } - - // Return true if the character `c' introduces a delimiter. - static bool - is_delimiter(char c) { - return is_blank(c) - or c == '(' or c == ')' or c == '\'' - or c == '`' or c == '#'; - } - - // Move the cursor past all consecutive blank characters, and - // return true if there are more input characters to consider. - static bool - skip_blank(Reader::State& s) { - for (bool done = false; s.cur < s.bytes.end and not done; ) - switch (*s.cur) { - case '\n': - ++s.bytes.lineno; - s.line = ++s.cur; - break; - case ' ': case '\t': case '\v': case '\r': case '\f': - ++s.cur; - break; - default: done = true; break; - } - return s.cur < s.bytes.end; - } - - // Move `cur' to end-of-line marker. - static void - skip_to_eol(Reader::State& s) { - // FIXME: properly handle CR+LF. - while (s.cur < s.bytes.end and *s.cur != '\n') - ++s.cur; - } - - // Move `cur' one-past a non-esacaped character `c'. - // Return true if the character was seen. - static bool - skip_to_nonescaped_char(Reader::State& s, char c) { - for (bool saw_escape = false; s.cur < s.bytes.end; ++s.cur) - if (saw_escape) - saw_escape = false; - else if (*s.cur == '\\') - saw_escape = true; - else if (*s.cur == c) { - ++s.cur; - return true; - } - return false; - } - - // Move the cursor past the closing quote of string literal. - // Return true if the closing quote was effectively seen. - static inline bool - skip_to_quote(Reader::State& s) { - return skip_to_nonescaped_char(s, '"'); - } - - template - static bool - advance_while(Reader::State& s, Pred p) { - while (s.cur < s.bytes.end and p(*s.cur)) - ++s.cur; - return s.cur < s.bytes.end; - } - - // Return true if the character `c' be part of a non-absolute - // identifier. - static bool - identifier_part(Byte c) { - switch (c) { - case '+': case '-': case '*': case '/': case '%': case '^': - case '~': case '@': case '$': case '&': case '=': - case '<': case '>': case '?': case '!': case '_': - case '[': case ']': case '{': case '}': - return true; - default: - return isalnum(c); - } - } - - // -- AtomSyntax -- - AtomSyntax::AtomSyntax(const Lexeme& t) : lex(t) { } - - // -- IntegerSyntax -- - IntegerSyntax::IntegerSyntax(const Lexeme& t) : AtomSyntax(t) { } - - void - IntegerSyntax::accept(Visitor& v) const { - v.visit(*this); - } - - // -- CharacterSyntax -- - CharacterSyntax::CharacterSyntax(const Lexeme& t) : AtomSyntax(t) { } - - void - CharacterSyntax::accept(Visitor& v) const { - v.visit(*this); - } - - // -- StringSyntax -- - StringSyntax::StringSyntax(const Lexeme& t) : AtomSyntax(t) { } - - void - StringSyntax::accept(Visitor& v) const { - v.visit(*this); - } - - // -- SymbolSyntax -- - SymbolSyntax::SymbolSyntax(const Lexeme& t, Kind k) - : AtomSyntax(t), sort(k) - { } - - void - SymbolSyntax::accept(Visitor& v) const { - v.visit(*this); - } - - // -- AnchorSyntax -- - AnchorSyntax::AnchorSyntax(size_t t, const Syntax* s) : tag(t), val(s) { } - - void - AnchorSyntax::accept(Visitor& v) const { - v.visit(*this); - } - - // -- ReferenceSyntax -- - ReferenceSyntax::ReferenceSyntax(const Lexeme& t, Ordinal n) - : AtomSyntax(t), pos(n) - { } - - void - ReferenceSyntax::accept(Visitor& v) const { - v.visit(*this); - } - - // -- QuoteSyntax -- - QuoteSyntax::QuoteSyntax(const Syntax* s) - : unary_form(s) - { } - - // -- AntiquoteSyntax -- - AntiquoteSyntax::AntiquoteSyntax(const Syntax* s) - : unary_form(s) - { } - - // -- Expand -- - Expand::Expand(const Syntax* s) : unary_form(s) { } - - // -- Eval -- - Eval::Eval(const Syntax* s) : unary_form(s) { } - - // -- Splice -- - Splice::Splice(const Syntax* s) : unary_form(s) { } - - // -- Function -- - Function::Function(const Syntax* s) : unary_form(s) { } - - // -- Include -- - Include::Include(const Syntax* c, const Syntax* s) - : binary_form(c, s) - { } - - // -- Exclude -- - Exclude::Exclude(const Syntax* c, const Syntax* s) - : binary_form(c, s) - { } - - // -- ListSyntax -- - ListSyntax::ListSyntax() : dot(false) { } - - ListSyntax::ListSyntax(const base& elts, bool d) - : base(elts), dot(d) - { } - - ListSyntax::~ListSyntax() { } - - void - ListSyntax::accept(Visitor& v) const { - v.visit(*this); - } - - // -- VectorSyntax -- - VectorSyntax::VectorSyntax() { } - - VectorSyntax::VectorSyntax(const base& elts) : base(elts) { } - - VectorSyntax::~VectorSyntax() { } - - void - VectorSyntax::accept(Visitor& v) const { - v.visit(*this); - } - - // --------------- - // -- Allocator -- - // --------------- - Allocator::Allocator() { } - - // This destructor is defined here so that it provides - // a single instantiation point for destructors of all - // used templates floating around. - Allocator::~Allocator() { } - - const CharacterSyntax* - Allocator::make_character(const Lexeme& t) { - return chars.make(t); - } - - const IntegerSyntax* - Allocator::make_integer(const Lexeme& t) { - return ints.make(t); - } - - const StringSyntax* - Allocator::make_string(const Lexeme& t) { - return strs.make(t); - } - - const SymbolSyntax* - Allocator::make_symbol(SymbolSyntax::Kind k, const Lexeme& t) { - return syms.make(t, k); - } - - const ReferenceSyntax* - Allocator::make_reference(size_t i, const Lexeme& t) { - return refs.make(t, i); - } - - const AnchorSyntax* - Allocator::make_anchor(size_t t, const Syntax* s) { - return ancs.make(t, s); - } - - const QuoteSyntax* - Allocator::make_quote(const Syntax* s) { - return quotes.make(s); - } - - const AntiquoteSyntax* - Allocator::make_antiquote(const Syntax* s) { - return antis.make(s); - } - - const Expand* - Allocator::make_expand(const Syntax* s) { - return exps.make(s); - } - - const Eval* - Allocator::make_eval(const Syntax* s) { - return evls.make(s); - } - - const Splice* - Allocator::make_splice(const Syntax* s) { - return spls.make(s); - } - - const Function* - Allocator::make_function(const Syntax* s) { - return funs.make(s); - } - - const Include* - Allocator::make_include(const Syntax* c, const Syntax* s) { - return incs.make(c, s); - } - - const Exclude* - Allocator::make_exclude(const Syntax* c, const Syntax* s) { - return excs.make(c, s); - } - - const ListSyntax* - Allocator::make_list(const std::vector& elts, bool dot) { - if (elts.empty()) - return &empty_list; - return lists.make(elts, dot); - } - - const VectorSyntax* - Allocator::make_vector(const std::vector& elts) { - if (elts.empty()) - return &empty_vector; - return vectors.make(elts); - } - - // The sequence of characters in [cur, last) consists - // entirely of digits. Return the corresponding natural value. - static size_t - natural_value(const Byte* cur, const Byte* last) { - size_t n = 0; - for (; cur < last; ++cur) - // FIXME: check for overflow. - n = 10 * n + (*cur - '0'); - return n; - } - - // -- Reader -- - Reader::Reader(const Byte* f, const Byte* l) - : st{ { f, l, 1 }, f, f } - { } - - Reader::Reader(const RawInput& ri) - : st { ri, ri.start, ri.start } - { } - - static const Syntax* read_sexpr(Reader::State&); - - // Parse a string literal - static const Syntax* - read_string(Reader::State& s) { - auto start = s.cur++; - if (not skip_to_quote(s)) - syntax_error("missing closing quote sign for string literal"); - Lexeme t = { { start, s.cur }, s.bytes.lineno }; - return s.alloc.make_string(t); - } - - // Parse an absolute identifier. - static const Syntax* - read_absolute_symbol(Reader::State& s) { - auto start = ++s.cur; - if (not skip_to_nonescaped_char(s, '|')) - syntax_error("missing closing bar sign for an absolute symbol"); - Lexeme t = { { start, s.cur - 1 }, s.bytes.lineno }; - return s.alloc.make_symbol(SymbolSyntax::absolute, t); - } - - // Read an atom starting with digits. - static const Syntax* - read_maybe_natural(Reader::State& s) { - auto start = s.cur; - advance_while (s, isdigit); - if (s.cur >= s.bytes.end or is_delimiter(*s.cur)) { - Lexeme t = { { start, s.cur }, s.bytes.lineno }; - return s.alloc.make_integer(t); - } - advance_while(s, identifier_part); - Lexeme t = { { start, s.cur }, s.bytes.lineno }; - return s.alloc.make_symbol(SymbolSyntax::ordinary, t); - } - - // Read an identifier. - static const Syntax* - read_identifier(Reader::State& s) { - auto start = s.cur; - advance_while(s, identifier_part); - Lexeme t = { { start, s.cur }, s.bytes.lineno }; - return s.alloc.make_symbol(SymbolSyntax::ordinary, t); - } - - // Read an atom starting with a '+' or '-' sign; this - // should be identifier, or a signed integer. - static const Syntax* - read_maybe_signed_number(Reader::State& s) { - auto start = s.cur++; - if (s.cur < s.bytes.end and isdigit(*s.cur)) { - advance_while(s, isdigit); - if (s.cur >= s.bytes.end or is_delimiter(*s.cur)) { - Lexeme t = { { start, s.cur }, s.bytes.lineno }; - return s.alloc.make_integer(t); - } - } - advance_while(s, identifier_part); - Lexeme t = { { start, s.cur }, s.bytes.lineno }; - return s.alloc.make_symbol(SymbolSyntax::ordinary, t); - } - - static const Syntax* - read_keyword(Reader::State& s) { - auto start = s.cur++; - advance_while(s, identifier_part); - Lexeme t = { { start, s.cur }, s.bytes.lineno }; - return s.alloc.make_symbol(SymbolSyntax::keyword, t); - } - - // Read an atom. - static const Syntax* - read_atom(Reader::State& s) { - switch (*s.cur) { - case '"': return read_string(s); - case ':': return read_keyword(s); - case '-': case '+': return read_maybe_signed_number(s); - - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return read_maybe_natural(s); - - default: - if (identifier_part(*s.cur)) - return read_identifier(s); - invalid_character(s); - ++s.cur; - return nullptr; - } - } - - // Parse a quote expression. - static const Syntax* - read_quote(Reader::State& s) { - ++s.cur; // skip the quote character - auto x = read_sexpr(s); - if (x == nullptr) - syntax_error("end of input reached after quote sign"); - return s.alloc.make_quote(x); - } - - // Parse a backquote expression. - static const Syntax* - read_backquote(Reader::State& s) { - ++s.cur; // skip the backquote character - auto x = read_sexpr(s); - if (x == nullptr) - syntax_error("end of input reached after backquote sign"); - return s.alloc.make_antiquote(x); - } - - // We've just seen "#(" indicating the start of a literal - // vector. Read the elements and return the corresponding form. - static const Syntax* - finish_literal_vector(Reader::State& s) { - ++s.cur; // Skip the open paren. - std::vector elts { }; - while (skip_blank(s) and *s.cur != ')') { - if (auto x = read_sexpr(s)) - elts.push_back(x); - else - syntax_error("syntax error while reading vector elements"); - } - if (s.cur >= s.bytes.end) - syntax_error("unfinished literal vector"); - else - ++s.cur; - return s.alloc.make_vector(elts); - } - - // We've just seen the sharp sign followed by a digit. We assume - // we are about to read an anchor or a back reference. - static const Syntax* - finish_anchor_or_reference(Reader::State& s) { - auto start = s.cur; - advance_while(s, isdigit); - if (s.cur >= s.bytes.end) - syntax_error("end-of-input after sharp-number sign"); - const Byte c = *s.cur; - if (c != '#' and c != '=') - syntax_error("syntax error after sharp-number-equal sign"); - Lexeme t = { { start, s.cur }, s.bytes.lineno }; - auto n = natural_value(start, s.cur); - ++s.cur; - if (c == '#') - return s.alloc.make_reference(n, t); - auto x = read_sexpr(s); - if (x == nullptr) - syntax_error("syntax error after sharp-number-equal sign"); - return s.alloc.make_anchor(n, x); - } - - static const Syntax* - finish_function(Reader::State& s) { - ++s.cur; // skip quote sign. - auto x = read_sexpr(s); - if (x == nullptr) - syntax_error("missing function designator after sharp-quote sign"); - return s.alloc.make_function(x); - } - - static const Syntax* - finish_uninterned_symbol(Reader::State& s) { - ++s.cur; // skip colon sign. - auto start = s.cur; - advance_while(s, identifier_part); - Lexeme t = { { start, s.cur }, s.bytes.lineno }; - return s.alloc.make_symbol(SymbolSyntax::uninterned, t); - } - - static const Syntax* - finish_readtime_eval(Reader::State& s) { - ++s.cur; // skip dot sign. - auto x = read_sexpr(s); - if (x == nullptr) - syntax_error("parse error after sharp-dot sign"); - return s.alloc.make_eval(x); - } - - static const Syntax* - finish_character(Reader::State& s) { - ++s.cur; // skip backslash sign - auto start = s.cur; - advance_while(s, identifier_part); - Lexeme t = { { start, s.cur }, s.bytes.lineno }; - return s.alloc.make_character(t); - } - - static const Syntax* - finish_include(Reader::State& s) { - ++s.cur; - auto cond = read_sexpr(s); - auto form = read_sexpr(s); - return s.alloc.make_include(cond, form); - } - - static const Syntax* - finish_exclude(Reader::State& s) { - ++s.cur; - auto cond = read_sexpr(s); - auto form = read_sexpr(s); - return s.alloc.make_exclude(cond, form); - } - - static const Syntax* - read_sharp_et_al(Reader::State& s) { - if (++s.cur >= s.bytes.end) - syntax_error("end-of-input reached after sharp sign"); - switch (*s.cur) { - case '(': return finish_literal_vector(s); - case '\'': return finish_function(s); - case ':': return finish_uninterned_symbol(s); - case '.': return finish_readtime_eval(s); - case '\\': return finish_character(s); - case '+': return finish_include(s); - case '-': return finish_exclude(s); - - default: - if (isdigit(*s.cur)) - return finish_anchor_or_reference(s); - syntax_error("syntax error after sharp-sign"); - } - return nullptr; - } - - // We have just seen a dot; read the tail and the closing parenthesis. - static const Syntax* - finish_dotted_list(Reader::State& s, std::vector& elts) { - ++s.cur; // Skip dot sign. - auto x = read_sexpr(s); - if (x == nullptr) - syntax_error("missing expression after dot sign"); - if (not skip_blank(s) or *s.cur != ')') - syntax_error("missing closing parenthesis"); - ++s.cur; - elts.push_back(x); - return s.alloc.make_list(elts, true); - } - - static const Syntax* - read_pair(Reader::State& s) { - ++s.cur; // skip opening parenthesis - std::vector elts { }; - while (skip_blank(s)) - switch (*s.cur) { - case '.': - if (elts.empty()) - syntax_error("missing expression before dot sign."); - return finish_dotted_list(s, elts); - - case ')': - ++s.cur; - return s.alloc.make_list(elts); - - default: - if (auto x = read_sexpr(s)) - elts.push_back(x); - else - syntax_error("unfinished pair expression"); - break; - } - syntax_error("end-of-input while looking for closing parenthesis"); - return nullptr; - } - - static const Syntax* - read_sexpr(Reader::State& s) { - while (skip_blank(s)) - switch (*s.cur) { - case ';': skip_to_eol(s); break; - case '\'': return read_quote(s); - case '`': return read_backquote(s); - case '|': return read_absolute_symbol(s); - case '#': return read_sharp_et_al(s); - case '(': return read_pair(s); - default: return read_atom(s); - } - return nullptr; - } - - const Syntax* - Reader::read() { - return read_sexpr(st); - } - - const Byte* - Reader::position(Ordinal p) { - st.cur = st.bytes.start + p; - st.line = st.cur; - // while (st.line > st.start and st.line[-1] != '\n') - // --st.line; - return st.cur; - } - - } -} diff --git a/src/syntax/sexpr.cxx b/src/syntax/sexpr.cxx new file mode 100644 index 00000000..f9d76825 --- /dev/null +++ b/src/syntax/sexpr.cxx @@ -0,0 +1,670 @@ +// Copyright (C) 2010-2013, Gabriel Dos Reis. +// All rights reserved. +// Written by Gabriel Dos Reis. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// - Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in +// the documentation and/or other materials provided with the +// distribution. +// +// - Neither the name of The Numerical Algorithms Group Ltd. nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// --% Author: Gabriel Dos Reis. + +#include +#include +#include +#include +#include +#include +#include + +namespace OpenAxiom { + namespace Sexpr { + static void + invalid_character(Reader::State& s) { + auto line = std::to_string(s.bytes.lineno); + auto column = std::to_string(s.cur - s.line); + auto msg = "invalid character on line " + line + + " and column " + column; + if (isprint(*s.cur)) + throw Diagnostics::BasicError(msg + ": " + std::string(1, *s.cur)); + throw Diagnostics::BasicError(msg + " with code " + std::to_string(*s.cur)); + } + + static void + syntax_error(const std::string& s) { + throw Diagnostics::BasicError(s); + } + + // Return true if character `c' introduces a blank. + static bool + is_blank(char c) { + return c == ' ' or c == '\t' or c == '\v' + or c == '\n' or c == '\f' or c == '\r'; + } + + // Return true if the character `c' introduces a delimiter. + static bool + is_delimiter(char c) { + return is_blank(c) + or c == '(' or c == ')' or c == '\'' + or c == '`' or c == '#'; + } + + // Move the cursor past all consecutive blank characters, and + // return true if there are more input characters to consider. + static bool + skip_blank(Reader::State& s) { + for (bool done = false; s.cur < s.bytes.end and not done; ) + switch (*s.cur) { + case '\n': + ++s.bytes.lineno; + s.line = ++s.cur; + break; + case ' ': case '\t': case '\v': case '\r': case '\f': + ++s.cur; + break; + default: done = true; break; + } + return s.cur < s.bytes.end; + } + + // Move `cur' to end-of-line marker. + static void + skip_to_eol(Reader::State& s) { + // FIXME: properly handle CR+LF. + while (s.cur < s.bytes.end and *s.cur != '\n') + ++s.cur; + } + + // Move `cur' one-past a non-esacaped character `c'. + // Return true if the character was seen. + static bool + skip_to_nonescaped_char(Reader::State& s, char c) { + for (bool saw_escape = false; s.cur < s.bytes.end; ++s.cur) + if (saw_escape) + saw_escape = false; + else if (*s.cur == '\\') + saw_escape = true; + else if (*s.cur == c) { + ++s.cur; + return true; + } + return false; + } + + // Move the cursor past the closing quote of string literal. + // Return true if the closing quote was effectively seen. + static inline bool + skip_to_quote(Reader::State& s) { + return skip_to_nonescaped_char(s, '"'); + } + + template + static bool + advance_while(Reader::State& s, Pred p) { + while (s.cur < s.bytes.end and p(*s.cur)) + ++s.cur; + return s.cur < s.bytes.end; + } + + // Return true if the character `c' be part of a non-absolute + // identifier. + static bool + identifier_part(Byte c) { + switch (c) { + case '+': case '-': case '*': case '/': case '%': case '^': + case '~': case '@': case '$': case '&': case '=': + case '<': case '>': case '?': case '!': case '_': + case '[': case ']': case '{': case '}': + return true; + default: + return isalnum(c); + } + } + + // -- AtomSyntax -- + AtomSyntax::AtomSyntax(const Lexeme& t) : lex(t) { } + + // -- IntegerSyntax -- + IntegerSyntax::IntegerSyntax(const Lexeme& t) : AtomSyntax(t) { } + + void + IntegerSyntax::accept(Visitor& v) const { + v.visit(*this); + } + + // -- CharacterSyntax -- + CharacterSyntax::CharacterSyntax(const Lexeme& t) : AtomSyntax(t) { } + + void + CharacterSyntax::accept(Visitor& v) const { + v.visit(*this); + } + + // -- StringSyntax -- + StringSyntax::StringSyntax(const Lexeme& t) : AtomSyntax(t) { } + + void + StringSyntax::accept(Visitor& v) const { + v.visit(*this); + } + + // -- SymbolSyntax -- + SymbolSyntax::SymbolSyntax(const Lexeme& t, Kind k) + : AtomSyntax(t), sort(k) + { } + + void + SymbolSyntax::accept(Visitor& v) const { + v.visit(*this); + } + + // -- AnchorSyntax -- + AnchorSyntax::AnchorSyntax(size_t t, const Syntax* s) : tag(t), val(s) { } + + void + AnchorSyntax::accept(Visitor& v) const { + v.visit(*this); + } + + // -- ReferenceSyntax -- + ReferenceSyntax::ReferenceSyntax(const Lexeme& t, Ordinal n) + : AtomSyntax(t), pos(n) + { } + + void + ReferenceSyntax::accept(Visitor& v) const { + v.visit(*this); + } + + // -- QuoteSyntax -- + QuoteSyntax::QuoteSyntax(const Syntax* s) + : unary_form(s) + { } + + // -- AntiquoteSyntax -- + AntiquoteSyntax::AntiquoteSyntax(const Syntax* s) + : unary_form(s) + { } + + // -- Expand -- + Expand::Expand(const Syntax* s) : unary_form(s) { } + + // -- Eval -- + Eval::Eval(const Syntax* s) : unary_form(s) { } + + // -- Splice -- + Splice::Splice(const Syntax* s) : unary_form(s) { } + + // -- Function -- + Function::Function(const Syntax* s) : unary_form(s) { } + + // -- Include -- + Include::Include(const Syntax* c, const Syntax* s) + : binary_form(c, s) + { } + + // -- Exclude -- + Exclude::Exclude(const Syntax* c, const Syntax* s) + : binary_form(c, s) + { } + + // -- ListSyntax -- + ListSyntax::ListSyntax() : dot(false) { } + + ListSyntax::ListSyntax(const base& elts, bool d) + : base(elts), dot(d) + { } + + ListSyntax::~ListSyntax() { } + + void + ListSyntax::accept(Visitor& v) const { + v.visit(*this); + } + + // -- VectorSyntax -- + VectorSyntax::VectorSyntax() { } + + VectorSyntax::VectorSyntax(const base& elts) : base(elts) { } + + VectorSyntax::~VectorSyntax() { } + + void + VectorSyntax::accept(Visitor& v) const { + v.visit(*this); + } + + // --------------- + // -- Allocator -- + // --------------- + Allocator::Allocator() { } + + // This destructor is defined here so that it provides + // a single instantiation point for destructors of all + // used templates floating around. + Allocator::~Allocator() { } + + const CharacterSyntax* + Allocator::make_character(const Lexeme& t) { + return chars.make(t); + } + + const IntegerSyntax* + Allocator::make_integer(const Lexeme& t) { + return ints.make(t); + } + + const StringSyntax* + Allocator::make_string(const Lexeme& t) { + return strs.make(t); + } + + const SymbolSyntax* + Allocator::make_symbol(SymbolSyntax::Kind k, const Lexeme& t) { + return syms.make(t, k); + } + + const ReferenceSyntax* + Allocator::make_reference(size_t i, const Lexeme& t) { + return refs.make(t, i); + } + + const AnchorSyntax* + Allocator::make_anchor(size_t t, const Syntax* s) { + return ancs.make(t, s); + } + + const QuoteSyntax* + Allocator::make_quote(const Syntax* s) { + return quotes.make(s); + } + + const AntiquoteSyntax* + Allocator::make_antiquote(const Syntax* s) { + return antis.make(s); + } + + const Expand* + Allocator::make_expand(const Syntax* s) { + return exps.make(s); + } + + const Eval* + Allocator::make_eval(const Syntax* s) { + return evls.make(s); + } + + const Splice* + Allocator::make_splice(const Syntax* s) { + return spls.make(s); + } + + const Function* + Allocator::make_function(const Syntax* s) { + return funs.make(s); + } + + const Include* + Allocator::make_include(const Syntax* c, const Syntax* s) { + return incs.make(c, s); + } + + const Exclude* + Allocator::make_exclude(const Syntax* c, const Syntax* s) { + return excs.make(c, s); + } + + const ListSyntax* + Allocator::make_list(const std::vector& elts, bool dot) { + if (elts.empty()) + return &empty_list; + return lists.make(elts, dot); + } + + const VectorSyntax* + Allocator::make_vector(const std::vector& elts) { + if (elts.empty()) + return &empty_vector; + return vectors.make(elts); + } + + // The sequence of characters in [cur, last) consists + // entirely of digits. Return the corresponding natural value. + static size_t + natural_value(const Byte* cur, const Byte* last) { + size_t n = 0; + for (; cur < last; ++cur) + // FIXME: check for overflow. + n = 10 * n + (*cur - '0'); + return n; + } + + // -- Reader -- + Reader::Reader(const Byte* f, const Byte* l) + : st{ { f, l, 1 }, f, f } + { } + + Reader::Reader(const RawInput& ri) + : st { ri, ri.start, ri.start } + { } + + static const Syntax* read_sexpr(Reader::State&); + + // Parse a string literal + static const Syntax* + read_string(Reader::State& s) { + auto start = s.cur++; + if (not skip_to_quote(s)) + syntax_error("missing closing quote sign for string literal"); + Lexeme t = { { start, s.cur }, s.bytes.lineno }; + return s.alloc.make_string(t); + } + + // Parse an absolute identifier. + static const Syntax* + read_absolute_symbol(Reader::State& s) { + auto start = ++s.cur; + if (not skip_to_nonescaped_char(s, '|')) + syntax_error("missing closing bar sign for an absolute symbol"); + Lexeme t = { { start, s.cur - 1 }, s.bytes.lineno }; + return s.alloc.make_symbol(SymbolSyntax::absolute, t); + } + + // Read an atom starting with digits. + static const Syntax* + read_maybe_natural(Reader::State& s) { + auto start = s.cur; + advance_while (s, isdigit); + if (s.cur >= s.bytes.end or is_delimiter(*s.cur)) { + Lexeme t = { { start, s.cur }, s.bytes.lineno }; + return s.alloc.make_integer(t); + } + advance_while(s, identifier_part); + Lexeme t = { { start, s.cur }, s.bytes.lineno }; + return s.alloc.make_symbol(SymbolSyntax::ordinary, t); + } + + // Read an identifier. + static const Syntax* + read_identifier(Reader::State& s) { + auto start = s.cur; + advance_while(s, identifier_part); + Lexeme t = { { start, s.cur }, s.bytes.lineno }; + return s.alloc.make_symbol(SymbolSyntax::ordinary, t); + } + + // Read an atom starting with a '+' or '-' sign; this + // should be identifier, or a signed integer. + static const Syntax* + read_maybe_signed_number(Reader::State& s) { + auto start = s.cur++; + if (s.cur < s.bytes.end and isdigit(*s.cur)) { + advance_while(s, isdigit); + if (s.cur >= s.bytes.end or is_delimiter(*s.cur)) { + Lexeme t = { { start, s.cur }, s.bytes.lineno }; + return s.alloc.make_integer(t); + } + } + advance_while(s, identifier_part); + Lexeme t = { { start, s.cur }, s.bytes.lineno }; + return s.alloc.make_symbol(SymbolSyntax::ordinary, t); + } + + static const Syntax* + read_keyword(Reader::State& s) { + auto start = s.cur++; + advance_while(s, identifier_part); + Lexeme t = { { start, s.cur }, s.bytes.lineno }; + return s.alloc.make_symbol(SymbolSyntax::keyword, t); + } + + // Read an atom. + static const Syntax* + read_atom(Reader::State& s) { + switch (*s.cur) { + case '"': return read_string(s); + case ':': return read_keyword(s); + case '-': case '+': return read_maybe_signed_number(s); + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return read_maybe_natural(s); + + default: + if (identifier_part(*s.cur)) + return read_identifier(s); + invalid_character(s); + ++s.cur; + return nullptr; + } + } + + // Parse a quote expression. + static const Syntax* + read_quote(Reader::State& s) { + ++s.cur; // skip the quote character + auto x = read_sexpr(s); + if (x == nullptr) + syntax_error("end of input reached after quote sign"); + return s.alloc.make_quote(x); + } + + // Parse a backquote expression. + static const Syntax* + read_backquote(Reader::State& s) { + ++s.cur; // skip the backquote character + auto x = read_sexpr(s); + if (x == nullptr) + syntax_error("end of input reached after backquote sign"); + return s.alloc.make_antiquote(x); + } + + // We've just seen "#(" indicating the start of a literal + // vector. Read the elements and return the corresponding form. + static const Syntax* + finish_literal_vector(Reader::State& s) { + ++s.cur; // Skip the open paren. + std::vector elts { }; + while (skip_blank(s) and *s.cur != ')') { + if (auto x = read_sexpr(s)) + elts.push_back(x); + else + syntax_error("syntax error while reading vector elements"); + } + if (s.cur >= s.bytes.end) + syntax_error("unfinished literal vector"); + else + ++s.cur; + return s.alloc.make_vector(elts); + } + + // We've just seen the sharp sign followed by a digit. We assume + // we are about to read an anchor or a back reference. + static const Syntax* + finish_anchor_or_reference(Reader::State& s) { + auto start = s.cur; + advance_while(s, isdigit); + if (s.cur >= s.bytes.end) + syntax_error("end-of-input after sharp-number sign"); + const Byte c = *s.cur; + if (c != '#' and c != '=') + syntax_error("syntax error after sharp-number-equal sign"); + Lexeme t = { { start, s.cur }, s.bytes.lineno }; + auto n = natural_value(start, s.cur); + ++s.cur; + if (c == '#') + return s.alloc.make_reference(n, t); + auto x = read_sexpr(s); + if (x == nullptr) + syntax_error("syntax error after sharp-number-equal sign"); + return s.alloc.make_anchor(n, x); + } + + static const Syntax* + finish_function(Reader::State& s) { + ++s.cur; // skip quote sign. + auto x = read_sexpr(s); + if (x == nullptr) + syntax_error("missing function designator after sharp-quote sign"); + return s.alloc.make_function(x); + } + + static const Syntax* + finish_uninterned_symbol(Reader::State& s) { + ++s.cur; // skip colon sign. + auto start = s.cur; + advance_while(s, identifier_part); + Lexeme t = { { start, s.cur }, s.bytes.lineno }; + return s.alloc.make_symbol(SymbolSyntax::uninterned, t); + } + + static const Syntax* + finish_readtime_eval(Reader::State& s) { + ++s.cur; // skip dot sign. + auto x = read_sexpr(s); + if (x == nullptr) + syntax_error("parse error after sharp-dot sign"); + return s.alloc.make_eval(x); + } + + static const Syntax* + finish_character(Reader::State& s) { + ++s.cur; // skip backslash sign + auto start = s.cur; + advance_while(s, identifier_part); + Lexeme t = { { start, s.cur }, s.bytes.lineno }; + return s.alloc.make_character(t); + } + + static const Syntax* + finish_include(Reader::State& s) { + ++s.cur; + auto cond = read_sexpr(s); + auto form = read_sexpr(s); + return s.alloc.make_include(cond, form); + } + + static const Syntax* + finish_exclude(Reader::State& s) { + ++s.cur; + auto cond = read_sexpr(s); + auto form = read_sexpr(s); + return s.alloc.make_exclude(cond, form); + } + + static const Syntax* + read_sharp_et_al(Reader::State& s) { + if (++s.cur >= s.bytes.end) + syntax_error("end-of-input reached after sharp sign"); + switch (*s.cur) { + case '(': return finish_literal_vector(s); + case '\'': return finish_function(s); + case ':': return finish_uninterned_symbol(s); + case '.': return finish_readtime_eval(s); + case '\\': return finish_character(s); + case '+': return finish_include(s); + case '-': return finish_exclude(s); + + default: + if (isdigit(*s.cur)) + return finish_anchor_or_reference(s); + syntax_error("syntax error after sharp-sign"); + } + return nullptr; + } + + // We have just seen a dot; read the tail and the closing parenthesis. + static const Syntax* + finish_dotted_list(Reader::State& s, std::vector& elts) { + ++s.cur; // Skip dot sign. + auto x = read_sexpr(s); + if (x == nullptr) + syntax_error("missing expression after dot sign"); + if (not skip_blank(s) or *s.cur != ')') + syntax_error("missing closing parenthesis"); + ++s.cur; + elts.push_back(x); + return s.alloc.make_list(elts, true); + } + + static const Syntax* + read_pair(Reader::State& s) { + ++s.cur; // skip opening parenthesis + std::vector elts { }; + while (skip_blank(s)) + switch (*s.cur) { + case '.': + if (elts.empty()) + syntax_error("missing expression before dot sign."); + return finish_dotted_list(s, elts); + + case ')': + ++s.cur; + return s.alloc.make_list(elts); + + default: + if (auto x = read_sexpr(s)) + elts.push_back(x); + else + syntax_error("unfinished pair expression"); + break; + } + syntax_error("end-of-input while looking for closing parenthesis"); + return nullptr; + } + + static const Syntax* + read_sexpr(Reader::State& s) { + while (skip_blank(s)) + switch (*s.cur) { + case ';': skip_to_eol(s); break; + case '\'': return read_quote(s); + case '`': return read_backquote(s); + case '|': return read_absolute_symbol(s); + case '#': return read_sharp_et_al(s); + case '(': return read_pair(s); + default: return read_atom(s); + } + return nullptr; + } + + const Syntax* + Reader::read() { + return read_sexpr(st); + } + + const Byte* + Reader::position(Ordinal p) { + st.cur = st.bytes.start + p; + st.line = st.cur; + // while (st.line > st.start and st.line[-1] != '\n') + // --st.line; + return st.cur; + } + + } +} diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am index d2f49303..a4963851 100644 --- a/src/utils/Makefile.am +++ b/src/utils/Makefile.am @@ -37,10 +37,9 @@ hammer_LDADD = \ noinst_LIBRARIES = libOpenAxiom.a libOpenAxiom_a_SOURCES = \ - storage.cc string-pool.cc command.cc \ + string-pool.cc command.cc \ filesystem.cc \ ../io/Input.cc \ - ../syntax/sexpr.cc \ ../rt/vm.cc \ ../rt/Lisp.cc \ ../rt/Database.cc diff --git a/src/utils/Makefile.in b/src/utils/Makefile.in index 2e2d184c..7c6d5798 100644 --- a/src/utils/Makefile.in +++ b/src/utils/Makefile.in @@ -145,9 +145,8 @@ am__v_AR_1 = libOpenAxiom_a_AR = $(AR) $(ARFLAGS) libOpenAxiom_a_LIBADD = am__dirstamp = $(am__leading_dot)dirstamp -am_libOpenAxiom_a_OBJECTS = storage.$(OBJEXT) string-pool.$(OBJEXT) \ - command.$(OBJEXT) filesystem.$(OBJEXT) ../io/Input.$(OBJEXT) \ - ../syntax/sexpr.$(OBJEXT) ../rt/vm.$(OBJEXT) \ +am_libOpenAxiom_a_OBJECTS = string-pool.$(OBJEXT) command.$(OBJEXT) \ + filesystem.$(OBJEXT) ../io/Input.$(OBJEXT) ../rt/vm.$(OBJEXT) \ ../rt/Lisp.$(OBJEXT) ../rt/Database.$(OBJEXT) libOpenAxiom_a_OBJECTS = $(am_libOpenAxiom_a_OBJECTS) PROGRAMS = $(noinst_PROGRAMS) @@ -420,10 +419,9 @@ hammer_LDADD = \ noinst_LIBRARIES = libOpenAxiom.a libOpenAxiom_a_SOURCES = \ - storage.cc string-pool.cc command.cc \ + string-pool.cc command.cc \ filesystem.cc \ ../io/Input.cc \ - ../syntax/sexpr.cc \ ../rt/vm.cc \ ../rt/Lisp.cc \ ../rt/Database.cc @@ -483,14 +481,6 @@ clean-noinstLIBRARIES: @: > ../io/$(DEPDIR)/$(am__dirstamp) ../io/Input.$(OBJEXT): ../io/$(am__dirstamp) \ ../io/$(DEPDIR)/$(am__dirstamp) -../syntax/$(am__dirstamp): - @$(MKDIR_P) ../syntax - @: > ../syntax/$(am__dirstamp) -../syntax/$(DEPDIR)/$(am__dirstamp): - @$(MKDIR_P) ../syntax/$(DEPDIR) - @: > ../syntax/$(DEPDIR)/$(am__dirstamp) -../syntax/sexpr.$(OBJEXT): ../syntax/$(am__dirstamp) \ - ../syntax/$(DEPDIR)/$(am__dirstamp) ../rt/$(am__dirstamp): @$(MKDIR_P) ../rt @: > ../rt/$(am__dirstamp) @@ -526,7 +516,6 @@ mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f ../io/*.$(OBJEXT) -rm -f ../rt/*.$(OBJEXT) - -rm -f ../syntax/*.$(OBJEXT) distclean-compile: -rm -f *.tab.c @@ -535,11 +524,9 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@../rt/$(DEPDIR)/Database.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@../rt/$(DEPDIR)/Lisp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@../rt/$(DEPDIR)/vm.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@../syntax/$(DEPDIR)/sexpr.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/command.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filesystem.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hammer.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/storage.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/string-pool.Po@am__quote@ .cc.o: @@ -688,8 +675,6 @@ distclean-generic: -rm -f ../io/$(am__dirstamp) -rm -f ../rt/$(DEPDIR)/$(am__dirstamp) -rm -f ../rt/$(am__dirstamp) - -rm -f ../syntax/$(DEPDIR)/$(am__dirstamp) - -rm -f ../syntax/$(am__dirstamp) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @@ -700,7 +685,7 @@ clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \ clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am - -rm -rf ../io/$(DEPDIR) ../rt/$(DEPDIR) ../syntax/$(DEPDIR) ./$(DEPDIR) + -rm -rf ../io/$(DEPDIR) ../rt/$(DEPDIR) ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags @@ -746,7 +731,7 @@ install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am - -rm -rf ../io/$(DEPDIR) ../rt/$(DEPDIR) ../syntax/$(DEPDIR) ./$(DEPDIR) + -rm -rf ../io/$(DEPDIR) ../rt/$(DEPDIR) ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic diff --git a/src/utils/storage.cc b/src/utils/storage.cc deleted file mode 100644 index 80cfea0d..00000000 --- a/src/utils/storage.cc +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright (C) 2010-2013, Gabriel Dos Reis. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// - Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// - Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in -// the documentation and/or other materials provided with the -// distribution. -// -// - Neither the name of The Numerical Algorithms Group Ltd. nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER -// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// --%: Gabriel Dos Reis. - -#include -#include -#ifdef HAVE_SYS_TYPES_H -# include -#endif -#ifdef HAVE_SYS_STAT_H -# include -#endif -#include -#ifdef HAVE_FCNTL_H -# include -#endif -#ifdef HAVE_UNISTD_H -# include -#endif -#ifdef HAVE_SYS_MMAN_H -# include -#endif -#ifdef OPENAXIOM_MS_WINDOWS_HOST -# include -#endif -#include -#include -#include - -namespace OpenAxiom { - // ---------------- - // -- SystemError -- - // ---------------- - SystemError::SystemError(const std::string& s) : text(s) { } - - SystemError::~SystemError() { } - - const std::string& - SystemError::message() const { - return text; - } - - void - filesystem_error(const std::string& s) { - throw SystemError(s); - } - - namespace Memory { - // Return storage page allocation unit in byte count. - size_t page_size() { -#if defined(OPENAXIOM_MS_WINDOWS_HOST) - SYSTEM_INFO si = { }; - GetSystemInfo(&si); - return si.dwPageSize; -#elif defined(HAVE_UNISTD_H) - return sysconf(_SC_PAGESIZE); -#else - // Well, we have to return a number. - return 4096; -#endif - } - - // Subroutine of os_acquire_raw_memory. Attempt to acquire - // storage from the host OS. Return null on failure. - static inline Pointer - os_allocate_read_write_raw_memory(size_t nbytes) { -#if defined(OPENAXIOM_MS_WINDOWS_HOST) - return VirtualAlloc(Pointer(), nbytes, - MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); -#elif defined(HAVE_SYS_MMAN_H) - Pointer p = mmap(Pointer(), nbytes, PROT_READ | PROT_WRITE, - MAP_PRIVATE | OPENAXIOM_MM_ANONYMOUS_MAP_FLAG, - -1, 0); - return p == MAP_FAILED ? Pointer() : p; -#else - return malloc(byte_count); -#endif - } - - Pointer - os_acquire_raw_memory(size_t nbytes) { - Pointer p = os_allocate_read_write_raw_memory(nbytes); - if (p == nullptr) - throw SystemError("cannot acquire more memory"); - return memset(p, nbytes, 0); - } - - void - os_release_raw_memory(Pointer p, size_t n) { -#if defined(OPENAXIOM_MS_WINDOWS_HOST) - VirtualFree(p, 0, MEM_RELEASE); -#elif defined(HAVE_SYS_MMAN_H) - munmap(p, n); -#else - free(p); -#endif - } - - // ------------- - // -- Storage -- - // ------------- - struct Storage::Handle { - size_t extent; // count of allocated bytes - void* start; // beginning of usable address. - }; - - static inline Pointer - storage_end(Storage::Handle* h) { - return Storage::byte_address(h) + h->extent; - } - - // Acquire storage chunk of at least `n' bytes. - // The result is a pointer to a storage object. That object - // `result' is constructed such that `begin(result)' points - // to the next allocatable address. - template - static T* - acquire_storage_with_header(size_t n) { - n = Storage::round_up(n, page_size()); - T* h = static_cast(os_acquire_raw_memory(n)); - h->extent = n; - h->start = h + 1; - return h; - } - - void - Storage::release(Handle* h) { - os_release_raw_memory(h, h->extent); - } - - Pointer - Storage::begin(Handle* h) { - return h->start; - } - - // ------------------------- - // -- SinglyLinkedStorage -- - // ------------------------- - struct OneWayLinkHeader : Storage::Handle { - Handle* previous; - }; - - SinglyLinkedStorage::Handle*& - SinglyLinkedStorage::previous(Handle* h) { - return static_cast(h)->previous; - } - - // ------------------------- - // -- DoublyLinkedStorage -- - // ------------------------- - struct TwoWayLinkHeader : Storage::Handle { - Handle* previous; - Handle* next; - }; - - static inline TwoWayLinkHeader* - two_way_link(Storage::Handle* h) { - return static_cast(h); - } - - DoublyLinkedStorage::Handle*& - DoublyLinkedStorage::previous(Handle* h) { - return two_way_link(h)->previous; - } - - DoublyLinkedStorage::Handle*& - DoublyLinkedStorage::next(Handle* h) { - return two_way_link(h)->next; - } - - DoublyLinkedStorage::Handle* - DoublyLinkedStorage::acquire(size_t n, size_t a) { - // Add enough padding space for specified alignment. - const size_t overhead = round_up(sizeof (TwoWayLinkHeader), a); - TwoWayLinkHeader* h = - acquire_storage_with_header(overhead + n); - h->start = byte_address (h) + overhead; - h->previous = nullptr; - h->next = nullptr; - return h; - } - - // ------------------ - // -- BlockStorage -- - // ------------------ - struct BlockHeader : OneWayLinkHeader { - Byte* available; - }; - - static inline BlockHeader* - block_header(BlockStorage::Handle* h) { - return static_cast(h); - } - - BlockStorage::Handle* - BlockStorage::acquire(size_t n, size_t a) { - const size_t overhead = round_up(sizeof (BlockHeader), a); - BlockHeader* h = - acquire_storage_with_header(overhead + n); - // Remember the next available address to allocate from. - h->available = byte_address(h) + overhead; - // That is also where the actual object storage starts. - h->start = h->available; - h->previous = nullptr; - return h; - } - - Pointer - BlockStorage::next_address(Handle* h) { - return block_header(h)->available; - } - - size_t - BlockStorage::room(Handle* h) { - return byte_address(storage_end(h)) - block_header(h)->available; - } - - Pointer - BlockStorage::book(Handle* h, size_t n) { - BlockHeader* block = block_header(h); - void* const p = block->available; - block->available += n; - return p; - } - - - // ----------------- - // -- FileMapping -- - // ----------------- - FileMapping::FileMapping(std::string path) - : start(), extent() { -#if defined(OPENAXIOM_MS_WINDOWS_HOST) - HANDLE file = CreateFile(path.c_str(), GENERIC_READ, 0, 0, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, 0); - if (file == INVALID_HANDLE_VALUE) - filesystem_error("could not access file " + path); - HANDLE mapping = CreateFileMapping(file, 0, PAGE_READONLY, 0, 0, 0); - if (mapping == 0) - filesystem_error("could not map file " + path); - start = static_cast - (MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0)); - extent = GetFileSize(file, 0); - CloseHandle(mapping); - CloseHandle(file); -#elif defined(HAVE_SYS_STAT_H) && defined(HAVE_SYS_MMAN_H) && defined(HAVE_FCNTL_H) - struct stat s; - errno = 0; - if (stat(path.c_str(), &s) < 0) - filesystem_error("could not access file " + path); - else if (!S_ISREG(s.st_mode)) - filesystem_error(path + " is not a regular file"); - int fd = open(path.c_str(), O_RDONLY); - if (fd < 0) - filesystem_error("could not open " + path); - start = static_cast - (mmap(Pointer(), s.st_size, PROT_READ, MAP_PRIVATE, fd, 0)); - close(fd); - if (start == MAP_FAILED) - filesystem_error("could not map file " + path); - extent = s.st_size; -#else -# error "Don't know how to map a file on this platform" -#endif // OPENAXIOM_MS_WINDOWS_HOST - } - - FileMapping::FileMapping(FileMapping&& f) - : start(f.start), extent(f.extent) { - f.start = nullptr; - f.extent = 0; - } - - FileMapping::~FileMapping() { - if (start != nullptr) { -#if defined(OPENAXIOM_MS_WINDOWS_HOST) - UnmapViewOfFile(start); -#elif defined(HAVE_SYS_MMAN_H) - munmap(start, extent); -#else -# error "Don't know how to unmap a file on this platform" -#endif - } - } - } -} diff --git a/src/utils/storage.cxx b/src/utils/storage.cxx new file mode 100644 index 00000000..80cfea0d --- /dev/null +++ b/src/utils/storage.cxx @@ -0,0 +1,314 @@ +// Copyright (C) 2010-2013, Gabriel Dos Reis. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// - Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in +// the documentation and/or other materials provided with the +// distribution. +// +// - Neither the name of The Numerical Algorithms Group Ltd. nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// --%: Gabriel Dos Reis. + +#include +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#include +#ifdef HAVE_FCNTL_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_SYS_MMAN_H +# include +#endif +#ifdef OPENAXIOM_MS_WINDOWS_HOST +# include +#endif +#include +#include +#include + +namespace OpenAxiom { + // ---------------- + // -- SystemError -- + // ---------------- + SystemError::SystemError(const std::string& s) : text(s) { } + + SystemError::~SystemError() { } + + const std::string& + SystemError::message() const { + return text; + } + + void + filesystem_error(const std::string& s) { + throw SystemError(s); + } + + namespace Memory { + // Return storage page allocation unit in byte count. + size_t page_size() { +#if defined(OPENAXIOM_MS_WINDOWS_HOST) + SYSTEM_INFO si = { }; + GetSystemInfo(&si); + return si.dwPageSize; +#elif defined(HAVE_UNISTD_H) + return sysconf(_SC_PAGESIZE); +#else + // Well, we have to return a number. + return 4096; +#endif + } + + // Subroutine of os_acquire_raw_memory. Attempt to acquire + // storage from the host OS. Return null on failure. + static inline Pointer + os_allocate_read_write_raw_memory(size_t nbytes) { +#if defined(OPENAXIOM_MS_WINDOWS_HOST) + return VirtualAlloc(Pointer(), nbytes, + MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); +#elif defined(HAVE_SYS_MMAN_H) + Pointer p = mmap(Pointer(), nbytes, PROT_READ | PROT_WRITE, + MAP_PRIVATE | OPENAXIOM_MM_ANONYMOUS_MAP_FLAG, + -1, 0); + return p == MAP_FAILED ? Pointer() : p; +#else + return malloc(byte_count); +#endif + } + + Pointer + os_acquire_raw_memory(size_t nbytes) { + Pointer p = os_allocate_read_write_raw_memory(nbytes); + if (p == nullptr) + throw SystemError("cannot acquire more memory"); + return memset(p, nbytes, 0); + } + + void + os_release_raw_memory(Pointer p, size_t n) { +#if defined(OPENAXIOM_MS_WINDOWS_HOST) + VirtualFree(p, 0, MEM_RELEASE); +#elif defined(HAVE_SYS_MMAN_H) + munmap(p, n); +#else + free(p); +#endif + } + + // ------------- + // -- Storage -- + // ------------- + struct Storage::Handle { + size_t extent; // count of allocated bytes + void* start; // beginning of usable address. + }; + + static inline Pointer + storage_end(Storage::Handle* h) { + return Storage::byte_address(h) + h->extent; + } + + // Acquire storage chunk of at least `n' bytes. + // The result is a pointer to a storage object. That object + // `result' is constructed such that `begin(result)' points + // to the next allocatable address. + template + static T* + acquire_storage_with_header(size_t n) { + n = Storage::round_up(n, page_size()); + T* h = static_cast(os_acquire_raw_memory(n)); + h->extent = n; + h->start = h + 1; + return h; + } + + void + Storage::release(Handle* h) { + os_release_raw_memory(h, h->extent); + } + + Pointer + Storage::begin(Handle* h) { + return h->start; + } + + // ------------------------- + // -- SinglyLinkedStorage -- + // ------------------------- + struct OneWayLinkHeader : Storage::Handle { + Handle* previous; + }; + + SinglyLinkedStorage::Handle*& + SinglyLinkedStorage::previous(Handle* h) { + return static_cast(h)->previous; + } + + // ------------------------- + // -- DoublyLinkedStorage -- + // ------------------------- + struct TwoWayLinkHeader : Storage::Handle { + Handle* previous; + Handle* next; + }; + + static inline TwoWayLinkHeader* + two_way_link(Storage::Handle* h) { + return static_cast(h); + } + + DoublyLinkedStorage::Handle*& + DoublyLinkedStorage::previous(Handle* h) { + return two_way_link(h)->previous; + } + + DoublyLinkedStorage::Handle*& + DoublyLinkedStorage::next(Handle* h) { + return two_way_link(h)->next; + } + + DoublyLinkedStorage::Handle* + DoublyLinkedStorage::acquire(size_t n, size_t a) { + // Add enough padding space for specified alignment. + const size_t overhead = round_up(sizeof (TwoWayLinkHeader), a); + TwoWayLinkHeader* h = + acquire_storage_with_header(overhead + n); + h->start = byte_address (h) + overhead; + h->previous = nullptr; + h->next = nullptr; + return h; + } + + // ------------------ + // -- BlockStorage -- + // ------------------ + struct BlockHeader : OneWayLinkHeader { + Byte* available; + }; + + static inline BlockHeader* + block_header(BlockStorage::Handle* h) { + return static_cast(h); + } + + BlockStorage::Handle* + BlockStorage::acquire(size_t n, size_t a) { + const size_t overhead = round_up(sizeof (BlockHeader), a); + BlockHeader* h = + acquire_storage_with_header(overhead + n); + // Remember the next available address to allocate from. + h->available = byte_address(h) + overhead; + // That is also where the actual object storage starts. + h->start = h->available; + h->previous = nullptr; + return h; + } + + Pointer + BlockStorage::next_address(Handle* h) { + return block_header(h)->available; + } + + size_t + BlockStorage::room(Handle* h) { + return byte_address(storage_end(h)) - block_header(h)->available; + } + + Pointer + BlockStorage::book(Handle* h, size_t n) { + BlockHeader* block = block_header(h); + void* const p = block->available; + block->available += n; + return p; + } + + + // ----------------- + // -- FileMapping -- + // ----------------- + FileMapping::FileMapping(std::string path) + : start(), extent() { +#if defined(OPENAXIOM_MS_WINDOWS_HOST) + HANDLE file = CreateFile(path.c_str(), GENERIC_READ, 0, 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, 0); + if (file == INVALID_HANDLE_VALUE) + filesystem_error("could not access file " + path); + HANDLE mapping = CreateFileMapping(file, 0, PAGE_READONLY, 0, 0, 0); + if (mapping == 0) + filesystem_error("could not map file " + path); + start = static_cast + (MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0)); + extent = GetFileSize(file, 0); + CloseHandle(mapping); + CloseHandle(file); +#elif defined(HAVE_SYS_STAT_H) && defined(HAVE_SYS_MMAN_H) && defined(HAVE_FCNTL_H) + struct stat s; + errno = 0; + if (stat(path.c_str(), &s) < 0) + filesystem_error("could not access file " + path); + else if (!S_ISREG(s.st_mode)) + filesystem_error(path + " is not a regular file"); + int fd = open(path.c_str(), O_RDONLY); + if (fd < 0) + filesystem_error("could not open " + path); + start = static_cast + (mmap(Pointer(), s.st_size, PROT_READ, MAP_PRIVATE, fd, 0)); + close(fd); + if (start == MAP_FAILED) + filesystem_error("could not map file " + path); + extent = s.st_size; +#else +# error "Don't know how to map a file on this platform" +#endif // OPENAXIOM_MS_WINDOWS_HOST + } + + FileMapping::FileMapping(FileMapping&& f) + : start(f.start), extent(f.extent) { + f.start = nullptr; + f.extent = 0; + } + + FileMapping::~FileMapping() { + if (start != nullptr) { +#if defined(OPENAXIOM_MS_WINDOWS_HOST) + UnmapViewOfFile(start); +#elif defined(HAVE_SYS_MMAN_H) + munmap(start, extent); +#else +# error "Don't know how to unmap a file on this platform" +#endif + } + } + } +} -- cgit v1.2.3