package cn.liutils.ripple;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.HashMap;
import java.util.List;
import net.minecraft.util.ResourceLocation;
import cn.liutils.core.LIUtils;
import cn.liutils.ripple.RippleException.RippleRuntimeException;
import cn.liutils.ripple.impl.compiler.Parser;
import cn.liutils.ripple.impl.compiler.Parser.ScriptObject;
import cn.liutils.util.generic.RegistryUtils;
/**
* The global object of a ripple program.
* It contains all functions and values, and handles script file parsing.
* @author acaly, WeAthFold
*
*/
public final class ScriptProgram {
public final ScriptNamespace root = new ScriptNamespace(this, new Path(null));
private final HashMap<String, Object> objectMap = new HashMap();
public ScriptProgram() {
Library.openLibrary(this);
}
public void loadScript(Reader input) {
loadScript(input, "<stream>");
}
/**
* Loads a script from the given reader. The fileName is just used for debug output.
*/
public void loadScript(Reader input, String fileName) {
List<ScriptObject> objects = Parser.parse(this, input, fileName);
for (ScriptObject object : objects) {
if (object.value == null) {
this.mergeFunctionAt(new Path(object.path), object.func, object.funcArgNum);
} else {
this.setValueAt(new Path(object.path), object.value);
}
}
}
/**
* Load the script from a ResourceLocation.
*/
public void loadScript(ResourceLocation location) {
loadScript(new InputStreamReader(RegistryUtils.getResourceStream(location)), location.toString());
}
public void setNativeFunction(Path path, NativeFunction func) {
this.mergeFunctionAt(path, func, func.getParamterCount());
func.bind(this, path);
}
public ScriptNamespace at(Path path) {
return new ScriptNamespace(this, path);
}
public ScriptNamespace at(String path) {
return new ScriptNamespace(this, new Path(path));
}
//may return an FunctionWrapper, Integer, Double, Boolean or null.
Object getObjectAt(Path path) {
synchronized (this) {
return objectMap.get(path.path);
}
}
void setValueAt(Path path, Object value) {
if (!Calculation.checkType(value)) {
throw new RippleRuntimeException("Invalid value type");
}
synchronized (this) {
if (objectMap.containsKey(path.path)) {
throw new RippleRuntimeException("Try to modify an existing object");
}
objectMap.put(path.path, value);
}
}
void mergeFunctionAt(Path path, IFunction value, int nargs) {
synchronized (this) {
Object objInMap = objectMap.get(path.path);
if (objInMap == null) {
ScriptFunction sf = new ScriptFunction(this, path);
sf.merge(value, nargs);
objectMap.put(path.path, sf);
} else if (objInMap instanceof ScriptFunction) {
((ScriptFunction) objInMap).merge(value, nargs);
} else {
throw new RippleRuntimeException("Try to override a value with a function");
}
}
}
}