C++でSchemeインタプリタを作ろう

少し作った。

(define (factorial n) (if (= n 1) 1 (* n (factorial (- n 1)))))
; ok
(factorial 10)
; 3628800

おー。Accelerated C++ と Effective C++ という本を読んだんだけど書き方がわからないのでひげぽんのコードを参考に勉強しようとしたんだけど日記にコードは殆ど載ってなくてつかえねーと思いつつ書いた。そういう訳であんまり C++ の勉強にはなってないような。ポインタとか参照とかリソース管理とかがさっぱりわからない。メモリが駄々漏れ。

以下ソースコード

main

#include <iostream>
#include <scheme.h>

using namespace std;
using namespace scheme;

int main() {
  Environment* env = createGlobalEnvironment();
  string str, line;
  while (getline(cin, line)) {
    str += line;
    if(Expression* exp = parse(str)) {
      cout << "; " << exp->eval(env)->string() << endl;
      str = "";
    }
  }

  return 0;
}

scheme.h

#ifndef SCHEME_H_INCLUDED
#define SCHEME_H_INCLUDED
#include <map>
#include <vector>
#include <boost/function.hpp>
#include <boost/lambda/lambda.hpp>

namespace scheme {

  class Expression;
  class Variable;
  typedef std::vector<Expression*> Expressions;


  // Parse
  Expression* parse(const std::string& str);


  // Environment - 変数テーブルをもってる
  class Environment {
  public:
    Environment(Environment* env = 0): base(env) {};
    Expression* variableValue(Variable* var);
    void defineVariable(Variable* var, Expression* value);
    Environment* extend(Expressions* vars, Expressions* vals);
  private:
    std::map<std::string, Expression*> table;
    Environment* base;
  };
  Environment* createGlobalEnvironment();


  // Expressions
  Expressions* evalAll(Expressions const* exprs, Environment* env);
  std::string toString(Expressions* exprs);

  // Expression
  class Expression {
  public:
    virtual ~Expression() {};
    virtual Expression* eval(Environment* env) = 0;
    virtual std::string string() const = 0;
    virtual bool boolValue() const { return false; }
  };

  class Number: public Expression {
  public:
    Number(int n): num(n) {};
    virtual Expression* eval(Environment* env);
    virtual std::string string() const;
    virtual bool boolValue() const;
    int intValue() const;
  private:
    int num;
  };

  Number* True = new Number(1);
  Number* False = new Number(0);

  class String: public Expression {
  public:
    String(const std::string& s): str(s) {};
    virtual Expression* eval(Environment* env);
    virtual std::string string() const;
  private:
    std::string str;
  };

  class Variable: public Expression {
  public:
    Variable(const std::string& str): name_(str) {}
    virtual Expression* eval(Environment* env);
    virtual std::string string() const;
    const std::string name() const;
  private:
    std::string name_;
  };

  class If: public Expression {
  public:
    If(Expression* p, Expression* c, Expression* a)
      : predicate(p), consequent(c), alternative(a) {}
    virtual Expression* eval(Environment* env);
    virtual std::string string() const;
  private:
    Expression* predicate;
    Expression* consequent;
    Expression* alternative;
  };

  class Sequence: public Expression {
  public:
    Sequence(Expressions* exprs): expressions(exprs) {};
    virtual Expression* eval(Environment* env);
    virtual std::string string() const;
  private:
    Expressions* expressions;
  };

  class Define: public Expression {
  public:
    Define(Variable* var, Expression* expr): variable(var), value(expr) {}
    virtual Expression* eval(Environment* env);
    virtual std::string string() const;
  private:
    Variable* variable;
    Expression* value;
  };

  class Lambda: public Expression {
  public:
    Lambda(Expressions* exprs, Expression* b): parameters(exprs), body(b) {}
    virtual Expression* eval(Environment* env);
    virtual std::string string() const;
  private:
    Expressions* parameters;
    Expression* body;
  };

  class Procedure: public Expression {
  public:
    Procedure(Expressions* params, Expression* expr, Environment* env)
      : parameters(params), body(expr), environment(env) {}
    virtual Expression* eval(Environment* env);
    virtual Expression* apply(Expressions* arguments);
    virtual std::string string() const;
  protected:
    Expressions* parameters;
    Expression* body;
    Environment* environment;
  };

  class Operation: public Expression {
  public:
    Operation(Expression* opr, Expressions* opd)
      : op(opr), operands(opd) {}
    virtual Expression* eval(Environment* env);
    virtual std::string string() const;
  protected:
    Expression* op;
    Expressions* operands;
  };

  class PrimitiveProcedure: public Procedure {
  public:
    PrimitiveProcedure(const std::string& str, boost::function<int (int, int)> f)
      : Procedure(0, 0, 0), op(str), applyFunction(f) {}
    virtual Expression* apply(Expressions* arguments);
    virtual std::string string() const;
  private:
    const std::string op;
    boost::function<int (int, int)> applyFunction;
  };

  using namespace boost::lambda;
  PrimitiveProcedure* Plus = new PrimitiveProcedure("+", _1 + _2);
  PrimitiveProcedure* Minus = new PrimitiveProcedure("-", _1 - _2);
  PrimitiveProcedure* Multiplies = new PrimitiveProcedure("*", _1 * _2);
  PrimitiveProcedure* Equals = new PrimitiveProcedure("=", _1 == _2);

}

#endif // SCHEME_H_INCLUDED

scheme.cpp

#include <scheme.h>
#include <boost/lexical_cast.hpp>

namespace scheme {
  using namespace std;

  // Environment
  Expression* Environment::variableValue(Variable* var) {
    if(Expression* result = table[var->name()])
      return result;
    if(base)
      return base->variableValue(var);
    throw "Unbound variable";
  }

  void Environment::defineVariable(Variable* var, Expression* value) {
    table[var->name()] = value;
  }

  Environment* Environment::extend(Expressions* vars, Expressions* vals) {
    if(vars->size() != vals->size())
      throw "Invalid arguments";
    Environment* child = new Environment(this);
    for (size_t i = 0; i < vars->size(); i++)
      child->defineVariable(dynamic_cast<Variable*>((*vars)[i]), (*vals)[i]);
    return child;
  }

  Environment* createGlobalEnvironment() {
    Environment* env = new Environment();
    env->defineVariable(new Variable("+"), Plus);
    env->defineVariable(new Variable("-"), Minus);
    env->defineVariable(new Variable("*"), Multiplies);
    env->defineVariable(new Variable("="), Equals);
    return env;
  }


  // Expressions
  Expressions* evalAll(Expressions const* exprs, Environment* env) {
    Expressions* evaled = new Expressions();
    Expressions::const_iterator from(exprs->begin()), end(exprs->end());
    while (from != end)
      evaled->push_back((*from++)->eval(env));
    return evaled;
  }
  string toString(Expressions* exprs) {
    string str;
    Expressions::const_iterator iter(exprs->begin()), end(exprs->end());
    while (iter != end) {
      str += (*iter++)->string();
      if(iter != end)
        str += " ";
    }
    return str;
  }


  // Expression
  Expression* Number::eval(Environment* env) {
    return this;
  }
  string Number::string() const {
    return boost::lexical_cast<std::string>(num);
  }
  bool Number::boolValue() const {
    return num;
  }
  int Number::intValue() const {
    return num;
  }


  Expression* String::eval(Environment* env) {
    return this;
  }
  string String::string() const {
    return str;
  }


  Expression* Variable::eval(Environment* env) {
    return env->variableValue(this);
  }
  string Variable::string() const {
    return name_;
  }
  const std::string Variable::name() const {
    return name_;
  }


  Expression* If::eval(Environment* env) {
    return predicate->eval(env)->boolValue() ? consequent->eval(env) : alternative->eval(env);
  }
  string If::string() const {
    return "(if " + predicate->string() + " " + consequent->string() + " " + alternative->string() + ")";
  }


  Expression* Sequence::eval(Environment* env) {
    Expressions::const_iterator iter(expressions->begin()), end(expressions->end());
    while (iter != end) {
      Expression* e = (*iter++)->eval(env);
      if(iter == end) return e;
    }
    throw "empty sequence";
  }
  string Sequence::string() const {
    return "(begin " + toString(expressions) + ")";
  }


  Expression* Procedure::eval(Environment* env) {
    return this;
  }
  Expression* Procedure::apply(Expressions* arguments) {
    return body->eval(environment->extend(parameters, arguments));
  }
  string Procedure::string() const {
    return "<#proc (lambda (" + toString(parameters) + ") " + body->string() + ")>";
  }

  Expression* PrimitiveProcedure::apply(Expressions* arguments) {
    Expressions::const_iterator begin(arguments->begin());
    int first = dynamic_cast<Number*>(*(arguments->begin()))->intValue();
    int second = dynamic_cast<Number*>(*(++(arguments->begin())))->intValue();
    return new Number(applyFunction(first, second));
  }
  string PrimitiveProcedure::string() const {
    return "<#proc (" + op + " x y)>";
  }

  Expression* Operation::eval(Environment* env) {
    return dynamic_cast<Procedure*>(op->eval(env))->apply(evalAll(operands, env));
  }
  string Operation::string() const {
    return "(" + op->string() + " " + toString(operands) + ")";
  }


  Expression* Lambda::eval(Environment* env) {
    return new Procedure(parameters, body, env);
  }
  string Lambda::string() const {
    return "(lambda (" + toString(parameters) + ") " + body->string() + ")";
  }

  Expression* Define::eval(Environment* env) {
    env->defineVariable(variable, value->eval(env));
    return new String("ok");
  }
  string Define::string() const {
    return "(define " + variable->string() + " " + value->string() + ")";
  }

}

parser

#include <scheme.h>
#include <stack>
#include <boost/spirit/core.hpp>

namespace scheme {
  using namespace std;
  using namespace boost::spirit;

  stack<Expression*> expressions;
  stack<Expressions*> sequences;

  // stack operations
  void clear() {
    expressions = stack<Expression*>();
    sequences = stack<Expressions*>();
  }
  void push(Expression* exp) {
    expressions.push(exp);
  }
  Expression* pop() {
    Expression* expr = expressions.top();
    expressions.pop();
    return expr;
  }
  void pushToSequence(Expression* exp) {
    sequences.top()->push_back(exp);
  }
  void pushEmptySequence() {
    sequences.push(new Expressions());
  }
  Expressions* popSequence() {
    Expressions* params = sequences.top();
    sequences.pop();
    return params;
  }

  // callbacks
  void pushNumber(int n) {
    push(new Number(n));
  }
  void pushSymbol(char const* first, char const* last) {
    push(new Variable(string(first, last)));
  }
  void pushBegin(char const* first, char const* last) {
    push(new Sequence(popSequence()));
  }
  void pushCond(char const* first, char const* last) {
    Expression* alternative = pop();
    Expression* consequent = pop();
    Expression* predicate = pop();
    push(new If(predicate, consequent, alternative));
  }
  void pushEmptySequence_(char const* first, char const* last) {
    pushEmptySequence();
  }
  void moveToSequence(char const* first, char const* last) {
    pushToSequence(pop());
  }
  void pushLambda(char const* first, char const* last) {
    push(new Lambda(popSequence(), pop()));
  }
  void pushDefine(char const* first, char const* last) {
    Expression* value = pop();
    Variable* variable = dynamic_cast<Variable*>(pop());
    push(new Define(variable, value));
  }
  void pushDefineLambda(char const* first, char const* last) {
    Expression* body = pop();
    Variable* variable = dynamic_cast<Variable*>(pop());
    push(new Define(variable, new Lambda(popSequence(), body)));
  }
  void pushCall(char const* first, char const* last) {
    push(new Operation(pop(), popSequence()));
  }


  struct SchemeParser: public grammar<SchemeParser> {
    template<typename S> struct definition {
      rule<S> s, num, symbol;
      rule<S> symbol_sequence, sequence;
      rule<S> begin, cond, lambda, define, define_lambda;
      rule<S> call, expr;

      definition(const SchemeParser& self) {
        s = +space_p;
        num = int_p[&pushNumber];
        symbol = (+(graph_p - '(' - ')' - digit_p))[&pushSymbol];

        symbol_sequence = epsilon_p[&pushEmptySequence_] >>
          !(symbol[&moveToSequence] >> *(s >> symbol[&moveToSequence]));
        sequence = epsilon_p[&pushEmptySequence_] >>
          !(expr[&moveToSequence] >> *(s >> expr[&moveToSequence]));

        begin = "(begin" >> s >> sequence >> ')';
        cond = "(if" >> s >> expr >> s >> expr >> s >> expr >> ')';

        lambda = "(lambda" >> s >> '(' >> symbol_sequence >> ')' >> s >> expr >> ')';

        define = "(define" >> s >> symbol >> s >> expr >> ')';
        define_lambda = "(define" >> s >> '(' >> symbol >> s >> symbol_sequence >> ')' >> s >> expr >> ')';

        call = '(' >> expr[&pushEmptySequence_] >> *(s >> expr[&moveToSequence]) >> ')';

        expr =
          num | symbol |
          begin[&pushBegin] | cond[&pushCond] | lambda[&pushLambda] |
          define[&pushDefine] | define_lambda[&pushDefineLambda] |
          call[&pushCall];
      }

      const rule<S>& start() const { return expr; }
    };
  };


  Expression* parse(const string& str) {
    clear();
    SchemeParser scmp;
    return parse(str.c_str(), scmp).full ? pop() : 0;
  }
}