Skip to content

Commit e1a9e1e

Browse files
authored
Merge pull request #1051 from Mr-Rm/fix/preproc
fix #1049 и #1050: исправлена работа директив препроцессора
2 parents d201fde + 234190f commit e1a9e1e

File tree

5 files changed

+123
-19
lines changed

5 files changed

+123
-19
lines changed

src/OneScript.Language.Tests/PreprocessorTests.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,43 @@ public void PreprocessingLexer_DirectiveNotOnNewLine()
559559
Assert.Throws<SyntaxErrorException>(() => { while (pp.NextLexem().Token != Token.EndOfText) ; });
560560
}
561561

562+
[Fact]
563+
public void PreprocessingLexer_DirectiveNotOnSingleLine()
564+
{
565+
var pp = new PreprocessingLexer();
566+
567+
var code = @"
568+
#Если Нет
569+
Тогда
570+
F;
571+
#КонецОбласти
572+
";
573+
574+
pp.Code = code;
575+
Assert.Throws<SyntaxErrorException>(() => { while (pp.NextLexem().Token != Token.EndOfText) ; });
576+
}
577+
578+
[Fact]
579+
public void PreprocessingLexer_ExcludedLines()
580+
{
581+
var pp = new PreprocessingLexer();
582+
pp.Define("Да");
583+
584+
var code = @"
585+
#Если Да Тогда
586+
F;
587+
#Иначе
588+
!!
589+
#КонецЕсли
590+
";
591+
592+
pp.Code = code;
593+
594+
Lexem lex;
595+
do { lex = pp.NextLexem(); } while (pp.NextLexem().Token != Token.EndOfText);
596+
Assert.Equal(Token.EndOfText, lex.Token);
597+
}
598+
562599
private string GetPreprocessedContent(PreprocessingLexer pp, string code)
563600
{
564601
pp.Code = code;

src/OneScript.Language/LexicalAnalysis/PreprocessingLexer.cs

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace OneScript.Language.LexicalAnalysis
1313
public class PreprocessingLexer : ILexemGenerator
1414
{
1515
HashSet<string> _definitions = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
16-
ILexemGenerator _lexer;
16+
FullSourceLexer _lexer;
1717
string _code;
1818

1919
Lexem _lastExtractedLexem;
@@ -28,7 +28,7 @@ private class PreprocessorBlock
2828

2929
public PreprocessingLexer()
3030
{
31-
_lexer = new Lexer();
31+
_lexer = new FullSourceLexer();
3232
}
3333

3434
public event EventHandler<PreprocessorUnknownTokenEventArgs> UnknownDirective;
@@ -113,7 +113,7 @@ private SyntaxErrorException PreprocessorError(string message)
113113

114114
private bool SolveExpression()
115115
{
116-
NextLexem();
116+
MoveNextSameLine();
117117

118118
return SolveOrExpression();
119119
}
@@ -128,7 +128,7 @@ private bool SolveOrExpression()
128128

129129
if (_lastExtractedLexem.Token == Token.Or)
130130
{
131-
NextLexem();
131+
MoveNextSameLine();
132132
var secondArgument = SolveOrExpression();
133133
return argument || secondArgument; // здесь нужны НЕ-сокращенные вычисления
134134
}
@@ -142,7 +142,7 @@ private bool SolveAndExpression()
142142

143143
if (_lastExtractedLexem.Token == Token.And)
144144
{
145-
NextLexem();
145+
MoveNextSameLine();
146146
var secondArgument = SolveAndExpression();
147147
return argument && secondArgument; // здесь нужны НЕ-сокращенные вычисления
148148
}
@@ -154,7 +154,7 @@ private bool SolveNotExpression()
154154
{
155155
if (_lastExtractedLexem.Token == Token.Not)
156156
{
157-
NextLexem();
157+
MoveNextSameLine();
158158
return !GetArgument();
159159
}
160160

@@ -165,11 +165,11 @@ private bool GetArgument()
165165
{
166166
if (_lastExtractedLexem.Token == Token.OpenPar)
167167
{
168-
NextLexem();
168+
MoveNextSameLine();
169169
var result = SolveOrExpression();
170170
if (_lastExtractedLexem.Token == Token.ClosePar)
171171
{
172-
NextLexem();
172+
MoveNextSameLine();
173173
return result;
174174
}
175175
throw PreprocessorError("Ожидается закрывающая скобка");
@@ -179,7 +179,7 @@ private bool GetArgument()
179179
throw PreprocessorError("Ожидается объявление препроцессора");
180180

181181
var expression = IsDefined(_lastExtractedLexem.Content);
182-
NextLexem();
182+
MoveNextSameLine();
183183
return expression;
184184
}
185185

@@ -203,10 +203,13 @@ public string Code
203203

204204
public Lexem NextLexem()
205205
{
206-
MoveNext();
207-
208-
if (_lastExtractedLexem.Type == LexemType.PreprocessorDirective)
209-
return Preprocess(_lastExtractedLexem);
206+
do
207+
{
208+
MoveNext();
209+
if (_lastExtractedLexem.Type == LexemType.PreprocessorDirective)
210+
_lastExtractedLexem = Preprocess(_lastExtractedLexem);
211+
}
212+
while (_lastExtractedLexem.Type == LexemType.Comment);
210213

211214
if (_lastExtractedLexem.Type == LexemType.EndOfText)
212215
{
@@ -224,6 +227,13 @@ private void MoveNext()
224227
_lastExtractedLexem = _lexer.NextLexem();
225228
}
226229

230+
private void MoveNextSameLine()
231+
{
232+
_lastExtractedLexem = _lexer.NextLexem();
233+
if (_lexer.Iterator.OnNewLine)
234+
throw PreprocessorError("Неожиданное завершение директивы");
235+
}
236+
227237
private Lexem Preprocess(Lexem directive)
228238
{
229239
if(directive.Token == Token.If)
@@ -323,14 +333,34 @@ private void CheckNewLine()
323333
throw PreprocessorError("Недопустимые символы в директиве");
324334
}
325335

336+
private void SkipComment()
337+
{
338+
_lexer.Iterator.MoveToContent();
339+
if (!_lexer.Iterator.OnNewLine)
340+
{
341+
MoveNext();
342+
if (_lastExtractedLexem.Type != LexemType.Comment)
343+
throw PreprocessorError("Недопустимые символы в директиве");
344+
}
345+
}
346+
347+
private void SkipErrors(object sender, LexerErrorEventArgs args)
348+
{
349+
args.Iterator.MoveNext();
350+
args.IsHandled = true;
351+
}
352+
326353
private void SkipTillNextDirective()
327354
{
328-
int currentLevel = BlockLevel();
329-
MoveNext();
330-
CheckNewLine();
355+
SkipComment();
356+
357+
_lexer.UnexpectedCharacterFound += SkipErrors;
331358

359+
int currentLevel = BlockLevel();
332360
while (true)
333361
{
362+
MoveNext();
363+
334364
while (_lastExtractedLexem.Type != LexemType.PreprocessorDirective)
335365
{
336366
if (_lastExtractedLexem.Token == Token.EndOfText)
@@ -343,11 +373,14 @@ private void SkipTillNextDirective()
343373
PushBlock();
344374
else if (_lastExtractedLexem.Token == Token.EndIf && BlockLevel() > currentLevel)
345375
PopBlock();
346-
else if (BlockLevel() == currentLevel)
376+
else if (BlockLevel() == currentLevel &&
377+
(_lastExtractedLexem.Token == Token.EndIf ||
378+
_lastExtractedLexem.Token == Token.ElseIf ||
379+
_lastExtractedLexem.Token == Token.Else) )
347380
break;
348-
349-
MoveNext();
350381
}
382+
383+
_lexer.UnexpectedCharacterFound -= SkipErrors;
351384
}
352385
}
353386
}

tests/directives.os

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоОбластиКомпилируются");
99
ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоИспользоватьНеКомпилируетсяЕслиНачалсяКод");
10+
ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоМногострочныеДирективыНеКомпилируются");
11+
ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоИсключенныеСтрокиПропускаются");
1012

1113
Возврат ВсеТесты;
1214

@@ -43,3 +45,19 @@
4345

4446
КонецФункции
4547

48+
Функция ТестДолжен_ПроверитьЧтоМногострочныеДирективыНеКомпилируются() Экспорт
49+
50+
юТест.ПроверитьРавенство(ФайлКомпилируется("preprocessor/multiline-fail.os"), Ложь,
51+
"Файл multiline-fail.os компилируется, хотя не должен!"
52+
);
53+
54+
КонецФункции
55+
56+
Функция ТестДолжен_ПроверитьЧтоИсключенныеСтрокиПропускаются() Экспорт
57+
58+
юТест.ПроверитьРавенство(ФайлКомпилируется("preprocessor/excluded.os"), Истина,
59+
"Файл excluded.os не компилируется, хотя должен!"
60+
);
61+
62+
КонецФункции
63+

tests/preprocessor/excluded.os

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#Если Windows И Linux Тогда
2+
!!
3+
#КонецЕсли
4+
5+
#Если Windows ИЛИ Linux Тогда //!! ok
6+
7+
#ИначеЕсли НЕ MacOS Тогда //??
8+
!!
9+
#Иначе //!!
10+
!!??
11+
#КонецЕсли
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#Если Windows
2+
или Linux
3+
Тогда
4+
//!!
5+
#КонецЕсли

0 commit comments

Comments
 (0)