Autor

Alejandro Alcalde

Graduado en Ingeniería Informática en la ETSIIT, Granada.

Más artículos de Alejandro Alcalde

Hace tiempo, para la asignatura “Modelos de Computación” desarrollé un conversor de MarkDown a LaTeX usando flex, muy sencillo, pensado para facilitarme un poco la vida a la hora de escribir en el blog y pasar a LaTeX. Básicamente con flex se van reconociendo partes del documento MarkDown mediante expresiones regulares y se traduce a su comando homólogo en LaTeX.

Por supuesto, más tarde descubrí pandoc, y es el programa que uso para todo tipo de conversiones entre formatos :-).


Ayúdanos a seguir escribiendo


Compilación

Para compilar el fichero simplemente es necesario el siguiente comando:

lex markdown2Latex.l && gcc -Wall lex.yy.c -o markdown2Latex -ll

Uso

y para ejecutarlo basta con

./markdown2Latex fichero.md

Código

El código está bastante bien comentado, podéis ir leyendo y comprendiendo el funcionamiento.

				 /*----- Sección de Declaraciones --------------*/

%option case-insensitive
%option debug
%option verbose

%{
#include<stdio.h>
#include<string.h>

char *trimCharacter(char *str, char *c);
void fatal(char *message);
void *ec_malloc(unsigned int size);

int from_italic_text = 0; /* Para saber si venimos de una italic anidada en la bold*/
int from_bold_text = 0;
int first_run = 1;
%}

/* Primitives */
anyChar	.+
word	[A-z0-9]+

scstrong	"__"
scem	"_"
list	^"* "
list2	^"- "
link	"["
h1	^#{1}[ ]{1}{anyChar}
h2	^#{2}[ ]{1}{anyChar}
h3	^#{3,6}[ ]{1}{anyChar}
multilineCode	^[`]{3}[^`]*
inlinecode	`{1}[^`]*`{1}
ignoreParsedCode	"\\begin{bashcode}"

%x IN_MARKDOWN_LIST IN_MARKDOWN_LIST2
%x IN_MARKDOWN_LINK OPTIONAL_TEXT CLOSE_BRACKET
%x BOLD_TEXT_NESTED_ITALIC ITALIC_TEXT
%x BOLD_TEXT ITALIC_TEXT_NESTED_BOLD
%x IN_MULTILINE_CODE
%x PARSED_CODE

%%
				 /*----- Sección de Reglas ----------------*/

{list}	{BEGIN(IN_MARKDOWN_LIST);fprintf(yyout, "\\begin{itemize}\n");}
{list2}	{BEGIN(IN_MARKDOWN_LIST2);fprintf(yyout, "\\begin{itemize}\n");}

<IN_MARKDOWN_LIST>{
	^\n	fprintf(yyout, "\\end{itemize}\n\n");BEGIN(INITIAL); /* si volvemos a detectar línea vacia, hemos acabado el itemize, o no era nada y salimos */
	^"* "	/* Eliminar la sintáxis de itemize en markdown */
	[^"*"\n]+	fprintf(yyout, "\t\\item {%s}\n", yytext);	/* Éste es el texto que compone cada línea del itemize */
	\n	yylineno++;BEGIN(IN_MARKDOWN_LIST);	/* Si detectamos salto de línea, aumentar el número de línea, y seguimos comprobando dentro de IN_MARKDOWN_LIST buscando más items*/
}

<IN_MARKDOWN_LIST2>{
	^\n	fprintf(yyout, "\\end{itemize}\n\n");BEGIN(INITIAL); /* si volvemos a detectar línea vacia, hemos acabado el itemize, o no era nada y salimos */
	^"- "	/* Eliminar la sintáxis de itemize en markdown */
	[^\-\n]+	fprintf(yyout, "\t\\item {%s}\n", yytext);	/* Éste es el texto que compone cada línea del itemize */
	\n	yylineno++;BEGIN(IN_MARKDOWN_LIST2);	/* Si detectamos salto de línea, aumentar el número de línea, y seguimos comprobando dentro de IN_MARKDOWN_LIST buscando más items*/
}

{multilineCode}	{
	fprintf(yyout, "\\begin{bashcode}\n%s\n", trimCharacter(yytext, "`"));
	BEGIN(IN_MULTILINE_CODE);
}

<IN_MULTILINE_CODE>{
	`{3}|~{3}	fprintf(yyout, "\\end{bashcode}"); BEGIN(INITIAL); // End of code syntax
}

{inlinecode}	{
	fprintf(yyout, "\\bashinline/%s/", trimCharacter(yytext, "`")); // Maybe verbatim
}

{h1}	{
	fprintf(yyout, "\\section{%s}", trimCharacter(yytext, "#"));
}
{h2}	{
	fprintf(yyout, "\\subsection{%s}", trimCharacter(yytext, "#"));
}
{h3}	{
	fprintf(yyout, "\\subsubsection{%s}", trimCharacter(yytext, "#"));
}

{scstrong}	{	from_italic_text=0; BEGIN(BOLD_TEXT_NESTED_ITALIC); /* Comienzo de un strong __....*/}
<BOLD_TEXT_NESTED_ITALIC>{
	"__"	fprintf(yyout, "}");BEGIN(INITIAL); // Eat the end and exit
	"_"	BEGIN(ITALIC_TEXT); 	// Hay otro elemento anidado, un italic, pasamos a procesarlo
	[^_\n]*	{
		if (from_italic_text)
			fprintf(yyout, "%s", yytext); // Texto a continuación del italic
		else
			fprintf(yyout, "\\textbf{%s", yytext);
	}
	\n	BEGIN(INITIAL);
}
<ITALIC_TEXT>{
	[^_\n]* fprintf(yyout, "\\textit{%s", yytext);
	"_"	fprintf(yyout, "}"); BEGIN(BOLD_TEXT_NESTED_ITALIC); from_italic_text = 1; /* Llegado al último _, cerramos }, volvemos al stado BOLD_TEXT y ponemos from_italic_text a 1 para saber que estuvimos aquí, y no cerra antes de tiempo el \textbf*/
}

{scem}	{from_bold_text=0; BEGIN(ITALIC_TEXT_NESTED_BOLD); /* Comienzo de un strong __....*/}
<ITALIC_TEXT_NESTED_BOLD>{
	"_"	fprintf(yyout, "}"); BEGIN(INITIAL); // Eat the end and exit
	"__"	BEGIN(BOLD_TEXT); 	// Hay otro elemento anidado, un italic, pasamos a procesarlo
	[^_\n]*	{
		if (from_bold_text)
			fprintf(yyout, "%s", yytext); // Texto a continuación del italic
		else
			fprintf(yyout, "\\textit{%s", yytext);
	}
\n	BEGIN(INITIAL);
}
<BOLD_TEXT>{
	[^_\n]* fprintf(yyout, "\\textbf{%s", yytext);
	"__"	fprintf(yyout, "}"); BEGIN(ITALIC_TEXT_NESTED_BOLD); from_bold_text = 1; /* Llegado al último _, cerramos }, volvemos al stado BOLD_TEXT y ponemos from_italic_text a 1 para saber que estuvimos aquí, y no cerra antes de tiempo el \textbf*/
}

%{
// Variables for holding the values of the matched string
char *linkText = NULL;
char *linkUrl = NULL;
%}
{link}	{	// We begin processing a markdown link if it starts with [
	BEGIN(IN_MARKDOWN_LINK);
}
<IN_MARKDOWN_LINK>{ // Once here, we get rid of
	"["		// the open [
	[^\]]*	{	// Here we match the link text, and store it
		linkText = (char*) ec_malloc(yyleng+1);
		strcpy(linkText, yytext);
		BEGIN(CLOSE_BRACKET); // Move to the closing ]
	}
}
<CLOSE_BRACKET>{
	"]"|"("|")"	// Ignore this chars.
	[^(")]*	{	// Here we store the link url
		linkUrl = (char*) ec_malloc(yyleng+1);
		strcpy(linkUrl, yytext);
		BEGIN(OPTIONAL_TEXT);
	}
}

<OPTIONAL_TEXT>{
	\"[^\")]+\"	// Ignore alt text "text"
	")"	{	// In latex, we have nothing to do with the tittle attribute of the link, so we ignore it. Ignore things like "alt text")
		fprintf(yyout, "\\href{%s}{%s}", linkUrl, linkText);
		free(linkText);
		free(linkUrl);
		BEGIN(INITIAL); // Ignore optional text
	}
}

%{
	// Ignore anything that can be matched in a block of text that is supposed to
	// be like it was originaly written, similar to a pre HTML tag.
%}
{ignoreParsedCode}	{ECHO; BEGIN(PARSED_CODE);}
<PARSED_CODE>{
	"\\end{bashcode}"	ECHO; BEGIN(INITIAL);
	.*	ECHO;
}

.|\n			{ECHO;}

%%
				/*----- Sección de Procedimientos --------*/

/**
 * Thanks to http://stackoverflow.com/a/122721/1612432
 * //fprintf(yyout, "\\textbf{%s}", trimCharacter(yytext, "_*"));
 **/
char *trimCharacter(char *str, char *c)
{
	char *end;

  // Trim leading characters
	while(strncmp(str, c, 1) == 0){
		str++;
	}

  if(*str == 0)  // All spaces?
    return str;

  // Trim trailing character
	end = str + strlen(str) - 1;
	while(end > str && (strncmp(end, c, 1) == 0)){
		end--;
	}

  // Write new null terminator
  *(end+1) = 0;

  return str;
}

// An error checked malloc() wrapper function (From the book Hacking, the art of exploitation)
void *ec_malloc(unsigned int size) {
   void *ptr;
   ptr = malloc(size);
   if(ptr == NULL)
      fatal("in ec_malloc() on memory allocation");
   return ptr;
}

// A function to display an error message and then exit (From the book Hacking, the art of exploitation)
void fatal(char *message) {
   char error_message[100];

   strcpy(error_message, "[!!] Fatal Error ");
   strncat(error_message, message, 83);
   perror(error_message);
   exit(-1);
}

int yywrap(){
	if (first_run == 1){
		first_run++;

		fclose(yyout);
		fclose(yyin);
		yyin = fopen ("/tmp/out", "rt");
		yyout = fopen("salida", "wt");

		if (yyin == NULL) {
			printf ("El fichero no se puede abrir\n");
			exit (-1);
		}
		if (yyout == NULL) {
			printf ("El fichero no se puede abrir\n");
			exit (-1);
		}
		BEGIN(INITIAL);
		return 0;
	} else {
		return 1;
	}
}

int main (int argc, char *argv[]) {
	if (argc == 2) {
		yyin = fopen (argv[1], "rt");
		if (yyin == NULL) {
			printf ("El fichero %s no se puede abrir\n", argv[1]);
			exit (-1);
		}
	} else
		yyin = stdin;

	yyout = fopen("/tmp/out", "wt");

	if (yyout == NULL) {
		printf ("El fichero %s no se puede abrir\n", argv[1]);
		exit (-1);
	}

	yylex ();

	return 0;
}

Referencias

Quizá también te interese leer...