Browse Source

impl lispoo

Signed-off-by: Rain <rain.by.zhou@gmail.com>
main
Rain 4 years ago
parent
commit
c6446f9247
  1. 4
      .gitignore
  2. 20
      README.md
  3. 3
      build.sh
  4. 73
      core.h
  5. 6
      example/lambda.lisp
  6. 6
      example/prog.lisp
  7. 8
      example/quote.lisp
  8. 6
      example/sum.lisp
  9. 25
      lispoo.cpp
  10. 308
      lispoo.h

4
.gitignore

@ -1,6 +1,10 @@
# Prerequisites
*.d
# lispoo
lispoo
# Compiled Object files
*.slo
*.lo

20
README.md

@ -1,2 +1,22 @@
# lispoo
Code Oops Lisp Interpreter
# build
```sh
$ ./build.sh
```
# example
```sh
$ ./lispoo example/lambda.lisp
(6)(18)
$ ./lispoo example/prog.lisp
(3)(2)(1)(2)(1)
$ ./lispoo example/quote.lisp
("false")((+ 1 2))
```

3
build.sh

@ -0,0 +1,3 @@
#!/bin/bash
g++ -std=c++17 -I. lispoo.cpp -o lispoo

73
core.h

@ -0,0 +1,73 @@
#pragma once
#include <lispoo.h>
namespace lispoo {
inline std::shared_ptr<Expr> sum(const std::shared_ptr<List>& args) {
len_eq(args, 2);
auto a = args->value()[0], b = args->value()[1];
if (a->type() == Type::Float
&& b->type() == Type::Float) {
return std::make_shared<Float>(std::static_pointer_cast<Float>(a)->value() +
std::static_pointer_cast<Float>(b)->value());
}
if (a->type() == Type::Integer
&& b->type() == Type::Integer) {
return std::make_shared<Integer>(std::static_pointer_cast<Integer>(a)->value() +
std::static_pointer_cast<Integer>(b)->value());
}
if (a->type() == Type::Float) {
return std::make_shared<Float>(std::static_pointer_cast<Float>(a)->value() +
std::static_pointer_cast<Integer>(b)->value());
}
if (b->type() == Type::Float) {
return std::make_shared<Float>(std::static_pointer_cast<Integer>(a)->value() +
std::static_pointer_cast<Float>(b)->value());
}
}
inline std::shared_ptr<Expr> message(const std::shared_ptr<Expr>& expr) {
if (!expr) {
return null_expr;
}
auto type = expr->type();
switch (type) {
case Type::Null: {
std::cout << "nil";
break;
}
case Type::Integer: {
std::cout << std::static_pointer_cast<Integer>(expr)->value();
break;
}
case Type::Float: {
std::cout << std::static_pointer_cast<Float>(expr)->value();
break;
}
case Type::Symbol: {
std::cout << std::static_pointer_cast<Symbol>(expr)->value();
break;
}
case Type::Callable: {
std::cout << "<function>: " << expr.get();
break;
}
case Type::List: {
auto& value = std::static_pointer_cast<List>(expr)->value();
std::cout << "(";
for (auto i = 0; i < value.size(); ++i) {
if (i > 0) {
std::cout << " ";
}
message(value[i]);
}
std::cout << ")";
}
default:
break;
}
return null_expr;
}
} // namespace lispoo

6
example/lambda.lisp

@ -0,0 +1,6 @@
(prog
(def abc (lambda (a b c) (+ a (+ b c))))
(set! x (abc 1 2 3))
(message x)
(prog (+ 1.2 3.9) (def plus (lambda (a b) (+ a b))) (message (plus 9 9)))
)

6
example/prog.lisp

@ -0,0 +1,6 @@
(prog
(set! i 3)
(while i (prog (message i) (set! i (+ i -1))))
(prog (set! x 0) (message (if x 1 2)))
(prog (set! x 1) (message x))
)

8
example/quote.lisp

@ -0,0 +1,8 @@
(prog
(def test (lambda (x) x))
(if (test (quote (+ 1 -1)))
(message (quote "true"))
(message (quote "false"))
)
(message (quote (+ 1 2)))
)

6
example/sum.lisp

@ -0,0 +1,6 @@
(prog
(message (+
(+ 2 1.1)
1))
(message (+ 1 2) (+ 1.1 1.1) (+ 2 1.1) (+ 1.9 -1))
)

25
lispoo.cpp

@ -0,0 +1,25 @@
#include <lispoo.h>
#include <core.h>
#include <sstream>
#include <fstream>
int main(int argc, char* argv[]) {
if (argc != 2) {
lispoo::oops("lispoo ./xxx.lisp");
}
std::ifstream ifs(argv[1]);
std::stringstream ss;
if (!ifs.is_open()) {
lispoo::oops("can't open: " + std::string(argv[1]));
}
ss << ifs.rdbuf();
std::vector<std::string> tokens;
lispoo::tokenize(ss.str(), tokens);
unsigned long cursor = 0;
auto expr = lispoo::parse(tokens, cursor);
lispoo::register_symbol("+", [](auto args) { return lispoo::sum(args); });
lispoo::register_symbol("message", [](auto args) { return lispoo::message(args); });
lispoo::eval(expr, lispoo::global);
return 0;
}

308
lispoo.h

@ -0,0 +1,308 @@
#pragma once
#include <iostream>
#include <exception>
#include <string>
#include <vector>
#include <memory>
#include <functional>
#include <unordered_map>
namespace lispoo {
inline void oops(const std::string& err) {
std::cerr << err << std::endl;
exit(-1);
}
enum class Type {
Null = 0,
Float,
Integer,
Symbol,
Atom,
List,
Callable,
};
class Expr {
public:
virtual Type type() = 0;
};
template <typename T>
class Atom: public Expr {
public:
explicit Atom(const T& value) : value_(value) {}
Type type() override { return Type::Atom; }
const T& value() const { return value_; }
private:
T value_;
};
class Symbol: public Atom<std::string> {
public:
explicit Symbol(const std::string& value) : Atom(value) {}
Type type() override { return Type::Symbol; }
};
class Integer: public Atom<long> {
public:
explicit Integer(const long& value) : Atom(value) {}
Type type() override { return Type::Integer; }
};
class Float: public Atom<double> {
public:
explicit Float(const double& value) : Atom(value) {}
Type type() override { return Type::Float; }
};
class Null: public Expr {
public:
Type type() override { return Type::Null; }
};
static const std::shared_ptr<Null> null_expr = std::make_shared<Null>();
class List: public Expr {
public:
Type type() override { return Type::List; }
const std::vector<std::shared_ptr<Expr>>& value() const { return value_; }
void append(const std::shared_ptr<Expr>& expr) { value_.emplace_back(expr); }
private:
std::vector<std::shared_ptr<Expr>> value_;
};
class Callable: public Expr {
public:
using Fn = std::function<std::shared_ptr<Expr>(const std::shared_ptr<List>&)>;
explicit Callable(Fn&& lambda) : lambda_(lambda) {}
Type type() override { return Type::Callable; }
const Fn& value() const {
return lambda_;
}
private:
Fn lambda_;
};
class Env {
public:
Env(const std::shared_ptr<Env>& env) : env_(env) {}
std::shared_ptr<Expr> get(const std::shared_ptr<Symbol>& symbol) const {
// std::cout << "get: " << symbol->value() << std::endl;
auto it = expr_map_.find(symbol->value());
if (it != expr_map_.end()) {
return it->second;
}
if (env_) {
return env_->get(symbol);
}
return null_expr;
}
void put(const std::shared_ptr<Symbol>& symbol, const std::shared_ptr<Expr>& expr) {
// std::cout << "put: " << symbol->value() << std::endl;
expr_map_[symbol->value()] = expr;
}
private:
std::unordered_map<std::string, std::shared_ptr<Expr>> expr_map_;
std::shared_ptr<Env> env_;
};
static std::shared_ptr<Env> global = std::make_shared<Env>(std::shared_ptr<Env>());
inline void register_symbol(const std::string& symbol, Callable::Fn&& lambda) {
global->put(std::make_shared<Symbol>(symbol), std::make_shared<Callable>(std::forward<Callable::Fn>(lambda)));
}
inline bool is_par(char ch) {
return ch == '(' || ch == ')';
}
inline bool is_number(const Type& type) {
return type == Type::Integer || type == Type::Float;
}
inline bool is_atom(const Type& type) {
return type < Type::Atom;
}
inline bool is_true(const std::shared_ptr<Expr>& expr) {
if (!is_number(expr->type())) {
return false;
}
if (expr->type() == Type::Integer) {
return std::static_pointer_cast<Integer>(expr)->value();
}
return std::static_pointer_cast<Float>(expr)->value() != 0.0;
}
inline void len_eq(const std::shared_ptr<Expr>& expr, unsigned long expect) {
if (expr->type() != Type::List) {
oops("len_eq() can't check non List type");
}
auto list = std::static_pointer_cast<List>(expr);
if (list->value().size() != expect) {
auto symbol = std::static_pointer_cast<Symbol>(list->value()[0]);
oops("symbol: " + symbol->value() + " length not eq: " + std::to_string(expect));
}
}
inline void tokenize(const std::string& str, std::vector<std::string>& tokens) {
for (auto i = 0; i < str.size(); ++i) {
if (std::isspace(str[i])) {
continue;
}
if (is_par(str[i])) {
tokens.emplace_back(str.c_str() + i, 1);
continue;
}
auto s = i;
for (; !(std::isspace(str[i]) || is_par(str[i])); ++i);
tokens.emplace_back(str.c_str() + s, i - s);
i--;
}
}
inline std::shared_ptr<Expr> parse_atom(const std::vector<std::string>& tokens, unsigned long& cursor) {
auto token = tokens[cursor];
auto type = Type::Symbol;
if (std::isdigit(token[0]) || token[0] == '-') {
type = Type::Integer;
for (auto i = 1; i < token.size(); ++i) {
if (token[i] == '.') {
if (type == Type::Float) {
oops("parse failed, token: " + token);
}
type = Type::Float;
continue;
}
if (!std::isdigit(token[i])) {
oops("parse failed, token: " + token);
}
}
}
switch (type) {
case Type::Symbol:
return std::make_shared<Symbol>(token);
case Type::Integer:
return std::make_shared<Integer>(std::stol(token));
case Type::Float:
return std::make_shared<Float>(std::stod(token));
}
return std::shared_ptr<Expr>();
}
inline std::shared_ptr<Expr> parse(const std::vector<std::string>& tokens, unsigned long& cursor) {
if (cursor >= tokens.size()) {
oops("parse error");
}
// std::cout << "parse: " << tokens[cursor] << std::endl;
if (tokens[cursor] == "(") {
auto list = std::make_shared<List>();
while (tokens[++cursor] != ")") {
list->append(parse(tokens, cursor));
}
return list;
}
return parse_atom(tokens, cursor);
}
inline std::shared_ptr<Expr> eval(const std::shared_ptr<Expr>& expr, const std::shared_ptr<Env>& env) {
if (!expr) {
oops("syntax error");
return null_expr;
}
auto type = expr->type();
if (is_number(type)) {
return expr;
}
if (type == Type::Symbol) {
return env->get(std::static_pointer_cast<Symbol>(expr));
}
if (type != Type::List) {
oops("syntax error");
}
auto& value = std::static_pointer_cast<List>(expr)->value();
std::shared_ptr<Symbol> _ = std::static_pointer_cast<Symbol>(value[0]);
auto& name = _->value();
if (name == "quote") {
len_eq(expr, 2);
return value[1];
}
if (name == "def") {
len_eq(expr, 3);
auto symbol = std::static_pointer_cast<Symbol>(value[1]);
if (env->get(symbol)->type() != Type::Null) {
oops("symbol defined: " + symbol->value());
}
env->put(symbol, eval(value[2], env));
return null_expr;
}
if (name == "set!") {
len_eq(expr, 3);
auto symbol = std::static_pointer_cast<Symbol>(value[1]);
env->put(symbol, eval(value[2], env));
return null_expr;
}
if (name == "prog") {
for (auto i = 1; i < value.size(); ++i) {
eval(value[i], env);
}
return null_expr;
}
if (name == "if") {
// (if (cond) (then body) (else body))
len_eq(expr, 4);
if (is_true(eval(value[1], env))) {
return eval(value[2], env);
} else {
return eval(value[3], env);
}
}
if (name == "while") {
// (while (cond) (loop body))
len_eq(expr, 3);
while (is_true(eval(value[1], env))) {
eval(value[2], env);
}
return null_expr;
}
if (name == "lambda") {
// (lambda (args) (body))
len_eq(expr, 3);
auto lambda = [expr, parent = env](const std::shared_ptr<List>& args) {
auto& value = std::static_pointer_cast<List>(expr)->value();
auto symbols = std::static_pointer_cast<List>(value[1]);
// arguments bind
len_eq(symbols, args->value().size());
auto env = std::make_shared<Env>(parent);
for (auto i = 0; i < symbols->value().size(); ++i) {
auto symbol = std::static_pointer_cast<Symbol>(symbols->value()[i]);
env->put(symbol, args->value()[i]);
}
// eval body
return eval(value[2], env);
};
return std::make_shared<Callable>(std::move(lambda));
}
// function/lambda call
// (symbol arg1 arg2 arg3 ... )
auto callable = env->get(_);
if (!callable) {
oops("unknow symbol: " + name);
}
if (callable->type() != Type::Callable) {
oops("can't call symbol: " + name);
}
auto args = std::make_shared<List>();
for (auto i = 1; i < value.size(); ++i) {
args->append(eval(value[i], env));
}
return std::static_pointer_cast<Callable>(callable)->value()(args);
}
} // namespace lispoo
Loading…
Cancel
Save