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));
+	}
+
+}