All Downloads are FREE. Search and download functionalities are using the official Maven repository.

js.prompto.runtime.Context.js Maven / Gradle / Ivy

var EnumeratedCategoryDeclaration = require("../declaration/EnumeratedCategoryDeclaration").EnumeratedCategoryDeclaration;
var EnumeratedNativeDeclaration = require("../declaration/EnumeratedNativeDeclaration").EnumeratedNativeDeclaration;
var ConcreteCategoryDeclaration = require("../declaration/ConcreteCategoryDeclaration").ConcreteCategoryDeclaration;
var BaseMethodDeclaration = require("../declaration/BaseMethodDeclaration").BaseMethodDeclaration;
var TestMethodDeclaration = require("../declaration/TestMethodDeclaration").TestMethodDeclaration;
var CategoryDeclaration = require("../declaration/CategoryDeclaration").CategoryDeclaration;
var AttributeDeclaration = require("../declaration/AttributeDeclaration").AttributeDeclaration;
var ContextualExpression = require("../value/ContextualExpression").ContextualExpression;
var MethodExpression = require("../expression/MethodExpression").MethodExpression;
var ConcreteInstance = require("../value/ConcreteInstance").ConcreteInstance;
var ExpressionValue = require("../value/ExpressionValue").ExpressionValue;
var ProblemListener = require("../problem/ProblemListener").ProblemListener;
var DecimalType = require("../type/DecimalType").DecimalType;
var Decimal = require("../value/Decimal").Decimal;
var Integer = require("../value/Integer").Integer;
var Variable = require("./Variable").Variable;
var LinkedValue = require("./LinkedValue").LinkedValue;

function Context() {
	this.globals = null;
	this.calling = null;
	this.parent = null; // for inner methods
	this.debugger = null;
	this.declarations = {};
    this.tests = {};
	this.instances = {};
	this.values = {};
    this.nativeBindings = {};
    this.problemListener = new ProblemListener();
	return this;
}

Context.newGlobalContext = function() {
	var context = new Context();
	context.globals = context;
	context.calling = null;
	context.parent = null;
	context.debugger = null;
	return context;
};


Context.prototype.isGlobalContext = function() {
	return this===this.globals;
};


Context.prototype.getCallingContext = function() {
	return this.calling;
};

Context.prototype.getParentMostContext = function() {
	if(this.parent==null) {
		return this;
	} else {
		return this.parent.getParentMostContext();
	}
};

Context.prototype.getParentContext = function() {
	return this.parent;
}

Context.prototype.setParentContext = function(parent) {
	this.parent = parent;
}

Context.prototype.newResourceContext = function() {
	var context = new ResourceContext();
	context.globals = this.globals;
	context.calling = this.calling;
	context.parent = this;
	context.debugger = this.debugger;
    context.problemListener = this.problemListener;
    return context;
};

Context.prototype.newLocalContext = function() {
	var context = new Context();
	context.globals = this.globals;
	context.calling = this;
	context.parent = null;
	context.debugger = this.debugger;
    context.problemListener = this.problemListener;
	return context;
};

Context.prototype.newDocumentContext = function(doc, isChild) {
	var context = new DocumentContext(doc);
	context.globals = this.globals;
	context.calling = isChild ? this.calling : this;
	context.parent = isChild ? this : null;
	context.debugger = this.debugger;
    context.problemListener = this.problemListener;
	return context;
};


Context.prototype.newBuiltInContext = function(value) {
    var context = new BuiltInContext(value);
    context.globals = this.globals;
    context.calling = this;
    context.parent = null;
    context.debugger = this.debugger;
    context.problemListener = this.problemListener;
    return context;
};


Context.prototype.newInstanceContext = function(instance, type) {
    var context = new InstanceContext(instance, type);
    context.globals = this.globals;
    context.calling = this;
    context.parent = null;
    context.debugger = this.debugger;
    context.problemListener = this.problemListener;
    return context;
};

Context.prototype.newChildContext = function() {
	var context = new Context();
	context.globals = this.globals;
	context.calling = this.calling;
	context.parent = this;
	context.debugger = this.debugger;
    context.problemListener = this.problemListener;
	return context;
};


Context.prototype.clone = function() {
    var context = new Context();
    context.globals = context;
    context.calling = null;
    context.parent = null;
    context.debugger = null;
    // copy from
    context.declarations = Object.create(this.declarations);
    context.tests = Object.create(this.tests);
    context.instances = Object.create(this.instances);
    context.values = Object.create(this.values);
    context.nativeBindings = Object.create(this.nativeBindings);
    return context;
};

Context.prototype.getCatalog = function() {
    if (this != this.globals)
        return this.globals.getCatalog();
    else
        return this.getLocalCatalog();
};

Context.prototype.getLocalCatalog = function() {
    var catalog = { attributes : [], methods : [], categories : [], enumerations : [], tests : []};
    for(var name in this.declarations) {
        var decl = this.declarations[name];
        if(decl instanceof AttributeDeclaration)
            catalog.attributes.push(name);
        else if(decl instanceof EnumeratedCategoryDeclaration)
            catalog.enumerations.push(name);
        else if(decl instanceof EnumeratedNativeDeclaration)
            catalog.enumerations.push(name);
        else if(decl instanceof CategoryDeclaration)
            catalog.categories.push(name);
        else if(decl instanceof MethodDeclarationMap) {
            var method = {};
            method.name = decl.name;
            method.protos = [];
            for (var proto in decl.protos) {
                var main = decl.protos[proto].isEligibleAsMain();
                method.protos.push({proto: proto, main: main});
            }
            catalog.methods.push(method);
        }
    }
    for(var name in this.tests)
        catalog.tests.push(name);
    // minimize for UI optimization
    if(!catalog.attributes.length)
        delete catalog.attributes;
    if(!catalog.methods.length)
        delete catalog.methods;
    if(!catalog.categories.length)
        delete catalog.categories;
    if(!catalog.tests.length)
        delete catalog.tests;
    return catalog;
};

Context.prototype.findAttribute = function(name) {
    if(this==this.globals)
        return this.declarations[name] || null;
    else
        return this.globals.findAttribute(name);
};

Context.prototype.getAllAttributes = function() {
    if(this==this.globals) {
        var list = [];
        for(var name in this.declarations) {
            if(this.declarations[name] instanceof AttributeDeclaration)
                list.push(this.declarations[name]);
        }
        return list;
    } else
        return this.globals.getAllAttributes();
};

Context.prototype.getRegistered = function(name) {
	// resolve upwards, since local names override global ones
	var actual = this.declarations[name] || null;
	if(actual!==null) {
		return actual;
	}
	actual = this.instances[name] || null;
	if(actual!==null) {
		return actual;
	} else if(this.parent!==null) {
		return this.parent.getRegistered(name);
	} else if(this.globals!==this) {
		return this.globals.getRegistered(name);
	} else {
		return null;
	}
};

Context.prototype.getRegisteredDeclaration = function(name) {
	// resolve upwards, since local names override global ones
	var actual = this.declarations[name] || null;
	if(actual!==null) {
		return actual;
	} else if(this.parent!==null) {
		return this.parent.getRegisteredDeclaration(name);
	} else if(this.globals!==this) {
		return this.globals.getRegisteredDeclaration(name);
	} else {
		return null;
	}
}

Context.prototype.registerDeclaration = function(declaration) {
    if(this.checkDuplicate(declaration))
    	this.declarations[declaration.name] = declaration;
};

Context.prototype.checkDuplicate = function(declaration) {
    var actual = this.getRegistered(declaration.name) || null;
    if (actual != null)
        this.problemListener.reportDuplicate(declaration.name, declaration);
    return actual == null;
}


Context.prototype.unregisterTestDeclaration = function(declaration) {
    delete this.tests[declaration.name];
};

Context.prototype.unregisterMethodDeclaration = function(declaration, proto) {
    var map = this.declarations[declaration.name];
    if(map && map.unregister(proto))
        delete this.declarations[declaration.name];
};

Context.prototype.unregisterDeclaration = function(declaration) {
    delete this.declarations[declaration.name];
};

Context.prototype.registerMethodDeclaration = function(declaration) {
    var actual = this.checkDuplicateMethod(declaration);
    if (actual === null) {
        actual = new MethodDeclarationMap(declaration.name);
        this.declarations[declaration.name] = actual;
    }
    actual.register(declaration, this);
};

Context.prototype.checkDuplicateMethod = function(declaration) {
    var actual = this.getRegistered(declaration.name) || null;
    if (actual !== null && !(actual instanceof MethodDeclarationMap))
        this.problemListener.reportDuplicate(declaration.name, declaration);
    return actual;
};

Context.prototype.registerTestDeclaration = function(declaration) {
    var actual = this.tests[declaration.name] || null;
    if(actual!==null)
        this.problemListener.reportDuplicate(declaration.name, declaration);
    this.tests[declaration.name] = declaration;
};

Context.prototype.getRegisteredTest = function(name) {
    // resolve upwards, since local names override global ones
    var actual = this.tests[name] || null;
    if(actual!==null) {
        return actual;
    } else if(this.parent!==null) {
        return this.parent.getRegisteredTest(name);
    } else if(this.globals!==this) {
        return this.globals.getRegisteredTest(name);
    } else {
        return null;
    }
};

Context.prototype.hasTests = function() {
    for(var test in this.tests)
        return true;
    return false;
};

Context.prototype.registerNativeBinding = function(type, declaration) {
    if(this==this.globals)
        this.nativeBindings[type] = declaration;
    else
        this.globals.registerNativeBinding(type, declaration);
};

Context.prototype.getNativeBinding = function(type) {
    if(this==this.globals)
        return this.nativeBindings[type] || null;
    else
        return this.globals.getNativeBinding(type);
};

function MethodDeclarationMap(name) {
	this.name = name;
	this.protos = {};
	return this;
}

MethodDeclarationMap.prototype.register = function(declaration, context) {
	var proto = declaration.getProto();
	var current = this.protos[proto] || null;
	if(current!==null)
        context.problemListener.reportDuplicate(declaration.name, declaration);
	this.protos[proto] = declaration;
};

MethodDeclarationMap.prototype.unregister = function(proto) {
    delete this.protos[proto];
    return Object.getOwnPropertyNames(this.protos).length == 0;
};

MethodDeclarationMap.prototype.registerIfMissing = function(declaration,context) {
	var proto = declaration.getProto();
	if(!(proto in this.protos)) {
		this.protos[proto] = declaration;
	}
};

MethodDeclarationMap.prototype.getFirst = function() {
    for(var proto in this.protos) {
        return this.protos[proto];
    }
};


Context.prototype.getRegisteredValue = function(name) {
    var context = this.contextForValue(name);
    if (context == null)
        return null;
    else
        return context.readRegisteredValue(name);
};



Context.prototype.readRegisteredValue = function(name) {
	return this.instances[name] || null;
};


Context.prototype.registerValue = function(value, checkDuplicate) {
    if(checkDuplicate === undefined)
        checkDuplicate = true;
	if(checkDuplicate) {
        // only explore current context
        var actual = this.instances[value.name] || null;
        if(actual!==null)
            this.problemListener.reportDuplicateVariable(value.id);
    }
	this.instances[value.name] = value;
};


Context.prototype.unregisterValue = function(value) {
    delete this.instances[value.name];
};



Context.prototype.hasValue = function(id) {
    return this.contextForValue(id.name)!=null;
};



Context.prototype.getValue = function(id) {
	var context = this.contextForValue(id.name);
	if(context===null)
        this.problemListener.reportUnknownVariable(id);
	return context.readValue(id);
};



Context.prototype.readValue = function(id) {
	var value = this.values[id.name] || null;
	if(value===null)
        this.problemListener.reportEmptyVariable(id);
    if(value instanceof LinkedValue)
        return value.context.getValue(id);
    else
        return value;
};



Context.prototype.setValue = function(id, value) {
	var context = this.contextForValue(id.name);
	if(context===null)
        this.problemListener.reportUnknownVariable(id);
	context.writeValue(id, value);
};


Context.prototype.writeValue = function(id, value) {
    value = this.autocast(id.name, value);
    var current = this.values[id.name];
    if(current instanceof LinkedValue)
        current.context.setValue(id, value);
    else
    	this.values[id.name] = value;
};

Context.prototype.autocast = function(name, value) {
    if(value!=null && value instanceof Integer) {
        var actual = this.instances[name];
        if(actual.getType(this)==DecimalType.instance)
            value = new Decimal(value.DecimalValue());
    }
    return value;
};

Context.prototype.contextForValue = function(name) {
	// resolve upwards, since local names override global ones
	var actual = this.instances[name] || null;
	if(actual!==null) {
		return this;
	} else if(this.parent!==null) {
		return this.parent.contextForValue(name);
	} else if(this.globals!==this) {
		return this.globals.contextForValue(name);
	} else {
		return null;
	}
};

Context.prototype.contextForDeclaration = function(name) {
    // resolve upwards, since local names override global ones
    var actual = this.declarations[name] || null;
    if(actual!==null) {
        return this;
    } else if(this.parent!==null) {
        return this.parent.contextForDeclaration(name);
    } else if(this.globals!==this) {
        return this.globals.contextForDeclaration(name);
    } else {
        return null;
    }
};

function ResourceContext() {
	Context.call(this);
	return this;
}

ResourceContext.prototype = Object.create(Context.prototype);
ResourceContext.prototype.constructor = ResourceContext;

function InstanceContext(instance, type) {
	Context.call(this);
	this.instance = instance || null;
    this.instanceType = type!=null ? type : instance.type;
	return this;
}

InstanceContext.prototype = Object.create(Context.prototype);
InstanceContext.prototype.constructor = InstanceContext;


InstanceContext.prototype.readRegisteredValue = function(name) {
    var actual = this.instances[name];
    // not very pure, but avoids a lot of complexity when registering a value
    if(actual==null) {
        var attr = this.getRegisteredDeclaration(name);
        var type = attr.getType();
        actual = new Variable(name, type);
        this.instances[name] = actual;
    }
    return actual;
};


InstanceContext.prototype.contextForValue = function(name) {
	// params and variables have precedence over members
	// so first look in context values
	var context = Context.prototype.contextForValue.call(this, name);
	if(context!=null) {
		return context;
	} else if(this.getDeclaration().hasAttribute(this, name)) {
		return this;
	} else {
		return null;
	}
};

InstanceContext.prototype.getDeclaration = function() {
    if(this.instance!=null)
        return this.instance.declaration;
    else
        return this.getRegisteredDeclaration(this.instanceType.name);
};

InstanceContext.prototype.readValue = function(id) {
	return this.instance.getMemberValue(this.calling, id.name);
};

InstanceContext.prototype.writeValue = function(id, value) {
	this.instance.setMember(this.calling, id.name, value);
};


function BuiltInContext(value) {
    Context.call(this);
    this.value = value;
    return this;
}


BuiltInContext.prototype = Object.create(Context.prototype);
BuiltInContext.prototype.constructor = BuiltInContext;



function DocumentContext(document) {
    Context.call(this);
    this.document = document;
    return this;
}

DocumentContext.prototype = Object.create(Context.prototype);
DocumentContext.prototype.constructor = DocumentContext;


DocumentContext.prototype.contextForValue = function(name) {
    // params and variables have precedence over members
    // so first look in context values
    var context = Context.prototype.contextForValue.call(this, name);
    if (context != null)
        return context;
    // since any name is valid in the context of a document
    // simply return this document context
    else
        return this;
};

DocumentContext.prototype.readValue = function(name) {
    return this.document.getMemberValue(this.calling, name);
};


DocumentContext.prototype.writeValue = function(name) {
    this.document.setMember(this.calling, name, value);
};


Context.prototype.enterMethod = function(method) {
	if(this.debugger!=null) {
		this.debugger.enterMethod(this, method);
	}
};

Context.prototype.leaveMethod = function(method) {
	if(this.debugger!=null) {
		this.debugger.leaveMethod(this, method);
	}
};

Context.prototype.enterStatement = function(statement) {
	if(this.debugger!=null) {
		this.debugger.enterStatement(this, statement);
	}
};

Context.prototype.leaveStatement = function(statement) {
	if(this.debugger!=null) {
		this.debugger.leaveStatement(this, statement);
	}
};

Context.prototype.terminated = function() {
	if (this.debugger != null) {
		this.debugger.terminated();
	}
};

Context.prototype.loadSingleton = function(type) {
    if(this==this.globals) {
        var value = this.values[type.name] || null;
        if(value==null) {
            var decl = this.declarations[type.name] || null;
            if(!(decl instanceof ConcreteCategoryDeclaration))
                throw new InternalError("No such singleton:" + type.name);
            value = new ConcreteInstance(this, decl);
            value.mutable = true; // a singleton is protected by "with x do", so always mutable in that context
            this.values[type.name] = value;
        }
        if(value instanceof ConcreteInstance)
            return value;
        else
            throw new InternalError("Not a concrete instance:" + value);
    } else
        return this.globals.loadSingleton(type);
};


exports.Context = Context;
exports.BuiltInContext = BuiltInContext;
exports.InstanceContext = InstanceContext;
exports.ResourceContext = ResourceContext;
exports.MethodDeclarationMap = MethodDeclarationMap;




© 2015 - 2025 Weber Informatics LLC | Privacy Policy