mirror of https://github.com/yasm/yasm.git
parent
05224a9fd9
commit
88d5a1e6c4
12 changed files with 974 additions and 820 deletions
@ -1,149 +1,154 @@ |
||||
#ifndef _dfa_h |
||||
#define _dfa_h |
||||
#ifndef re2c_dfa_h |
||||
#define re2c_dfa_h |
||||
|
||||
#include <iostream.h> |
||||
#include <stdio.h> |
||||
#include "re.h" |
||||
|
||||
extern void prtCh(ostream&, uchar); |
||||
extern void printSpan(ostream&, uint, uint); |
||||
|
||||
class DFA; |
||||
class State; |
||||
|
||||
class Action { |
||||
public: |
||||
State *state; |
||||
public: |
||||
Action(State*); |
||||
virtual void emit(ostream&) = 0; |
||||
}; |
||||
|
||||
class Match: public Action { |
||||
public: |
||||
Match(State*); |
||||
void emit(ostream&); |
||||
}; |
||||
|
||||
class Enter: public Action { |
||||
public: |
||||
uint label; |
||||
public: |
||||
Enter(State*, uint); |
||||
void emit(ostream&); |
||||
}; |
||||
|
||||
class Save: public Match { |
||||
public: |
||||
uint selector; |
||||
public: |
||||
Save(State*, uint); |
||||
void emit(ostream&); |
||||
}; |
||||
|
||||
class Move: public Action { |
||||
public: |
||||
Move(State*); |
||||
void emit(ostream&); |
||||
}; |
||||
|
||||
class Accept: public Action { |
||||
public: |
||||
uint nRules; |
||||
uint *saves; |
||||
State **rules; |
||||
public: |
||||
Accept(State*, uint, uint*, State**); |
||||
void emit(ostream&); |
||||
}; |
||||
|
||||
class Rule: public Action { |
||||
public: |
||||
RuleOp *rule; |
||||
public: |
||||
Rule(State*, RuleOp*); |
||||
void emit(ostream&); |
||||
}; |
||||
|
||||
class Span { |
||||
public: |
||||
extern void prtCh(FILE *, uchar); |
||||
extern void printSpan(FILE *, uint, uint); |
||||
|
||||
struct DFA; |
||||
struct State; |
||||
|
||||
typedef enum { |
||||
MATCHACT = 1, |
||||
ENTERACT, |
||||
SAVEMATCHACT, |
||||
MOVEACT, |
||||
ACCEPTACT, |
||||
RULEACT |
||||
} ActionType; |
||||
|
||||
typedef struct Action { |
||||
struct State *state; |
||||
ActionType type; |
||||
union { |
||||
/* data for Enter */ |
||||
uint label; |
||||
/* data for SaveMatch */ |
||||
uint selector; |
||||
/* data for Accept */ |
||||
struct { |
||||
uint nRules; |
||||
uint *saves; |
||||
struct State **rules; |
||||
} Accept; |
||||
/* data for Rule */ |
||||
RegExp *rule; /* RuleOp */ |
||||
} d; |
||||
} Action; |
||||
|
||||
void Action_emit(Action*, FILE *); |
||||
|
||||
typedef struct Span { |
||||
uint ub; |
||||
State *to; |
||||
public: |
||||
uint show(ostream&, uint); |
||||
}; |
||||
struct State *to; |
||||
} Span; |
||||
|
||||
uint Span_show(Span*, FILE *, uint); |
||||
|
||||
class Go { |
||||
public: |
||||
typedef struct Go { |
||||
uint nSpans; |
||||
Span *span; |
||||
public: |
||||
void genGoto(ostream&, State*); |
||||
void genBase(ostream&, State*); |
||||
void genLinear(ostream&, State*); |
||||
void genBinary(ostream&, State*); |
||||
void genSwitch(ostream&, State*); |
||||
void compact(); |
||||
void unmap(Go*, State*); |
||||
}; |
||||
|
||||
class State { |
||||
public: |
||||
} Go; |
||||
|
||||
typedef struct State { |
||||
uint label; |
||||
RuleOp *rule; |
||||
State *next; |
||||
State *link; |
||||
uint depth; // for finding SCCs
|
||||
RegExp *rule; /* RuleOp */ |
||||
struct State *next; |
||||
struct State *link; |
||||
uint depth; /* for finding SCCs */ |
||||
uint kCount; |
||||
Ins **kernel; |
||||
bool isBase:1; |
||||
uint isBase:1; |
||||
Go go; |
||||
Action *action; |
||||
public: |
||||
State(); |
||||
~State(); |
||||
void emit(ostream&); |
||||
friend ostream& operator<<(ostream&, const State&); |
||||
friend ostream& operator<<(ostream&, const State*); |
||||
}; |
||||
|
||||
class DFA { |
||||
public: |
||||
} State; |
||||
|
||||
void Go_genGoto(Go*, FILE *, State*); |
||||
void Go_genBase(Go*, FILE *, State*); |
||||
void Go_genLinear(Go*, FILE *, State*); |
||||
void Go_genBinary(Go*, FILE *, State*); |
||||
void Go_genSwitch(Go*, FILE *, State*); |
||||
void Go_compact(Go*); |
||||
void Go_unmap(Go*, Go*, State*); |
||||
|
||||
State *State_new(void); |
||||
void State_delete(State*); |
||||
void State_emit(State*, FILE *); |
||||
void State_out(FILE *, const State*); |
||||
|
||||
typedef struct DFA { |
||||
uint lbChar; |
||||
uint ubChar; |
||||
uint nStates; |
||||
State *head, **tail; |
||||
State *toDo; |
||||
public: |
||||
DFA(Ins*, uint, uint, uint, Char*); |
||||
~DFA(); |
||||
void addState(State**, State*); |
||||
State *findState(Ins**, uint); |
||||
void split(State*); |
||||
|
||||
void findSCCs(); |
||||
void emit(ostream&); |
||||
|
||||
friend ostream& operator<<(ostream&, const DFA&); |
||||
friend ostream& operator<<(ostream&, const DFA*); |
||||
}; |
||||
|
||||
inline Action::Action(State *s) : state(s) { |
||||
s->action = this; |
||||
} DFA; |
||||
|
||||
DFA *DFA_new(Ins*, uint, uint, uint, Char*); |
||||
void DFA_delete(DFA*); |
||||
void DFA_addState(DFA*, State**, State*); |
||||
State *DFA_findState(DFA*, Ins**, uint); |
||||
void DFA_split(DFA*, State*); |
||||
|
||||
void DFA_findSCCs(DFA*); |
||||
void DFA_emit(DFA*, FILE *); |
||||
void DFA_out(FILE *, const DFA*); |
||||
|
||||
static inline Action * |
||||
Action_new_Match(State *s) |
||||
{ |
||||
Action *a = malloc(sizeof(Action)); |
||||
a->type = MATCHACT; |
||||
a->state = s; |
||||
s->action = a; |
||||
return a; |
||||
} |
||||
|
||||
inline Match::Match(State *s) : Action(s) |
||||
{ } |
||||
|
||||
inline Enter::Enter(State *s, uint l) : Action(s), label(l) |
||||
{ } |
||||
static inline Action * |
||||
Action_new_Enter(State *s, uint l) |
||||
{ |
||||
Action *a = malloc(sizeof(Action)); |
||||
a->type = ENTERACT; |
||||
a->state = s; |
||||
a->d.label = l; |
||||
s->action = a; |
||||
return a; |
||||
} |
||||
|
||||
inline Save::Save(State *s, uint i) : Match(s), selector(i) |
||||
{ } |
||||
static inline Action * |
||||
Action_new_Save(State *s, uint i) |
||||
{ |
||||
Action *a = malloc(sizeof(Action)); |
||||
a->type = SAVEMATCHACT; |
||||
a->state = s; |
||||
a->d.selector = i; |
||||
s->action = a; |
||||
return a; |
||||
} |
||||
|
||||
inline ostream& operator<<(ostream &o, const State *s) |
||||
{ return o << *s; } |
||||
static inline Action * |
||||
Action_new_Move(State *s) |
||||
{ |
||||
Action *a = malloc(sizeof(Action)); |
||||
a->type = MOVEACT; |
||||
a->state = s; |
||||
s->action = a; |
||||
return a; |
||||
} |
||||
|
||||
inline ostream& operator<<(ostream &o, const DFA *dfa) |
||||
{ return o << *dfa; } |
||||
Action *Action_new_Accept(State*, uint, uint*, State**); |
||||
|
||||
static inline Action * |
||||
Action_new_Rule(State *s, RegExp *r) /* RuleOp */ |
||||
{ |
||||
Action *a = malloc(sizeof(Action)); |
||||
a->type = RULEACT; |
||||
a->state = s; |
||||
a->d.rule = r; |
||||
s->action = a; |
||||
return a; |
||||
} |
||||
|
||||
#endif |
||||
|
@ -1,20 +1,27 @@ |
||||
#ifndef _parser_h |
||||
#define _parser_h |
||||
#ifndef re2c_parser_h |
||||
#define re2c_parser_h |
||||
|
||||
#include "scanner.h" |
||||
#include "re.h" |
||||
|
||||
class Symbol { |
||||
public: |
||||
static Symbol *first; |
||||
Symbol *next; |
||||
typedef struct Symbol { |
||||
struct Symbol *next; |
||||
Str name; |
||||
RegExp *re; |
||||
public: |
||||
Symbol(const SubStr&); |
||||
static Symbol *find(const SubStr&); |
||||
}; |
||||
} Symbol; |
||||
|
||||
void parse(int, ostream&); |
||||
void Symbol_init(Symbol *, const SubStr*); |
||||
static inline Symbol *Symbol_new(const SubStr*); |
||||
Symbol *Symbol_find(const SubStr*); |
||||
|
||||
void parse(int, FILE *); |
||||
|
||||
static inline Symbol * |
||||
Symbol_new(const SubStr *str) |
||||
{ |
||||
Symbol *r = malloc(sizeof(Symbol)); |
||||
Symbol_init(r, str); |
||||
return r; |
||||
} |
||||
|
||||
#endif |
||||
|
@ -1,20 +1,27 @@ |
||||
#ifndef _parser_h |
||||
#define _parser_h |
||||
#ifndef re2c_parser_h |
||||
#define re2c_parser_h |
||||
|
||||
#include "scanner.h" |
||||
#include "re.h" |
||||
|
||||
class Symbol { |
||||
public: |
||||
static Symbol *first; |
||||
Symbol *next; |
||||
typedef struct Symbol { |
||||
struct Symbol *next; |
||||
Str name; |
||||
RegExp *re; |
||||
public: |
||||
Symbol(const SubStr&); |
||||
static Symbol *find(const SubStr&); |
||||
}; |
||||
} Symbol; |
||||
|
||||
void parse(int, ostream&); |
||||
void Symbol_init(Symbol *, const SubStr*); |
||||
static inline Symbol *Symbol_new(const SubStr*); |
||||
Symbol *Symbol_find(const SubStr*); |
||||
|
||||
void parse(int, FILE *); |
||||
|
||||
static inline Symbol * |
||||
Symbol_new(const SubStr *str) |
||||
{ |
||||
Symbol *r = malloc(sizeof(Symbol)); |
||||
Symbol_init(r, str); |
||||
return r; |
||||
} |
||||
|
||||
#endif |
||||
|
@ -1,178 +1,164 @@ |
||||
#ifndef _re_h |
||||
#define _re_h |
||||
#ifndef re2c_re_h |
||||
#define re2c_re_h |
||||
|
||||
#include <iostream.h> |
||||
#include <stdio.h> |
||||
#include "token.h" |
||||
#include "ins.h" |
||||
|
||||
struct CharPtn { |
||||
typedef struct CharPtn { |
||||
uint card; |
||||
CharPtn *fix; |
||||
CharPtn *nxt; |
||||
}; |
||||
struct CharPtn *fix; |
||||
struct CharPtn *nxt; |
||||
} CharPtn; |
||||
|
||||
struct CharSet { |
||||
typedef struct CharSet { |
||||
CharPtn *fix; |
||||
CharPtn *freeHead, **freeTail; |
||||
CharPtn *rep[nChars]; |
||||
CharPtn ptn[nChars]; |
||||
}; |
||||
|
||||
class Range { |
||||
public: |
||||
Range *next; |
||||
uint lb, ub; // [lb,ub)
|
||||
public: |
||||
Range(uint l, uint u) : next(NULL), lb(l), ub(u) |
||||
{ } |
||||
Range(Range &r) : next(NULL), lb(r.lb), ub(r.ub) |
||||
{ } |
||||
friend ostream& operator<<(ostream&, const Range&); |
||||
friend ostream& operator<<(ostream&, const Range*); |
||||
}; |
||||
|
||||
inline ostream& operator<<(ostream &o, const Range *r){ |
||||
return r? o << *r : o; |
||||
} CharSet; |
||||
|
||||
typedef struct Range { |
||||
struct Range *next; |
||||
uint lb, ub; /* [lb,ub) */ |
||||
} Range; |
||||
|
||||
static inline void |
||||
Range_init(Range *r, uint l, uint u) |
||||
{ |
||||
r->next = NULL; |
||||
r->lb = l; |
||||
r->ub = u; |
||||
} |
||||
|
||||
static inline Range * |
||||
Range_new(uint l, uint u) |
||||
{ |
||||
Range *r = malloc(sizeof(Range)); |
||||
r->next = NULL; |
||||
r->lb = l; |
||||
r->ub = u; |
||||
return r; |
||||
} |
||||
|
||||
static inline void |
||||
Range_copy(Range *ro, const Range *r) |
||||
{ |
||||
ro->next = NULL; |
||||
ro->lb = r->lb; |
||||
ro->ub = r->ub; |
||||
} |
||||
|
||||
class RegExp { |
||||
public: |
||||
static inline Range * |
||||
Range_new_copy(Range *r) |
||||
{ |
||||
Range *ro = malloc(sizeof(Range)); |
||||
ro->next = NULL; |
||||
ro->lb = r->lb; |
||||
ro->ub = r->ub; |
||||
return ro; |
||||
} |
||||
|
||||
void Range_out(FILE *, const Range *); |
||||
|
||||
typedef enum { |
||||
NULLOP = 1, |
||||
MATCHOP, |
||||
RULEOP, |
||||
ALTOP, |
||||
CATOP, |
||||
CLOSEOP |
||||
} RegExpType; |
||||
|
||||
typedef struct RegExp { |
||||
RegExpType type; |
||||
uint size; |
||||
public: |
||||
virtual char *typeOf() = 0; |
||||
RegExp *isA(char *t) |
||||
{ return typeOf() == t? this : NULL; } |
||||
virtual void split(CharSet&) = 0; |
||||
virtual void calcSize(Char*) = 0; |
||||
virtual uint fixedLength(); |
||||
virtual void compile(Char*, Ins*) = 0; |
||||
virtual void display(ostream&) const = 0; |
||||
friend ostream& operator<<(ostream&, const RegExp&); |
||||
friend ostream& operator<<(ostream&, const RegExp*); |
||||
}; |
||||
|
||||
inline ostream& operator<<(ostream &o, const RegExp &re){ |
||||
re.display(o); |
||||
return o; |
||||
union { |
||||
/* for MatchOp */ |
||||
Range *match; |
||||
/* for RuleOp */ |
||||
struct { |
||||
struct RegExp *exp; |
||||
struct RegExp *ctx; |
||||
Ins *ins; |
||||
uint accept; |
||||
Token *code; |
||||
uint line; |
||||
} RuleOp; |
||||
/* for AltOp and CatOp*/ |
||||
struct { |
||||
struct RegExp *exp1, *exp2; |
||||
} AltCatOp; |
||||
/* for CloseOp */ |
||||
struct RegExp *exp; |
||||
} d; |
||||
} RegExp; |
||||
|
||||
static inline RegExp * |
||||
RegExp_isA(RegExp *r, RegExpType t) |
||||
{ |
||||
return r->type == t ? r : NULL; |
||||
} |
||||
|
||||
inline ostream& operator<<(ostream &o, const RegExp *re){ |
||||
return o << *re; |
||||
void RegExp_split(RegExp*, CharSet*); |
||||
void RegExp_calcSize(RegExp*, Char*); |
||||
uint RegExp_fixedLength(RegExp*); |
||||
void RegExp_compile(RegExp*, Char*, Ins*); |
||||
void RegExp_display(RegExp*, FILE *); |
||||
|
||||
static inline RegExp * |
||||
RegExp_new_NullOp(void) |
||||
{ |
||||
RegExp *r = malloc(sizeof(RegExp)); |
||||
r->type = NULLOP; |
||||
return r; |
||||
} |
||||
|
||||
class NullOp: public RegExp { |
||||
public: |
||||
static char *type; |
||||
public: |
||||
char *typeOf() |
||||
{ return type; } |
||||
void split(CharSet&); |
||||
void calcSize(Char*); |
||||
uint fixedLength(); |
||||
void compile(Char*, Ins*); |
||||
void display(ostream &o) const { |
||||
o << "_"; |
||||
} |
||||
}; |
||||
|
||||
class MatchOp: public RegExp { |
||||
public: |
||||
static char *type; |
||||
Range *match; |
||||
public: |
||||
MatchOp(Range *m) : match(m) |
||||
{ } |
||||
char *typeOf() |
||||
{ return type; } |
||||
void split(CharSet&); |
||||
void calcSize(Char*); |
||||
uint fixedLength(); |
||||
void compile(Char*, Ins*); |
||||
void display(ostream&) const; |
||||
}; |
||||
|
||||
class RuleOp: public RegExp { |
||||
private: |
||||
RegExp *exp; |
||||
public: |
||||
RegExp *ctx; |
||||
static char *type; |
||||
Ins *ins; |
||||
uint accept; |
||||
Token *code; |
||||
uint line; |
||||
public: |
||||
RuleOp(RegExp*, RegExp*, Token*, uint); |
||||
char *typeOf() |
||||
{ return type; } |
||||
void split(CharSet&); |
||||
void calcSize(Char*); |
||||
void compile(Char*, Ins*); |
||||
void display(ostream &o) const { |
||||
o << exp << "/" << ctx << ";"; |
||||
} |
||||
}; |
||||
|
||||
class AltOp: public RegExp { |
||||
private: |
||||
RegExp *exp1, *exp2; |
||||
public: |
||||
static char *type; |
||||
public: |
||||
AltOp(RegExp *e1, RegExp *e2) |
||||
{ exp1 = e1; exp2 = e2; } |
||||
char *typeOf() |
||||
{ return type; } |
||||
void split(CharSet&); |
||||
void calcSize(Char*); |
||||
uint fixedLength(); |
||||
void compile(Char*, Ins*); |
||||
void display(ostream &o) const { |
||||
o << exp1 << "|" << exp2; |
||||
} |
||||
friend RegExp *mkAlt(RegExp*, RegExp*); |
||||
}; |
||||
|
||||
class CatOp: public RegExp { |
||||
private: |
||||
RegExp *exp1, *exp2; |
||||
public: |
||||
static char *type; |
||||
public: |
||||
CatOp(RegExp *e1, RegExp *e2) |
||||
{ exp1 = e1; exp2 = e2; } |
||||
char *typeOf() |
||||
{ return type; } |
||||
void split(CharSet&); |
||||
void calcSize(Char*); |
||||
uint fixedLength(); |
||||
void compile(Char*, Ins*); |
||||
void display(ostream &o) const { |
||||
o << exp1 << exp2; |
||||
} |
||||
}; |
||||
|
||||
class CloseOp: public RegExp { |
||||
private: |
||||
RegExp *exp; |
||||
public: |
||||
static char *type; |
||||
public: |
||||
CloseOp(RegExp *e) |
||||
{ exp = e; } |
||||
char *typeOf() |
||||
{ return type; } |
||||
void split(CharSet&); |
||||
void calcSize(Char*); |
||||
void compile(Char*, Ins*); |
||||
void display(ostream &o) const { |
||||
o << exp << "+"; |
||||
} |
||||
}; |
||||
|
||||
extern void genCode(ostream&, RegExp*); |
||||
static inline RegExp * |
||||
RegExp_new_MatchOp(Range *m) |
||||
{ |
||||
RegExp *r = malloc(sizeof(RegExp)); |
||||
r->type = MATCHOP; |
||||
r->d.match = m; |
||||
return r; |
||||
} |
||||
|
||||
RegExp *RegExp_new_RuleOp(RegExp*, RegExp*, Token*, uint); |
||||
|
||||
static inline RegExp * |
||||
RegExp_new_AltOp(RegExp *e1, RegExp *e2) |
||||
{ |
||||
RegExp *r = malloc(sizeof(RegExp)); |
||||
r->type = ALTOP; |
||||
r->d.AltCatOp.exp1 = e1; |
||||
r->d.AltCatOp.exp2 = e2; |
||||
return r; |
||||
} |
||||
|
||||
static inline RegExp * |
||||
RegExp_new_CatOp(RegExp *e1, RegExp *e2) |
||||
{ |
||||
RegExp *r = malloc(sizeof(RegExp)); |
||||
r->type = CATOP; |
||||
r->d.AltCatOp.exp1 = e1; |
||||
r->d.AltCatOp.exp2 = e2; |
||||
return r; |
||||
} |
||||
|
||||
static inline RegExp * |
||||
RegExp_new_CloseOp(RegExp *e) |
||||
{ |
||||
RegExp *r = malloc(sizeof(RegExp)); |
||||
r->type = CLOSEOP; |
||||
r->d.exp = e; |
||||
return r; |
||||
} |
||||
|
||||
extern void genCode(FILE *, RegExp*); |
||||
extern RegExp *mkDiff(RegExp*, RegExp*); |
||||
extern RegExp *strToRE(SubStr); |
||||
extern RegExp *ranToRE(SubStr); |
||||
|
||||
extern RegExp *mkAlt(RegExp*, RegExp*); |
||||
|
||||
#endif |
||||
|
@ -1,30 +1,44 @@ |
||||
#ifndef _scanner_h |
||||
#define _scanner_h |
||||
|
||||
#include <stdio.h> |
||||
#include "token.h" |
||||
|
||||
class Scanner { |
||||
private: |
||||
int in; |
||||
typedef struct Scanner { |
||||
FILE *in; |
||||
uchar *bot, *tok, *ptr, *cur, *pos, *lim, *top, *eof; |
||||
uint tchar, tline, cline; |
||||
private: |
||||
uchar *fill(uchar*); |
||||
public: |
||||
Scanner(int); |
||||
int echo(ostream&); |
||||
int scan(); |
||||
void fatal(char*); |
||||
SubStr token(); |
||||
uint line(); |
||||
}; |
||||
|
||||
inline SubStr Scanner::token(){ |
||||
return SubStr(tok, cur - tok); |
||||
} Scanner; |
||||
|
||||
void Scanner_init(Scanner*, FILE *); |
||||
static inline Scanner *Scanner_new(FILE *); |
||||
|
||||
int Scanner_echo(Scanner*, FILE *); |
||||
int Scanner_scan(Scanner*); |
||||
void Scanner_fatal(Scanner*, char*); |
||||
SubStr Scanner_token(Scanner*); |
||||
static inline uint Scanner_line(Scanner*); |
||||
|
||||
inline SubStr |
||||
Scanner_token(Scanner *s) |
||||
{ |
||||
SubStr r; |
||||
SubStr_init_u(&r, s->tok, s->cur - s->tok); |
||||
return r; |
||||
} |
||||
|
||||
static inline uint |
||||
Scanner_line(Scanner *s) |
||||
{ |
||||
return s->cline; |
||||
} |
||||
|
||||
inline uint Scanner::line(){ |
||||
return cline; |
||||
static inline Scanner * |
||||
Scanner_new(FILE *i) |
||||
{ |
||||
Scanner *r = malloc(sizeof(Scanner)); |
||||
Scanner_init(r, i); |
||||
return r; |
||||
} |
||||
|
||||
#endif |
||||
|
@ -1,18 +1,30 @@ |
||||
#ifndef _token_h |
||||
#define _token_h |
||||
#ifndef re2c_token_h |
||||
#define re2c_token_h |
||||
|
||||
#include "substr.h" |
||||
|
||||
class Token { |
||||
public: |
||||
typedef struct Token { |
||||
Str text; |
||||
uint line; |
||||
public: |
||||
Token(SubStr, uint); |
||||
}; |
||||
} Token; |
||||
|
||||
inline Token::Token(SubStr t, uint l) : text(t), line(l) { |
||||
; |
||||
static inline void Token_init(Token *, SubStr, uint); |
||||
static inline Token *Token_new(SubStr, uint); |
||||
|
||||
static inline void |
||||
Token_init(Token *r, SubStr t, uint l) |
||||
{ |
||||
Str_copy(&r->text, &t); |
||||
r->line = l; |
||||
} |
||||
|
||||
static inline Token * |
||||
Token_new(SubStr t, uint l) |
||||
{ |
||||
Token *r = malloc(sizeof(Token)); |
||||
Str_copy(&r->text, &t); |
||||
r->line = l; |
||||
return r; |
||||
} |
||||
|
||||
#endif |
||||
|
Loading…
Reference in new issue