MI2 grammar

Message ID CAF5HaEXB6dUW-aP9cKDsy=QtJCaS-7YNLHMZx5bVsv-nMKon4w@mail.gmail.com
State New, archived
Headers

Commit Message

Daniel Gutson May 18, 2016, 7:55 p.m. UTC
  Hi,

   this is a very initial version of the MI grammar for lex and yacc.

This is actually a standalone application (containing C++ source code)
that accepts
the gdb MI output; the important part is the .y and .l files, the rest is just
a C++ thing using the parser.

I'm posting this here for two purposes:

 - so other people can use it if interested
 - to propose create an official grammar by adding the .l and .y files
somewhere to the
   source tree, and keeping them updated if the grammar changes.

As a side note, the quit command replies a nonconforming output since
the "(gdb)" string is not included,
as required in https://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Output-Syntax.html#GDB_002fMI-Output-Syntax

Thanks,

   Daniel.
  

Comments

Simon Marchi May 24, 2016, 6:23 p.m. UTC | #1
On 16-05-18 03:55 PM, Daniel Gutson wrote:
> Hi,
> 
>    this is a very initial version of the MI grammar for lex and yacc.
> 
> This is actually a standalone application (containing C++ source code)
> that accepts
> the gdb MI output; the important part is the .y and .l files, the rest is just
> a C++ thing using the parser.
> 
> I'm posting this here for two purposes:
> 
>  - so other people can use it if interested
>  - to propose create an official grammar by adding the .l and .y files
> somewhere to the
>    source tree, and keeping them updated if the grammar changes.
> 
> As a side note, the quit command replies a nonconforming output since
> the "(gdb)" string is not included,
> as required in https://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Output-Syntax.html#GDB_002fMI-Output-Syntax
> 
> Thanks,
> 
>    Daniel.

I am pretty sure that we'll forget to update this if the grammar evolves.  Things
like this tend to be forgotten when we update the code.

One way to avoid it would be to actually use it.  It would be awesome (I think) to
find a way to plug this in the test framework, and have it verify that everything gdb
outputs is valid MI (or maybe it could be baked in GDB itself when built in developer
mode).  I know there is at least one issue where GDB outputs bad MI:

  https://sourceware.org/bugzilla/show_bug.cgi?id=14733

but I wouldn't be surprised if there were other cases (more serious/subtle than
the quit example).

While I'm at it, I might as well plug this little piece of code that a friend and
I wrote, it might be useful to someone:

  https://github.com/simark/pygdbmi

It came out of a need to be able to pretty print MI output to make it readable,
writing/debugging some MI-outputting code.

Simon
  

Patch

diff -Naur ./nothing/examples/example1.mi ./MI/examples/example1.mi
--- ./nothing/examples/example1.mi    1969-12-31 21:00:00.000000000 -0300
+++ ./MI/examples/example1.mi    2016-05-18 14:41:26.814610554 -0300
@@ -0,0 +1,3 @@ 
+^done,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0x00000000004007c5",func="main()",file="shuffle.cpp",fullname="/home/dgutson/sandbox/shuffle.cpp",line="223",thread-groups=["i1"],times="0",original-location="main"}
+(gdb)
+
diff -Naur ./nothing/examples/example2.mi ./MI/examples/example2.mi
--- ./nothing/examples/example2.mi    1969-12-31 21:00:00.000000000 -0300
+++ ./MI/examples/example2.mi    2016-05-18 14:41:31.994610323 -0300
@@ -0,0 +1,17 @@ 
+^done,OSDataTable={nr_rows="9",nr_cols="3",hdr=[{width="10",alignment="-1",col_name="col0",colhdr="Type"},{width="10",alignment="-1",col_name="col1",colhdr="Description"},{width="10",alignment="-1",col_name="col2",colhdr="Title"}],body=[item={col0="processes",col1="Listing
+of all processes",col2="Processes"},item={col0="procgroups",col1="Listing
+of all process groups",col2="Process
+groups"},item={col0="threads",col1="Listing of all
+threads",col2="Threads"},item={col0="files",col1="Listing of all file
+descriptors",col2="File
+descriptors"},item={col0="sockets",col1="Listing of all
+internet-domain
+sockets",col2="Sockets"},item={col0="shm",col1="Listing of all
+shared-memory regions",col2="Shared-memory
+regions"},item={col0="semaphores",col1="Listing of all
+semaphores",col2="Semaphores"},item={col0="msg",col1="Listing of all
+message queues",col2="Message
+queues"},item={col0="modules",col1="Listing of all loaded kernel
+modules",col2="Kernel modules"}]}
+(gdb)
+
diff -Naur ./nothing/examples/example3.mi ./MI/examples/example3.mi
--- ./nothing/examples/example3.mi    1969-12-31 21:00:00.000000000 -0300
+++ ./MI/examples/example3.mi    2016-05-18 15:30:56.374478158 -0300
@@ -0,0 +1,22 @@ 
+=thread-group-added,id="i1"
+~"GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1\n"
+~"Copyright (C) 2014 Free Software Foundation, Inc.\n"
+~"License GPLv3+: GNU GPL version 3 or later
<http://gnu.org/licenses/gpl.html>\nThis is free software: you are
free to change and redistribute it.\nThere is NO WARRANTY, to the
extent permitted by law.  Type \"show copying\"\nand \"show warranty\"
for details.\n"
+~"This GDB was configured as \"x86_64-linux-gnu\".\nType \"show
configuration\" for configuration details."
+~"\nFor bug reporting instructions, please see:\n"
+~"<http://www.gnu.org/software/gdb/bugs/>.\n"
+~"Find the GDB manual and other documentation resources online
at:\n<http://www.gnu.org/software/gdb/documentation/>.\n"
+~"For help, type \"help\".\n"
+~"Type \"apropos word\" to search for commands related to \"word\"...\n"
+=cmd-param-changed,param="print object",value="on"
+~"Reading symbols from ../bin/mi..."
+~"(no debugging symbols found)...done.\n"
+(gdb)
+^done,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0x0000000000403d8b",at="<main+4>",thread-groups=["i1"],times="0",original-location="main"}
+(gdb)
+^done,OSDataTable={nr_rows="9",nr_cols="3",hdr=[{width="10",alignment="-1",col_name="col0",colhdr="Type"},{width="10",alignment="-1",col_name="col1",colhdr="Description"},{width="10",alignment="-1",col_name="col2",colhdr="Title"}],body=[item={col0="processes",col1="Listing
of all processes",col2="Processes"},item={col0="procgroups",col1="Listing
of all process groups",col2="Process
groups"},item={col0="threads",col1="Listing of all
threads",col2="Threads"},item={col0="files",col1="Listing of all file
descriptors",col2="File
descriptors"},item={col0="sockets",col1="Listing of all
internet-domain
sockets",col2="Sockets"},item={col0="shm",col1="Listing of all
shared-memory regions",col2="Shared-memory
regions"},item={col0="semaphores",col1="Listing of all
semaphores",col2="Semaphores"},item={col0="msg",col1="Listing of all
message queues",col2="Message
queues"},item={col0="modules",col1="Listing of all loaded kernel
modules",col2="Kernel modules"}]}
+(gdb)
+^done,OSDataTable={nr_rows="9",nr_cols="3",hdr=[{width="10",alignment="-1",col_name="col0",colhdr="Type"},{width="10",alignment="-1",col_name="col1",colhdr="Description"},{width="10",alignment="-1",col_name="col2",colhdr="Title"}],body=[item={col0="processes",col1="Listing
of all processes",col2="Processes"},item={col0="procgroups",col1="Listing
of all process groups",col2="Process
groups"},item={col0="threads",col1="Listing of all
threads",col2="Threads"},item={col0="files",col1="Listing of all file
descriptors",col2="File
descriptors"},item={col0="sockets",col1="Listing of all
internet-domain
sockets",col2="Sockets"},item={col0="shm",col1="Listing of all
shared-memory regions",col2="Shared-memory
regions"},item={col0="semaphores",col1="Listing of all
semaphores",col2="Semaphores"},item={col0="msg",col1="Listing of all
message queues",col2="Message
queues"},item={col0="modules",col1="Listing of all loaded kernel
modules",col2="Kernel modules"}]}
+(gdb)
+&"quit\n"
+(gdb)
diff -Naur ./nothing/main.cpp ./MI/main.cpp
--- ./nothing/main.cpp    1969-12-31 21:00:00.000000000 -0300
+++ ./MI/main.cpp    2016-05-18 16:42:18.510287243 -0300
@@ -0,0 +1,50 @@ 
+/*
+   Main function.
+   Copyright (C) 2016 Roman Alarcon, Taller Technologies S.A.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <cstdlib>
+#include <iostream>
+#include "MIParserController.h"
+
+int main(int argc, char **argv)
+{
+    int ret = EXIT_SUCCESS;
+
+    std::clog << "mi parser - v0.2" << std::endl;
+
+    MIParserController controller;
+
+    switch(argc)
+    {
+        case 1:
+            controller.runOverStdIn();
+            break;
+
+        case 2:
+            controller.runOverFile(argv[1]);
+            break;
+
+        default:
+            std::cerr << "Usage: " << argv[0] << " [input-file]" << std::endl;
+            ret = EXIT_FAILURE;
+    }
+
+    if(ret == EXIT_SUCCESS)
+        std::cout << "DONE." << std::endl;
+
+    return ret;
+}
diff -Naur ./nothing/Makefile ./MI/Makefile
--- ./nothing/Makefile    1969-12-31 21:00:00.000000000 -0300
+++ ./MI/Makefile    2016-05-18 13:54:30.000000000 -0300
@@ -0,0 +1,36 @@ 
+CC=g++
+CFLAGS=-c -Wno-write-strings -Wreturn-type
+LDFLAGS=
+SOURCES=MIFlexScanner.cpp MIBisonParser.cpp MIParserController.cpp main.cpp
+OBJECTS=$(SOURCES:.cpp=.o)
+EXECUTABLE_PATH=../bin
+EXECUTABLE="$(EXECUTABLE_PATH)/mi"
+LIBPATH=
+INCPATH=
+LIBS=
+
+all: $(SOURCES) $(EXECUTABLE)
+
+clean:
+    rm -f $(EXECUTABLE)
+    rm -f $(OBJECTS)
+    rm -f *.cpp~
+    rm -f *.h~
+    rm -f MIFlexScanner.cpp MIBisonParser.cpp MIBisonParser.h
+
+$(EXECUTABLE): $(OBJECTS)
+    @if [ ! -d "$(EXECUTABLE_PATH)" ]; then \
+       mkdir "$(EXECUTABLE_PATH)"; \
+    fi
+    $(CC) $(LDFLAGS) $(OBJECTS) -o $@ $(LIBPATH) $(LIBS)
+
+MIBisonParser.cpp: MIGrammar.y
+    bison -d MIGrammar.y
+    mv MIGrammar.tab.c MIBisonParser.cpp
+    mv MIGrammar.tab.h MIBisonParser.h
+
+MIFlexScanner.cpp: MILexer.l
+    flex -oMIFlexScanner.cpp MILexer.l
+
+.cpp.o:
+    $(CC) $(INCPATH) $(CFLAGS) $< -o $@
diff -Naur ./nothing/MIGrammar.y ./MI/MIGrammar.y
--- ./nothing/MIGrammar.y    1969-12-31 21:00:00.000000000 -0300
+++ ./MI/MIGrammar.y    2016-05-18 16:35:21.378305840 -0300
@@ -0,0 +1,259 @@ 
+/*
+   MI Grammar specification.
+   Copyright (C) 2016 Roman Alarcon, Taller Technologies S.A.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+%{
+
+#include <cstdio>
+#include "MIParserDelegate.h"
+
+/*
+ * "Private" variables
+ */
+static MIParserDelegate *_parserDelegate;
+
+/*
+ * Imported functions
+ */
+extern int yylex();
+extern std::string getLexLastToken();
+extern int getLexLineNumber();
+extern int getLexColumnNumber();
+
+/*
+ * Exported functions
+ */
+void setParserDelegate(MIParserDelegate *parserDelegate);
+
+static int yyerror(char *s);
+std::string removeDoubleQuote(const std::string& inputString);
+
+%}
+
+%union
+{
+    char* stringValue;
+}
+
+%token MI_GDB
+%token MI_THREAD_GROUP_ADDED
+%token MI_CMD_PARAM_CHANGED
+%token MI_DONE
+%token MI_RUNNING
+%token MI_CONNECTED
+%token MI_ERROR
+%token MI_EXIT
+%token MI_STOPPED
+%token MI_CONST
+%token MI_EMPTY_TUPLE
+%token MI_EMPTY_LIST
+%token MI_CR
+%token MI_CRLF
+%token <stringValue> MI_NUMBER
+%token <stringValue> MI_STRING
+%token <stringValue> MI_IDENTIFIER
+
+%%
+
+outputList          : outputList output
+                      {
+                      }
+                    | output
+                      {
+                      }
+                    ;
+
+output              : outOfBandRecordList resultRecord MI_GDB
+                      {
+                      }
+                    ;
+
+outOfBandRecordList : outOfBandRecordList outOfBandRecord
+                      {
+                      }
+                    | /* EPSILON */
+                      {
+                      }
+                    ;
+
+outOfBandRecord     : tokenOpt asyncRecord
+                      {
+                      }
+                    | streamRecord
+                      {
+                      }
+                    ;
+
+asyncRecord         : '*' asyncOutput
+                      {
+                      }
+                    | '+' asyncOutput
+                      {
+                      }
+                    | '=' asyncOutput
+                      {
+                      }
+                    ;
+
+asyncOutput         : asyncClass
+                      {
+                      }
+                    | asyncClass ',' resultList
+                      {
+                      }
+                    ;
+
+asyncClass          : MI_STOPPED
+                      {
+
+                      }
+                    | MI_THREAD_GROUP_ADDED
+                      {
+
+                      }
+                    | MI_CMD_PARAM_CHANGED
+                      {
+
+                      }
+                    ;
+
+resultRecord        : tokenOpt '^' resultClass ',' resultListOpt
+                      {
+                      }
+                    | /* EPSILON */
+                      {
+                      }
+                    ;
+
+resultClass         : MI_DONE
+                      {
+                      }
+                    | MI_RUNNING
+                      {
+                      }
+                    | MI_CONNECTED
+                      {
+                      }
+                    | MI_ERROR
+                      {
+                      }
+                    | MI_EXIT
+                      {
+                      }
+                    ;
+
+resultListOpt       : resultList
+                      {
+                      }
+                    | /* EPSILON */
+                      {
+                      }
+                    ;
+
+resultList          : resultList ',' result
+                      {
+                      }
+                    | result
+                      {
+                      }
+                    ;
+
+result              : MI_IDENTIFIER '=' value
+                      {
+                        free($1);
+                      }
+                    ;
+
+value               : MI_NUMBER
+                      {
+                        free($1);
+                      }
+                    | MI_STRING
+                      {
+                        free($1);
+                      }
+                    | tuple
+                      {
+                      }
+                    | list
+                      {
+                      }
+                    ;
+
+tuple               : MI_EMPTY_TUPLE
+                      {
+                      }
+                    | '{' resultList '}'
+                      {
+                      }
+                    ;
+
+list                : MI_EMPTY_LIST
+                      {
+                      }
+                    | '[' valueList ']'
+                      {
+                      }
+                    | '[' resultList ']'
+                      {
+                      }
+                    ;
+
+valueList           : valueList ',' value
+                      {
+                      }
+                    | value
+                      {
+                      }
+                    ;
+
+streamRecord        : '~' MI_STRING
+                      {
+                        free($2);
+                      }
+                    | '@' MI_STRING
+                      {
+                        free($2);
+                      }
+                    | '&' MI_STRING
+                      {
+                        free($2);
+                      }
+                    ;
+
+tokenOpt            : MI_NUMBER
+                      {
+                        free($1);
+                      }
+                    | /* EPSILON */
+                      {
+                      }
+                    ;
+
+%%
+
+void setParserDelegate(MIParserDelegate *parserDelegate)
+{
+    _parserDelegate = parserDelegate;
+}
+
+int yyerror(char *s)
+{
+    _parserDelegate->notifySyntaxError(getLexLastToken());
+
+    return 0;
+}
diff -Naur ./nothing/MILexer.l ./MI/MILexer.l
--- ./nothing/MILexer.l    1969-12-31 21:00:00.000000000 -0300
+++ ./MI/MILexer.l    2016-05-18 16:43:23.558284342 -0300
@@ -0,0 +1,135 @@ 
+/*
+   MI Scanner specification.
+   Copyright (C) 2016 Roman Alarcon, Taller Technologies S.A.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+%{
+
+#include <assert.h>
+#include <string>
+#include <cstdio>
+#include "MIBisonParser.h"
+
+/*
+ * "Private" variables
+ */
+static std::string _lastToken;
+
+/*
+ * Functions able to be accessed from outside
+ */
+int setInputFile(const char * filename);
+std::string getLexLastToken();
+
+/*
+ * "Private" functions
+ */
+static void updateLastToken(const char * token);
+static void skipSingleLineComment();
+
+%}
+
+digit                   [0-9]
+letter                  [A-Za-z_\-]
+no_quote_or_backslash   [^\"\\]
+utf8char                \\(u|U)[0-9A-Fa-f]+
+escapechar              \\[^uU]
+string                  \"({no_quote_or_backslash}|{utf8char}|{escapechar})*\"
+identifier              {letter}({letter}|{digit})*
+number                  (0|[1-9]{digit}*)
+
+%%
+
+[\t\r\n ]+            {}
+"(gdb)"               { updateLastToken(yytext); return MI_GDB;
         }
+"thread-group-added"  { updateLastToken(yytext); return
MI_THREAD_GROUP_ADDED; }
+"cmd-param-changed"   { updateLastToken(yytext); return
MI_CMD_PARAM_CHANGED;  }
+"done"                { updateLastToken(yytext); return MI_DONE;
         }
+"running"             { updateLastToken(yytext); return MI_RUNNING;
         }
+"connected"           { updateLastToken(yytext); return MI_CONNECTED;
         }
+"error"               { updateLastToken(yytext); return MI_ERROR;
         }
+"exit"                { updateLastToken(yytext); return MI_EXIT;
         }
+"stopped"             { updateLastToken(yytext); return MI_STOPPED;
         }
+"{}"                  { updateLastToken(yytext); return
MI_EMPTY_TUPLE;        }
+"[]"                  { updateLastToken(yytext); return
MI_EMPTY_LIST;         }
+
+{string}              {
+                          yylval.stringValue = strdup(yytext);
+                          updateLastToken(yytext);
+                          return MI_STRING;
+                      }
+
+{number}              {
+                          yylval.stringValue = strdup(yytext);
+                          updateLastToken(yytext);
+                          return MI_NUMBER;
+                      }
+
+{identifier}          {
+                          yylval.stringValue = strdup(yytext);
+                          updateLastToken(yytext);
+                          return MI_IDENTIFIER;
+                      }
+
+.                     {
+                          updateLastToken((std::string(&yytext[0],
1)).c_str());
+                          return yytext[0];
+                      }
+
+%%
+
+/**
+ * setInputFile: sets the file to be scanned
+ * Return values: 0 means OK, file is opened to read. 1 means error.
+ */
+int setInputFile(const char* filename)
+{
+    assert(filename != NULL);
+
+    if (filename[0] != 0)
+        yyin = std::fopen(filename, "r");
+    else
+        yyin = stdin;
+    return (yyin != NULL);
+}
+
+/**
+ * getLexLastToken: returns the last token read
+ */
+std::string getLexLastToken()
+{
+    return _lastToken;
+}
+
+/**
+ *
+ */
+static void updateLastToken(const char* token)
+{
+    _lastToken = std::string(yytext);
+
+    // Uncomment out to see list of tokens
+    // std::printf("Token '%s'\n", _lastToken.c_str());
+}
+
+/**
+ *
+ */
+int yywrap()
+{
+    return 1;
+}
+
diff -Naur ./nothing/MIParserController.cpp ./MI/MIParserController.cpp
--- ./nothing/MIParserController.cpp    1969-12-31 21:00:00.000000000 -0300
+++ ./MI/MIParserController.cpp    2016-05-18 15:48:00.710432489 -0300
@@ -0,0 +1,54 @@ 
+/*
+   MIParserController - Wrapper for C-based parser
+   Copyright (C) 2016 Roman Alarcon, Taller Technologies S.A.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <iostream>
+#include "MIParserController.h"
+
+extern int yyparse();
+extern int setInputFile(const char * filename);
+void setParserDelegate(MIParserDelegate *parserDelegate);
+
+
+MIParserController::MIParserController()
+{
+    setParserDelegate(this);
+}
+
+MIParserController::~MIParserController()
+{
+}
+
+void MIParserController::runOverFile(const std::string& inputFile)
+{
+    setInputFile(inputFile.c_str());
+    if(yyparse() == 0)
+    {
+        std::cout << "Parsing was successful." << std::endl;
+    }
+}
+
+void MIParserController::runOverStdIn()
+{
+    runOverFile(std::string());
+}
+
+void MIParserController::notifySyntaxError(const std::string& token)
+{
+    std::cerr << "Error: unexpected token '" <<  token << "'" << std::endl;
+}
+
diff -Naur ./nothing/MIParserController.h ./MI/MIParserController.h
--- ./nothing/MIParserController.h    1969-12-31 21:00:00.000000000 -0300
+++ ./MI/MIParserController.h    2016-05-18 15:46:15.706437171 -0300
@@ -0,0 +1,38 @@ 
+/*
+   MIParserController - Wrapper for C-based parser
+   Copyright (C) 2016 Roman Alarcon, Taller Technologies S.A.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __MIPARSERCONTROLLER_H
+#define __MIPARSERCONTROLLER_H
+
+#include "MIParserDelegate.h"
+
+class MIParserController: private MIParserDelegate
+{
+public:
+    MIParserController();
+    ~MIParserController();
+
+    void runOverFile(const std::string& inputFile);
+    void runOverStdIn();
+
+private:
+    virtual void notifySyntaxError(const std::string& token);
+};
+
+#endif
+
diff -Naur ./nothing/MIParserDelegate.h ./MI/MIParserDelegate.h
--- ./nothing/MIParserDelegate.h    1969-12-31 21:00:00.000000000 -0300
+++ ./MI/MIParserDelegate.h    2016-05-18 14:39:36.000000000 -0300
@@ -0,0 +1,30 @@ 
+/*
+   MIParserDelegate - Callbacks declarations
+   Copyright (C) 2016 Roman Alarcon, Taller Technologies S.A.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __MIPARSERDELEGATE_H
+#define __MIPARSERDELEGATE_H
+
+#include <string>
+
+class MIParserDelegate
+{
+public:
+    virtual void notifySyntaxError(const std::string& token) = 0;
+};
+
+#endif