diff src/nabble/naml/compiler/Source.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/compiler/Source.java	Thu Mar 21 19:15:52 2019 -0600
@@ -0,0 +1,154 @@
+package nabble.naml.compiler;
+
+import fschmidt.util.java.Interner;
+import nabble.naml.dom.Container;
+import nabble.naml.dom.Element;
+import nabble.naml.dom.ElementName;
+import nabble.naml.dom.Naml;
+import nabble.naml.dom.ParseException;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Arrays;
+
+
+public final class Source implements Comparable<Source> {
+	public final String id;
+	public final String content;
+	private final Set<ElementName> ignoreTags;
+	private List<Macro> macros = null;
+	private Set<String> staticNames = null;
+
+	private Source(String id,String content,Set<ElementName> ignoreTags) {
+		this.id = id;
+		this.content = content;
+		this.ignoreTags = ignoreTags;
+	}
+
+	@Override public boolean equals(Object obj) {
+		if( !(obj instanceof Source) )
+			return false;
+		Source source = (Source)obj;
+		return source.id.equals(id) && source.content.equals(content);
+	}
+
+	@Override public int hashCode() {
+		return content.hashCode();
+	}
+
+	@Override public int compareTo(Source source) {
+		if( this == source )
+			return 0;
+		int rtn = id.compareTo(source.id);
+		if( rtn != 0 )
+			return rtn;
+		return content.compareTo(source.content);
+	}
+
+	@Override public String toString() {
+		return id;
+	}
+
+	public Collection<Macro> getMacros() throws CompileException {
+		compile();
+		return macros;
+	}
+
+	Collection<String> staticNames() throws CompileException {
+		compile();
+		return staticNames;
+	}
+
+	private static final Set<ElementName> macroNames = new LinkedHashSet<ElementName>(Arrays.asList(
+		new ElementName( Macro.Type.MACRO.tagName ),
+		new ElementName( Macro.OVERRIDE_PREFIX + Macro.Type.MACRO.tagName ),
+		new ElementName( Macro.Type.SUBROUTINE.tagName ),
+		new ElementName( Macro.OVERRIDE_PREFIX + Macro.Type.SUBROUTINE.tagName ),
+		new ElementName( Macro.Type.TRANSLATION.tagName ),
+		new ElementName( Macro.OVERRIDE_PREFIX + Macro.Type.TRANSLATION.tagName ),
+		new ElementName( Macro.Type.NAMESPACE.tagName )
+	) );
+	private static final ElementName staticName = new ElementName("static");
+
+	public Naml parse() throws CompileException {
+		StackTrace stackTrace = new StackTrace();
+		try {
+			return Naml.parser().parse(content);
+		} catch(ParseException e) {
+			stackTrace.push( new StackTraceElement(this,e.lineNumber) );
+			try {
+				throw new CompileException(stackTrace,"parse exception in "+id+" - "+e.getMessage(),e);
+			} finally {
+				stackTrace.pop();
+			}
+		}
+	}
+
+	private synchronized void compile() throws CompileException {
+		if( macros != null )
+			return;
+		List<Macro> macros = new ArrayList<Macro>();
+		staticNames = new HashSet<String>();
+		StackTrace stackTrace = new StackTrace();
+		Map<String,Integer> macroCount = new HashMap<String,Integer>();
+		for( Object obj : parse() ) {
+			if( obj instanceof Element ) {
+				Element element = (Element)obj;
+				stackTrace.push( new StackTraceElement(this,element) );
+				try {
+					ElementName tagName = element.name();
+					if( tagName.equals(staticName) ) {
+						if( !(element instanceof Container) )
+							throw new CompileException(stackTrace,"empty '"+tagName+"' not allowed");
+						Container container = (Container)element;
+						Naml staticNaml = container.contents();
+						if( staticNaml.isEmpty() )
+							throw new CompileException(stackTrace,"static tag may not be empty");
+						if( staticNaml.size() > 1 )
+							throw new CompileException(stackTrace,"static tag may only contain a list of names");
+						Object objS = staticNaml.get(0);
+						if( !(objS instanceof String) )
+							throw new CompileException(stackTrace,"static tag may only contain a list of names");
+						String names = (String)objS;
+						for( String name : names.split("\\s+") ) {
+							name = name.toLowerCase();
+							staticNames.add(name);
+						}
+						continue;
+					}
+					if( macroNames.contains(tagName) ) {
+						Macro macro = new Macro(this,element,stackTrace,macroCount);
+						macros.add(macro);
+					} else if( !ignoreTags.contains(tagName) ) {
+						Set<ElementName> allowed = new LinkedHashSet<ElementName>();
+						allowed.addAll(macroNames);
+						allowed.add(staticName);
+						allowed.addAll(ignoreTags);
+						throw new CompileException(stackTrace,"'"+tagName+"' not valid, only "+allowed+" tags allowed at root");
+					}
+				} finally {
+					stackTrace.pop();
+				}
+			}
+		}
+		this.macros = macros;
+	}
+
+
+	private static final Interner<Source> interner = new Interner<Source>();
+
+	public static Source getInstance(String id,String content,Set<ElementName> ignoreTags) {
+		return interner.intern(new Source(id,content,ignoreTags));
+	}
+
+	public static Source getInstance(String id,String content) {
+		return getInstance(id,content,Collections.<ElementName>emptySet());
+	}
+}