1 // Scintilla source code edit control
2 /** @file LexCsound.cxx
3  ** Lexer for Csound (Orchestra & Score)
4  ** Written by Georg Ritter - <ritterfuture A T gmail D O T com>
5  **/
6 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
7 // The License.txt file describes the conditions under which this software may be distributed.
8 
9 #include <stdlib.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <stdarg.h>
13 #include <assert.h>
14 #include <ctype.h>
15 
16 #include "ILexer.h"
17 #include "Scintilla.h"
18 #include "SciLexer.h"
19 
20 #include "WordList.h"
21 #include "LexAccessor.h"
22 #include "Accessor.h"
23 #include "StyleContext.h"
24 #include "CharacterSet.h"
25 #include "LexerModule.h"
26 
27 using namespace Scintilla;
28 
IsAWordChar(const int ch)29 static inline bool IsAWordChar(const int ch) {
30 	return (ch < 0x80) && (isalnum(ch) || ch == '.' ||
31 		ch == '_' || ch == '?');
32 }
33 
IsAWordStart(const int ch)34 static inline bool IsAWordStart(const int ch) {
35 	return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '.' ||
36 		ch == '%' || ch == '@' || ch == '$' || ch == '?');
37 }
38 
IsCsoundOperator(char ch)39 static inline bool IsCsoundOperator(char ch) {
40 	if (IsASCII(ch) && isalnum(ch))
41 		return false;
42 	// '.' left out as it is used to make up numbers
43 	if (ch == '*' || ch == '/' || ch == '-' || ch == '+' ||
44 		ch == '(' || ch == ')' || ch == '=' || ch == '^' ||
45 		ch == '[' || ch == ']' || ch == '<' || ch == '&' ||
46 		ch == '>' || ch == ',' || ch == '|' || ch == '~' ||
47 		ch == '%' || ch == ':')
48 		return true;
49 	return false;
50 }
51 
ColouriseCsoundDoc(Sci_PositionU startPos,Sci_Position length,int initStyle,WordList * keywordlists[],Accessor & styler)52 static void ColouriseCsoundDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordlists[],
53 				Accessor &styler) {
54 
55 	WordList &opcode = *keywordlists[0];
56 	WordList &headerStmt = *keywordlists[1];
57 	WordList &otherKeyword = *keywordlists[2];
58 
59 	// Do not leak onto next line
60 	if (initStyle == SCE_CSOUND_STRINGEOL)
61 		initStyle = SCE_CSOUND_DEFAULT;
62 
63 	StyleContext sc(startPos, length, initStyle, styler);
64 
65 	for (; sc.More(); sc.Forward())
66 	{
67 		// Handle line continuation generically.
68 		if (sc.ch == '\\') {
69 			if (sc.chNext == '\n' || sc.chNext == '\r') {
70 				sc.Forward();
71 				if (sc.ch == '\r' && sc.chNext == '\n') {
72 					sc.Forward();
73 				}
74 				continue;
75 			}
76 		}
77 
78 		// Determine if the current state should terminate.
79 		if (sc.state == SCE_CSOUND_OPERATOR) {
80 			if (!IsCsoundOperator(static_cast<char>(sc.ch))) {
81 			    sc.SetState(SCE_CSOUND_DEFAULT);
82 			}
83 		}else if (sc.state == SCE_CSOUND_NUMBER) {
84 			if (!IsAWordChar(sc.ch)) {
85 				sc.SetState(SCE_CSOUND_DEFAULT);
86 			}
87 		} else if (sc.state == SCE_CSOUND_IDENTIFIER) {
88 			if (!IsAWordChar(sc.ch) ) {
89 				char s[100];
90 				sc.GetCurrent(s, sizeof(s));
91 
92 				if (opcode.InList(s)) {
93 					sc.ChangeState(SCE_CSOUND_OPCODE);
94 				} else if (headerStmt.InList(s)) {
95 					sc.ChangeState(SCE_CSOUND_HEADERSTMT);
96 				} else if (otherKeyword.InList(s)) {
97 					sc.ChangeState(SCE_CSOUND_USERKEYWORD);
98 				} else if (s[0] == 'p') {
99 					sc.ChangeState(SCE_CSOUND_PARAM);
100 				} else if (s[0] == 'a') {
101 					sc.ChangeState(SCE_CSOUND_ARATE_VAR);
102 				} else if (s[0] == 'k') {
103 					sc.ChangeState(SCE_CSOUND_KRATE_VAR);
104 				} else if (s[0] == 'i') { // covers both i-rate variables and i-statements
105 					sc.ChangeState(SCE_CSOUND_IRATE_VAR);
106 				} else if (s[0] == 'g') {
107 					sc.ChangeState(SCE_CSOUND_GLOBAL_VAR);
108 				}
109 				sc.SetState(SCE_CSOUND_DEFAULT);
110 			}
111 		}
112 		else if (sc.state == SCE_CSOUND_COMMENT ) {
113 			if (sc.atLineEnd) {
114 				sc.SetState(SCE_CSOUND_DEFAULT);
115 			}
116 		}
117 		else if ((sc.state == SCE_CSOUND_ARATE_VAR) ||
118 			(sc.state == SCE_CSOUND_KRATE_VAR) ||
119 		(sc.state == SCE_CSOUND_IRATE_VAR)) {
120 			if (!IsAWordChar(sc.ch)) {
121 				sc.SetState(SCE_CSOUND_DEFAULT);
122 			}
123 		}
124 
125 		// Determine if a new state should be entered.
126 		if (sc.state == SCE_CSOUND_DEFAULT) {
127 			if (sc.ch == ';'){
128 				sc.SetState(SCE_CSOUND_COMMENT);
129 			} else if (isdigit(sc.ch) || (sc.ch == '.' && isdigit(sc.chNext))) {
130 				sc.SetState(SCE_CSOUND_NUMBER);
131 			} else if (IsAWordStart(sc.ch)) {
132 				sc.SetState(SCE_CSOUND_IDENTIFIER);
133 			} else if (IsCsoundOperator(static_cast<char>(sc.ch))) {
134 				sc.SetState(SCE_CSOUND_OPERATOR);
135 			} else if (sc.ch == 'p') {
136 				sc.SetState(SCE_CSOUND_PARAM);
137 			} else if (sc.ch == 'a') {
138 				sc.SetState(SCE_CSOUND_ARATE_VAR);
139 			} else if (sc.ch == 'k') {
140 				sc.SetState(SCE_CSOUND_KRATE_VAR);
141 			} else if (sc.ch == 'i') { // covers both i-rate variables and i-statements
142 				sc.SetState(SCE_CSOUND_IRATE_VAR);
143 			} else if (sc.ch == 'g') {
144 				sc.SetState(SCE_CSOUND_GLOBAL_VAR);
145 			}
146 		}
147 	}
148 	sc.Complete();
149 }
150 
FoldCsoundInstruments(Sci_PositionU startPos,Sci_Position length,int,WordList * [],Accessor & styler)151 static void FoldCsoundInstruments(Sci_PositionU startPos, Sci_Position length, int /* initStyle */, WordList *[],
152 		Accessor &styler) {
153 	Sci_PositionU lengthDoc = startPos + length;
154 	int visibleChars = 0;
155 	Sci_Position lineCurrent = styler.GetLine(startPos);
156 	int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
157 	int levelCurrent = levelPrev;
158 	char chNext = styler[startPos];
159 	int stylePrev = 0;
160 	int styleNext = styler.StyleAt(startPos);
161 	for (Sci_PositionU i = startPos; i < lengthDoc; i++) {
162 		char ch = chNext;
163 		chNext = styler.SafeGetCharAt(i + 1);
164 		int style = styleNext;
165 		styleNext = styler.StyleAt(i + 1);
166 		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
167 		if ((stylePrev != SCE_CSOUND_OPCODE) && (style == SCE_CSOUND_OPCODE)) {
168 			char s[20];
169 			unsigned int j = 0;
170 			while ((j < (sizeof(s) - 1)) && (iswordchar(styler[i + j]))) {
171 				s[j] = styler[i + j];
172 				j++;
173 			}
174 			s[j] = '\0';
175 
176 			if (strcmp(s, "instr") == 0)
177 				levelCurrent++;
178 			if (strcmp(s, "endin") == 0)
179 				levelCurrent--;
180 		}
181 
182 		if (atEOL) {
183 			int lev = levelPrev;
184 			if (visibleChars == 0)
185 				lev |= SC_FOLDLEVELWHITEFLAG;
186 			if ((levelCurrent > levelPrev) && (visibleChars > 0))
187 				lev |= SC_FOLDLEVELHEADERFLAG;
188 			if (lev != styler.LevelAt(lineCurrent)) {
189 				styler.SetLevel(lineCurrent, lev);
190 			}
191 			lineCurrent++;
192 			levelPrev = levelCurrent;
193 			visibleChars = 0;
194 		}
195 		if (!isspacechar(ch))
196 			visibleChars++;
197 		stylePrev = style;
198 	}
199 	// Fill in the real level of the next line, keeping the current flags as they will be filled in later
200 	int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
201 	styler.SetLevel(lineCurrent, levelPrev | flagsNext);
202 }
203 
204 
205 static const char * const csoundWordListDesc[] = {
206 	"Opcodes",
207 	"Header Statements",
208 	"User keywords",
209 	0
210 };
211 
212 LexerModule lmCsound(SCLEX_CSOUND, ColouriseCsoundDoc, "csound", FoldCsoundInstruments, csoundWordListDesc);
213