Skip to content

Commit c8304aa

Browse files
committed
Implements parsing a lisp program into an ast.
Initial commit for a lisp interpreter in modern c++. Got parsing into an ast implemented. Next is evaluation. I make heavy use of boost for its variant, string, and lexical_cast libraries.
0 parents  commit c8304aa

File tree

5 files changed

+181
-0
lines changed

5 files changed

+181
-0
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
3+
Requirements:
4+
* gcc (TODO: version?)
5+
* boost (sudo apt-get install libboost-all-dev)

lisp++.cpp

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#include <iostream>
2+
#include "lisp++.h"
3+
#include "lispUtils.h"
4+
#include <boost/algorithm/string.hpp>
5+
6+
// Tokenize the input string and fill tokens with it
7+
template<typename StrVec>
8+
void tokenize(StrVec& tokens, const std::string& inp) {
9+
// copy tokenize string since we need to modify it
10+
std::string str(inp);
11+
12+
// add space to parens
13+
boost::replace_all(str,"("," ( ");
14+
boost::replace_all(str,")"," ) ");
15+
boost::trim(str);
16+
17+
// split by space (compress extra spaces)
18+
boost::split(tokens,
19+
str,
20+
boost::is_any_of(" "),
21+
boost::token_compress_on);
22+
}
23+
24+
Expression atom(const std::string& val) {
25+
// attempts to parse as int
26+
try
27+
{
28+
return boost::lexical_cast<int>(val);
29+
}
30+
catch(const boost::bad_lexical_cast &) {}
31+
32+
// attempts to parse as double
33+
try
34+
{
35+
return boost::lexical_cast<double>(val);
36+
}
37+
catch(const boost::bad_lexical_cast &) {}
38+
39+
// just a string then
40+
return val;
41+
}
42+
43+
// Read the tokens into an ast
44+
template<typename InputIterator>
45+
Expression read_from_tokens(InputIterator& first, InputIterator& last) {
46+
if (first == last) {
47+
throw std::runtime_error("Syntax error: unexpected EOF while reading");
48+
}
49+
50+
if ("(" == *first) {
51+
List out;
52+
while ( *(++first) != ")" ) {
53+
out.push_back(read_from_tokens(first,last));
54+
}
55+
return out;
56+
} else if (")" == *first) {
57+
throw std::runtime_error("Syntax error: unexpected ')'");
58+
}
59+
else {
60+
return atom(*first);
61+
}
62+
}
63+
64+
Expression parse(const std::string& program) {
65+
std::vector<std::string> tokens;
66+
tokenize(tokens, program);
67+
68+
// take copies of iterators so we can pass by reference
69+
auto begin = tokens.begin();
70+
auto end = tokens.end();
71+
return read_from_tokens(begin,end);
72+
}
73+
74+
75+
int main() {
76+
77+
auto program = "(foo (bar baz) (print hello world) (+ 1 1.5))";
78+
std::cout << "program:" << program << std::endl;
79+
80+
print_visitor visitor;
81+
Expression ast = parse(program);
82+
std::cout << "ast: " << ::apply_visitor(visitor,ast) << std::endl;
83+
84+
return 0;
85+
}

lisp++.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#pragma once
2+
3+
#include <vector>
4+
#include <boost/variant.hpp>
5+
6+
// define relevant types
7+
typedef boost::variant<int,double> Number;
8+
typedef std::string Symbol;
9+
10+
11+
typedef boost::make_recursive_variant<Number, Symbol, std::vector< boost::recursive_variant_ > >::type Expression;
12+
13+
typedef std::vector<Expression> List;
14+
15+
16+
// forward declare functions
17+
Expression atom(const std::string& val);
18+
19+
template<typename InputIterator>
20+
Expression read_from_tokens(InputIterator& first, InputIterator& last);
21+
22+
template<typename StrVec>
23+
void tokenize(StrVec& tokens, const std::string& inp);
24+
25+
Expression parse(const std::string& program);
26+
27+

lispUtils.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#include <boost/lexical_cast.hpp>
2+
3+
// need this to use visitor on vector
4+
template<typename Visitor, typename Element>
5+
typename Visitor::result_type
6+
apply_visitor(const Visitor& visitor, const Element& operand) {
7+
return visitor(operand);
8+
}
9+
10+
class print_visitor
11+
: public boost::static_visitor<std::string>
12+
{
13+
public:
14+
15+
std::string operator()( const std::string& str ) const
16+
{
17+
return "'"+str+"'";
18+
}
19+
20+
template<typename T>
21+
std::string operator()( const T& val ) const
22+
{
23+
return boost::lexical_cast<std::string>(val);
24+
}
25+
26+
std::string operator()( const Expression& val ) const
27+
{
28+
return boost::apply_visitor(*this,val);
29+
}
30+
31+
template<typename T>
32+
std::string operator()( const std::vector<T>& list ) const
33+
{
34+
std::string out;
35+
out += "(";
36+
for (auto elem : list) {
37+
out += " "+::apply_visitor(*this,elem);
38+
}
39+
out += ")";
40+
41+
return out;
42+
}
43+
44+
};
45+

makefile

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
CC = g++
2+
CFLAGS = -std=c++11 -g
3+
LDFLAGS =
4+
5+
all: lisp++ clean
6+
7+
lisp++: lisp++.o
8+
$(CC) -o $@ $^ $(LDFLAGS)
9+
10+
lisp++.o: lisp++.cpp
11+
$(CC) -c $(CFLAGS) $<
12+
13+
.PHONY: clean cleanest
14+
15+
clean:
16+
rm *.o
17+
18+
cleanest: clean
19+
rm lisp++

0 commit comments

Comments
 (0)