import { Modes } from '../modes'; import { Types } from "../../typeSystem/types"; import { StoreObject } from './storeObject'; import { IType } from '../../typeSystem/itype'; import { StoreObjectRef } from './storeObjectRef'; import { ArrayType } from '../../typeSystem/array_type'; import { ArrayStoreValue } from './array_store_value'; import { IStoreValue } from './istore_value'; import { StoreValue } from './store_value'; import { Location } from '../../memory/location'; import { StoreValueRef } from './store_value_ref'; export class Store { static canImplicitTypeCast (castType: IType, sourceType: IType) { if (castType.isCompatible(Types.INTEGER) || castType.isCompatible(Types.REAL)) { if (sourceType.isCompatible(Types.INTEGER) || sourceType.isCompatible(Types.REAL)) { return true; } } return false; } static doImplicitCasting (castType: IType, stoObj: IStoreValue) { if(!Store.canImplicitTypeCast(castType, stoObj.type)) { throw new Error("!!!Critical error: attempted to type cast invalid types"); } if(castType.isCompatible(Types.INTEGER)) { return new StoreValue(Types.INTEGER, stoObj.get().trunc()); } else { return new StoreValue(Types.REAL, stoObj.get()); } } private store: Map; public nextStore?: Store public mode: Symbol; constructor(public name: String) { this.store = new Map(); this.mode = Modes.RUN; } extendStore (nextStore: Store) { this.nextStore = nextStore; } applyStore (id: String): IStoreValue { if (!this.store.has(id)) { if (this.nextStore != null) { return this.nextStore.applyStore(id); } else { throw new Error(`Variable ${id} not found.`); } } const val = this.store.get(id)!; let result = null if (val.type instanceof ArrayType) { result = new ArrayStoreValue(val.type, val.value, 0, 0, val.id, val.readOnly); } else { result = new StoreValue(val.type, val.value, val.id, val.readOnly); } return result; } updateStore (id: String, stoValue: IStoreValue): Store { if (!this.store.has(id)) { if (this.nextStore != null) { this.nextStore.updateStore(id, stoValue); return this; } else { // TODO: better error message throw new Error(`Variable ${id} not found.`); } } else { const oldObj = this.store.get(id)!; if (oldObj.readOnly) { // TODO: better error message throw new Error("Cannot change value of a read only variable: " + id); } if (oldObj.type instanceof ArrayType) { // oldObj.updateRef(stoValue); return this; } else if (oldObj.isCompatible(stoValue)) { // TODO check for array.... // const loc_address = Location.allocate(stoValue.get()); // const newObj = new StoreObject(stoValue.type, loc_address, stoValue.isConst); // newObj.setID(id); // this.store.get(id)!.destroy(); // this.store.set(id, newObj); const loc_address = oldObj.locAddress; Location.updateAddress(loc_address, stoValue.get()); return this; } else { const oldType = oldObj.type; const stoType = stoValue.type; // TODO: better error message throw new Error(`${oldType} is not compatible with type ${stoType} given`); } } } //In case of future use of ref, it needs to have a special function to update the storeRefObject // and no the StoreObject refferenced by it // updateStoreRef(id, stoObjAddress) {...} insertStore (id: String, stoValue: IStoreValue) { if (this.store.has(id)) { // TODO: better error message throw new Error(`${id} is already defined`); } // TODO check for array.... let newObj:StoreObject; if(stoValue instanceof StoreValueRef) { // TODO check for array.... newObj = new StoreObjectRef(stoValue, stoValue.getRefAddress()); } else { const loc_address = Location.allocate(stoValue.get()); newObj = new StoreObject(stoValue.type, loc_address, stoValue.isConst); } newObj.setID(id); this.store.set(id, newObj); return this; } /** * Helper function similar to applyStore. But it returns the actual object in the store be it ref or not * applyStore will return the refferenced object if the object in the store is a ref */ getStoreObject (id: String): StoreObject { if (!this.store.has(id)) { if (this.nextStore != null) { return this.nextStore.getStoreObject(id); } else { throw new Error(`Variable ${id} not found.`); } } return this.store.get(id)!; } destroy (): void { this.store.forEach(sto => sto.destroy(), this); } }