1 using System;
2 using System.Collections;
3 using System.IO;
4 using System.Text;
5
6
7
8
9
10 namespace soulcm.scmlg
11 {
12 public enum IdentifierClassKind
13 {
14 none, unicode, ascii
15 }
16 public class LexerContext
17 {
18 public LexerContext(IdentifierClassKind identifierClassKind) :
19 nextNfaStateId(0), nextDfaStateId(0), lexerStatementIndex(-1), classIndex(0), any(), epsilon(eps),
20 idStart(new Class(classIndex++)), idCont(new Class(classIndex++)),
21 classMap(null), tokens(null), keywords(null), expressions(null), lexer(null), parser(null), currentExpression(null),
22 moduleId(Uuid.Random())
23 {
24 if (identifierClassKind == IdentifierClassKind.unicode)
25 {
26 MakeUnicodeIdentifierClasses(*this);
27 symbols.Add(idStart);
28 symbols.Add(idCont);
29 }
30 else if (identifierClassKind == IdentifierClassKind.ascii)
31 {
32 MakeAsciiIdentifierClasses(*this);
33 symbols.Add(idStart);
34 symbols.Add(idCont);
35 }
36 }
37 public ~LexerContext()
38 {
39 for (NfaState* state : nfaStates)
40 {
41 delete state;
42 }
43 for (DfaState* state : dfaStates)
44 {
45 delete state;
46 }
47 for (Symbol* symbol : symbols)
48 {
49 delete symbol;
50 }
51 for (Class* cls : canonicalClasses)
52 {
53 delete cls;
54 }
55 }
56 public const List<NfaState*>& NfaStates() const
57 {
58 return nfaStates;
59 }
60 public Symbol* MakeAny()
61 {
62 return &any;
63 }
64 public Symbol* MakeEpsilon()
65 {
66 return ε
67 }
68 public Class* MakeIdStart()
69 {
70 return idStart;
71 }
72 public Class* MakeIdCont()
73 {
74 return idCont;
75 }
76 public const List<Symbol*>& Symbols() const
77 {
78 return symbols;
79 }
80 public const string& FileName() const
81 {
82 return fileName;
83 }
84 public ClassMap* GetClassMap() const
85 {
86 return classMap;
87 }
88 public const List<Cm.Ast.Node*>& GetUsingNodes() const
89 {
90 return usingNodes;
91 }
92 public Tokens* GetTokens() const
93 {
94 return tokens;
95 }
96 public Keywords* GetKeywords() const
97 {
98 return keywords;
99 }
100 public void SetLexer(Lexer* lexer_)
101 {
102 lexer = lexer_;
103 }
104 public Lexer* GetLexer() const
105 {
106 return lexer;
107 }
108 public void SetParser(Parser* parser_)
109 {
110 parser = parser_;
111 }
112 public Parser* GetParser() const
113 {
114 return parser;
115 }
116 public void SetCurrentExpression(Expression* currentExpression_)
117 {
118 currentExpression = currentExpression_;
119 }
120 public const List<Class*>& Partition() const
121 {
122 return partition;
123 }
124 public void SetApi(const ustring& api_)
125 {
126 api = api_;
127 }
128 public const ustring& Api() const
129 {
130 return api;
131 }
132 public NfaState* MakeNfaState()
133 {
134 NfaState* state = new NfaState(nextNfaStateId++, lexerStatementIndex);
135 nfaStates.Add(state);
136 return state;
137 }
138 public DfaState* MakeDfaState(const List<NfaState*>& nfaStates)
139 {
140 List<NfaState*> s(nfaStates);
141 Sort(s.Begin(), s.End());
142 for (DfaState* state : dfaStates)
143 {
144 if (state->NfaStates() == s)
145 {
146 return state;
147 }
148 }
149 DfaState* state = new DfaState(nextDfaStateId++, s);
150 dfaStates.Add(state);
151 return state;
152 }
153 public Symbol* MakeChar(uchar c)
154 {
155 Map<uchar, Symbol*>.ConstIterator it = charSymbols.CFind(c);
156 if (it != charSymbols.CEnd())
157 {
158 return it->second;
159 }
160 Symbol* symbol = new Char(c);
161 symbol->DontSetContained();
162 symbols.Add(symbol);
163 charSymbols[c] = symbol;
164 return symbol;
165 }
166 public Symbol* MakeRange(uchar start, uchar end)
167 {
168 Range range(start, end);
169 Map<Range, Symbol*>.ConstIterator it = rangeSymbols.CFind(range);
170 if (it != rangeSymbols.CEnd())
171 {
172 return it->second;
173 }
174 Symbol* symbol = new Range(start, end);
175 symbols.Add(symbol);
176 rangeSymbols[range] = symbol;
177 return symbol;
178 }
179 public Class* MakeClass()
180 {
181 Class* cls = new Class(classIndex++);
182 symbols.Add(cls);
183 return cls;
184 }
185 public void SetClassMap(ClassMap* classMap_)
186 {
187 if ((classMap != null))
188 {
189 throw Exception("error in " + fileName + ": cannot have more than one class map declaration per .lexer file");
190 }
191 classMap = classMap_;
192 }
193 public void AddUsingNode(Cm.Ast.Node* usingNode)
194 {
195 usingNodes.Add(usingNode);
196 }
197 public void SetTokens(Tokens* tokens_)
198 {
199 if ((tokens != null))
200 {
201 throw Exception("error in " + fileName + ": cannot have more than one set of token definitions per .lexer file");
202 }
203 tokens = tokens_;
204 }
205 public void SetKeywords(Keywords* keywords_)
206 {
207 if ((keywords != null))
208 {
209 throw Exception("error in " + fileName + ": cannot have more than one set of keyword definitions per .lexer file");
210 }
211 keywords = keywords_;
212 }
213 public void SetExpressions(Expressions* expressions_)
214 {
215 if ((expressions != null))
216 {
217 throw Exception("error in " + fileName + ": cannot have more than one set of expressions per .lexer file");
218 }
219 expressions = expressions_;
220 }
221 public void SetFileName(const string& fileName_)
222 {
223 fileName = fileName_;
224 }
225 public Nfa MakeExpr(const ustring& id)
226 {
227 if (id == u"idstart")
228 {
229 return MakeNfa(*this, MakeIdStart());
230 }
231 else if (id == u"idcont")
232 {
233 return MakeNfa(*this, MakeIdCont());
234 }
235 if ((expressions != null))
236 {
237 Expression* expr = expressions->Get(id);
238 if ((currentExpression != null))
239 {
240 if (expr->Index() >= currentExpression->Index())
241 {
242 throw Exception("error: forward reference \'" + ToUtf8(expr->Id()) + "\' from expression \'" + ToUtf8(currentExpression->Id()) + "\'");
243 }
244 }
245 if ((parser != null))
246 {
247 Expression* prevExpression = currentExpression;
248 currentExpression = expr;
249 Nfa nfa = parser->Parse(expr->Value(), this, expr->Line());
250 currentExpression = prevExpression;
251 return nfa;
252 }
253 else
254 {
255 throw Exception("parser not set");
256 }
257 }
258 else
259 {
260 throw Exception("expressions not set");
261 }
262 }
263 public void MakeCanonicalClasses()
264 {
265 List<Symbol*> s = symbols;
266 for (Symbol* symbol : s)
267 {
268 if (symbol->Contained()) continue;
269 if (symbol->IsClass())
270 {
271 Class* cls = cast<Class*>(symbol);
272 Class* canonicalClass = cls->MakeCanonical(*this);
273 canonicalClasses.Add(canonicalClass);
274 }
275 else if (symbol->IsChar())
276 {
277 Char* chr = cast<Char*>(symbol);
278 Class* canonicalClass = new Class(-1);
279 canonicalClass->Ranges().Add(Range(chr->Chr(), chr->Chr()));
280 canonicalClasses.Add(canonicalClass);
281 }
282 else if (symbol->IsAny())
283 {
284 Symbol* range = MakeRange(cast<uchar>(1), cast<uchar>(1114111));
285 Class* canonicalClass = new Class(-1);
286 canonicalClass->Ranges().Add(Range(cast<uchar>(1), cast<uchar>(1114111)));
287 canonicalClasses.Add(canonicalClass);
288 }
289 else if (symbol->IsRange())
290 {
291 Range* range = cast<Range*>(symbol);
292 Class* canonicalClass = new Class(-1);
293 canonicalClass->Ranges().Add(*range);
294 canonicalClasses.Add(canonicalClass);
295 }
296 }
297 for (Class* canonicalClass : canonicalClasses)
298 {
299 for (const Range& range : canonicalClass->Ranges())
300 {
301 if (canonicalClass->Chars().IsEmpty())
302 {
303 canonicalClass->AddChar(range.Start());
304 }
305 break;
306 }
307 }
308 }
309 public void MakeClassPartition(bool debug)
310 {
311 LinkedList<Class*> classes;
312 for (Class* cls : canonicalClasses)
313 {
314 classes.Add(cls);
315 }
316 LinkedList<Class*>.Iterator classIt = classes.Begin();
317 while (classIt != classes.End())
318 {
319 bool classChanged = false;
320 Class* cls = *classIt;
321 LinkedList<Class*>.Iterator sourceIt = classes.Begin();
322 while (!cls->Ranges().IsEmpty() && sourceIt != classes.End())
323 {
324 if (sourceIt == classIt)
325 {
326 ++sourceIt;
327 continue;
328 }
329 bool sourceChanged = false;
330 Class* source = *sourceIt;
331 UniquePtr<Class> splitClass(new Class(-1));
332 LinkedList<Range>.Iterator leftIt = cls->Ranges().Begin();
333 while (leftIt != cls->Ranges().End())
334 {
335 bool leftChanged = false;
336 LinkedList<Range>.Iterator rightIt = source->Ranges().Begin();
337 while (leftIt != cls->Ranges().End() && rightIt != source->Ranges().End())
338 {
339 bool rightChanged = false;
340 if (Intersect(*leftIt, *rightIt))
341 {
342 List<Range> leftRanges = *leftIt - *rightIt;
343 List<Range> rightRanges = *rightIt - *leftIt;
344 Range intersection = *leftIt & *rightIt;
345 for (const Range& leftRange : leftRanges)
346 {
347 if (leftRange.IsEmpty()) continue;
348 LinkedList<Range>.Iterator it = leftIt;
349 ++it;
350 cls->Ranges().Insert(it, leftRange);
351 }
352 leftIt = cls->Ranges().Remove(leftIt);
353 leftChanged = true;
354 if (!intersection.IsEmpty())
355 {
356 splitClass->Ranges().Add(intersection);
357 }
358 for (const Range& rightRange : rightRanges)
359 {
360 if (rightRange.IsEmpty()) continue;
361 LinkedList<Range>.Iterator it = rightIt;
362 ++it;
363 source->Ranges().Insert(it, rightRange);
364 }
365 rightIt = source->Ranges().Remove(rightIt);
366 rightChanged = true;
367 }
368 if (!rightChanged)
369 {
370 ++rightIt;
371 }
372 }
373 if (!leftChanged)
374 {
375 ++leftIt;
376 }
377 }
378 if (source->Ranges().IsEmpty())
379 {
380 sourceIt = classes.Remove(sourceIt);
381 sourceChanged = true;
382 }
383 if (!splitClass->Ranges().IsEmpty())
384 {
385 classes.Insert(sourceIt, splitClass.Get());
386 symbols.Add(splitClass.Release());
387 sourceChanged = true;
388 }
389 if (!sourceChanged)
390 {
391 ++sourceIt;
392 }
393 }
394 if (cls->Ranges().IsEmpty())
395 {
396 classIt = classes.Remove(classIt);
397 classChanged = true;
398 }
399 if (!classChanged)
400 {
401 ++classIt;
402 }
403 }
404 for (Class* cls : classes)
405 {
406 cls->MakeMinimal(*this);
407 partition.Add(cls);
408 }
409 for (int i = 0; i < partition.Count(); ++i;)
410 {
411 Class* cls = partition[i];
412 cls->SetIndex(i);
413 }
414 if (debug)
415 {
416 CodeFormatter formatter(Console.Out());
417 formatter.WriteLine("partition:");
418 for (int i = 0; i < partition.Count(); ++i;)
419 {
420 Class* cls = partition[i];
421 formatter.Write(ToString(cls->Index()));
422 formatter.Write(" : ");
423 cls->Print(formatter);
424 formatter.WriteLine();
425 }
426 }
427 }
428 public void MakeClassMap(const string& root, bool verbose, bool noClassMapCompression)
429 {
430 List<int> classMapVec(1114112, -1);
431 for (Class* cls : partition)
432 {
433 for (const Range& range : cls->Ranges())
434 {
435 for (uchar i = range.Start(); i <= range.End(); i = cast<uchar>(cast<int>(i) + 1);)
436 {
437 if (classMapVec[cast<int>(i)] == -1)
438 {
439 classMapVec[cast<int>(i)] = cls->Index();
440 }
441 }
442 }
443 }
444 string classMapName = "ClassMap";
445 if ((classMap != null))
446 {
447 classMapName = ToUtf8(classMap->Name());
448 }
449 string classMapSourceFileName = GetFullPath(Path.Combine(root, classMapName + ".cm"));
450 Cm.Ast.CompileUnitNode classMapUnit(System.Lex.Span(), moduleId, classMapSourceFileName);
451 classMapUnit.GlobalNs()->AddMember(new Cm.Ast.NamespaceImportNode(System.Lex.Span(), moduleId, new Cm.Ast.IdentifierNode(System.Lex.Span(), moduleId, u"System")));
452 classMapUnit.GlobalNs()->AddMember(new Cm.Ast.CommentNode(System.Lex.Span(), moduleId, u"this file has been automatically generated from \'" + ToUtf32(FileName()) + u"\' using soulcm lexer generator scmlg version " + ToUtf32(LexerGeneratorVersionStr())));
453 UniquePtr<Cm.Ast.GlobalVariableNode> classMapDataVariable(new Cm.Ast.GlobalVariableNode(System.Lex.Span(), moduleId, Cm.Ast.Specifiers.private_, new Cm.Ast.ArrayNode(System.Lex.Span(), moduleId, new Cm.Ast.ByteNode(System.Lex.Span(), moduleId), null), new Cm.Ast.IdentifierNode(System.Lex.Span(), moduleId, u"classMapData"), &classMapUnit));
454 UniquePtr<Cm.Ast.ArrayLiteralNode> arrayLiteral(new Cm.Ast.ArrayLiteralNode(System.Lex.Span(), moduleId));
455 SharedPtr<ByteStream> stream = System.Lex.MakeClassMapData(classMapVec, !noClassMapCompression);
456 int x = stream->ReadByte();
457 while (x != -1)
458 {
459 arrayLiteral->AddValue(new Cm.Ast.ByteLiteralNode(System.Lex.Span(), moduleId, cast<byte>(x)));
460 x = stream->ReadByte();
461 }
462 classMapDataVariable->SetInitializer(arrayLiteral.Release());
463 classMapUnit.GlobalNs()->AddMember(classMapDataVariable.Release());
464 UniquePtr<Cm.Ast.GlobalVariableNode> classMapVariable(new Cm.Ast.GlobalVariableNode(System.Lex.Span(), moduleId, Cm.Ast.Specifiers.private_, new Cm.Ast.PointerNode(System.Lex.Span(), moduleId, new Cm.Ast.IntNode(System.Lex.Span(), moduleId)), new Cm.Ast.IdentifierNode(System.Lex.Span(), moduleId, u"classMap"), &classMapUnit));
465 classMapVariable->SetInitializer(new Cm.Ast.NullLiteralNode(System.Lex.Span(), moduleId));
466 classMapUnit.GlobalNs()->AddMember(classMapVariable.Release());
467 UniquePtr<Cm.Ast.GlobalVariableNode> classMapCompressedVariable(new Cm.Ast.GlobalVariableNode(System.Lex.Span(), moduleId, Cm.Ast.Specifiers.private_, new Cm.Ast.BoolNode(System.Lex.Span(), moduleId), new Cm.Ast.IdentifierNode(System.Lex.Span(), moduleId, u"classMapDataCompressed"), &classMapUnit));
468 classMapCompressedVariable->SetInitializer(new Cm.Ast.BooleanLiteralNode(System.Lex.Span(), moduleId, !noClassMapCompression));
469 classMapUnit.GlobalNs()->AddMember(classMapCompressedVariable.Release());
470 UniquePtr<Cm.Ast.ClassNode> cls(new Cm.Ast.ClassNode(System.Lex.Span(), moduleId, Cm.Ast.Specifiers.public_ | Cm.Ast.Specifiers.static_, new Cm.Ast.IdentifierNode(System.Lex.Span(), moduleId, ToUtf32(classMapName)), null));
471 UniquePtr<Cm.Ast.MemberFunctionNode> initMemFun(new Cm.Ast.MemberFunctionNode(System.Lex.Span(), moduleId, Cm.Ast.Specifiers.public_ | Cm.Ast.Specifiers.static_, new Cm.Ast.VoidNode(System.Lex.Span(), moduleId), u"Init", null));
472 UniquePtr<Cm.Ast.CompoundStatementNode> initMemFunBody(new Cm.Ast.CompoundStatementNode(System.Lex.Span(), moduleId));
473 UniquePtr<Cm.Ast.CompoundStatementNode> initMemFunThenS(new Cm.Ast.CompoundStatementNode(System.Lex.Span(), moduleId));
474 UniquePtr<Cm.Ast.InvokeNode> initMemFunInvoke(new Cm.Ast.InvokeNode(System.Lex.Span(), moduleId, new Cm.Ast.DotNode(System.Lex.Span(), moduleId,
475 new Cm.Ast.DotNode(System.Lex.Span(), moduleId, new Cm.Ast.IdentifierNode(System.Lex.Span(), moduleId, u"System"), new Cm.Ast.IdentifierNode(System.Lex.Span(), moduleId, u"Lex")),
476 new Cm.Ast.IdentifierNode(System.Lex.Span(), moduleId, u"CreateClassMap"))));
477 initMemFunInvoke->AddArgument(new Cm.Ast.InvokeNode(System.Lex.Span(), moduleId,
478 new Cm.Ast.DotNode(System.Lex.Span(), moduleId, new Cm.Ast.IdentifierNode(System.Lex.Span(), moduleId, u"classMapData"),
479 new Cm.Ast.IdentifierNode(System.Lex.Span(), moduleId, u"Begin"))));
480 initMemFunInvoke->AddArgument(new Cm.Ast.InvokeNode(System.Lex.Span(), moduleId,
481 new Cm.Ast.DotNode(System.Lex.Span(), moduleId, new Cm.Ast.IdentifierNode(System.Lex.Span(), moduleId, u"classMapData"), new Cm.Ast.IdentifierNode(System.Lex.Span(), moduleId, u"Length"))));
482 initMemFunInvoke->AddArgument(new Cm.Ast.IdentifierNode(System.Lex.Span(), moduleId, u"classMapDataCompressed"));
483 UniquePtr<Cm.Ast.AssignmentStatementNode> initMemFunAssigmentStmt(new Cm.Ast.AssignmentStatementNode(System.Lex.Span(), moduleId, new Cm.Ast.IdentifierNode(System.Lex.Span(), moduleId, u"classMap"),
484 initMemFunInvoke.Release()));
485 initMemFunThenS->AddStatement(initMemFunAssigmentStmt.Release());
486 UniquePtr<Cm.Ast.IfStatementNode> initMemFunIfStmt(new Cm.Ast.IfStatementNode(System.Lex.Span(), moduleId,
487 new Cm.Ast.EqualNode(System.Lex.Span(), moduleId,
488 new Cm.Ast.IdentifierNode(System.Lex.Span(), moduleId, u"classMap"), new Cm.Ast.NullLiteralNode(System.Lex.Span(), moduleId)),
489 initMemFunThenS.Release(), null));
490 initMemFunBody->AddStatement(initMemFunIfStmt.Release());
491 initMemFun->SetBody(initMemFunBody.Release());
492 cls->AddMember(initMemFun.Release());
493 UniquePtr<Cm.Ast.MemberFunctionNode> getClassMemFun(new Cm.Ast.MemberFunctionNode(System.Lex.Span(), moduleId, Cm.Ast.Specifiers.public_ | Cm.Ast.Specifiers.static_, new Cm.Ast.IntNode(System.Lex.Span(), moduleId), u"GetClass", null));
494 getClassMemFun->AddParameter(new Cm.Ast.ParameterNode(System.Lex.Span(), moduleId, new Cm.Ast.UCharNode(System.Lex.Span(), moduleId), new Cm.Ast.IdentifierNode(System.Lex.Span(), moduleId, u"c")));
495 UniquePtr<Cm.Ast.CompoundStatementNode> body(new Cm.Ast.CompoundStatementNode(System.Lex.Span(), moduleId));
496 UniquePtr<Cm.Ast.ConstructionStatementNode> constructStmt(new Cm.Ast.ConstructionStatementNode(System.Lex.Span(), moduleId, new Cm.Ast.IntNode(System.Lex.Span(), moduleId), new Cm.Ast.IdentifierNode(System.Lex.Span(), moduleId, u"i")));
497 constructStmt->SetAssignment();
498 constructStmt->AddArgument(new Cm.Ast.CastNode(System.Lex.Span(), moduleId, new Cm.Ast.IntNode(System.Lex.Span(), moduleId), new Cm.Ast.IdentifierNode(System.Lex.Span(), moduleId, u"c")));
499 body->AddStatement(constructStmt.Release());
500 UniquePtr<Cm.Ast.CompoundStatementNode> thenS(new Cm.Ast.CompoundStatementNode(System.Lex.Span(), moduleId));
501 UniquePtr<Cm.Ast.ReturnStatementNode> returnClassMapI(new Cm.Ast.ReturnStatementNode(System.Lex.Span(), moduleId, new Cm.Ast.IndexingNode(System.Lex.Span(), moduleId, new Cm.Ast.IdentifierNode(System.Lex.Span(), moduleId, u"classMap"), new Cm.Ast.IdentifierNode(System.Lex.Span(), moduleId, u"i"))));
502 thenS->AddStatement(returnClassMapI.Release());
503 UniquePtr<Cm.Ast.CompoundStatementNode> elseS(new Cm.Ast.CompoundStatementNode(System.Lex.Span(), moduleId));
504 UniquePtr<Cm.Ast.ReturnStatementNode> returnMinusOne(new Cm.Ast.ReturnStatementNode(System.Lex.Span(), moduleId, new Cm.Ast.IntLiteralNode(System.Lex.Span(), moduleId, -1)));
505 elseS->AddStatement(returnMinusOne.Release());
506 UniquePtr<Cm.Ast.IfStatementNode> ifStmt(new Cm.Ast.IfStatementNode(System.Lex.Span(), moduleId, new Cm.Ast.LessNode(System.Lex.Span(), moduleId, new Cm.Ast.IdentifierNode(System.Lex.Span(), moduleId, u"i"), new Cm.Ast.IntLiteralNode(System.Lex.Span(), moduleId, 1114112)), thenS.Release(), elseS.Release()));
507 body->AddStatement(ifStmt.Release());
508 getClassMemFun->SetBody(body.Release());
509 cls->AddMember(getClassMemFun.Release());
510 classMapUnit.GlobalNs()->AddMember(cls.Release());
511 System.IO.StreamWriter classMapFile(System.IO.File.CreateText(classMapSourceFileName));
512 CodeFormatter formatter(classMapFile);
513 Cm.Ast.SourceWriter sourceWriter(formatter);
514 classMapUnit.Accept(sourceWriter);
515 if (verbose)
516 {
517 Console.Out() << "==> " << classMapSourceFileName << endl();
518 }
519 }
520 public inline nothrow const Uuid& ModuleId() const
521 {
522 return moduleId;
523 }
524 private int nextNfaStateId;
525 private int nextDfaStateId;
526 private int lexerStatementIndex;
527 private int classIndex;
528 private string fileName;
529 private List<NfaState*> nfaStates;
530 private List<DfaState*> dfaStates;
531 private List<Symbol*> symbols;
532 private List<Class*> canonicalClasses;
533 private List<Class*> partition;
534 private List<Cm.Ast.Node*> usingNodes;
535 private Map<uchar, Symbol*> charSymbols;
536 private Map<Range, Symbol*> rangeSymbols;
537 private Any any;
538 private Char epsilon;
539 private Class* idStart;
540 private Class* idCont;
541 private ClassMap* classMap;
542 private Usings* usings;
543 private Tokens* tokens;
544 private Keywords* keywords;
545 private Expressions* expressions;
546 private Lexer* lexer;
547 private Parser* parser;
548 private Expression* currentExpression;
549 private ustring api;
550 private Uuid moduleId;
551 }
552 public class ClassLess
553 {
554 public bool operator()(const Class* left, const Class* right) const
555 {
556 if (left->Ranges().Count() < right->Ranges().Count()) return false;
557 if (left->Ranges().Count() > right->Ranges().Count()) return false;
558 if (!left->Ranges().IsEmpty())
559 {
560 Range leftRange = left->Ranges().Front();
561 Range rightRange = right->Ranges().Front();
562 return leftRange < rightRange;
563 }
564 return false;
565 }
566 }
567 public class ClassesEqual
568 {
569 public bool operator()(const Class* left, const Class* right) const
570 {
571 return left->Ranges() == right->Ranges();
572 }
573 }
574 }