Grammar Checking in Emacs

23 Sep 2013

Grammar checking is apparently an error prone task. Or at least it has been claimed: http://faculty.washington.edu/sandeep/check/

Looks like there is GNU diction, but it just highlights potentially misused words. It doesn't indicate where an error occurred. I'm not comfortable investing much time into getting this working with Emacs. Since there doesn't appear to be much return for the effort.

I came across Baoqiu Cui's EmacsWiki user page. Baoqui has created a grammar checker for Emacs and links to his package from his page. There aren't many details, but the screenshots on his user page appear promising. The package can be found here http://code.google.com/p/bcui-emacs/source/browse/#svn/trunk/grammar.

This will be my attempt at getting it working with Emacs.

According to Baoqiu's README file the package uses the Link Grammar Parser. The first step looks like I should install the Link Grammar Parser. The parser is available from MacPorts.

$ sudo port install link-grammar

As can bee seen in the grammar.cc in Baoqui's package we need the link-includes.h file to be on our system. Let's verify that the MacPorts package included that file.

$ port contents link-grammar | grep "link-includes"
  /opt/local/include/link-grammar/link-includes.h

So, yup this is included. The next thing we need to do is download Baoqui's package. We'll have to checkout the source code from its repo hosted on code.google.com. I like to keep third-party source code in my ~/src directory.

$ cd ~/src
$ svn checkout http://bcui-emacs.googlecode.com/svn/trunk/grammar grammar.svn
A    grammar.svn/grammar.el
A    grammar.svn/grammar.cc
A    grammar.svn/README
A    grammar.svn/Makefile
 U   grammar.svn
Checked out revision 51.

We need to fix up the Makefile so that gcc knows where to find the include file we installed with MacPorts. This is as simple as changing one line in the Makefile.

INCDIRS = -I. -I/opt/local/include/link-grammar

Next we'll attempt to run make. Hopefully this will compile without incident.

$ make
g++ -g -I. -I/opt/local/include/link-grammar -c grammar.cc
In file included from grammar.cc:28:
/opt/local/include/link-grammar/link-includes.h:16:40: error:
link-grammar/link-features.h: No such file or directory
...
grammar.cc:30: error: expected constructor, destructor, or type conversion before ‘int’
make: *** [grammar.o] Error 1

Looks like we got a few screen fulls of errors. Let's investigate the first. gcc says it can't find the file link-features.h. The first thing I want to do is check that it was included when we installed the Link Grammar Parser with MacPorts.

$ port contents link-grammar | grep "link-features"
  /opt/local/include/link-grammar/link-features.h

Okay, the file exist in the MacPorts package. This means something else is wrong. I suspect that the path we changed in the include file is wrong for the way link-includes.h is expecting our includes to be setup. My guess is that link-includes.h references the missing file using its parent directory and then the include file, so it would look like #include "link-grammar/link-features.h". This is a common pattern in C. We can easily check this using sed.

$ sed -n 16,16p /opt/local/include/link-grammar/link-includes.h
#include <link-grammar/link-features.h>

So, my theory was correct. Well have to fix the Makefile and fix the grammar.cc file.

Makefile

INCDIRS = -I. -I/opt/local/include

grammar.cc

#include "link-grammar/link-includes.h"

Now we're ready to try running make again. This run should compile correctly.

$ make
g++ -g -I. -I/opt/local/include -c grammar.cc
g++ -g -llink-grammar -o grammar grammar.o
ld: library not found for -llink-grammar
collect2: ld returned 1 exit status
make: *** [all] Error 1

This one is easy, it's just a linker error. Since we're using MacPorts we need to tell the linker where to find Link Grammar Parser's library file. Since we installed the library with MacPorts we know the library will be in /opt/local/lib. We just need fix the Makefile.

First change: add a LDFLAGS variable to the Makefile right after the INCDIRS variables.

LDFLAGS=-L/opt/local/lib

Next change the all rule to use the new LDFLAGS variable.

all: $(objects)
    g++ -g $(LDFLAGS) -llink-grammar -o grammar $(objects)

Now this thing should finally compile.

$ make
g++ -g -L/opt/local/lib -llink-grammar -o grammar grammar.o

Awesome! It worked. Now we can move on to getting this working in Emacs.

Copy the files into your ~/.emacs.d/site-lisp directory.

 $ mkdir -p ~/.emacs.d/site-lisp/grammar
 $ cp grammar ~/.emacs.d/site-lisp/grammar
 $ cp grammar.el ~/.emacs.d/site-lisp/grammar

Then add the following to your ~/.emacs file.

;; Grammar Checker
(add-to-list 'load-path "~/.emacs.d/site-lisp/grammar")
(add-to-list 'exec-path "/Users/eddie/.emacs.d/site-lisp/grammar")
(autoload 'grammar-mode "grammar" nil t)

Now we can try out grammar-mode. Switch to a buffer for typing (*scratch* is as good as any). Turn on grammar-mode by typing M-x grammar-mode. Now try typing one of the examples from Baoqiu's user page. You'll have to type two spaces or a newline after you type the period to get grammar-mode to check the sentence. I did example 2.

The books is good for beginning reader.

You'll see the words "The", "books", "for", and "reader" underlined with green. This means there are some grammar errors. You can, obviously, fix the errors by changing the sentence to the following. Remember you need to type two spaces or a newline after you type the period.

The books are good for beginning readers.

You can also use the mode's shortcut to run a check on the sentence before or at the current point. The shortcut is C-M-g.

Caveats: there isn't an interactive mode like ispell. So you have to check the grammar as you type it or one sentence at a time. You can't just type your document and as a final step check your grammar. The other big issue I have is that there is no description of the error. The last issue I have, which is least important, is that there are no suggestions for correcting the errors.