// Scintilla source code edit control
/** @file LexCrontab.cxx
 ** Lexer to use with extended crontab files used by a powerful
 ** Windows scheduler/event monitor/automation manager nnCron.
 ** (http://nemtsev.eserv.ru/)
 **/
// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.

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

#include "Platform.h"

#include "PropSet.h"
#include "Accessor.h"
#include "KeyWords.h"
#include "Scintilla.h"
#include "SciLexer.h"

#ifdef SCI_NAMESPACE
using namespace Scintilla;
#endif

bool is_whitespace(int ch){
    return ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ';
}

bool is_blank(int ch){
    return ch == '\t' || ch == ' ';
}
//#define FORTH_DEBUG
#ifdef FORTH_DEBUG
static FILE *f_debug;
#define log(x)  fputs(f_debug,x);
#else
#define log(x)
#endif

#define STATE_LOCALE
#define BL ' '

static Accessor *st;
static int cur_pos,pos1,pos2,pos0,lengthDoc;
char *buffer;

char getChar(bool is_bl){
    char ch=st->SafeGetCharAt(cur_pos);
    if(is_bl) if(is_whitespace(ch)) ch=BL;
    return ch;
}

char getCharBL(){
    char ch=st->SafeGetCharAt(cur_pos);
    return ch;
}
bool is_eol(char ch){
    return ch=='\n' || ch=='\r';
}

int parse(char ch, bool skip_eol){
// pos1 - start pos of word
// pos2 - pos after of word
// pos0 - start pos
    char c=0;
    int len;
    bool is_bl=ch==BL;
    pos0=pos1=pos2=cur_pos;
    for(;cur_pos<lengthDoc && (c=getChar(is_bl))==ch; cur_pos++){
        if(is_eol(c) && !skip_eol){
            pos2=pos1;
            return 0;
        }
    }
    pos1=cur_pos;
    pos2=pos1;
    if(cur_pos==lengthDoc) return 0;
    for(len=0;cur_pos<lengthDoc && (c=getChar(is_bl))!=ch; cur_pos++){
        if(is_eol(c) && !skip_eol) break;
        pos2++;
        buffer[len++]=c;
    }
    if(c==ch) pos2--;
    buffer[len]='\0';
#ifdef FORTH_DEBUG
    fprintf(f_debug,"parse: %c %s\n",ch,buffer);
#endif
    return len;
}

bool _is_number(char *s,int base){
    for(;*s;s++){
        int digit=((int)*s)-(int)'0';
#ifdef FORTH_DEBUG
    fprintf(f_debug,"digit: %c %d\n",*s,digit);
#endif
        if(digit>9 && base>10) digit-=7;
        if(digit<0) return false;
        if(digit>=base) return false;
    }
    return true;
}

bool is_number(char *s){
    if(strncmp(s,"0x",2)==0) return _is_number(s+2,16);
    return _is_number(s,10);
}

static void ColouriseForthDoc(unsigned int startPos, int length, int, WordList *keywordLists[], Accessor &styler)
{
    st=&styler;
    cur_pos=startPos;
    lengthDoc = startPos + length;
    buffer = new char[length];

#ifdef FORTH_DEBUG
    f_debug=fopen("c:\\sci.log","at");
#endif

    WordList &control = *keywordLists[0];
    WordList &keyword = *keywordLists[1];
    WordList &defword = *keywordLists[2];
    WordList &preword1 = *keywordLists[3];
    WordList &preword2 = *keywordLists[4];
    WordList &strings = *keywordLists[5];

    // go through all provided text segment
    // using the hand-written state machine shown below
    styler.StartAt(startPos);
    styler.StartSegment(startPos);
    while(parse(BL,true)!=0){
        if(pos0!=pos1){
            styler.ColourTo(pos0,SCE_FORTH_DEFAULT);
            styler.ColourTo(pos1-1,SCE_FORTH_DEFAULT);
        }
        if(strcmp("\\",buffer)==0){
            styler.ColourTo(pos1,SCE_FORTH_COMMENT);
            parse(1,false);
            styler.ColourTo(pos2,SCE_FORTH_COMMENT);
        }else if(strcmp("(",buffer)==0){
            styler.ColourTo(pos1,SCE_FORTH_COMMENT);
            parse(')',true);
            if(cur_pos<lengthDoc) cur_pos++;
            styler.ColourTo(cur_pos,SCE_FORTH_COMMENT);
        }else if(strcmp("[",buffer)==0){
            styler.ColourTo(pos1,SCE_FORTH_STRING);
            parse(']',true);
            if(cur_pos<lengthDoc) cur_pos++;
            styler.ColourTo(cur_pos,SCE_FORTH_STRING);
        }else if(strcmp("{",buffer)==0){
            styler.ColourTo(pos1,SCE_FORTH_LOCALE);
            parse('}',false);
            if(cur_pos<lengthDoc) cur_pos++;
            styler.ColourTo(cur_pos,SCE_FORTH_LOCALE);
        }else if(strings.InList(buffer)) {
            styler.ColourTo(pos1,SCE_FORTH_STRING);
            parse('"',false);
            if(cur_pos<lengthDoc) cur_pos++;
            styler.ColourTo(cur_pos,SCE_FORTH_STRING);
        }else if(control.InList(buffer)) {
            styler.ColourTo(pos1,SCE_FORTH_CONTROL);
            styler.ColourTo(pos2,SCE_FORTH_CONTROL);
        }else if(keyword.InList(buffer)) {
            styler.ColourTo(pos1,SCE_FORTH_KEYWORD);
            styler.ColourTo(pos2,SCE_FORTH_KEYWORD);
        }else if(defword.InList(buffer)) {
            styler.ColourTo(pos1,SCE_FORTH_KEYWORD);
            styler.ColourTo(pos2,SCE_FORTH_KEYWORD);
            parse(BL,false);
            styler.ColourTo(pos1-1,SCE_FORTH_DEFAULT);
            styler.ColourTo(pos1,SCE_FORTH_DEFWORD);
            styler.ColourTo(pos2,SCE_FORTH_DEFWORD);
        }else if(preword1.InList(buffer)) {
            styler.ColourTo(pos1,SCE_FORTH_PREWORD1);
            parse(BL,false);
            styler.ColourTo(pos2,SCE_FORTH_PREWORD1);
        }else if(preword2.InList(buffer)) {
            styler.ColourTo(pos1,SCE_FORTH_PREWORD2);
            parse(BL,false);
            styler.ColourTo(pos2,SCE_FORTH_PREWORD2);
            parse(BL,false);
            styler.ColourTo(pos1,SCE_FORTH_STRING);
            styler.ColourTo(pos2,SCE_FORTH_STRING);
        }else if(is_number(buffer)){
            styler.ColourTo(pos1,SCE_FORTH_NUMBER);
            styler.ColourTo(pos2,SCE_FORTH_NUMBER);
        }
    }
#ifdef FORTH_DEBUG
    fclose(f_debug);
#endif
    delete []buffer;
    return;
/*
                        if(control.InList(buffer)) {
                            styler.ColourTo(i,SCE_FORTH_CONTROL);
                        } else if(keyword.InList(buffer)) {
                            styler.ColourTo(i-1,SCE_FORTH_KEYWORD );
                        } else if(defword.InList(buffer)) {
                            styler.ColourTo(i-1,SCE_FORTH_DEFWORD );
//                            prev_state=SCE_FORTH_DEFWORD
                        } else if(preword1.InList(buffer)) {
                            styler.ColourTo(i-1,SCE_FORTH_PREWORD1 );
//                            state=SCE_FORTH_PREWORD1;
                        } else if(preword2.InList(buffer)) {
                            styler.ColourTo(i-1,SCE_FORTH_PREWORD2 );
                         } else {
                            styler.ColourTo(i-1,SCE_FORTH_DEFAULT);
                        }
*/
/*
    chPrev=' ';
    for (int i = startPos; i < lengthDoc; i++) {
        char ch = chNext;
        chNext = styler.SafeGetCharAt(i + 1);
        if(i!=startPos) chPrev=styler.SafeGetCharAt(i - 1);

        if (styler.IsLeadByte(ch)) {
            chNext = styler.SafeGetCharAt(i + 2);
            i++;
            continue;
        }
#ifdef FORTH_DEBUG
        fprintf(f_debug,"%c %d ",ch,state);
#endif
        switch(state) {
            case SCE_FORTH_DEFAULT:
                if(is_whitespace(ch)) {
                    // whitespace is simply ignored here...
                    styler.ColourTo(i,SCE_FORTH_DEFAULT);
                    break;
                } else if( ch == '\\' && is_blank(chNext)) {
                    // signals the start of an one line comment...
                    state = SCE_FORTH_COMMENT;
                    styler.ColourTo(i,SCE_FORTH_COMMENT);
                } else if( is_whitespace(chPrev) &&  ch == '(' &&  is_whitespace(chNext)) {
                    // signals the start of a plain comment...
                    state = SCE_FORTH_COMMENT_ML;
                    styler.ColourTo(i,SCE_FORTH_COMMENT_ML);
                } else if( isdigit(ch) ) {
                    // signals the start of a number
                    bufferCount = 0;
                    buffer[bufferCount++] = ch;
                    state = SCE_FORTH_NUMBER;
                } else if( !is_whitespace(ch)) {
                    // signals the start of an identifier
                    bufferCount = 0;
                    buffer[bufferCount++] = ch;
                    state = SCE_FORTH_IDENTIFIER;
                } else {
                    // style it the default style..
                    styler.ColourTo(i,SCE_FORTH_DEFAULT);
                }
                break;

            case SCE_FORTH_COMMENT:
                // if we find a newline here,
                // we simply go to default state
                // else continue to work on it...
                if( ch == '\n' || ch == '\r' ) {
                    state = SCE_FORTH_DEFAULT;
                } else {
                    styler.ColourTo(i,SCE_FORTH_COMMENT);
                }
                break;

            case SCE_FORTH_COMMENT_ML:
                if( ch == ')') {
                    state = SCE_FORTH_DEFAULT;
                } else {
                    styler.ColourTo(i+1,SCE_FORTH_COMMENT_ML);
                }
                break;

            case SCE_FORTH_IDENTIFIER:
                // stay  in CONF_IDENTIFIER state until we find a non-alphanumeric
                if( !is_whitespace(ch) ) {
                    buffer[bufferCount++] = ch;
                } else {
                    state = SCE_FORTH_DEFAULT;
                    buffer[bufferCount] = '\0';
#ifdef FORTH_DEBUG
        fprintf(f_debug,"\nid %s\n",buffer);
#endif

                    // check if the buffer contains a keyword,
                    // and highlight it if it is a keyword...
//                    switch(prev_state)
//                    case SCE_FORTH_DEFAULT:
                        if(control.InList(buffer)) {
                            styler.ColourTo(i,SCE_FORTH_CONTROL);
                        } else if(keyword.InList(buffer)) {
                            styler.ColourTo(i-1,SCE_FORTH_KEYWORD );
                        } else if(defword.InList(buffer)) {
                            styler.ColourTo(i-1,SCE_FORTH_DEFWORD );
//                            prev_state=SCE_FORTH_DEFWORD
                        } else if(preword1.InList(buffer)) {
                            styler.ColourTo(i-1,SCE_FORTH_PREWORD1 );
//                            state=SCE_FORTH_PREWORD1;
                        } else if(preword2.InList(buffer)) {
                            styler.ColourTo(i-1,SCE_FORTH_PREWORD2 );
                         } else {
                            styler.ColourTo(i-1,SCE_FORTH_DEFAULT);
                        }
//                        break;
//                    case

                    // push back the faulty character
                    chNext = styler[i--];
                }
                break;

            case SCE_FORTH_NUMBER:
                // stay  in CONF_NUMBER state until we find a non-numeric
                if( isdigit(ch) ) {
                    buffer[bufferCount++] = ch;
                } else {
                    state = SCE_FORTH_DEFAULT;
                    buffer[bufferCount] = '\0';
                    // Colourize here... (normal number)
                    styler.ColourTo(i-1,SCE_FORTH_NUMBER);
                    // push back a character
                    chNext = styler[i--];
                }
                break;
        }
    }
#ifdef FORTH_DEBUG
    fclose(f_debug);
#endif
    delete []buffer;
*/
}

static void FoldForthDoc(unsigned int, int, int, WordList *[],
                       Accessor &) {
}

static const char * const forthWordLists[] = {
            "control keywords",
            "keywords",
            "definition words",
            "prewords with one argument",
            "prewords with two arguments",
            "string definition keywords",
            0,
        };

LexerModule lmForth(SCLEX_FORTH, ColouriseForthDoc, "forth",FoldForthDoc,forthWordLists);