summaryrefslogtreecommitdiffstats
path: root/src/boost/libs/flyweight/example/composite.cpp
blob: 1395f412d2614dcb5cbc9b86ff78eb1a59b71794 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/* Boost.Flyweight example of a composite design.
 *
 * Copyright 2006-2014 Joaquin M Lopez Munoz.
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 *
 * See http://www.boost.org/libs/flyweight for library home page.
 */

#include <boost/flyweight.hpp>
#include <boost/functional/hash.hpp>
#include <boost/tokenizer.hpp>
#include <boost/variant.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/recursive_wrapper.hpp>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>

using namespace boost::flyweights;

/* A node of a lisp-like list can be modeled as a boost::variant of
 *   1. A string (primitive node)
 *   2. A vector of nodes (embedded list)
 * To save space, 2 is stored as a vector of flyweights.
 * As is usual with recursive data structures, a node can be thought
 * of also as a list. To close the flyweight circle, the final
 * type list is a flyweight wrapper, so that the final structure can
 * be described as follows in BNF-like style:
 *
 *   list      ::= flyweight<list_impl>
 *   list_impl ::= std::string | std::vector<list>
 */

struct list_elems;

typedef boost::variant<
  std::string,
  boost::recursive_wrapper<list_elems>  
> list_impl;

struct list_elems:std::vector<flyweight<list_impl> >{};

typedef flyweight<list_impl> list;

/* list_impl must be hashable to be used by flyweight: If a
 * node is a std::string, its hash resolves to that of the string;
 * if it is a vector of flyweight nodes, we combine their corresponding
 * hash values. As hashing a flyweight does not involve actually hashing
 * its pointed-to value, the resulting computation does not recursively
 * descend down the entire data structure.
 */

struct list_hasher:boost::static_visitor<std::size_t>
{
  std::size_t operator()(const std::string& str)const
  {
    boost::hash<std::string> h;
    return h(str);
  }

  std::size_t operator()(const list_elems& elms)const
  {
    std::size_t res=0;
    for(list_elems::const_iterator it=elms.begin(),it_end=elms.end();
        it!=it_end;++it){
      boost::hash_combine(res,*it);
    }
    return res;
  }
};

#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP)
namespace boost{
#endif

std::size_t hash_value(const list_impl& limpl)
{
  return boost::apply_visitor(list_hasher(),limpl);
}

#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP)
} /* namespace boost */
#endif

/* basic pretty printer with indentation according to the nesting level */

struct list_pretty_printer:boost::static_visitor<>
{
  list_pretty_printer():nest(0){}

  void operator()(const std::string& str)
  {
    indent();
    std::cout<<str<<"\n";
  }

  void operator()(const boost::recursive_wrapper<list_elems>& elmsw)
  {
    indent();
    std::cout<<"(\n";
    ++nest;
    const list_elems& elms=elmsw.get();
    for(list_elems::const_iterator it=elms.begin(),it_end=elms.end();
        it!=it_end;++it){
      boost::apply_visitor(*this,it->get());
    }
    --nest;
    indent();
    std::cout<<")\n";
  }

private:
  void indent()const
  {
    for(int i=nest;i--;)std::cout<<"  ";
  }

  int nest;
};

void pretty_print(const list& l)
{
  list_pretty_printer pp;
  boost::apply_visitor(pp,l.get());
}

/* list parser */

template<typename InputIterator>
list parse_list(InputIterator& first,InputIterator last,int nest)
{
  list_elems elms;
  while(first!=last){
    std::string str=*first++;
    if(str=="("){
      elms.push_back(parse_list(first,last,nest+1));   
    }
    else if(str==")"){
      if(nest==0)throw std::runtime_error("unmatched )");
      return list(elms);
    }
    else{
      elms.push_back(list(str)); 
    }
  }
  if(nest!=0)throw std::runtime_error("unmatched (");
  return list(elms);
}

list parse_list(const std::string str)
{
  typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
  tokenizer tok(str,boost::char_separator<char>(" ","()"));
  tokenizer::iterator begin=tok.begin();
  return parse_list(begin,tok.end(),0);
}

int main()
{
  std::cout<<"enter list: ";
  std::string str;
  std::getline(std::cin,str);
  try{
    pretty_print(parse_list(str));
  }
  catch(const std::exception& e){
    std::cout<<"error: "<<e.what()<<"\n";
  }

  return 0;
}