comparison src/nabble/naml/dom/ParserImpl.java @ 0:7ecd1a4ef557

add content
author Franklin Schmidt <fschmidt@gmail.com>
date Thu, 21 Mar 2019 19:15:52 -0600
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:7ecd1a4ef557
1 package nabble.naml.dom;
2
3 import org.slf4j.Logger;
4 import org.slf4j.LoggerFactory;
5 import java.util.List;
6 import java.util.ArrayList;
7
8
9 final class ParserImpl implements Parser {
10 private static final Logger logger = LoggerFactory.getLogger(ParserImpl.class);
11
12 private int startingLine = 0;
13 private int i;
14 private int iLine;
15 private int line;
16 private int len;
17 private String text;
18 private ParserImpl subParser = null;
19
20 public Naml parse(String source) throws ParseException {
21 i = 0;
22 iLine = 0;
23 line = startingLine;
24 len = source.length();
25 text = source;
26 return doParse(null).naml;
27 }
28
29 private static class Return {
30 final Naml naml;
31 final String spaceAtEndOfClosingTag;
32
33 Return(Naml naml,String spaceAtEndOfClosingTag) {
34 this.naml = naml;
35 this.spaceAtEndOfClosingTag = spaceAtEndOfClosingTag;
36 }
37 }
38
39 private Return doParse(String tagName) throws ParseException {
40 int start = i;
41 Naml naml = new Naml();
42 int i2;
43 while( i < len ) {
44 i2 = text.indexOf('<',i);
45 if( i2 == -1 ) {
46 if( tagName != null )
47 throw parseException("unclosed tag: "+tagName,start);
48 naml.add( text.substring(i).intern() );
49 return new Return(naml,null);
50 }
51 if( i < i2 )
52 naml.add( text.substring(i,i2).intern() );
53 if( text.startsWith("<!--",i2) ) {
54 i = text.indexOf("-->",i2);
55 if( i < i2+4 )
56 throw parseException("unclosed comment",i2);
57 naml.add( new Comment( text.substring(i2+4,i) ) );
58 i += 3;
59 } else if( text.startsWith("<![CDATA[",i2) ) {
60 i = text.indexOf("]]>",i2);
61 if( i == -1 )
62 throw parseException("unclosed CDATA",i2);
63 naml.add( new Cdata( text.substring(i2+9,i) ) );
64 i += 3;
65 } else {
66 i = text.indexOf('>',i2);
67 if( i == -1 )
68 throw parseException("unclosed tag",i2);
69 setLine(i2);
70 if( text.charAt(i2+1) == '/' ) {
71 int nameStart = i2 + 2;
72 int nameEnd = nameEnd(nameStart);
73 int spaceEnd = skipSpace(nameEnd);
74 if( spaceEnd != i )
75 throw parseException("illegal char",spaceEnd);
76 if( !text.substring(nameStart,nameEnd).equals(tagName) ) {
77 if( tagName == null )
78 throw parseException("unmatched closing tag: "+text.substring(i2,i+1),i2);
79 else
80 throw parseException("closing tag "+text.substring(i2,i+1)+" doesn't match expected </"+tagName+">",i2);
81 }
82 i++;
83 return new Return(naml,text.substring(nameEnd,spaceEnd));
84 }
85 int startLine = line;
86 if( text.charAt(i-1) == '/' ) {
87 TagInfo tagInfo = new TagInfo(i2+1,i-1);
88 if( tagInfo.name.endsWithDot() )
89 throw parseException("empty tag can't end with dot: "+tagInfo.tagName,i);
90 naml.add( new EmptyElement(tagInfo.name,tagInfo.attributes,tagInfo.spaceAtEnd,startLine) );
91 i++;
92 } else {
93 TagInfo tagInfo = new TagInfo(i2+1,i);
94 i++;
95 String s = tagInfo.tagName.toLowerCase();
96 Return r = doParse(tagInfo.tagName);
97 naml.add( new Container(tagInfo.name,tagInfo.attributes,tagInfo.spaceAtEnd,startLine,r.naml,r.spaceAtEndOfClosingTag) );
98 }
99 }
100 }
101 if( tagName != null )
102 throw parseException("unclosed tag: "+tagName,start);
103 return new Return(naml,null);
104 }
105
106 private class TagInfo {
107 final String tagName;
108 final ElementName name;
109 final List<Attribute> attributes = new ArrayList<Attribute>();
110 final String spaceAtEnd;
111
112 TagInfo(int start,int end) throws ParseException {
113 int i = nameEnd(start);
114 tagName = text.substring(start,i);
115 name = new ElementName(tagName);
116 int afterSpace;
117 while( (afterSpace = skipSpace(i)) < end ) {
118 int afterName = nameEnd(afterSpace);
119 int afterSpaceAfterName = skipSpace(afterName);
120 if( text.charAt(afterSpaceAfterName) != '=' )
121 throw parseException("expected '=' not found for attribute '"+text.substring(afterSpace,afterName)+"'",afterSpaceAfterName);
122 int beforeSpaceBeforeValue = afterSpaceAfterName + 1;
123 int afterSpaceBeforeValue = skipSpace(beforeSpaceBeforeValue);
124 char quote = text.charAt(afterSpaceBeforeValue);
125 if( quote != '"' && quote != '\'' )
126 throw parseException("missing quote",afterSpaceBeforeValue);
127 int startValue = afterSpaceBeforeValue + 1;
128 int endValue = startValue;
129 while(true) {
130 if( endValue == end )
131 throw parseException("missing closing quote",endValue);
132 char c = text.charAt(endValue);
133 if( c == quote )
134 break;
135 if( c == '<' || c == '>' )
136 throw parseException("invalid quoted char '"+c+"'",endValue);
137 endValue++;
138 }
139 String spaceBeforeName = text.substring(i,afterSpace);
140 String name = text.substring(afterSpace,afterName);
141 String spaceAfterName = text.substring(afterName,afterSpaceAfterName);
142 String spaceBeforeValue = text.substring(beforeSpaceBeforeValue,afterSpaceBeforeValue);
143 String valueStr = text.substring(startValue,endValue);
144 Naml value;
145 if( quote == '"' ) {
146 setLine(startValue);
147 ParserImpl parser = parser();
148 String source = valueStr.replace('[','<').replace(']','>');
149 value = parser.parse(source);
150 } else {
151 value = new Naml();
152 value.add(valueStr);
153 }
154 attributes.add( new Attribute(spaceBeforeName,name,spaceAfterName,spaceBeforeValue,value,quote) );
155 i = endValue + 1;
156 }
157 spaceAtEnd = text.substring(i,end);
158 }
159 }
160
161 private ParserImpl parser() {
162 if( subParser == null )
163 subParser = new ParserImpl();
164 subParser.startingLine = line;
165 return subParser;
166 }
167
168 private int skipSpace(int i) {
169 while( i<len && Character.isWhitespace(text.charAt(i)) )
170 i++;
171 return i;
172 }
173
174 private int nameEnd(int i) throws ParseException {
175 if( i >= len )
176 throw parseException("name not found",i);
177 char c = text.charAt(i);
178 if( !( Character.isLetter(c) || c=='_' ) )
179 throw parseException("invalid char: '"+c+"'",i);
180 while( ++i < len ) {
181 c = text.charAt(i);
182 if( !( Character.isLetterOrDigit(c) || c=='_' || c=='.' || c=='-' || c==':' ) )
183 break;
184 }
185 return i;
186 }
187
188 private void setLine(int i) {
189 line += lines(iLine,i);
190 iLine = i;
191 }
192
193 private int lines(int start,int end) {
194 int n = 0;
195 int i = start - 1;
196 while(true) {
197 i = text.indexOf('\n',i+1);
198 if( i == -1 || i >= end )
199 return n;
200 n++;
201 }
202 }
203
204 private ParseException parseException(String msg,int i) {
205 return new ParseException(msg,startingLine+lines(0,i));
206 }
207
208 }