| // |
| // Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) |
| // |
| // Distributed under the Boost Software License, Version 1.0. (See |
| // accompanying file LICENSE_1_0.txt or copy at |
| // http://www.boost.org/LICENSE_1_0.txt) |
| // |
| #include "mo_lambda.hpp" |
| #include <string.h> |
| #include <stdlib.h> |
| |
| namespace boost { |
| namespace locale { |
| namespace gnu_gettext { |
| namespace lambda { |
| |
| namespace { // anon |
| struct identity : public plural { |
| virtual int operator()(int n) const |
| { |
| return n; |
| }; |
| virtual identity *clone() const |
| { |
| return new identity(); |
| } |
| }; |
| |
| struct unary : public plural |
| { |
| unary(plural_ptr ptr) : |
| op1(ptr) |
| { |
| } |
| protected: |
| plural_ptr op1; |
| }; |
| |
| |
| struct binary : public plural |
| { |
| binary(plural_ptr p1,plural_ptr p2) : |
| op1(p1), |
| op2(p2) |
| { |
| } |
| protected: |
| plural_ptr op1,op2; |
| }; |
| |
| struct number : public plural |
| { |
| number(int v) : |
| val(v) |
| { |
| } |
| virtual int operator()(int /*n*/) const |
| { |
| return val; |
| } |
| virtual number *clone() const |
| { |
| return new number(val); |
| } |
| |
| private: |
| int val; |
| }; |
| |
| #define UNOP(name,oper) \ |
| struct name: public unary { \ |
| name(plural_ptr op) : unary(op) \ |
| { \ |
| }; \ |
| virtual int operator()(int n) const \ |
| { \ |
| return oper (*op1)(n); \ |
| } \ |
| virtual name *clone() const \ |
| { \ |
| plural_ptr op1_copy(op1->clone()); \ |
| return new name(op1_copy); \ |
| } \ |
| }; |
| |
| #define BINOP(name,oper) \ |
| struct name : public binary \ |
| { \ |
| name(plural_ptr p1,plural_ptr p2) : \ |
| binary(p1,p2) \ |
| { \ |
| } \ |
| \ |
| virtual int operator()(int n) const \ |
| { \ |
| return (*op1)(n) oper (*op2)(n); \ |
| } \ |
| virtual name *clone() const \ |
| { \ |
| plural_ptr op1_copy(op1->clone()); \ |
| plural_ptr op2_copy(op2->clone()); \ |
| return new name(op1_copy,op2_copy); \ |
| } \ |
| }; |
| |
| #define BINOPD(name,oper) \ |
| struct name : public binary { \ |
| name(plural_ptr p1,plural_ptr p2) : \ |
| binary(p1,p2) \ |
| { \ |
| } \ |
| virtual int operator()(int n) const \ |
| { \ |
| int v1=(*op1)(n); \ |
| int v2=(*op2)(n); \ |
| return v2==0 ? 0 : v1 oper v2; \ |
| } \ |
| virtual name *clone() const \ |
| { \ |
| plural_ptr op1_copy(op1->clone()); \ |
| plural_ptr op2_copy(op2->clone()); \ |
| return new name(op1_copy,op2_copy); \ |
| } \ |
| }; |
| |
| enum { END = 0 , SHL = 256, SHR, GTE,LTE, EQ, NEQ, AND, OR, NUM, VARIABLE }; |
| |
| UNOP(l_not,!) |
| UNOP(minus,-) |
| UNOP(bin_not,~) |
| |
| BINOP(mul,*) |
| BINOPD(div,/) |
| BINOPD(mod,%) |
| static int level10[]={3,'*','/','%'}; |
| |
| BINOP(add,+) |
| BINOP(sub,-) |
| static int level9[]={2,'+','-'}; |
| |
| BINOP(shl,<<) |
| BINOP(shr,>>) |
| static int level8[]={2,SHL,SHR}; |
| |
| BINOP(gt,>) |
| BINOP(lt,<) |
| BINOP(gte,>=) |
| BINOP(lte,<=) |
| static int level7[]={4,'<','>',GTE,LTE}; |
| |
| BINOP(eq,==) |
| BINOP(neq,!=) |
| static int level6[]={2,EQ,NEQ}; |
| |
| BINOP(bin_and,&) |
| static int level5[]={1,'&'}; |
| |
| BINOP(bin_xor,^) |
| static int level4[]={1,'^'}; |
| |
| BINOP(bin_or,|) |
| static int level3[]={1,'|'}; |
| |
| BINOP(l_and,&&) |
| static int level2[]={1,AND}; |
| |
| BINOP(l_or,||) |
| static int level1[]={1,OR}; |
| |
| struct conditional : public plural { |
| conditional(plural_ptr p1,plural_ptr p2,plural_ptr p3) : |
| op1(p1), |
| op2(p2), |
| op3(p3) |
| { |
| } |
| virtual int operator()(int n) const |
| { |
| return (*op1)(n) ? (*op2)(n) : (*op3)(n); |
| } |
| virtual conditional *clone() const |
| { |
| plural_ptr op1_copy(op1->clone()); |
| plural_ptr op2_copy(op2->clone()); |
| plural_ptr op3_copy(op3->clone()); |
| return new conditional(op1_copy,op2_copy,op3_copy); |
| } |
| private: |
| plural_ptr op1,op2,op3; |
| }; |
| |
| |
| plural_ptr bin_factory(int value,plural_ptr left,plural_ptr right) |
| { |
| |
| switch(value) { |
| case '/': return plural_ptr(new div(left,right)); |
| case '*': return plural_ptr(new mul(left,right)); |
| case '%': return plural_ptr(new mod(left,right)); |
| case '+': return plural_ptr(new add(left,right)); |
| case '-': return plural_ptr(new sub(left,right)); |
| case SHL: return plural_ptr(new shl(left,right)); |
| case SHR: return plural_ptr(new shr(left,right)); |
| case '>': return plural_ptr(new gt(left,right)); |
| case '<': return plural_ptr(new lt(left,right)); |
| case GTE: return plural_ptr(new gte(left,right)); |
| case LTE: return plural_ptr(new lte(left,right)); |
| case EQ: return plural_ptr(new eq(left,right)); |
| case NEQ: return plural_ptr(new neq(left,right)); |
| case '&': return plural_ptr(new bin_and(left,right)); |
| case '^': return plural_ptr(new bin_xor(left,right)); |
| case '|': return plural_ptr(new bin_or (left,right)); |
| case AND: return plural_ptr(new l_and(left,right)); |
| case OR: return plural_ptr(new l_or(left,right)); |
| default: |
| return plural_ptr(); |
| } |
| } |
| |
| plural_ptr un_factory(int value,plural_ptr op) |
| { |
| switch(value) { |
| case '!': return plural_ptr(new l_not(op)); |
| case '~': return plural_ptr(new bin_not(op)); |
| case '-': return plural_ptr(new minus(op)); |
| default: |
| return plural_ptr(); |
| } |
| } |
| |
| static inline bool is_in(int v,int *p) |
| { |
| int len=*p; |
| p++; |
| while(len && *p!=v) { p++;len--; } |
| return len!=0; |
| } |
| |
| |
| class tokenizer { |
| public: |
| tokenizer(char const *s) { text=s; pos=0; step(); }; |
| int get(int *val=NULL){ |
| int iv=int_value; |
| int res=next_tocken; |
| step(); |
| if(val && res==NUM){ |
| *val=iv; |
| } |
| return res; |
| }; |
| int next(int *val=NULL) { |
| if(val && next_tocken==NUM) { |
| *val=int_value; |
| return NUM; |
| } |
| return next_tocken; |
| } |
| private: |
| char const *text; |
| int pos; |
| int next_tocken; |
| int int_value; |
| bool is_blank(char c) |
| { |
| return c==' ' || c=='\r' || c=='\n' || c=='\t'; |
| } |
| bool isdigit(char c) |
| { |
| return '0'<=c && c<='9'; |
| } |
| void step() |
| { |
| while(text[pos] && is_blank(text[pos])) pos++; |
| char const *ptr=text+pos; |
| char *tmp_ptr; |
| if(strncmp(ptr,"<<",2)==0) { pos+=2; next_tocken=SHL; } |
| else if(strncmp(ptr,">>",2)==0) { pos+=2; next_tocken=SHR; } |
| else if(strncmp(ptr,"&&",2)==0) { pos+=2; next_tocken=AND; } |
| else if(strncmp(ptr,"||",2)==0) { pos+=2; next_tocken=OR; } |
| else if(strncmp(ptr,"<=",2)==0) { pos+=2; next_tocken=LTE; } |
| else if(strncmp(ptr,">=",2)==0) { pos+=2; next_tocken=GTE; } |
| else if(strncmp(ptr,"==",2)==0) { pos+=2; next_tocken=EQ; } |
| else if(strncmp(ptr,"!=",2)==0) { pos+=2; next_tocken=NEQ; } |
| else if(*ptr=='n') { pos++; next_tocken=VARIABLE; } |
| else if(isdigit(*ptr)) { int_value=strtol(text+pos,&tmp_ptr,0); pos=tmp_ptr-text; next_tocken=NUM; } |
| else if(*ptr=='\0') { next_tocken=0; } |
| else { next_tocken=*ptr; pos++; } |
| } |
| }; |
| |
| |
| #define BINARY_EXPR(expr,hexpr,list) \ |
| plural_ptr expr() \ |
| { \ |
| plural_ptr op1,op2; \ |
| if((op1=hexpr()).get()==0) \ |
| return plural_ptr(); \ |
| while(is_in(t.next(),list)) { \ |
| int o=t.get(); \ |
| if((op2=hexpr()).get()==0) \ |
| return plural_ptr(); \ |
| op1=bin_factory(o,op1,op2); \ |
| } \ |
| return op1; \ |
| } |
| |
| class parser { |
| public: |
| |
| parser(tokenizer &tin) : t(tin) {}; |
| |
| plural_ptr compile() |
| { |
| plural_ptr res=cond_expr(); |
| if(res.get() && t.next()!=END) { |
| return plural_ptr(); |
| }; |
| return res; |
| } |
| |
| private: |
| |
| plural_ptr value_expr() |
| { |
| plural_ptr op; |
| if(t.next()=='(') { |
| t.get(); |
| if((op=cond_expr()).get()==0) |
| return plural_ptr(); |
| if(t.get()!=')') |
| return plural_ptr(); |
| return op; |
| } |
| else if(t.next()==NUM) { |
| int value; |
| t.get(&value); |
| return plural_ptr(new number(value)); |
| } |
| else if(t.next()==VARIABLE) { |
| t.get(); |
| return plural_ptr(new identity()); |
| } |
| return plural_ptr(); |
| }; |
| |
| plural_ptr un_expr() |
| { |
| plural_ptr op1; |
| static int level_unary[]={3,'-','!','~'}; |
| if(is_in(t.next(),level_unary)) { |
| int op=t.get(); |
| if((op1=un_expr()).get()==0) |
| return plural_ptr(); |
| switch(op) { |
| case '-': |
| return plural_ptr(new minus(op1)); |
| case '!': |
| return plural_ptr(new l_not(op1)); |
| case '~': |
| return plural_ptr(new bin_not(op1)); |
| default: |
| return plural_ptr(); |
| } |
| } |
| else { |
| return value_expr(); |
| } |
| }; |
| |
| BINARY_EXPR(l10,un_expr,level10); |
| BINARY_EXPR(l9,l10,level9); |
| BINARY_EXPR(l8,l9,level8); |
| BINARY_EXPR(l7,l8,level7); |
| BINARY_EXPR(l6,l7,level6); |
| BINARY_EXPR(l5,l6,level5); |
| BINARY_EXPR(l4,l5,level4); |
| BINARY_EXPR(l3,l4,level3); |
| BINARY_EXPR(l2,l3,level2); |
| BINARY_EXPR(l1,l2,level1); |
| |
| plural_ptr cond_expr() |
| { |
| plural_ptr cond,case1,case2; |
| if((cond=l1()).get()==0) |
| return plural_ptr(); |
| if(t.next()=='?') { |
| t.get(); |
| if((case1=cond_expr()).get()==0) |
| return plural_ptr(); |
| if(t.get()!=':') |
| return plural_ptr(); |
| if((case2=cond_expr()).get()==0) |
| return plural_ptr(); |
| } |
| else { |
| return cond; |
| } |
| return plural_ptr(new conditional(cond,case1,case2)); |
| } |
| |
| tokenizer &t; |
| |
| }; |
| |
| } // namespace anon |
| |
| plural_ptr compile(char const *str) |
| { |
| tokenizer t(str); |
| parser p(t); |
| return p.compile(); |
| } |
| |
| |
| } // lambda |
| } // gnu_gettext |
| } // locale |
| } // boost |
| |
| // vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 |
| |