Как сопоставить скобки для разбора S-выражения?
Я пытаюсь создать функцию, которая делает следующее:
Предполагая, что ввод кода - это "(a 1 2 (b 3 4 5 (c 6) |7) 8 9)", где труба | символом является позиция курсора,
функция возвращает: a String "b 3 4 5 (c 6) 7", представляющий код, находящийся в области видимости курсора
int 8, представляющий начальный индекс строки относительно ввода
целое число 30, представляющее конечный индекс строки относительно ввода
У меня уже есть рабочий код, который возвращает именно это. Однако проблема заключается в игнорировании комментариев при отслеживании контекста (например, строковых литералов, моих собственных литеральных разделителей и т. Д.).
Вот код, который отслеживает контекст:
public static void applyContext(Context context, String s, String snext, String sprev) {
if (s.equals("\"")) {
if (context.context == Context.Contexts.MAIN) {
context.context = Context.Contexts.STRING;
context.stringDelimiterIsADoubleQuote = true;
} else if (context.context == Context.Contexts.STRING && context.stringDelimiterIsADoubleQuote && !sprev.equals("\\"))
context.context = Context.Contexts.MAIN;
} else if (s.equals("\'")) {
if (context.context == Context.Contexts.MAIN) {
context.context = Context.Contexts.STRING;
context.stringDelimiterIsADoubleQuote = false;
} else if (context.context == Context.Contexts.STRING && !context.stringDelimiterIsADoubleQuote && !sprev.equals("\""))
context.context = Context.Contexts.MAIN;
} else if (s.equals("/") && snext.equals("/")) {
if (context.context == Context.Contexts.MAIN)
context.context = Context.Contexts.COMMENT;
} else if (s.equals("\n")) {
if(context.context == Context.Contexts.COMMENT)
context.context = Context.Contexts.MAIN;
}
else if (s.equals("\\")) {
if(context.context == Context.Contexts.MAIN)
context.context = Context.Contexts.PATTERN;
else if(context.context == Context.Contexts.PATTERN)
context.context = Context.Contexts.MAIN;
}
}
Во-первых, я буду использовать вышеописанную функцию следующим образом:
String sampleCode = "(a b "cdef" g \c4 bb2 eb4 g4v0.75\)";
Context c = new Context(Context.Contexts.MAIN);
for(int i = 0; i < sampleCode.length(); i++) {
String s = String.valueOf(sampleCode.charAt(i));
String snext = *nullcheck* ? String.valueOf(sampleCode.charAt(i + 1)) : "";
String sprev = *nullcheck* ? String.valueOf(sampleCode.charAt(i - 1)) : "";
applyContext(c, s, snext, sprev);
if(c.context == blahlbah) doBlah();
}
Во-вторых, я буду использовать это для перемотки назад, так как текущий метод выполнения функции, указанной в верхней части описания, (в псевдокоде) это:
function returnCodeInScopeOfCursor(theWholeCode::String, cursorIndex::int) {
var depthOfCodeAtCursorPosition::int = getDepth(theWholeCode, cursorIndex);
Context c = new Context(getContextAt(theWholeCode, cursorIndex));
var currDepth::int = depthOfCodeAtCursorPosition;
var startIndex::int, endIndex::int;
for(i = cursorIndex; i >= 0; i--) {//going backwards
s = .....
snext = ......
sprev = ......
applyContext(c, s, snext, sprev);
if(c.context == Context.MAIN) {
if s = "(" then currDepth --;
if s = ")" then currDepth ++;
}
when currDepth < depthOfCodeAtCursorPosition
startIndex = i + 1;
break;
}
currDepth = depthOfCodeAtCursorPosition;//reset
for(i = cursorIndex; i < theWholeCode.length; i++) {//going forwards
s = ...
snex......
sprev.....
applyContext(c, s, snext, sprev);
if(c.context == Context.MAIN) {
if s = "(" then currDepth ++;
if s = ")" then currDepth --;
}
when currDepth < depthOfCodeAtCursorPosition
endIndex = i - 1;
break;
}
var returnedStr = theWholeCode->from startIndex->to endIndex
return new IndexedCode(returnedStr, startIndex, endIndex);
Как видите, эта функция будет работать как вперед, так и назад. Или, по крайней мере, большая часть этого. Единственная проблема заключается в том, что, если бы я использовал эту функцию в обратном направлении, правильное сканирование комментариев (обозначается стандартной двойной косой чертой ECMA "//") становится бесполезным.
Если бы я должен был создать отдельную функцию для приложения с обратным контекстом и рекурсивно проверять каждую строку на наличие двойной косой черты, а затем сделать все после этого "//" КОММЕНТАРИЙ (или в направлении использования функции, все до этого //), это займет слишком много времени обработки, так как я хочу использовать это в качестве среды живого кодирования для музыки.
Кроме того, удаляя комментарии, прежде чем пытаться сделать это returnCodeInScopeOfCursor
метод не может быть осуществимым... так как мне нужно отслеживать индексы кода, а что нет. Если бы я удалил комментарии, был бы большой беспорядок со всеми позициями кода и отслеживанием того, где я удалил, что именно, сколько символов и т. Д..... GUI ввода текстовой области, с которым я работаю (RichTextFX) не поддерживает отслеживание Line-Char, поэтому все отслеживается с использованием только индекса индекса, поэтому проблемы...
Итак... Я совершенно озадачен тем, что делать с моим текущим кодом. Любая помощь, предложения, советы и т.д... будет принята с благодарностью.
2 ответа
Не могли бы вы предварительно преобразовать комментарии из // This is a comment<CR>
в { This is a comment}<CR>
тогда у вас есть язык, по которому вы можете ходить взад и вперед.
Примените это преобразование на входе и измените его на выходе, и все должно быть хорошо. Обратите внимание, мы заменяем //...
с {...}
поэтому все смещения символов сохраняются.
В любом случае, после небольшого эксперимента с идеей OldCurmudgeon, я придумал отдельную функцию для получения контекста кода в обратном направлении.
public static void applyContextBackwards(Context context, String entireCode, int caretPos) {
String s = String.valueOf(entireCode.charAt(caretPos));
//So far this is not used
//String snext = caretPos + 1 < entireCode.length() ? String.valueOf(entireCode.charAt(caretPos + 1)) : "";
String sprev = caretPos - 1 >= 0 ? String.valueOf(entireCode.charAt(caretPos - 1)) : "";
//Check for all the flags and what not...
if(context.commentedCharsLeft > 0) {
context.commentedCharsLeft--;
if(context.commentedCharsLeft == 0)
context.context = Context.Contexts.MAIN;//The comment is over
}
if(context.expectingEndOfString){
context.context = Context.Contexts.MAIN;
context.expectingEndOfString = false;
}
if(context.expectingEndOfPattern) {
context.context = Context.Contexts.MAIN;
context.expectingEndOfPattern = false;
}
//After all the flags are cleared, do this
if(context.commentedCharsLeft == 0) {
if (s.equals("\"")) {
if (context.context == Context.Contexts.MAIN) {
context.context = Context.Contexts.STRING;
context.stringDelimiterIsADoubleQuote = true;
} else if (context.context == Context.Contexts.STRING && context.stringDelimiterIsADoubleQuote && !sprev.equals("\\"))
context.expectingEndOfString = true;//Change the next char to a MAIN, cuz this one's still part of the string
} else if (s.equals("\'")) {
if (context.context == Context.Contexts.MAIN) {
context.context = Context.Contexts.STRING;
context.stringDelimiterIsADoubleQuote = false;
} else if (context.context == Context.Contexts.STRING && !context.stringDelimiterIsADoubleQuote && !sprev.equals("\""))
context.expectingEndOfString = true;//Change the next char to a MAIN, cuz this one's still part of the string
} else if (s.equals("\n")) {
int earliestOccuranceOfSingleLineCommentDelimiterAsDistanceFromThisNewLine = -1;//-1 for no comments
//Loop until the next \n is found. In the process, determine location of comment if any
for(int i = caretPos; i >= 0; i--) {
String curr = String.valueOf(entireCode.charAt(i));
String prev = i - 1 >= 0 ? String.valueOf(entireCode.charAt(i - 1)) : "";
if(curr.equals("\n"))
break;//Line has been scanned through
if(curr.equals("/") && prev.equals("/"))
earliestOccuranceOfSingleLineCommentDelimiterAsDistanceFromThisNewLine = caretPos - i;
}
//Set the comment context flag
//If no comments, -1 + 1 will be 0 and will be treated as no comments.
context.commentedCharsLeft = earliestOccuranceOfSingleLineCommentDelimiterAsDistanceFromThisNewLine + 1;
if(earliestOccuranceOfSingleLineCommentDelimiterAsDistanceFromThisNewLine > 0) {
context.context = Context.Contexts.COMMENT;
}
} else if (s.equals("\\")) {
if (context.context == Context.Contexts.MAIN)
context.context = Context.Contexts.PATTERN;
else if (context.context == Context.Contexts.PATTERN)
context.expectingEndOfPattern = true;//Change the next char to a MAIN cuz this one's still part of the Pattern
}
}
}