1 package org.simantics.scl.compiler.completions.parsing;
3 import java.util.ArrayList;
6 public class RobustModuleSplitter {
8 private static final int NORMAL_START_OF_LINE = 0;
9 private static final int NORMAL = 1;
10 private static final int NORMAL_1QUOTE = 2;
11 private static final int NORMAL_2QUOTE = 3;
12 private static final int SHORT_STRING = 4;
13 private static final int SHORT_STRING_BACKSLASH = 5;
14 private static final int LONG_STRING = 6;
15 private static final int LONG_STRING_1QUOTE = 7;
16 private static final int LONG_STRING_2QUOTE = 8;
17 private static final int CHAR_LITERAL = 9;
18 private static final int CHAR_LITERAL_BACKSLASH = 10;
19 private static final int NORMAL_1SLASH = 11;
20 private static final int C_COMMENT = 12;
21 private static final int C_COMMENT_STAR = 13;
22 private static final int CPP_COMMENT = 14;
24 private final String sourceText;
25 private ArrayList<ModuleSegment> segments = new ArrayList<ModuleSegment>();
27 private RobustModuleSplitter(String sourceText) {
28 this.sourceText = sourceText;
31 private static boolean isLineEnd(char c) {
32 return c == '\n' || c == 0;
35 private void split() {
37 int begin = 0, pos = 0, curEntityBegin = 0, parenthesesBalance = 0;
38 boolean hasErrors = false;
39 int length = sourceText.length();
41 char c = pos == length ? 0 : sourceText.charAt(pos++);
45 case NORMAL_START_OF_LINE:
46 if(c == '\n') // Don't care about empty lines
49 int end = c == 0 ? pos : pos-1;
50 segments.add(new ModuleSegment(begin, end, parenthesesBalance, hasErrors));
51 parenthesesBalance = 0;
58 state = NORMAL_1QUOTE;
60 state = NORMAL_1SLASH;
63 else if(c == '(' || c == '[' || c == '{')
65 else if(c == ')' || c == ']' || c == '}')
68 state = NORMAL_START_OF_LINE;
74 state = NORMAL_2QUOTE;
76 state = SHORT_STRING_BACKSLASH;
93 state = SHORT_STRING_BACKSLASH;
94 else if(c == '"' || isLineEnd(c) /* unclosed string */) {
96 state = NORMAL_START_OF_LINE;
102 case SHORT_STRING_BACKSLASH:
103 if(isLineEnd(c) /* unclosed string */)
104 state = NORMAL_START_OF_LINE;
106 state = SHORT_STRING;
110 state = LONG_STRING_1QUOTE;
112 // Unclosed long string
113 curEntityBegin = pos;
118 case LONG_STRING_1QUOTE:
120 state = LONG_STRING_2QUOTE;
124 case LONG_STRING_2QUOTE:
131 if(c == '\'' || isLineEnd(c) /* unclosed char literal */) {
133 state = NORMAL_START_OF_LINE;
136 hasErrors = c != '\'';
139 state = CHAR_LITERAL_BACKSLASH;
141 case CHAR_LITERAL_BACKSLASH:
142 if(isLineEnd(c) /* unclosed char literal */) {
143 state = NORMAL_START_OF_LINE;
147 state = CHAR_LITERAL;
154 curEntityBegin = pos;
164 state = C_COMMENT_STAR;
166 // Unclosed C comment
167 pos = curEntityBegin;
181 state = NORMAL_START_OF_LINE;
186 segments.add(new ModuleSegment(begin, length, parenthesesBalance, hasErrors));
189 private void combineByParenthesesBalance() {
190 ArrayList<ModuleSegment> segmentStack = null;
191 for(ModuleSegment segment : segments)
192 if(segment.parenthesesBalance > 0) {
193 if(segmentStack == null)
194 segmentStack = new ArrayList<ModuleSegment>();
195 for(int i=0;i<segment.parenthesesBalance;++i)
196 segmentStack.add(segment);
198 else if(segment.parenthesesBalance < 0) {
199 if(segmentStack == null) {
200 segment.parenthesesBalance = 0;
201 segment.hasErrors = true;
204 int r = -segment.parenthesesBalance;
205 while(r > 0 && !segmentStack.isEmpty()) {
206 segmentStack.remove(segmentStack.size()-1);
210 segment.parenthesesBalance += r;
211 segment.hasErrors = true;
215 if(segmentStack == null)
217 for(ModuleSegment segment : segmentStack) {
218 --segment.parenthesesBalance;
219 segment.hasErrors = true;
222 ArrayList<ModuleSegment> oldSegments = segments;
223 segments = new ArrayList<ModuleSegment>(oldSegments.size());
225 int currentBalance = 0;
227 boolean hasErrors = false;
228 for(ModuleSegment segment : oldSegments) {
229 if(currentBalance == 0) {
230 if(segment.parenthesesBalance == 0)
231 segments.add(segment);
233 begin = segment.begin;
234 currentBalance = segment.parenthesesBalance;
235 hasErrors = segment.hasErrors;
239 currentBalance += segment.parenthesesBalance;
240 hasErrors |= segment.hasErrors;
241 if(currentBalance == 0)
242 segments.add(new ModuleSegment(begin, segment.end, 0, hasErrors));
247 public static List<ModuleSegment> split(String sourceText) {
248 RobustModuleSplitter splitter = new RobustModuleSplitter(sourceText);
250 splitter.combineByParenthesesBalance();
251 return splitter.segments;