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