Bison+Flex
Пример интерпретатора простого языка типа BASIC, в котором есть комментарии (смотри пример), операторы присваивания, ввода (INPUT), вывода (PRINT), ветвления (IF…THEN…ELSE…END IF) и цикла (WHILE…WEND).
Суммарный объем 222 строки, аналогичный код в CAIO – 118 строк.
basic.lex:
%option case-insensitive noyywrap bison
%top{
#include "basic.h"
static string s;
%}
%x S_STRING
%%
else return ELSE;
end return END;
if return IF;
input return INPUT;
mod return MOD;
print return PRINT;
then return THEN;
wend return WEND;
while return WHILE;
[-()*+/=\n] return yytext[0];
[ \t\r] ;
"/'"(.|\n)*?"'/"|'.* ;
\d+ { yylval.int_=stoi(yytext); return NUMBER; }
[a-zA-Z]\w* { yylval.int_=find_id(yytext); return IDENT; }
\" { BEGIN(S_STRING); s=""; }
<S_STRING>\"\" { s+='"'; }
<S_STRING>\" { BEGIN(INITIAL);
yylval.int_=tc.size();
tc.push_back(s);
return (STRING);
}
<S_STRING>. s+=yytext;
<S_STRING>\n yyerror("Ошибка в строке");
. ;
%%
void yystart(FILE *f)
{ yyrestart(f);
}
basic.grm:
%define parse.error verbose
%{
#include "basic.h"
%}
%union {
int int_;
list<Statement> * Code_;
Expr Expr_;
Statement Statement_;
}
%token ELSE "else" END "end" IF "if" INPUT "input" MOD "mod"
%token PRINT "print" THEN "then" WEND "wend" WHILE "while"
%token <int_> IDENT "?идент?" STRING "?строка?" NUMBER "?число?"
%type <Code_> code optelse
%type <Expr_> expr
%type <Statement_> operator
%left '+' '-'
%left '*' '/' "mod"
%left UMINUS
%destructor { delete $$; } <Statement_>
%destructor { delete $$; } <Expr_>
%destructor { for(auto s:*$$) delete s; delete $$; } <Code_>
%%
program: code { interpret($1); }
;
code : { $$=new list<Statement>(); }
| code operator '\n' { $$=$1; $$->push_back($2); $1=nullptr; }
;
operator: { $$=new emptystmt(); }
| IDENT '=' expr { $$=new assign($1,$3); }
| "input" IDENT { $$=new input($2); }
| "print" expr { $$=new print($2); }
| "print" STRING { $$=new printstr($2); }
| "while" expr '\n' code "wend" { $$=new whilestmt($2,$4); }
| "if" expr optthen '\n' code optelse "end" "if" { $$=new ifstmt($2,$5,$6); }
;
expr : expr '+' expr { $$=new op2('+',$1,$3); }
| expr '-' expr { $$=new op2('-',$1,$3); }
| expr '*' expr { $$=new op2('*',$1,$3); }
| expr '/' expr { $$=new op2('/',$1,$3); }
| expr "mod" expr { $$=new op2('%',$1,$3); }
| '-' expr %prec UMINUS { $$=new op2('-',new number(0),$2); }
| '(' expr ')' { $$=$2; }
| NUMBER { $$=new number($1); }
| IDENT { $$=new ident($1); }
;
optthen : | "then" ;
optelse : { $$=new list<Statement>(); }
| "else" '\n' code { $$=$3; }
;
%%
int mem[1000];
int eval(Expr e)
{ if(auto n=dynamic_cast<op2 *>(e)) {
switch(n->op)
{ case '+': return eval(n->x)+eval(n->y);
case '-': return eval(n->x)-eval(n->y);
case '*': return eval(n->x)*eval(n->y);
case '/': return eval(n->x)/eval(n->y);
case '%': return eval(n->x)%eval(n->y);
}
}
else if(auto n=dynamic_cast<number *>(e)) {
return n->x;
}
else if(auto n=dynamic_cast<ident *>(e)) {
return mem[n->v];
}
}
void interpret(list<Statement> *p)
{ for(auto s:*p)
{ if(auto n=dynamic_cast<assign *>(s)) mem[n->v]=eval(n->e);
else if(auto n=dynamic_cast<input *>(s)) cin>>mem[n->v];
else if(auto n=dynamic_cast<print *>(s)) cout<<eval(n->e)<<endl;
else if(auto n=dynamic_cast<printstr *>(s)) cout<<tc[n->c]<<endl;
else if(auto n=dynamic_cast<whilestmt *>(s)) {
while(eval(n->e)) interpret(n->p1);
}
else if(auto n=dynamic_cast<ifstmt *>(s)) {
if(eval(n->e)) interpret(n->p1);
else interpret(n->p2);
}
}
}
map<string,int> ti;
int find_id(string name)
{ for(auto &ch:name) ch=toupper(ch);
int k=ti[name];
if(k==0) k=ti[name]=ti.size();
return k;
}
vector<string> tc;
void yyerror(const char* msg)
{ cerr<<msg<<"\n";
exit(1);
}
int main(int argc, char **argv)
{ if(argc>1)
{ FILE *f=fopen(argv[1],"r");
if(!f) yyerror("Can't open file");
else yystart(f);
}
return yyparse();
}
basic.h:
#ifndef BASIC_H
#define BASIC_H 1
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <iostream>
#include <map>
#include <list>
#include <vector>
#include <string>
using namespace std;
struct _domain {
virtual ~_domain(){}
};
struct Expr_domain : _domain {};
typedef Expr_domain *Expr;
struct Statement_domain : _domain {};
typedef Statement_domain *Statement;
struct op2 : Expr_domain {
char op;
Expr x,y;
op2(char op, Expr x, Expr y):op(op), x(x), y(y){}
~op2() { delete x; delete y; }
};
struct ident: Expr_domain {
int v;
ident(int v):v(v){}
};
struct number: Expr_domain {
int x;
number(int x):x(x){}
};
struct assign: Statement_domain {
int v;
Expr e;
assign(int v, Expr e):v(v), e(e){}
~assign() { delete e; }
};
struct ifstmt : Statement_domain {
Expr e;
list<Statement>* p1;
list<Statement>* p2;
ifstmt(Expr e, list<Statement>* p1, list<Statement>* p2):e(e), p1(p1), p2(p2){}
~ifstmt() {
delete e;
for(auto s:*p1) delete s;
for(auto s:*p2) delete s;
delete p1;
delete p2;
}
};
struct input: Statement_domain {
int v;
input(int v):v(v){}
};
struct print: Statement_domain {
Expr e;
print(Expr e):e(e){}
~print() { delete e; }
};
struct printstr : Statement_domain {
int c;
printstr(int c):c(c){}
};
struct whilestmt : Statement_domain {
Expr e;
list<Statement> *p1;
whilestmt(Expr e, list<Statement> *p1):e(e), p1(p1){}
~whilestmt() {
delete e;
for(auto s:*p1) delete s;
delete p1;
}
};
struct emptystmt : Statement_domain {};
void yystart(FILE *);
int yylex();
void yyerror(const char*);
void interpret(list<Statement> *);
int find_id(string name);
extern vector<string> tc;
#include "y.tab.h"
#endif
Запуск интерпретатора:
program test.prg
Пример программы test.prg:
/' Программа для проверки гипотезы Коллатца (гипотеза 3n+1):
какое бы начальное число n мы ни взяли,
рано или поздно мы получим единицу
'/
print "Введите n:"
input n
k=0
while n-1
if n mod 2 then ' если n нечетное
n=n*3+1
else
n=n/2
end if
print n
k=k+1
wend
print "Количество итераций"
print k