%{ /* $Id: parser.y,v 1.5 2001/05/21 20:15:35 sandro Exp $ */ int genid_count=0; bool trace = false; #include "parser.h" extern int yylex(); extern "C" { #include extern FILE *yyin; extern char *lexer_source_begin; extern char *lexer_source_end; extern char *yytext; extern int lineno; extern char linebuf[]; extern void yyerror(char *errmsg); } int lineno=0; #include #include #include string genid(bool announce=true); string subject; string property; string value; string prefix; map prefix_table; stack subject_stack; stack property_stack; string make_data_uri(char* s); char* lexer_source_begin; char* lexer_source_end; static const bool tracing = false; static TupleStream* sot; static TupleStream* root_sot; Symbol* final = (Symbol *) 1; // HACK to be non-zero string getid(); void push_current(); void pop_current(); void push_context(); void pop_context(); string context_identifier; bool swapsv=false; #define YYSTYPE string %} %start message %token PREFIX %token ABSTERM %token NSTERM %token NSDEFTERM %token DQSTRING %token TQSTRING %token PERIOD %token A %token OTHER_WORD %token HAS %% message : /* NOTHING */ /* { final = $1; } */ | sentence_list sentence_list : sentence /* | error sentence { yyerrok; } */ | sentence_list PERIOD | sentence_list PERIOD sentence sentence : directive | declarative directive : PREFIX prefix_alone term { if (prefix.at(prefix.length()-1) != ':') { cerr << "prefixes must end with a colon to be used"; yyerror("syntax error"); return 0; } prefix_table[prefix] = $3; /* map::iterator iter = prefix_table.find(prefix); if (iter == prefix_table.end()) { prefix_table[prefix] = term; if (trace) cout << "prefix \"" << prefix << "\" now defined as \"" << term << "\"" << endl; } else { cerr << "Prefix \"" << prefix << "\" previously defined as \"" << iter->second << "\"" << endl; yyerror("usage error"); return 0; } */ } declarative : subject property_value_list | subject HAS { swapsv=true; } property_value_list { swapsv=false; } | described_term /* odd syntax bit */ property_value_list : property value_list | property_value_list ';' property value_list value_list : value | value_list ',' value subject: term { subject=$1; if (trace) cout << "Got subject " << subject << endl; } property: term { property=$1; if (trace) cout << "Got property " << property << endl; } value: term { value=$1; if (trace) cout << "Got value " << value << endl; if (swapsv) { sot->write(value, property, subject); } else { sot->write(subject, property, value); } } term : prefixed_and_word | angle_bracketed_text | double_quoted_string | triple_quoted_string | described_term | nested_context | A { $$ = vocab[AN_INSTANCE]; } | '=' { $$ = vocab[THE_EQUIVALENT]; } | '(' list ')' { $$=$2; } list : { $$ = vocab[NIL]; } | term list { $$ = genid(); sot->write($$, vocab[HEAD], $1); sot->write($$, vocab[TAIL], $2); } null_or_property_value_list : /* null */ | property_value_list | property_value_list PERIOD | property_value_list ';' PERIOD described_term: '[' { push_current(); } null_or_property_value_list ']' { if (trace) cout << "popping subject " << subject << endl; $$=subject; pop_current(); } nested_context: '{' { push_context(); } message '}' { $$=context_identifier; pop_context(); // cout << "** popped context identifier " << $$ << endl; } prefix_alone: NSDEFTERM { string yy=yytext; int p = yy.find(":"); prefix=yy.substr(0,p+1); } prefixed_and_word: NSTERM { string yy=yytext; int p = yy.find(":"); string pre=yy.substr(0,p+1); string post=yy.substr(p+1); map::iterator iter = prefix_table.find(pre); if (iter == prefix_table.end()) { cerr << "undefined prefix: \"" << pre << "\"" << endl; yyerror("usage error"); return 0; } $$=(iter->second)+post; if (trace) cout << "NSTERM: " << yy << " => " << pre << " " << post << " => " << $$ << endl; } angle_bracketed_text: ABSTERM { string yy=yytext; $$=yy.substr(1, yy.length()-2); if (trace) cout << "ABSTERM: " << yy << " => " << $$ << endl; } double_quoted_string: DQSTRING { string yy=yytext; // trim leading " and trailing " yytext+=1; yytext[strlen(yytext)-1] = '\0'; $$ = make_data_uri(yytext); if (trace) cout << "DQSTRING: " << yy << " => " << $$ << endl; } triple_quoted_string: TQSTRING { string yy=yytext; // trim leading """ and trailing """ yytext+=3; yytext[strlen(yytext)-3] = '\0'; $$ = make_data_uri(yytext); if (trace) cout << "TQSTRING: " << yy << " => " << $$ << endl; } %% #include "parser.h" extern "C" void yyerror(char *errmsg) { cerr << errmsg << " near \"" << yytext << "\" (line " << lineno << ")" << endl; cerr << "offending line: " << linebuf << endl; } int yyparse(void); Symbol* parse_to(TupleStream& passed_sot) { sot = &passed_sot; root_sot = sot; context_identifier = genid(false); if (yyparse()) { return 0; /* error */ } else { /* g_assert(rule == NULL); happens with syntax errors */ return final; } } Symbol* parse(TupleStream& sot, char* expr) { lexer_source_begin = expr; lexer_source_end = lexer_source_begin+strlen(expr); return parse_to(sot); } Symbol* parse(TupleStream& sot, string expr) { return parse(sot, expr.c_str()); } Symbol* parse_stdin(TupleStream& sot) { return parse_to(sot); } Symbol* parse_file(TupleStream& sot, const char* filename) { if (filename[0] == '-' && filename[1] == '\0') return parse_stdin(sot); yyin = fopen(filename, "r"); if (!yyin) { fprintf(stderr, "file \"%s\":", filename); perror("can't open"); return 0; } return parse_to(sot); } string make_data_uri(char* s) { string result; // what, haven't you ever re-implemented the wheel before?! result="data:,"; for (; *s; ++s) { char c=*s; bool okay=false; if (isalnum(c)) { okay=true; } else { switch(c) { case ':': case '.': case '@': okay=true; } } if (okay) { result+=c; } else { static char buf[4]; sprintf(buf, "%%%02x", (unsigned short) c); result+=buf; } } return result; } /* Symbol* new_dep_sym(char* n_name, Symbol* o1, Symbol* o2=0) { string name(n_name); if (o1->is_univar() || (o2 && o2->is_univar())) { name.insert(0, "$"); assert(0); } else if (o1->is_exivar() || (o2 && o2->is_exivar())) { name.insert(0, "!"); } return sot->get_sym(name); } */ string genid(bool announce) { static char buf[4096]; ////////////// SECURITY -- NO OVERRUN CHECK! sprintf(buf, "%s%d", genid_prefix, ++genid_count); string result(buf); if (announce && flag_anons) root_sot->write("", vocab[FOR_SOME], result); return result; } void push_current() { subject_stack.push(subject); property_stack.push(property); subject=genid(); } void pop_current() { subject=subject_stack.top(); property=property_stack.top(); subject_stack.pop(); property_stack.pop(); } class Nested : public TupleStream { public: Nested(TupleStream* _base, string _base_identifier) : base(_base), base_identifier(_base_identifier) { list = vocab[NIL]; context_identifier = genid(); } TupleStream* base; string base_identifier; string list; virtual void write(string subject, string property, string value) { string id = genid(); base->write(id, vocab[SUBJECT], subject); base->write(id, vocab[PROPERTY], property); base->write(id, vocab[VALUE], value); string old_list = list; list=genid(); base->write(list, vocab[HEAD], id); base->write(list, vocab[TAIL], old_list); } void flush() { base->write(context_identifier, vocab[ENUMERATION], list); } }; void push_context() { push_current(); // cout << "** pushed context identifier " << endl; sot = new Nested(sot, context_identifier); } void pop_context() { Nested* n = (Nested*) sot; // @@@@@@ bad downcast n->flush(); sot = n->base; context_identifier = n->base_identifier; delete n; pop_current(); }