New \grammartoken markup, similar to \token but allowed everywhere.
[python/dscho.git] / Mac / Contrib / BBPy.lm / BBPy.c
blobbcb8d4e9d0d2bf2ebe0fdcef6f2547c7e8650026
1 #include <AEDataModel.h>
3 #define DEBUG 0
5 #define kComponentSignatureString "BBPy.LM"
6 #include <Debugging.h>
9 #include <BBLMInterface.h>
10 #include <BBXTInterface.h>
11 //#include <BBLMTextIterator.h>
13 #include <ctype.h>
14 #include <string.h>
15 #include <stdio.h>
16 #include <stdarg.h>
18 #include <Sound.h>
20 #if DEBUG
21 void debugf_(const char* func,const char* fileName,long line, const char*fmt,...)
23 va_list arg;
24 char msg[256];
25 va_start(arg, fmt);
26 vsnprintf(msg,256 ,fmt, arg);
27 DebugAssert(COMPONENT_SIGNATURE, DEBUG_NO_OPTIONS, kComponentSignatureString ": " , msg, nil, fileName, line, 0 );
29 //debug_string(msg);
31 #define debugf(FMT,...) debugf_( __FUNCTION__,__FILE__, __LINE__,FMT,__VA_ARGS__);
32 #else
33 #define debugf(FMT,...)
34 #endif
36 typedef const char *Str;
39 enum{
40 kPyBBLMStringSubst = kBBLMFirstUserRunKind
43 #define iswordchar(x) (isalnum(x)||x=='_')
46 struct runloc{
47 bool past_gap;
48 long pos;
49 long last_start;
50 unsigned char*p;
53 char start(struct runloc& r,BBLMParamBlock &pb)
55 r.past_gap = false;
56 r.last_start = pb.fCalcRunParams.fStartOffset;
57 r.pos = pb.fCalcRunParams.fStartOffset;
58 r.p = ((unsigned char*)pb.fText) + pb.fCalcRunParams.fStartOffset;
59 // Adjust for the gap if weÕre not already past it.
60 if ((!r.past_gap) && (r.pos >= pb.fTextGapLocation)){
61 r.p += pb.fTextGapLength;
62 r.past_gap = true;
64 return *r.p;
68 char nextchar(struct runloc&r,BBLMParamBlock &pb)
70 if ( r.pos< pb.fTextLength){
71 ++r.pos;
72 ++r.p;
73 if ((!r.past_gap) && (r.pos >= pb.fTextGapLocation)){
74 r.p += pb.fTextGapLength;
75 r.past_gap = true;
77 return *r.p;
79 else{
80 return 0;
84 bool addRun(BBLMRunCode kind, int start,int len , const BBLMCallbackBlock& bblm_callbacks)
86 if (len > 0){ // Tie off the code run we were in, unless the length is zero.
87 debugf("Run %d %d:%d", kind, start, start+len-1 );
88 return bblmAddRun( &bblm_callbacks, 'Pyth',
89 kind, start, len, false);
92 else{
93 return true;
97 bool addRunBefore (BBLMRunCode kind,struct runloc& r, const BBLMCallbackBlock& bblm_callbacks)
99 bool more_runs = addRun(kind, r.last_start, r.pos - r.last_start, bblm_callbacks);
100 r.last_start = r.pos;
101 return more_runs;
104 bool addRunTo (BBLMRunCode kind, struct runloc& r, const BBLMCallbackBlock& bblm_callbacks)
106 bool more_runs = addRun(kind, r.last_start, r.pos - r.last_start+1, bblm_callbacks);
107 r.last_start = r.pos+1;
108 return more_runs;
112 bool colorstr( char delim,
113 BBLMParamBlock &pb,
114 struct runloc &r,
115 const BBLMCallbackBlock &bblm_callbacks)
117 bool tripple = false , pers = false, lookup = false, more_runs = true;
118 char c = nextchar(r,pb);
120 if (c == delim){
121 c = nextchar(r,pb);
122 if (c == delim){
123 tripple = true;
124 c = nextchar(r,pb);
126 else{
127 //double
128 return addRunBefore(kBBLMRunIsSingleString,r,bblm_callbacks);
131 while (c && more_runs){
132 if (pers ){
133 if (isalpha(c)){
134 more_runs = addRunTo(kPyBBLMStringSubst,r,bblm_callbacks);
136 else if (c == '('){
137 lookup = true;
140 pers = false;
141 if (c == delim){
142 if (tripple){
143 if ((c = nextchar(r,pb))== delim && (c = nextchar(r,pb)) == delim){
144 break; // end of tripple-quote.
147 else{
148 break; // end of single-quote.
152 else if (c== '\\'){
153 nextchar(r,pb);
155 else if (c=='\r'||c=='\n'){
156 if (!tripple){
157 break;
160 else if (c=='%'){
161 more_runs = addRunBefore(kBBLMRunIsSingleString,r,bblm_callbacks);
162 pers = true;
164 else if (c==')' && lookup){
165 more_runs = addRunTo(kPyBBLMStringSubst,r,bblm_callbacks);
166 lookup = false;
168 c = nextchar(r,pb);
170 return more_runs && addRunTo(lookup?kPyBBLMStringSubst:kBBLMRunIsSingleString,r,bblm_callbacks);
173 bool colorcomment(BBLMParamBlock &pb,
174 struct runloc &r,
175 const BBLMCallbackBlock &bblm_callbacks)
177 while (char c = nextchar(r,pb)){
178 if (c=='\r'|| c=='\n'){
179 break;
182 return addRunTo(kBBLMRunIsLineComment,r,bblm_callbacks);
185 void CalculateRuns(BBLMParamBlock &pb,
186 const BBLMCallbackBlock &bblm_callbacks)
189 const struct rundesc *state = NULL;
190 bool more_runs=true;
192 struct runloc r;
194 char c = start(r,pb);
196 while (c && more_runs){
197 loop:
198 // Process a char
199 if (state==NULL){
200 //If we're in the basic 'code' state, check for each interesting char (rundelims[i].start).
201 switch (c){
202 case '\'':
203 case '"':
204 more_runs = addRunBefore(kBBLMRunIsCode,r,bblm_callbacks);
205 if (more_runs){
206 more_runs = colorstr(c,pb,r,bblm_callbacks);
208 break;
209 case '#' :
210 more_runs = addRunBefore(kBBLMRunIsCode,r,bblm_callbacks);
211 if (more_runs){
212 more_runs = colorcomment(pb,r,bblm_callbacks);
214 break;
215 default:
216 break;
220 c = nextchar(r,pb);
222 if (more_runs){
223 addRunBefore(kBBLMRunIsCode,r,bblm_callbacks);
228 static void AdjustRange(BBLMParamBlock &params,
229 const BBLMCallbackBlock &callbacks)
231 DescType language;
232 BBLMRunCode kind;
233 SInt32 charPos;
234 SInt32 length;
235 UInt32 index = params.fAdjustRangeParams.fStartIndex;
237 while( index > 0 &&
238 bblmGetRun(&callbacks, index, language, kind, charPos, length) &&
239 (kind==kPyBBLMStringSubst||kind==kBBLMRunIsSingleString)){
240 index--;
242 params.fAdjustRangeParams.fStartIndex = index;
246 // The next couple funcs process the text of a file assumming it's in 1 piece in memory,
247 // so they may not be called from CalculateRuns.
249 bool matchword(BBLMParamBlock &pb, const char *pat ,unsigned long *pos)
251 const char *asciText = (const char *) (pb.fTextIsUnicode?NULL:pb.fText);
253 int i;
254 for (i=0; pat[i]; i++){
255 if (*pos+i>=pb.fTextLength){
256 return false;
258 if (asciText[*pos+i] != pat[i]){
259 return false;
262 if ((*pos+i<pb.fTextLength)&&iswordchar(asciText[*pos+i])){
263 return false;
265 *pos+=i;
266 return true;
269 int matchindent(BBLMParamBlock &pb, UInt32 *pos)
271 const char *asciText = (const char *) (pb.fTextIsUnicode?NULL:pb.fText);
272 int indent=0;
274 while(*pos<pb.fTextLength){
275 switch (/*(char)(pb.fTextIsUnicode?uniText[pos]:*/asciText[*pos]/*)*/){
276 case ' ':
277 ++*pos;
278 indent++;
279 break;
280 case '\t':
281 ++*pos;
282 indent+=8;
283 break;
284 case '#':
285 return -1;
286 break;
287 default:
288 return indent;
289 break;
295 void eat_line(BBLMParamBlock &pb, unsigned long* pos)
297 const char *asciText = (const char *) (pb.fTextIsUnicode?NULL:pb.fText);
298 while (asciText[*pos]!='\r' && asciText[*pos]!='\n' && *pos<pb.fTextLength) {++*pos;}
299 while ((asciText[*pos]=='\r' || asciText[*pos]=='\n') && *pos<pb.fTextLength) {++*pos;}
303 void addItem(BBLMParamBlock &pb, UInt32 pos, int nest, BBLMFunctionKinds kind,
304 const BBLMCallbackBlock *bblm_callbacks)
306 UInt32 funcstartpos = pos;
307 UInt32 funcnamelen=0;
308 UInt32 offset=0;
309 const char *asciText = (const char *) pb.fText;
310 UInt32 index;
311 OSErr err;
313 while (isspace(asciText[pos]) && pos<pb.fTextLength) {++pos;}
314 UInt32 fnamestart = pos;
315 while ((isalnum(asciText[pos])||asciText[pos]=='_') && pos<pb.fTextLength) {pos++; funcnamelen++;}
317 err = bblmAddTokenToBuffer( bblm_callbacks,
318 pb.fFcnParams.fTokenBuffer,
319 (void*)&asciText[fnamestart],
320 funcnamelen,
321 pb.fTextIsUnicode,
322 &offset);
323 BBLMProcInfo procInfo;
324 procInfo.fFunctionStart = fnamestart; // char offset in file of first character of function
325 procInfo.fFunctionEnd = pos; // char offset of last character of function
327 procInfo.fSelStart = fnamestart; // first character to select when choosing function
328 procInfo.fSelEnd = pos; // last character to select when choosing function
330 procInfo.fFirstChar = fnamestart; // first character to make visible when choosing function
332 procInfo.fKind = kind;
334 procInfo.fIndentLevel = nest; // indentation level of token
335 procInfo.fFlags = 0; // token flags (see BBLMFunctionFlags)
336 procInfo.fNameStart = offset; // char offset in token buffer of token name
337 procInfo.fNameLength = funcnamelen; // length of token name
339 err = bblmAddFunctionToList(bblm_callbacks,
340 pb.fFcnParams.fFcnList,
341 procInfo,
342 &index);
347 enum{
348 maxnest=5
351 void ScanForFunctions(BBLMParamBlock &pb,
352 const BBLMCallbackBlock &bblm_callbacks)
355 const char *asciText = (const char *) (pb.fTextIsUnicode?NULL:pb.fText);
356 UniCharPtr uniText = (UniCharPtr) (pb.fTextIsUnicode?pb.fText:NULL);
358 int indents[maxnest]= {0};
359 int nest = 0;
361 UInt32 pos=0; // current character offset
364 while (pos<pb.fTextLength){
366 int indent = matchindent(pb, &pos);
368 if (indent >= 0){
369 for (int i=0; i <= nest; i++){
370 if (indent<=indents[i]){
371 nest = i;
372 indents[nest]=indent;
373 goto x;
376 indents[++nest]=indent;
379 if (matchword(pb,"def",&pos)){
380 addItem( pb, pos, nest, kBBLMFunctionMark, &bblm_callbacks);
382 else if (matchword(pb, "class", &pos)){
383 addItem( pb, pos, nest, kBBLMTypedef, &bblm_callbacks);
386 eat_line(pb,&pos);
391 OSErr main( BBLMParamBlock &params,
392 const BBLMCallbackBlock &bblm_callbacks,
393 const BBXTCallbackBlock &bbxt_callbacks)
395 OSErr result;
397 if ((params.fSignature != kBBLMParamBlockSignature) ||
398 (params.fLength < sizeof(BBLMParamBlock)))
400 return paramErr;
403 switch (params.fMessage)
405 case kBBLMInitMessage:
406 case kBBLMDisposeMessage:
408 result = noErr; // nothing to do
409 break;
412 case kBBLMCalculateRunsMessage:
413 CalculateRuns(params, bblm_callbacks);
414 result = noErr;
415 break;
417 case kBBLMScanForFunctionsMessage:
418 ScanForFunctions(params, bblm_callbacks);
419 result = noErr;
420 break;
422 case kBBLMAdjustRangeMessage:
423 AdjustRange(params, bblm_callbacks);
424 result = noErr;
425 break;
427 case kBBLMMapRunKindToColorCodeMessage:
428 switch (params.fMapRunParams.fRunKind){
429 case kPyBBLMStringSubst:
430 params.fMapRunParams.fColorCode = kBBLMSGMLAttributeNameColor;
431 params.fMapRunParams.fMapped = true;
432 break;
433 default:
434 params.fMapRunParams.fMapped = false;
436 result = noErr;
437 break;
439 case kBBLMEscapeStringMessage:
440 case kBBLMAdjustEndMessage:
441 case kBBLMMapColorCodeToColorMessage:
442 case kBBLMSetCategoriesMessage:
443 case kBBLMMatchKeywordMessage:
445 result = userCanceledErr;
446 break;
449 default:
451 result = paramErr;
452 break;
455 return result;