-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathLanguageCompiler.cs
More file actions
153 lines (123 loc) · 5.24 KB
/
LanguageCompiler.cs
File metadata and controls
153 lines (123 loc) · 5.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright (c) Microsoft Corporation. All rights reserved.
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using ColorCode.Common;
namespace ColorCode.Compilation
{
public class LanguageCompiler : ILanguageCompiler
{
private static readonly Regex numberOfCapturesRegex = new Regex(@"(?x)(?<!\\)\((?!\?)", RegexOptions.Compiled);
private readonly Dictionary<string, CompiledLanguage> compiledLanguages;
private readonly ReaderWriterLockSlim compileLock;
public LanguageCompiler(Dictionary<string, CompiledLanguage> compiledLanguages)
{
this.compiledLanguages = compiledLanguages;
compileLock = new ReaderWriterLockSlim();
}
public CompiledLanguage Compile(ILanguage language)
{
Guard.ArgNotNull(language, "language");
if (string.IsNullOrEmpty(language.Id))
throw new ArgumentException("The language identifier must not be null.", "language");
CompiledLanguage compiledLanguage;
compileLock.EnterReadLock();
try
{
// for performance reasons we should first try with
// only a read lock since the majority of the time
// it'll be created already and upgradeable lock blocks
if (compiledLanguages.ContainsKey(language.Id))
return compiledLanguages[language.Id];
}
finally
{
compileLock.ExitReadLock();
}
compileLock.EnterUpgradeableReadLock();
try
{
if (compiledLanguages.ContainsKey(language.Id))
compiledLanguage = compiledLanguages[language.Id];
else
{
compileLock.EnterWriteLock();
try
{
if (string.IsNullOrEmpty(language.Name))
throw new ArgumentException("The language name must not be null or empty.", "language");
if (language.Rules == null || language.Rules.Count == 0)
throw new ArgumentException("The language rules collection must not be empty.", "language");
compiledLanguage = CompileLanguage(language);
compiledLanguages.Add(compiledLanguage.Id, compiledLanguage);
}
finally
{
compileLock.ExitWriteLock();
}
}
}
finally
{
compileLock.ExitUpgradeableReadLock();
}
return compiledLanguage;
}
private static CompiledLanguage CompileLanguage(ILanguage language)
{
string id = language.Id;
string name = language.Name;
Regex regex;
IList<string> captures;
CompileRules(language.Rules, out regex, out captures);
return new CompiledLanguage(id, name, regex, captures);
}
private static void CompileRules(IList<LanguageRule> rules,
out Regex regex,
out IList<string> captures)
{
StringBuilder regexBuilder = new StringBuilder();
captures = new List<string>();
regexBuilder.AppendLine("(?x)");
captures.Add(null);
CompileRule(rules[0], regexBuilder, captures, true);
for (int i = 1; i < rules.Count; i++)
CompileRule(rules[i], regexBuilder, captures, false);
regex = new Regex(regexBuilder.ToString());
}
private static void CompileRule(LanguageRule languageRule,
StringBuilder regex,
ICollection<string> captures,
bool isFirstRule)
{
if (!isFirstRule)
{
regex.AppendLine();
regex.AppendLine();
regex.AppendLine("|");
regex.AppendLine();
}
regex.AppendFormat("(?-xis)(?m)({0})(?x)", languageRule.Regex);
int numberOfCaptures = GetNumberOfCaptures(languageRule.Regex);
for (int i = 0; i <= numberOfCaptures; i++)
{
string scope = null;
foreach (int captureIndex in languageRule.Captures.Keys)
{
if (i == captureIndex)
{
scope = languageRule.Captures[captureIndex];
break;
}
}
captures.Add(scope);
}
}
private static int GetNumberOfCaptures(string regex)
{
return numberOfCapturesRegex.Matches(regex).Count;
}
}
}