Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / SCDoc / SCDoc.y
blobb7710f977d1383d15ac08e8ee70b7c6e7302da5b
1 %{
2 /************************************************************************
4 * Copyright 2012 Jonatan Liljedahl <lijon@kymatica.com>
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 ************************************************************************/
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include "SCDoc.h"
26 //#define YYLEX_PARAM &yylval, &yylloc
28 int scdocparse();
30 extern int scdoclineno;
31 extern char *scdoctext;
32 extern int scdoc_start_token;
33 extern FILE *scdocin;
34 //extern struct YYLTYPE scdoclloc;
36 //int scdoc_metadata_mode;
38 static const char * method_type = NULL;
40 static DocNode * topnode;
42 void scdocerror(const char *str);
44 extern void error(const char *fmt, ...);
45 extern void post(const char *fmt, ...);
47 static inline bool stringEqual(const char * a, const char * b)
49 return strcmp(a, b) == 0;
53 %locations
54 %error-verbose
55 %union {
56 int i;
57 const char *id;
58 char *str;
59 DocNode *doc_node;
61 // single line header tags that take text
62 %token CLASS TITLE SUMMARY RELATED CATEGORIES REDIRECT
63 // single line body tags that take text
64 %token CLASSTREE COPYMETHOD KEYWORD PRIVATE
65 // single line structural tags that take text, with children
66 %token SECTION SUBSECTION METHOD ARGUMENT
67 // single line structural tags with no text, with children
68 %token DESCRIPTION CLASSMETHODS INSTANCEMETHODS EXAMPLES RETURNS DISCUSSION
69 // nestable range tags with no text, with children
70 %token LIST TREE NUMBEREDLIST DEFINITIONLIST TABLE FOOTNOTE NOTE WARNING
71 // modal range tags that take multi-line text
72 %token CODE LINK ANCHOR SOFT IMAGE TELETYPE MATH STRONG EMPHASIS
73 %token CODEBLOCK "CODE block" TELETYPEBLOCK "TELETYPE block" MATHBLOCK "MATH block"
74 // symbols
75 %token TAGSYM "::" BARS "||" HASHES "##"
76 // text and whitespace
77 %token <str> TEXT "text" URL COMMA METHODNAME "method name" METHODARGS "arguments string"
78 %token NEWLINE "newline" EMPTYLINES "empty lines"
79 %token BAD_METHODNAME "bad method name"
81 %token END 0 "end of file"
83 %type <id> headtag sectiontag listtag rangetag inlinetag blocktag
84 %type <str> anyword words anywordnl wordsnl anywordurl words2 nocommawords optMETHODARGS methodname
85 %type <doc_node> document arg optreturns optdiscussion body bodyelem
86 %type <doc_node> optsubsections optsubsubsections methodbody
87 %type <doc_node> dochead headline optsections sections section
88 %type <doc_node> subsections subsection subsubsection subsubsections
89 %type <doc_node> optbody optargs args listbody tablebody tablecells tablerow
90 %type <doc_node> prose proseelem blockA blockB commalist
91 %type <doc_node> deflistbody deflistrow defterms methnames
93 %token START_FULL START_PARTIAL START_METADATA
95 %start start
97 %destructor { doc_node_free_tree($$); } <doc_node>
98 %destructor { free($$); } <str>
101 //int scdoclex (YYSTYPE * yylval_param, struct YYLTYPE * yylloc_param );
102 int scdoclex (void);
107 start: document { topnode = $1; }
108 | document error { topnode = NULL; doc_node_free_tree($1); }
111 document: START_FULL eateol dochead optsections
113 $$ = doc_node_create("DOCUMENT");
114 doc_node_add_child($$, $3);
115 doc_node_add_child($$, $4);
117 | START_PARTIAL sections
119 $$ = doc_node_make_take_children("BODY",NULL,$2);
121 | START_METADATA dochead optsections
123 $$ = doc_node_create("DOCUMENT");
124 doc_node_add_child($$, $2);
125 doc_node_add_child($$, $3);
129 eateol: eol
130 | /* empty */
133 dochead: dochead headline { $$ = doc_node_add_child($1,$2); }
134 | headline { $$ = doc_node_make("HEADER",NULL,$1); }
137 headline: headtag words2 eol { $$ = doc_node_make($1,$2,NULL); }
138 | CATEGORIES commalist eol { $$ = doc_node_make_take_children("CATEGORIES",NULL,$2); }
139 | RELATED commalist eol { $$ = doc_node_make_take_children("RELATED",NULL,$2); }
142 headtag: CLASS { $$ = "TITLE"; } /* no need for the separate class:: tag actually. */
143 | TITLE { $$ = "TITLE"; }
144 | SUMMARY { $$ = "SUMMARY"; }
145 | REDIRECT { $$ = "REDIRECT"; }
148 sectiontag: CLASSMETHODS { $$ = "CLASSMETHODS"; method_type = "CMETHOD"; }
149 | INSTANCEMETHODS { $$ = "INSTANCEMETHODS"; method_type = "IMETHOD"; }
150 | DESCRIPTION { $$ = "DESCRIPTION"; method_type = "METHOD"; }
151 | EXAMPLES { $$ = "EXAMPLES"; method_type = "METHOD"; }
154 optsections: sections
155 | { $$ = doc_node_make("BODY",NULL,NULL); }
158 sections: sections section { $$ = doc_node_add_child($1,$2); }
159 | section { $$ = doc_node_make("BODY",NULL,$1); }
160 | subsubsections { $$ = doc_node_make_take_children("BODY",NULL,$1); } /* allow text before first section */
163 section: SECTION { method_type = "METHOD"; } words2 eol optsubsections { $$ = doc_node_make_take_children("SECTION",$3,$5); }
164 | sectiontag optsubsections { $$ = doc_node_make_take_children($1, NULL,$2); }
167 optsubsections: subsections
168 | { $$ = NULL; }
171 subsections: subsections subsection { $$ = doc_node_add_child($1,$2); }
172 | subsection { $$ = doc_node_make("(SUBSECTIONS)",NULL,$1); }
173 | subsubsections
176 subsection: SUBSECTION words2 eol optsubsubsections { $$ = doc_node_make_take_children("SUBSECTION", $2, $4); }
179 optsubsubsections: subsubsections
180 | { $$ = NULL; }
183 subsubsections: subsubsections subsubsection { $$ = doc_node_add_child($1,$2); }
184 | subsubsection { $$ = doc_node_make("(SUBSUBSECTIONS)",NULL,$1); }
185 | body { $$ = doc_node_make_take_children("(SUBSUBSECTIONS)",NULL,$1); }
188 subsubsection: METHOD methnames optMETHODARGS eol methodbody
190 $2->id = "METHODNAMES";
191 $$ = doc_node_make(method_type,$3,$2);
192 doc_node_add_child($$, $5);
193 // doc_node_add_child($2, $3);
195 | COPYMETHOD words eol { $$ = doc_node_make(
196 stringEqual(method_type, "CMETHOD") ? "CCOPYMETHOD"
197 : (stringEqual(method_type, "IMETHOD") ? "ICOPYMETHOD"
198 : "COPYMETHOD"),
199 $2, NULL
200 ); }
201 | PRIVATE commalist eoleof { $$ = doc_node_make_take_children( stringEqual(method_type, "CMETHOD") ? "CPRIVATE"
202 : "IPRIVATE",
203 NULL, $2); }
206 optMETHODARGS: { $$ = NULL; }
207 | METHODARGS
209 // $$ = doc_node_make("ARGSTRING",$1,NULL);
210 $$ = $1;
211 if(!stringEqual(method_type, "METHOD")) {
212 yyerror("METHOD argument string is not allowed inside CLASSMETHODS or INSTANCEMETHODS");
213 YYERROR;
218 methodname: METHODNAME
220 char *p = $1+strlen($1)-1;
221 if(*p=='_') {
222 post("WARNING: SCDoc: In %s\n Property setter %s should be documented without underscore.\n", scdoc_current_file, $1);
223 *p = '\0';
225 $$ = $1;
229 methnames: methnames COMMA methodname { free($2); $2 = NULL; $$ = doc_node_add_child($1, doc_node_make("STRING",$3,NULL)); }
230 | methodname { $$ = doc_node_make("(METHODNAMES)",NULL,doc_node_make("STRING",$1,NULL)); }
233 methodbody: optbody optargs optreturns optdiscussion
235 $$ = doc_node_make_take_children("METHODBODY",NULL,$1);
236 doc_node_add_child($$, $2);
237 doc_node_add_child($$, $3);
238 doc_node_add_child($$, $4);
242 optbody: body
243 | { $$ = NULL; }
246 optargs: args
247 | { $$ = NULL; }
250 args: args arg { $$ = doc_node_add_child($1,$2); }
251 | arg { $$ = doc_node_make("ARGUMENTS",NULL,$1); }
254 arg: ARGUMENT words eol optbody { $$ = doc_node_make_take_children("ARGUMENT", $2, $4); }
255 | ARGUMENT eol body { $$ = doc_node_make_take_children("ARGUMENT", NULL, $3); }
258 optreturns: RETURNS body { $$ = doc_node_make_take_children("RETURNS",NULL,$2); }
259 | { $$ = NULL; }
262 optdiscussion: DISCUSSION body { $$ = doc_node_make_take_children("DISCUSSION",NULL,$2); }
263 | { $$ = NULL; }
267 body contains a list of bodyelem's (A) and prose (B) such that
268 the list can start and end with either A or B, and A can repeat while B can not
271 body: blockA
272 | blockB
275 blockA: blockB bodyelem { $$ = doc_node_add_child($1,$2); }
276 | blockA bodyelem { $$ = doc_node_add_child($1,$2); }
277 | bodyelem { $$ = doc_node_make("(SECTIONBODY)",NULL,$1); }
280 blockB: blockA prose { $$ = doc_node_add_child($1,$2); }
281 | prose { $$ = doc_node_make("(SECTIONBODY)",NULL,$1); }
284 bodyelem: rangetag body TAGSYM { $$ = doc_node_make_take_children($1,NULL,$2); }
285 | listtag listbody TAGSYM { $$ = doc_node_make_take_children($1,NULL,$2); }
286 | TABLE tablebody TAGSYM { $$ = doc_node_make_take_children("TABLE",NULL,$2); }
287 | DEFINITIONLIST deflistbody TAGSYM { $$ = doc_node_make_take_children("DEFINITIONLIST",NULL,$2); }
288 | blocktag wordsnl TAGSYM { $$ = doc_node_make($1,$2,NULL); }
289 | CLASSTREE words eoleof { $$ = doc_node_make("CLASSTREE",$2,NULL); }
290 | KEYWORD commalist eoleof { $$ = doc_node_make_take_children("KEYWORD",NULL,$2);
291 // printf("keyword '%s'\n",$2->children[0]->text);
293 | EMPTYLINES { $$ = NULL; }
294 | IMAGE words2 TAGSYM { $$ = doc_node_make("IMAGE",$2,NULL); }
297 prose: prose proseelem { $$ = doc_node_add_child($1, $2); }
298 | proseelem { $$ = doc_node_make("PROSE",NULL,$1); }
301 proseelem: anyword { $$ = doc_node_make(NODE_TEXT,$1,NULL); } // one TEXT for each word
302 | URL { $$ = doc_node_make("LINK",$1,NULL); }
303 | inlinetag words TAGSYM { $$ = doc_node_make($1,$2,NULL); }
304 | FOOTNOTE body TAGSYM { $$ = doc_node_make_take_children("FOOTNOTE",NULL,$2); }
305 | NEWLINE { $$ = doc_node_create(NODE_NL); }
308 inlinetag: LINK { $$ = "LINK"; }
309 | STRONG { $$ = "STRONG"; }
310 | SOFT { $$ = "SOFT"; }
311 | EMPHASIS { $$ = "EMPHASIS"; }
312 | CODE { $$ = "CODE"; }
313 | TELETYPE { $$ = "TELETYPE"; }
314 | MATH { $$ = "MATH"; }
315 | ANCHOR { $$ = "ANCHOR"; }
318 blocktag: CODEBLOCK { $$ = "CODEBLOCK"; }
319 | TELETYPEBLOCK { $$ = "TELETYPEBLOCK"; }
320 | MATHBLOCK { $$ = "MATHBLOCK"; }
323 listtag: LIST { $$ = "LIST"; }
324 | TREE { $$ = "TREE"; }
325 | NUMBEREDLIST { $$ = "NUMBEREDLIST"; }
328 rangetag: WARNING { $$ = "WARNING"; }
329 | NOTE { $$ = "NOTE"; }
332 listbody: listbody HASHES body { $$ = doc_node_add_child($1, doc_node_make_take_children("ITEM",NULL,$3)); }
333 | HASHES body { $$ = doc_node_make("(LISTBODY)",NULL, doc_node_make_take_children("ITEM",NULL,$2)); }
336 tablerow: HASHES tablecells { $$ = doc_node_make_take_children("TABROW",NULL,$2); }
339 tablebody: tablebody tablerow { $$ = doc_node_add_child($1,$2); }
340 | tablerow { $$ = doc_node_make("(TABLEBODY)",NULL,$1); }
343 tablecells: tablecells BARS optbody { $$ = doc_node_add_child($1, doc_node_make_take_children("TABCOL",NULL,$3)); }
344 | optbody { $$ = doc_node_make("(TABLECELLS)",NULL, doc_node_make_take_children("TABCOL",NULL,$1)); }
347 defterms: defterms HASHES body { $$ = doc_node_add_child($1,doc_node_make_take_children("TERM",NULL,$3)); }
348 | HASHES body { $$ = doc_node_make("(TERMS)",NULL,doc_node_make_take_children("TERM",NULL,$2)); }
351 deflistrow: defterms BARS optbody
353 $$ = doc_node_make_take_children("DEFLISTITEM", NULL, $1);
354 doc_node_add_child($$, doc_node_make_take_children("DEFINITION", NULL, $3));
358 deflistbody: deflistbody deflistrow { $$ = doc_node_add_child($1,$2); }
359 | deflistrow { $$ = doc_node_make("(DEFLISTBODY)",NULL,$1); }
362 anywordurl: anyword
363 | URL
366 anyword: TEXT
367 | COMMA
370 words: words anyword { $$ = strmerge($1,$2); }
371 | anyword
374 words2: words2 anywordurl { $$ = strmerge($1,$2); }
375 | anywordurl
378 eol: NEWLINE
379 | EMPTYLINES
382 eoleof: eol
383 | END
386 anywordnl: anyword
387 | eol { $$ = strdup("\n"); }
390 wordsnl: wordsnl anywordnl { $$ = strmerge($1,$2); }
391 | anywordnl
394 nocommawords: nocommawords TEXT { $$ = strmerge($1,$2); }
395 | nocommawords URL { $$ = strmerge($1,$2); }
396 | TEXT
397 | URL
400 commalist: commalist COMMA nocommawords { free($2); $2=NULL; $$ = doc_node_add_child($1,doc_node_make("STRING",$3,NULL)); }
401 | nocommawords { $$ = doc_node_make("(COMMALIST)",NULL,doc_node_make("STRING",$1,NULL)); }
406 DocNode * scdoc_parse_run(int mode) {
407 int modes[] = {START_FULL, START_PARTIAL, START_METADATA};
408 if(mode<0 || mode>=sizeof(modes)) {
409 error("scdoc_parse_run(): unknown mode: %d\n",mode);
411 scdoc_start_token = modes[mode];
412 /* scdoc_start_token = START_FULL;
413 scdoc_metadata_mode = 0;
414 if(mode==SCDOC_PARSE_PARTIAL) {
415 scdoc_start_token = START_PARTIAL;
416 } else
417 if(mode==SCDOC_PARSE_METADATA) {
418 scdoc_metadata_mode = 1;
420 topnode = NULL;
421 method_type = "METHOD";
422 if(scdocparse()!=0) {
423 return NULL;
425 return topnode;
428 void scdocerror(const char *str)
430 error("In %s:\n At line %d: %s\n\n",scdoc_current_file,scdoclineno,str);
432 /* FIXME: this does not work well, since the reported linenumber is often *after* the actual error line
433 fseek(scdocin, 0, SEEK_SET);
434 int line = 1;
435 char buf[256],*txt;
436 while(line!=scdoclineno && !feof(scdocin)) {
437 int c = fgetc(scdocin);
438 if(c=='\n') line++;
440 txt = fgets(buf, 256, scdocin);
441 if(txt)
442 fprintf(stderr," %s\n-------------------\n", txt);