| #include <AEDataModel.h> |
| |
| #define DEBUG 0 |
| |
| #define kComponentSignatureString "BBPy.LM" |
| #include <Debugging.h> |
| |
| |
| #include <BBLMInterface.h> |
| #include <BBXTInterface.h> |
| //#include <BBLMTextIterator.h> |
| |
| #include <ctype.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <stdarg.h> |
| |
| #include <Sound.h> |
| |
| #if DEBUG |
| void debugf_(const char* func,const char* fileName,long line, const char*fmt,...) |
| { |
| va_list arg; |
| char msg[256]; |
| va_start(arg, fmt); |
| vsnprintf(msg,256 ,fmt, arg); |
| DebugAssert(COMPONENT_SIGNATURE, DEBUG_NO_OPTIONS, kComponentSignatureString ": " , msg, nil, fileName, line, 0 ); |
| |
| //debug_string(msg); |
| } |
| #define debugf(FMT,...) debugf_( __FUNCTION__,__FILE__, __LINE__,FMT,__VA_ARGS__); |
| #else |
| #define debugf(FMT,...) |
| #endif |
| |
| typedef const char *Str; |
| |
| |
| enum{ |
| kPyBBLMStringSubst = kBBLMFirstUserRunKind |
| }; |
| |
| #define iswordchar(x) (isalnum(x)||x=='_') |
| |
| |
| struct runloc{ |
| bool past_gap; |
| long pos; |
| long last_start; |
| unsigned char*p; |
| }; |
| |
| char start(struct runloc& r,BBLMParamBlock &pb) |
| { |
| r.past_gap = false; |
| r.last_start = pb.fCalcRunParams.fStartOffset; |
| r.pos = pb.fCalcRunParams.fStartOffset; |
| r.p = ((unsigned char*)pb.fText) + pb.fCalcRunParams.fStartOffset; |
| // Adjust for the gap if weÕre not already past it. |
| if ((!r.past_gap) && (r.pos >= pb.fTextGapLocation)){ |
| r.p += pb.fTextGapLength; |
| r.past_gap = true; |
| } |
| return *r.p; |
| |
| } |
| |
| char nextchar(struct runloc&r,BBLMParamBlock &pb) |
| { |
| if ( r.pos< pb.fTextLength){ |
| ++r.pos; |
| ++r.p; |
| if ((!r.past_gap) && (r.pos >= pb.fTextGapLocation)){ |
| r.p += pb.fTextGapLength; |
| r.past_gap = true; |
| } |
| return *r.p; |
| } |
| else{ |
| return 0; |
| } |
| } |
| |
| bool addRun(BBLMRunCode kind, int start,int len , const BBLMCallbackBlock& bblm_callbacks) |
| { |
| if (len > 0){ // Tie off the code run we were in, unless the length is zero. |
| debugf("Run %d %d:%d", kind, start, start+len-1 ); |
| return bblmAddRun( &bblm_callbacks, 'Pyth', |
| kind, start, len, false); |
| |
| } |
| else{ |
| return true; |
| } |
| } |
| |
| bool addRunBefore (BBLMRunCode kind,struct runloc& r, const BBLMCallbackBlock& bblm_callbacks) |
| { |
| bool more_runs = addRun(kind, r.last_start, r.pos - r.last_start, bblm_callbacks); |
| r.last_start = r.pos; |
| return more_runs; |
| } |
| |
| bool addRunTo (BBLMRunCode kind, struct runloc& r, const BBLMCallbackBlock& bblm_callbacks) |
| { |
| bool more_runs = addRun(kind, r.last_start, r.pos - r.last_start+1, bblm_callbacks); |
| r.last_start = r.pos+1; |
| return more_runs; |
| } |
| |
| |
| bool colorstr( char delim, |
| BBLMParamBlock &pb, |
| struct runloc &r, |
| const BBLMCallbackBlock &bblm_callbacks) |
| { |
| bool tripple = false , pers = false, lookup = false, more_runs = true; |
| char c = nextchar(r,pb); |
| |
| if (c == delim){ |
| c = nextchar(r,pb); |
| if (c == delim){ |
| tripple = true; |
| c = nextchar(r,pb); |
| } |
| else{ |
| //double |
| return addRunBefore(kBBLMRunIsSingleString,r,bblm_callbacks); |
| } |
| } |
| while (c && more_runs){ |
| if (pers ){ |
| if (isalpha(c)){ |
| more_runs = addRunTo(kPyBBLMStringSubst,r,bblm_callbacks); |
| } |
| else if (c == '('){ |
| lookup = true; |
| } |
| } |
| pers = false; |
| if (c == delim){ |
| if (tripple){ |
| if ((c = nextchar(r,pb))== delim && (c = nextchar(r,pb)) == delim){ |
| break; // end of tripple-quote. |
| } |
| } |
| else{ |
| break; // end of single-quote. |
| } |
| |
| } |
| else if (c== '\\'){ |
| nextchar(r,pb); |
| } |
| else if (c=='\r'||c=='\n'){ |
| if (!tripple){ |
| break; |
| } |
| } |
| else if (c=='%'){ |
| more_runs = addRunBefore(kBBLMRunIsSingleString,r,bblm_callbacks); |
| pers = true; |
| } |
| else if (c==')' && lookup){ |
| more_runs = addRunTo(kPyBBLMStringSubst,r,bblm_callbacks); |
| lookup = false; |
| } |
| c = nextchar(r,pb); |
| } |
| return more_runs && addRunTo(lookup?kPyBBLMStringSubst:kBBLMRunIsSingleString,r,bblm_callbacks); |
| } |
| |
| bool colorcomment(BBLMParamBlock &pb, |
| struct runloc &r, |
| const BBLMCallbackBlock &bblm_callbacks) |
| { |
| while (char c = nextchar(r,pb)){ |
| if (c=='\r'|| c=='\n'){ |
| break; |
| } |
| } |
| return addRunTo(kBBLMRunIsLineComment,r,bblm_callbacks); |
| } |
| |
| void CalculateRuns(BBLMParamBlock &pb, |
| const BBLMCallbackBlock &bblm_callbacks) |
| |
| { |
| const struct rundesc *state = NULL; |
| bool more_runs=true; |
| |
| struct runloc r; |
| |
| char c = start(r,pb); |
| |
| while (c && more_runs){ |
| loop: |
| // Process a char |
| if (state==NULL){ |
| //If we're in the basic 'code' state, check for each interesting char (rundelims[i].start). |
| switch (c){ |
| case '\'': |
| case '"': |
| more_runs = addRunBefore(kBBLMRunIsCode,r,bblm_callbacks); |
| if (more_runs){ |
| more_runs = colorstr(c,pb,r,bblm_callbacks); |
| } |
| break; |
| case '#' : |
| more_runs = addRunBefore(kBBLMRunIsCode,r,bblm_callbacks); |
| if (more_runs){ |
| more_runs = colorcomment(pb,r,bblm_callbacks); |
| } |
| break; |
| default: |
| break; |
| } |
| |
| } |
| c = nextchar(r,pb); |
| } |
| if (more_runs){ |
| addRunBefore(kBBLMRunIsCode,r,bblm_callbacks); |
| } |
| |
| |
| } |
| static void AdjustRange(BBLMParamBlock ¶ms, |
| const BBLMCallbackBlock &callbacks) |
| { |
| DescType language; |
| BBLMRunCode kind; |
| SInt32 charPos; |
| SInt32 length; |
| UInt32 index = params.fAdjustRangeParams.fStartIndex; |
| |
| while( index > 0 && |
| bblmGetRun(&callbacks, index, language, kind, charPos, length) && |
| (kind==kPyBBLMStringSubst||kind==kBBLMRunIsSingleString)){ |
| index--; |
| }; |
| params.fAdjustRangeParams.fStartIndex = index; |
| } |
| |
| |
| // The next couple funcs process the text of a file assumming it's in 1 piece in memory, |
| // so they may not be called from CalculateRuns. |
| |
| bool matchword(BBLMParamBlock &pb, const char *pat ,unsigned long *pos) |
| { |
| const char *asciText = (const char *) (pb.fTextIsUnicode?NULL:pb.fText); |
| |
| int i; |
| for (i=0; pat[i]; i++){ |
| if (*pos+i>=pb.fTextLength){ |
| return false; |
| } |
| if (asciText[*pos+i] != pat[i]){ |
| return false; |
| } |
| } |
| if ((*pos+i<pb.fTextLength)&&iswordchar(asciText[*pos+i])){ |
| return false; |
| } |
| *pos+=i; |
| return true; |
| } |
| |
| int matchindent(BBLMParamBlock &pb, UInt32 *pos) |
| { |
| const char *asciText = (const char *) (pb.fTextIsUnicode?NULL:pb.fText); |
| int indent=0; |
| |
| while(*pos<pb.fTextLength){ |
| switch (/*(char)(pb.fTextIsUnicode?uniText[pos]:*/asciText[*pos]/*)*/){ |
| case ' ': |
| ++*pos; |
| indent++; |
| break; |
| case '\t': |
| ++*pos; |
| indent+=8; |
| break; |
| case '#': |
| return -1; |
| break; |
| default: |
| return indent; |
| break; |
| } |
| } |
| } |
| |
| |
| void eat_line(BBLMParamBlock &pb, unsigned long* pos) |
| { |
| const char *asciText = (const char *) (pb.fTextIsUnicode?NULL:pb.fText); |
| while (asciText[*pos]!='\r' && asciText[*pos]!='\n' && *pos<pb.fTextLength) {++*pos;} |
| while ((asciText[*pos]=='\r' || asciText[*pos]=='\n') && *pos<pb.fTextLength) {++*pos;} |
| |
| } |
| |
| void addItem(BBLMParamBlock &pb, UInt32 pos, int nest, BBLMFunctionKinds kind, |
| const BBLMCallbackBlock *bblm_callbacks) |
| { |
| UInt32 funcstartpos = pos; |
| UInt32 funcnamelen=0; |
| UInt32 offset=0; |
| const char *asciText = (const char *) pb.fText; |
| UInt32 index; |
| OSErr err; |
| |
| while (isspace(asciText[pos]) && pos<pb.fTextLength) {++pos;} |
| UInt32 fnamestart = pos; |
| while ((isalnum(asciText[pos])||asciText[pos]=='_') && pos<pb.fTextLength) {pos++; funcnamelen++;} |
| |
| err = bblmAddTokenToBuffer( bblm_callbacks, |
| pb.fFcnParams.fTokenBuffer, |
| (void*)&asciText[fnamestart], |
| funcnamelen, |
| pb.fTextIsUnicode, |
| &offset); |
| BBLMProcInfo procInfo; |
| procInfo.fFunctionStart = fnamestart; // char offset in file of first character of function |
| procInfo.fFunctionEnd = pos; // char offset of last character of function |
| |
| procInfo.fSelStart = fnamestart; // first character to select when choosing function |
| procInfo.fSelEnd = pos; // last character to select when choosing function |
| |
| procInfo.fFirstChar = fnamestart; // first character to make visible when choosing function |
| |
| procInfo.fKind = kind; |
| |
| procInfo.fIndentLevel = nest; // indentation level of token |
| procInfo.fFlags = 0; // token flags (see BBLMFunctionFlags) |
| procInfo.fNameStart = offset; // char offset in token buffer of token name |
| procInfo.fNameLength = funcnamelen; // length of token name |
| |
| err = bblmAddFunctionToList(bblm_callbacks, |
| pb.fFcnParams.fFcnList, |
| procInfo, |
| &index); |
| } |
| |
| |
| |
| enum{ |
| maxnest=5 |
| }; |
| |
| void ScanForFunctions(BBLMParamBlock &pb, |
| const BBLMCallbackBlock &bblm_callbacks) |
| { |
| |
| const char *asciText = (const char *) (pb.fTextIsUnicode?NULL:pb.fText); |
| UniCharPtr uniText = (UniCharPtr) (pb.fTextIsUnicode?pb.fText:NULL); |
| |
| int indents[maxnest]= {0}; |
| int nest = 0; |
| |
| UInt32 pos=0; // current character offset |
| |
| |
| while (pos<pb.fTextLength){ |
| |
| int indent = matchindent(pb, &pos); |
| |
| if (indent >= 0){ |
| for (int i=0; i <= nest; i++){ |
| if (indent<=indents[i]){ |
| nest = i; |
| indents[nest]=indent; |
| goto x; |
| } |
| } |
| indents[++nest]=indent; |
| x: |
| |
| if (matchword(pb,"def",&pos)){ |
| addItem( pb, pos, nest, kBBLMFunctionMark, &bblm_callbacks); |
| } |
| else if (matchword(pb, "class", &pos)){ |
| addItem( pb, pos, nest, kBBLMTypedef, &bblm_callbacks); |
| } |
| } |
| eat_line(pb,&pos); |
| } |
| |
| } |
| |
| OSErr main( BBLMParamBlock ¶ms, |
| const BBLMCallbackBlock &bblm_callbacks, |
| const BBXTCallbackBlock &bbxt_callbacks) |
| { |
| OSErr result; |
| |
| if ((params.fSignature != kBBLMParamBlockSignature) || |
| (params.fLength < sizeof(BBLMParamBlock))) |
| { |
| return paramErr; |
| } |
| |
| switch (params.fMessage) |
| { |
| case kBBLMInitMessage: |
| case kBBLMDisposeMessage: |
| { |
| result = noErr; // nothing to do |
| break; |
| } |
| |
| case kBBLMCalculateRunsMessage: |
| CalculateRuns(params, bblm_callbacks); |
| result = noErr; |
| break; |
| |
| case kBBLMScanForFunctionsMessage: |
| ScanForFunctions(params, bblm_callbacks); |
| result = noErr; |
| break; |
| |
| case kBBLMAdjustRangeMessage: |
| AdjustRange(params, bblm_callbacks); |
| result = noErr; |
| break; |
| |
| case kBBLMMapRunKindToColorCodeMessage: |
| switch (params.fMapRunParams.fRunKind){ |
| case kPyBBLMStringSubst: |
| params.fMapRunParams.fColorCode = kBBLMSGMLAttributeNameColor; |
| params.fMapRunParams.fMapped = true; |
| break; |
| default: |
| params.fMapRunParams.fMapped = false; |
| } |
| result = noErr; |
| break; |
| |
| case kBBLMEscapeStringMessage: |
| case kBBLMAdjustEndMessage: |
| case kBBLMMapColorCodeToColorMessage: |
| case kBBLMSetCategoriesMessage: |
| case kBBLMMatchKeywordMessage: |
| { |
| result = userCanceledErr; |
| break; |
| } |
| |
| default: |
| { |
| result = paramErr; |
| break; |
| } |
| } |
| return result; |
| } |