Mercurial Hosting > nabble
view src/nabble/naml/dom/ParserImpl.java @ 62:4674ed7d56df default tip
remove n2
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Sat, 30 Sep 2023 20:25:29 -0600 |
parents | 7ecd1a4ef557 |
children |
line wrap: on
line source
package nabble.naml.dom; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; import java.util.ArrayList; final class ParserImpl implements Parser { private static final Logger logger = LoggerFactory.getLogger(ParserImpl.class); private int startingLine = 0; private int i; private int iLine; private int line; private int len; private String text; private ParserImpl subParser = null; public Naml parse(String source) throws ParseException { i = 0; iLine = 0; line = startingLine; len = source.length(); text = source; return doParse(null).naml; } private static class Return { final Naml naml; final String spaceAtEndOfClosingTag; Return(Naml naml,String spaceAtEndOfClosingTag) { this.naml = naml; this.spaceAtEndOfClosingTag = spaceAtEndOfClosingTag; } } private Return doParse(String tagName) throws ParseException { int start = i; Naml naml = new Naml(); int i2; while( i < len ) { i2 = text.indexOf('<',i); if( i2 == -1 ) { if( tagName != null ) throw parseException("unclosed tag: "+tagName,start); naml.add( text.substring(i).intern() ); return new Return(naml,null); } if( i < i2 ) naml.add( text.substring(i,i2).intern() ); if( text.startsWith("<!--",i2) ) { i = text.indexOf("-->",i2); if( i < i2+4 ) throw parseException("unclosed comment",i2); naml.add( new Comment( text.substring(i2+4,i) ) ); i += 3; } else if( text.startsWith("<![CDATA[",i2) ) { i = text.indexOf("]]>",i2); if( i == -1 ) throw parseException("unclosed CDATA",i2); naml.add( new Cdata( text.substring(i2+9,i) ) ); i += 3; } else { i = text.indexOf('>',i2); if( i == -1 ) throw parseException("unclosed tag",i2); setLine(i2); if( text.charAt(i2+1) == '/' ) { int nameStart = i2 + 2; int nameEnd = nameEnd(nameStart); int spaceEnd = skipSpace(nameEnd); if( spaceEnd != i ) throw parseException("illegal char",spaceEnd); if( !text.substring(nameStart,nameEnd).equals(tagName) ) { if( tagName == null ) throw parseException("unmatched closing tag: "+text.substring(i2,i+1),i2); else throw parseException("closing tag "+text.substring(i2,i+1)+" doesn't match expected </"+tagName+">",i2); } i++; return new Return(naml,text.substring(nameEnd,spaceEnd)); } int startLine = line; if( text.charAt(i-1) == '/' ) { TagInfo tagInfo = new TagInfo(i2+1,i-1); if( tagInfo.name.endsWithDot() ) throw parseException("empty tag can't end with dot: "+tagInfo.tagName,i); naml.add( new EmptyElement(tagInfo.name,tagInfo.attributes,tagInfo.spaceAtEnd,startLine) ); i++; } else { TagInfo tagInfo = new TagInfo(i2+1,i); i++; String s = tagInfo.tagName.toLowerCase(); Return r = doParse(tagInfo.tagName); naml.add( new Container(tagInfo.name,tagInfo.attributes,tagInfo.spaceAtEnd,startLine,r.naml,r.spaceAtEndOfClosingTag) ); } } } if( tagName != null ) throw parseException("unclosed tag: "+tagName,start); return new Return(naml,null); } private class TagInfo { final String tagName; final ElementName name; final List<Attribute> attributes = new ArrayList<Attribute>(); final String spaceAtEnd; TagInfo(int start,int end) throws ParseException { int i = nameEnd(start); tagName = text.substring(start,i); name = new ElementName(tagName); int afterSpace; while( (afterSpace = skipSpace(i)) < end ) { int afterName = nameEnd(afterSpace); int afterSpaceAfterName = skipSpace(afterName); if( text.charAt(afterSpaceAfterName) != '=' ) throw parseException("expected '=' not found for attribute '"+text.substring(afterSpace,afterName)+"'",afterSpaceAfterName); int beforeSpaceBeforeValue = afterSpaceAfterName + 1; int afterSpaceBeforeValue = skipSpace(beforeSpaceBeforeValue); char quote = text.charAt(afterSpaceBeforeValue); if( quote != '"' && quote != '\'' ) throw parseException("missing quote",afterSpaceBeforeValue); int startValue = afterSpaceBeforeValue + 1; int endValue = startValue; while(true) { if( endValue == end ) throw parseException("missing closing quote",endValue); char c = text.charAt(endValue); if( c == quote ) break; if( c == '<' || c == '>' ) throw parseException("invalid quoted char '"+c+"'",endValue); endValue++; } String spaceBeforeName = text.substring(i,afterSpace); String name = text.substring(afterSpace,afterName); String spaceAfterName = text.substring(afterName,afterSpaceAfterName); String spaceBeforeValue = text.substring(beforeSpaceBeforeValue,afterSpaceBeforeValue); String valueStr = text.substring(startValue,endValue); Naml value; if( quote == '"' ) { setLine(startValue); ParserImpl parser = parser(); String source = valueStr.replace('[','<').replace(']','>'); value = parser.parse(source); } else { value = new Naml(); value.add(valueStr); } attributes.add( new Attribute(spaceBeforeName,name,spaceAfterName,spaceBeforeValue,value,quote) ); i = endValue + 1; } spaceAtEnd = text.substring(i,end); } } private ParserImpl parser() { if( subParser == null ) subParser = new ParserImpl(); subParser.startingLine = line; return subParser; } private int skipSpace(int i) { while( i<len && Character.isWhitespace(text.charAt(i)) ) i++; return i; } private int nameEnd(int i) throws ParseException { if( i >= len ) throw parseException("name not found",i); char c = text.charAt(i); if( !( Character.isLetter(c) || c=='_' ) ) throw parseException("invalid char: '"+c+"'",i); while( ++i < len ) { c = text.charAt(i); if( !( Character.isLetterOrDigit(c) || c=='_' || c=='.' || c=='-' || c==':' ) ) break; } return i; } private void setLine(int i) { line += lines(iLine,i); iLine = i; } private int lines(int start,int end) { int n = 0; int i = start - 1; while(true) { i = text.indexOf('\n',i+1); if( i == -1 || i >= end ) return n; n++; } } private ParseException parseException(String msg,int i) { return new ParseException(msg,startingLine+lines(0,i)); } }