Autor

Alejandro Alcalde

Graduado en Ingeniería Informática en la ETSIIT, Granada. Creador de El Baúl del Programador

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 :-).


¿Te gusta el blog? Ayúdame 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

¿Has visto algún error?: Por favor, ayúdame a corregirlo contactando conmigo.

Categorías:Etiquetas:

Quizá también te interese leer...