1// Scintilla source code edit control
2/** @file LexAsn1.cxx
3 ** Lexer for ASN.1
4 **/
5// Copyright 2004 by Herr Pfarrer rpfarrer <at> yahoo <dot> de
6// Last Updated: 20/07/2004
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
15#include <string>
16#include <string_view>
17
18#include "ILexer.h"
19#include "Scintilla.h"
20#include "SciLexer.h"
21
22#include "WordList.h"
23#include "LexAccessor.h"
24#include "Accessor.h"
25#include "StyleContext.h"
26#include "CharacterSet.h"
27#include "LexerModule.h"
28
29using namespace Lexilla;
30
31// Some char test functions
32static bool isAsn1Number(int ch)
33{
34 return (ch >= '0' && ch <= '9');
35}
36
37static bool isAsn1Letter(int ch)
38{
39 return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
40}
41
42static bool isAsn1Char(int ch)
43{
44 return (ch == '-' ) || isAsn1Number(ch) || isAsn1Letter (ch);
45}
46
47//
48// Function determining the color of a given code portion
49// Based on a "state"
50//
51static void ColouriseAsn1Doc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordLists[], Accessor &styler)
52{
53 // The keywords
54 WordList &Keywords = *keywordLists[0];
55 WordList &Attributes = *keywordLists[1];
56 WordList &Descriptors = *keywordLists[2];
57 WordList &Types = *keywordLists[3];
58
59 // Parse the whole buffer character by character using StyleContext
60 StyleContext sc(startPos, length, initStyle, styler);
61 for (; sc.More(); sc.Forward())
62 {
63 // The state engine
64 switch (sc.state)
65 {
66 case SCE_ASN1_DEFAULT: // Plain characters
67asn1_default:
68 if (sc.ch == '-' && sc.chNext == '-')
69 // A comment begins here
70 sc.SetState(SCE_ASN1_COMMENT);
71 else if (sc.ch == '"')
72 // A string begins here
73 sc.SetState(SCE_ASN1_STRING);
74 else if (isAsn1Number (sc.ch))
75 // A number starts here (identifier should start with a letter in ASN.1)
76 sc.SetState(SCE_ASN1_SCALAR);
77 else if (isAsn1Char (sc.ch))
78 // An identifier starts here (identifier always start with a letter)
79 sc.SetState(SCE_ASN1_IDENTIFIER);
80 else if (sc.ch == ':')
81 // A ::= operator starts here
82 sc.SetState(SCE_ASN1_OPERATOR);
83 break;
84 case SCE_ASN1_COMMENT: // A comment
85 if (sc.ch == '\r' || sc.ch == '\n')
86 // A comment ends here
87 sc.SetState(SCE_ASN1_DEFAULT);
88 break;
89 case SCE_ASN1_IDENTIFIER: // An identifier (keyword, attribute, descriptor or type)
90 if (!isAsn1Char (sc.ch))
91 {
92 // The end of identifier is here: we can look for it in lists by now and change its state
93 char s[100];
94 sc.GetCurrent(s, sizeof(s));
95 if (Keywords.InList(s))
96 // It's a keyword, change its state
97 sc.ChangeState(SCE_ASN1_KEYWORD);
98 else if (Attributes.InList(s))
99 // It's an attribute, change its state
100 sc.ChangeState(SCE_ASN1_ATTRIBUTE);
101 else if (Descriptors.InList(s))
102 // It's a descriptor, change its state
103 sc.ChangeState(SCE_ASN1_DESCRIPTOR);
104 else if (Types.InList(s))
105 // It's a type, change its state
106 sc.ChangeState(SCE_ASN1_TYPE);
107
108 // Set to default now
109 sc.SetState(SCE_ASN1_DEFAULT);
110 }
111 break;
112 case SCE_ASN1_STRING: // A string delimited by ""
113 if (sc.ch == '"')
114 {
115 // A string ends here
116 sc.ForwardSetState(SCE_ASN1_DEFAULT);
117
118 // To correctly manage a char sticking to the string quote
119 goto asn1_default;
120 }
121 break;
122 case SCE_ASN1_SCALAR: // A plain number
123 if (!isAsn1Number (sc.ch))
124 // A number ends here
125 sc.SetState(SCE_ASN1_DEFAULT);
126 break;
127 case SCE_ASN1_OPERATOR: // The affectation operator ::= and wath follows (eg: ::= { org 6 } OID or ::= 12 trap)
128 if (sc.ch == '{')
129 {
130 // An OID definition starts here: enter the sub loop
131 for (; sc.More(); sc.Forward())
132 {
133 if (isAsn1Number (sc.ch) && (!isAsn1Char (sc.chPrev) || isAsn1Number (sc.chPrev)))
134 // The OID number is highlighted
135 sc.SetState(SCE_ASN1_OID);
136 else if (isAsn1Char (sc.ch))
137 // The OID parent identifier is plain
138 sc.SetState(SCE_ASN1_IDENTIFIER);
139 else
140 sc.SetState(SCE_ASN1_DEFAULT);
141
142 if (sc.ch == '}')
143 // Here ends the OID and the operator sub loop: go back to main loop
144 break;
145 }
146 }
147 else if (isAsn1Number (sc.ch))
148 {
149 // A trap number definition starts here: enter the sub loop
150 for (; sc.More(); sc.Forward())
151 {
152 if (isAsn1Number (sc.ch))
153 // The trap number is highlighted
154 sc.SetState(SCE_ASN1_OID);
155 else
156 {
157 // The number ends here: go back to main loop
158 sc.SetState(SCE_ASN1_DEFAULT);
159 break;
160 }
161 }
162 }
163 else if (sc.ch != ':' && sc.ch != '=' && sc.ch != ' ')
164 // The operator doesn't imply an OID definition nor a trap, back to main loop
165 goto asn1_default; // To be sure to handle actually the state change
166 break;
167 }
168 }
169 sc.Complete();
170}
171
172static void FoldAsn1Doc(Sci_PositionU, Sci_Position, int, WordList *[], Accessor &styler)
173{
174 // No folding enabled, no reason to continue...
175 if( styler.GetPropertyInt("fold") == 0 )
176 return;
177
178 // No folding implemented: doesn't make sense for ASN.1
179}
180
181static const char * const asn1WordLists[] = {
182 "Keywords",
183 "Attributes",
184 "Descriptors",
185 "Types",
186 0, };
187
188
189LexerModule lmAsn1(SCLEX_ASN1, ColouriseAsn1Doc, "asn1", FoldAsn1Doc, asn1WordLists);
190