Skip to content

Commit

Permalink
add spell checking
Browse files Browse the repository at this point in the history
  • Loading branch information
Walter Bright committed Mar 5, 2010
1 parent 993c2a9 commit 8c55b9f
Show file tree
Hide file tree
Showing 12 changed files with 204 additions and 10 deletions.
24 changes: 23 additions & 1 deletion src/dsymbol.c
@@ -1,6 +1,6 @@

// Compiler implementation of the D programming language
// Copyright (c) 1999-2009 by Digital Mars
// Copyright (c) 1999-2010 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
Expand All @@ -13,6 +13,7 @@
#include <assert.h>

#include "rmem.h"
#include "speller.h"

#include "mars.h"
#include "dsymbol.h"
Expand Down Expand Up @@ -332,6 +333,27 @@ Dsymbol *Dsymbol::search(Loc loc, Identifier *ident, int flags)
return NULL;
}

/***************************************************
* Search for symbol with correct spelling.
*/

void *symbol_search_fp(void *arg, const char *seed)
{
Dsymbol *s = (Dsymbol *)arg;
Identifier id(seed, 0);
Module::clearCache();
s = s->search(0, &id, 4|2);
return s;
}

Dsymbol *Dsymbol::search_correct(Identifier *ident)
{
if (global.gag)
return NULL; // don't do it for speculative compiles; too time consuming

return (Dsymbol *)speller(ident->toChars(), &symbol_search_fp, this, idchars);
}

/***************************************
* Search for identifier id as a member of 'this'.
* id may be a template instance.
Expand Down
3 changes: 2 additions & 1 deletion src/dsymbol.h
@@ -1,6 +1,6 @@

// Compiler implementation of the D programming language
// Copyright (c) 1999-2009 by Digital Mars
// Copyright (c) 1999-2010 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
Expand Down Expand Up @@ -147,6 +147,7 @@ struct Dsymbol : Object
virtual void semantic3(Scope *sc);
virtual void inlineScan();
virtual Dsymbol *search(Loc loc, Identifier *ident, int flags);
Dsymbol *search_correct(Identifier *id);
Dsymbol *searchX(Loc loc, Scope *sc, Identifier *id);
virtual int overloadInsert(Dsymbol *s);
#ifdef _DH
Expand Down
16 changes: 15 additions & 1 deletion src/expression.c
Expand Up @@ -2091,7 +2091,21 @@ Expression *IdentifierExp::semantic(Scope *sc)
}
return e->semantic(sc);
}
error("undefined identifier %s", ident->toChars());
#if DMDV2
if (ident == Id::ctfe)
{ // Create the magic __ctfe bool variable
VarDeclaration *vd = new VarDeclaration(loc, Type::tbool, Id::ctfe, NULL);
Expression *e = new VarExp(loc, vd);
e = e->semantic(sc);
return e;
}
#endif

s = sc->search_correct(ident);
if (s)
error("undefined identifier %s, did you mean %s %s?", ident->toChars(), s->kind(), s->toChars());
else
error("undefined identifier %s", ident->toChars());
type = Type::terror;
return this;
}
Expand Down
8 changes: 8 additions & 0 deletions src/module.c
Expand Up @@ -864,6 +864,7 @@ void Module::gensymfile()

int Module::needModuleInfo()
{
//printf("needModuleInfo() %s, %d, %d\n", toChars(), needmoduleinfo, global.params.cov);
return needmoduleinfo || global.params.cov;
}

Expand Down Expand Up @@ -902,6 +903,13 @@ Dsymbol *Module::symtabInsert(Dsymbol *s)
return Package::symtabInsert(s);
}

void Module::clearCache()
{
for (int i = 0; i < amodules.dim; i++)
{ Module *m = (Module *)amodules.data[i];
m->searchCacheIdent = NULL;
}
}

/*******************************************
* Can't run semantic on s now, try again later.
Expand Down
1 change: 1 addition & 0 deletions src/module.h
Expand Up @@ -141,6 +141,7 @@ struct Module : Package
void deleteObjFile();
void addDeferredSemantic(Dsymbol *s);
static void runDeferredSemantic();
static void clearCache();
int imports(Module *m);

// Back end
Expand Down
14 changes: 11 additions & 3 deletions src/mtype.c
Expand Up @@ -592,7 +592,7 @@ Expression *Type::getProperty(Loc loc, Identifier *ident)
else if (ident == Id::size)
{
error(loc, ".size property should be replaced with .sizeof");
e = new IntegerExp(loc, size(loc), Type::tsize_t);
e = new ErrorExp();
}
else if (ident == Id::alignof)
{
Expand Down Expand Up @@ -630,8 +630,16 @@ Expression *Type::getProperty(Loc loc, Identifier *ident)
}
else
{
error(loc, "no property '%s' for type '%s'", ident->toChars(), toChars());
e = new IntegerExp(loc, 1, Type::tint32);
Dsymbol *s = NULL;
if (ty == Tstruct || ty == Tclass || ty == Tenum || ty == Ttypedef)
s = toDsymbol(NULL);
if (s)
s = s->search_correct(ident);
if (s)
error(loc, "no property '%s' for type '%s', did you mean '%s'?", ident->toChars(), toChars(), s->toChars());
else
error(loc, "no property '%s' for type '%s'", ident->toChars(), toChars());
e = new ErrorExp();
}
return e;
}
Expand Down
98 changes: 98 additions & 0 deletions src/root/speller.c
@@ -0,0 +1,98 @@

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "speller.h"

const char idchars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";

/**************************************************
* Looks for correct spelling.
* Currently only looks a 'distance' of one from the seed[].
* This does an exhaustive search, so can potentially be very slow.
* Input:
* seed wrongly spelled word
* fp search function
* fparg argument to search function
* charset character set
* Returns:
* NULL no correct spellings found
* void* value returned by fp() for first possible correct spelling
*/

void *speller(const char *seed, fp_speller_t fp, void *fparg, const char *charset)
{
size_t seedlen = strlen(seed);
if (!seedlen)
return NULL;

char *buf = (char *)alloca(seedlen + 2); // leave space for extra char
if (!buf)
return NULL; // no matches

/* Deletions */
memcpy(buf, seed + 1, seedlen);
for (int i = 0; i < seedlen; i++)
{
//printf("del buf = '%s'\n", buf);
void *p = (*fp)(fparg, buf);
if (p)
return p;

buf[i] = seed[i];
}

/* Transpositions */
memcpy(buf, seed, seedlen + 1);
for (int i = 0; i + 1 < seedlen; i++)
{
// swap [i] and [i + 1]
buf[i] = seed[i + 1];
buf[i + 1] = seed[i];

//printf("tra buf = '%s'\n", buf);
void *p = (*fp)(fparg, buf);
if (p)
return p;

buf[i] = seed[i];
}

if (charset && *charset)
{
/* Substitutions */
memcpy(buf, seed, seedlen + 1);
for (int i = 0; i < seedlen; i++)
{
for (const char *s = charset; *s; s++)
{
buf[i] = *s;

//printf("sub buf = '%s'\n", buf);
void *p = (*fp)(fparg, buf);
if (p)
return p;
}
buf[i] = seed[i];
}

/* Insertions */
memcpy(buf + 1, seed, seedlen + 1);
for (int i = 0; i <= seedlen; i++) // yes, do seedlen+1 iterations
{
for (const char *s = charset; *s; s++)
{
buf[i] = *s;

//printf("ins buf = '%s'\n", buf);
void *p = (*fp)(fparg, buf);
if (p)
return p;
}
buf[i] = seed[i]; // going past end of seed[] is ok, as we hit the 0
}
}

return NULL; // didn't find any corrections
}
7 changes: 7 additions & 0 deletions src/root/speller.h
@@ -0,0 +1,7 @@

typedef void *(fp_speller_t)(void *, const char *);

extern const char idchars[];

void *speller(const char *seed, fp_speller_t fp, void *fparg, const char *charset);

27 changes: 26 additions & 1 deletion src/scope.c
@@ -1,5 +1,5 @@

// Copyright (c) 1999-2005 by Digital Mars
// Copyright (c) 1999-2010 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
Expand All @@ -11,6 +11,7 @@
#include <assert.h>

#include "root.h"
#include "speller.h"

#include "mars.h"
#include "init.h"
Expand Down Expand Up @@ -359,3 +360,27 @@ void Scope::setNoFree()
//assert(0);
}
}


/************************************************
* Given the failed search attempt, try to find
* one with a close spelling.
*/

void *scope_search_fp(void *arg, const char *seed)
{
//printf("scope_search_fp('%s')\n", seed);
Scope *sc = (Scope *)arg;
Identifier id(seed, 0);
Module::clearCache();
Dsymbol *s = sc->search(0, &id, NULL);
return s;
}

Dsymbol *Scope::search_correct(Identifier *ident)
{
if (global.gag)
return NULL; // don't do it for speculative compiles; too time consuming

return (Dsymbol *)speller(ident->toChars(), &scope_search_fp, this, idchars);
}
1 change: 1 addition & 0 deletions src/scope.h
Expand Up @@ -110,6 +110,7 @@ struct Scope
void mergeCallSuper(Loc loc, unsigned cs);

Dsymbol *search(Loc loc, Identifier *ident, Dsymbol **pscopesym);
Dsymbol *search_correct(Identifier *ident);
Dsymbol *insert(Dsymbol *s);

ClassDeclaration *getClassScope();
Expand Down
7 changes: 6 additions & 1 deletion src/template.c
Expand Up @@ -3659,7 +3659,12 @@ TemplateDeclaration *TemplateInstance::findTemplateDeclaration(Scope *sc)
id = name;
s = sc->search(loc, id, &scopesym);
if (!s)
{ error("template '%s' is not defined", id->toChars());
{
s = sc->search_correct(id);
if (s)
error("template '%s' is not defined, did you mean %s?", id->toChars(), s->toChars());
else
error("template '%s' is not defined", id->toChars());
return NULL;
}
#if LOG
Expand Down
8 changes: 6 additions & 2 deletions src/win32.mak
Expand Up @@ -96,7 +96,7 @@ OBJ8= go.obj gdag.obj gother.obj gflow.obj gloop.obj var.obj el.obj \
# from ROOT

ROOTOBJS= lstring.obj array.obj gnuc.obj man.obj rmem.obj port.obj root.obj \
stringtable.obj dchar.obj response.obj async.obj
stringtable.obj dchar.obj response.obj async.obj speller.obj

OBJS= $(OBJ1) $(OBJ8) $(ROOTOBJS)

Expand Down Expand Up @@ -149,7 +149,8 @@ ROOTSRC= $(ROOT)\dchar.h $(ROOT)\dchar.c $(ROOT)\lstring.h \
$(ROOT)\rmem.h $(ROOT)\rmem.c $(ROOT)\port.h \
$(ROOT)\stringtable.h $(ROOT)\stringtable.c \
$(ROOT)\gnuc.h $(ROOT)\gnuc.c $(ROOT)\man.c $(ROOT)\port.c \
$(ROOT)\response.c $(ROOT)\async.h $(ROOT)\async.c
$(ROOT)\response.c $(ROOT)\async.h $(ROOT)\async.c \
$(ROOT)\speller.h $(ROOT)\speller.c

MAKEFILES=win32.mak linux.mak osx.mak freebsd.mak solaris.mak

Expand Down Expand Up @@ -420,6 +421,9 @@ root.obj : $(ROOT)\root.c
response.obj : $(ROOT)\response.c
$(CC) -c $(CFLAGS) $(ROOT)\response.c

speller.obj : $(ROOT)\speller.h $(ROOT)\speller.c
$(CC) -c $(CFLAGS) $(ROOT)\speller.c

stringtable.obj : $(ROOT)\stringtable.c
$(CC) -c $(CFLAGS) $(ROOT)\stringtable.c

Expand Down

0 comments on commit 8c55b9f

Please sign in to comment.