Skip to content

Instantly share code, notes, and snippets.

@virtualanup
Created October 31, 2013 18:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save virtualanup/7254524 to your computer and use it in GitHub Desktop.
Save virtualanup/7254524 to your computer and use it in GitHub Desktop.
Small LISP Parser made in c++
/*********************************************
* Small lisp parser
* By virtualanup
* http://www.virtualanup.com
*********************************************/
#include<iostream>
#include<string>
#include<cmath>
#include<sstream>
#include<stdio.h>
using namespace std;
inline bool isnum(char c)
{
return c>='0' && c<='9';
}
class CLisp
{
private:
enum tok { TOK_END,TOK_BRKSTART,TOK_BRKEND,TOK_NUM,TOK_OPERATOR,TOK_FUNCTION};//the token types
struct Token
{
tok tokentype;
double value;
Token(tok toktype,double val=0):tokentype(toktype),value(val)
{}
};
//it supports functions with one or two parameters
typedef double(*oneparamfunc)(double);
typedef double(*twoparamfunc)(double,double);
struct function
{
oneparamfunc func;
int params;
string name;
};
static function funcs[];//this will contain all the funcitons
static int nofuncs;//the total number of functions
stringstream formula;//where we store our formula
Token GetNextToken();
void Skipspace()
{
while(formula.peek() == ' ')
formula.get();
}
double applyoper(double val1,double val2,char oper)
{
switch(oper)
{
case '+':
return val1+val2;
case'-':
return val1-val2;
case '*':
return val1*val2;
case '/':
return val1/val2;
}
return 0;
}
public:
struct Error{
string error;
int pos;
Error(const string& err,int position):error(err),pos(position){}
};
void SetFormula(const string& Formula)
{
formula.write(Formula.c_str(),Formula.size());
}
double Parse();
};
double mod(double a,double b)
{
return long(a)%long(b);
}
CLisp::function CLisp::funcs[]={
{sin,1,"sin"},
{cos,1,"cos"},
{tan,1,"tan"},
{asin,1,"acos"},
{acos,1,"acos"},
{atan,1,"atan"},
{exp,1,"exp"},
{log,1,"log"},
{log10,1,"log10"},
{sqrt,1,"sqrt"},
{(oneparamfunc)((twoparamfunc)atan2),2,"atan2"},
{(oneparamfunc)((twoparamfunc)mod),2,"mod"},
{(oneparamfunc)((twoparamfunc)pow),2,"expt"}
};
int CLisp::nofuncs=18;
CLisp::Token CLisp::GetNextToken()
{
Skipspace();
if(formula.peek()== EOF)
return Token(TOK_END);
if(formula.peek()=='(')
{
formula.get();
return Token(TOK_BRKSTART);
}
if(formula.peek()==')')
{
formula.get();
return Token(TOK_BRKEND);
}
if(string("+-*/").find(formula.peek()) != string::npos)
{
int opid=formula.get();
//check to see if it is really an operator
if(formula.peek() != ' ')//since there is no space after the operator, it can be a unary negation sign
formula.unget();
else
return Token(TOK_OPERATOR,opid);
}
if(isnum(formula.peek()) || formula.peek() == '-')
{
double val;
formula>>val;
//the number MUST be followed by space or closing bracket
if(formula.peek() != ' ' && formula.peek() != ')')
throw Error("Invalid character after number ",formula.tellg());
return Token(TOK_NUM,val);
}
int curpos=formula.tellg();
string fname;//name of the function
formula>>fname;
for(int i=0;i<nofuncs;i++)
{
if(funcs[i].name == fname)
{
return Token(TOK_FUNCTION,i);//the ith function
}
}
throw Error("Invalid character",formula.tellg());
}
double CLisp::Parse()
{
int curpos=formula.tellg();
Token tok=GetNextToken();
if(tok.tokentype == TOK_NUM)
{
return tok.value;
}
if(tok.tokentype != TOK_BRKSTART)
throw Error("Expected ( ",curpos);
Token tokop=GetNextToken();
if(tokop.tokentype !=TOK_OPERATOR && tokop.tokentype !=TOK_FUNCTION)
{
throw Error("Expected operator or function ",curpos);
}
//if the token is a function
if(tokop.tokentype ==TOK_FUNCTION)
{
int fid=tokop.value;//function id
//get the required number of parameters
double* params=new double[funcs[fid].params];
for(int j=0;j<funcs[fid].params;j++)
{
params[j]=Parse();
}
//apply the function
double Ret;
if(funcs[fid].params == 1)
Ret= (* reinterpret_cast<oneparamfunc>(funcs[fid].func))(params[0]);
else
Ret= (* reinterpret_cast<twoparamfunc>(funcs[fid].func))(params[0],params[1]);
delete []params;
int curpos=formula.tellg();
Token end=GetNextToken();
if(end.tokentype != TOK_BRKEND)
throw Error("Expected ) ",curpos);
return Ret;
}
//else
//get the number
double num1,num2;//num1 is the result
num1=Parse();
while(1)
{
num2=Parse();
num1=applyoper(num1,num2,tokop.value);
int curpos=formula.tellg();
Token nexttoken=GetNextToken();
if(nexttoken.tokentype == TOK_BRKEND)
{
return num1;
}
if(nexttoken.tokentype == TOK_END)
throw Error("Unexpected end of line ",formula.tellg());
//restore the position
formula.seekg(curpos);
}
}
int main()
{
cout<<"The small lisp arithemetic interpreter\nBy virtualanup ( www.virtualanup.com )\n";
for(;;)
{
cout<<">>> ";
string s;
getline(cin,s);
CLisp Int;
Int.SetFormula(s);
if(s.size()==0)
return 0;
try
{
cout<<Int.Parse()<<endl;
}
catch(const CLisp::Error& error)
{
cout<<"Error: "<<error.error<<" at position "<<error.pos+1<<endl;
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment