eolian: new doc token lexer/parser

This should allow us to more easily extend the format if desired
and overall makes the doc syntax parsing more readable and simpler.
This commit is contained in:
Daniel Kolesa 2015-06-23 15:28:46 +01:00
parent d9db8888ac
commit c26134df7a
1 changed files with 131 additions and 152 deletions

View File

@ -252,6 +252,116 @@ cend:
if (tok) tok->value.s = eina_stringshare_add(eina_strbuf_string_get(ls->buff));
}
enum Doc_Tokens {
DOC_MANGLED = -2, DOC_UNFINISHED = -1, DOC_TEXT = 0, DOC_SINCE = 1
};
static int
doc_lex(Eo_Lexer *ls, Eina_Bool *term, Eina_Bool allow_since)
{
Eina_Bool contdoc = EINA_FALSE;
eina_strbuf_reset(ls->buff);
for (;; contdoc = EINA_TRUE) switch (ls->current)
{
/* error case */
case '\0':
return DOC_UNFINISHED;
/* newline case: if two or more newlines are present, new paragraph
* if only one newline is present, append space to the text buffer
* when starting new paragraph, reset doc continutation
*/
case '\n':
case '\r':
next_line(ls);
skip_ws(ls);
if (!is_newline(ls->current))
{
eina_strbuf_append_char(ls->buff, ' ');
continue;
}
while (is_newline(ls->current))
next_line_ws(ls);
eina_strbuf_trim(ls->buff);
return DOC_TEXT;
/* escape case: for any \X, output \X
* except for \\]], then output just ]]
*/
case '\\':
next_char(ls);
if (ls->current == ']')
{
next_char(ls);
if (ls->current == ']')
{
next_char(ls);
eina_strbuf_append(ls->buff, "]]");
}
else
eina_strbuf_append(ls->buff, "\\]");
}
else
eina_strbuf_append_char(ls->buff, '\\');
continue;
/* terminating case */
case ']':
next_char(ls);
if (ls->current == ']')
{
/* terminate doc */
next_char(ls);
*term = EINA_TRUE;
eina_strbuf_trim(ls->buff);
return DOC_TEXT;
}
eina_strbuf_append_char(ls->buff, ']');
continue;
/* @since case - only when starting a new paragraph */
case '@':
eina_strbuf_append_char(ls->buff, '@');
next_char(ls);
while (ls->current && isalpha(ls->current))
{
eina_strbuf_append_char(ls->buff, ls->current);
next_char(ls);
}
if (contdoc)
continue;
if (!strcmp(eina_strbuf_string_get(ls->buff), "@since"))
{
/* since-token */
if (!allow_since)
return DOC_MANGLED;
*term = EINA_TRUE;
eina_strbuf_reset(ls->buff);
skip_ws(ls);
while (ls->current && (ls->current == '.' ||
ls->current == '_' ||
isalnum(ls->current)))
{
eina_strbuf_append_char(ls->buff, ls->current);
next_char(ls);
}
if (!eina_strbuf_length_get(ls->buff))
return DOC_UNFINISHED;
skip_ws(ls);
while (is_newline(ls->current))
next_line_ws(ls);
if (ls->current == ']')
next_char(ls);
if (ls->current != ']')
return DOC_MANGLED;
next_char(ls);
eina_strbuf_trim(ls->buff);
return DOC_SINCE;
}
/* default case - append character */
default:
eina_strbuf_append_char(ls->buff, ls->current);
next_char(ls);
continue;
}
}
void doc_error(Eo_Lexer *ls, const char *msg, Eolian_Documentation *doc, Eina_Strbuf *buf)
{
eina_stringshare_del(doc->summary);
@ -269,168 +379,37 @@ read_doc(Eo_Lexer *ls, Eo_Token *tok, int line, int column)
doc->base.line = line;
doc->base.column = column;
eina_strbuf_reset(ls->buff);
skip_ws(ls);
while (is_newline(ls->current))
next_line_ws(ls);
for (;;)
{
if (!ls->current)
{
doc_error(ls, "unfinished documentation", doc, NULL);
return; /* unreachable, for static analysis */
}
if (is_newline(ls->current))
{
next_line_ws(ls);
if (is_newline(ls->current))
{
while (is_newline(ls->current))
next_line_ws(ls);
break;
}
else
eina_strbuf_append_char(ls->buff, ' ');
}
else
{
if (ls->current == ']')
{
next_char(ls);
if (ls->current != ']')
eina_strbuf_append_char(ls->buff, ']');
else
{
next_char(ls);
eina_strbuf_trim(ls->buff);
doc->summary = eina_stringshare_add(
eina_strbuf_string_get(ls->buff));
tok->value.doc = doc;
return;
}
}
eina_strbuf_append_char(ls->buff, ls->current);
next_char(ls);
}
}
eina_strbuf_trim(ls->buff);
doc->summary = eina_stringshare_add(eina_strbuf_string_get(ls->buff));
Eina_Strbuf *rbuf = eina_strbuf_new();
Eina_Bool had_nl = EINA_TRUE;
for (;;)
Eina_Bool term = EINA_FALSE;
while (!term)
{
if (!ls->current)
int read = doc_lex(ls, &term, !!doc->summary);
switch (read)
{
doc_error(ls, "unfinished documentation", doc, rbuf);
return; /* unreachable, for static analysis */
}
eina_strbuf_reset(ls->buff);
if (had_nl && ls->current == '@')
{
#define LEX_SINCE(c, failure) \
next_char(ls); \
if (ls->current != c) \
{ \
eina_strbuf_append(ls->buff, failure); \
goto normal; \
}
LEX_SINCE('s', "@");
LEX_SINCE('i', "@s");
LEX_SINCE('n', "@si");
LEX_SINCE('c', "@sin");
LEX_SINCE('e', "@sinc");
LEX_SINCE(' ', "@since");
#undef LEX_SINCE
skip_ws(ls);
/* gotta have a value */
if (!ls->current || is_newline(ls->current) || ls->current == ']')
goto docerr;
/* append the since string */
while (ls->current && ls->current != ']' && !is_newline(ls->current))
{
eina_strbuf_append_char(ls->buff, ls->current);
next_char(ls);
}
/* trigger "unfinished documentation" if things end early */
if (!ls->current)
continue;
/* strip final whitespace */
while (isspace(ls->current))
{
if (is_newline(ls->current))
next_line(ls);
else
next_char(ls);
}
if (ls->current != ']') goto docerr;
next_char(ls);
if (ls->current != ']') goto docerr;
next_char(ls);
eina_strbuf_trim(ls->buff);
doc->since = eina_stringshare_add(eina_strbuf_string_get(ls->buff));
goto done;
docerr:
case DOC_MANGLED:
doc_error(ls, "mangled documentation", doc, rbuf);
return;
}
normal:
had_nl = EINA_FALSE;
while (ls->current && !is_newline(ls->current))
{
if (ls->current == ']')
{
next_char(ls);
if (ls->current != ']')
eina_strbuf_append_char(ls->buff, ']');
else
{
next_char(ls);
eina_strbuf_trim(ls->buff);
eina_strbuf_append(rbuf, eina_strbuf_string_get(ls->buff));
goto done;
}
}
eina_strbuf_append_char(ls->buff, ls->current);
next_char(ls);
}
eina_strbuf_trim(ls->buff);
eina_strbuf_append(rbuf, eina_strbuf_string_get(ls->buff));
if (is_newline(ls->current))
{
next_line_ws(ls);
/* new paragraph */
if (is_newline(ls->current))
eina_strbuf_append(rbuf, "\n\n");
case DOC_UNFINISHED:
doc_error(ls, "unfinished documentation", doc, rbuf);
return;
case DOC_TEXT:
if (!doc->summary)
doc->summary = eina_stringshare_add(eina_strbuf_string_get(ls->buff));
else
eina_strbuf_append_char(rbuf, ' ');
while (is_newline(ls->current))
next_line_ws(ls);
had_nl = EINA_TRUE;
{
if (eina_strbuf_length_get(rbuf))
eina_strbuf_append(rbuf, "\n\n");
eina_strbuf_append(rbuf, eina_strbuf_string_get(ls->buff));
}
break;
case DOC_SINCE:
doc->since = eina_stringshare_add(eina_strbuf_string_get(ls->buff));
break;
}
}
done:
eina_strbuf_trim(rbuf);
if (eina_strbuf_string_get(rbuf)[0])
if (eina_strbuf_length_get(rbuf))
doc->description = eina_stringshare_add(eina_strbuf_string_get(rbuf));
eina_strbuf_free(rbuf);
tok->value.doc = doc;