Created
October 31, 2013 18:26
-
-
Save virtualanup/7254524 to your computer and use it in GitHub Desktop.
Small LISP Parser made in c++
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/********************************************* | |
* 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