view 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 source

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