Mercurial Hosting > nabble
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/nabble/naml/dom/ParserImpl.java Thu Mar 21 19:15:52 2019 -0600 @@ -0,0 +1,208 @@ +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)); + } + +}