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 './value/array_store_value'; import { Location } from '../../memory/location'; import { StoreObjectArray } from './storeObjectArray'; import { IStoreValue } from './value/istore_value'; import { StoreValue } from './value/store_value'; import { StoreValueAddress } from './value/store_value_address'; import { StoreValueRef } from './value/store_value_ref'; import { ArrayStoreValueRef } from './value/array_store_value_ref'; import { StoreObjectArrayRef } from './store_object_array_ref'; export class Store { static canImplicitTypeCast (castType: IType, sourceType: IType): boolean { 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): 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) { const array = val as StoreObjectArray; const array_type = array.type as ArrayType; let l = 0, c = 0; const values = array.value.map( v => { if(array.isVector) { return new StoreValueAddress(array_type.innerType, v, l++, undefined, array.id, array.readOnly); } else { if(c >= array.columns) { c = 0; l += 1; } return new StoreValueAddress(array_type.innerType, v, l, c++, array.id, array.readOnly); } }); result = new ArrayStoreValue(array_type, values, array.lines, array.columns, 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 instanceof StoreObjectArray) { const array_value = stoValue as ArrayStoreValue; if(oldObj.isCompatible(array_value)) { if(oldObj.isVector) { array_value.get().forEach((val, index) => { oldObj.setAt(val, index, undefined); }); } else { let line = 0; let column = 0; array_value.get().forEach((val) => { oldObj.setAt(val, line, column); column += 1; if(column >= oldObj.columns) { line += 1; column = 0; } }); } return this; } } else if (oldObj.isCompatible(stoValue)) { const loc_address = oldObj.locAddress; Location.updateAddress(loc_address, stoValue.get()); return this; } const oldType = oldObj.type; const stoType = stoValue.type; // TODO: better error message throw new Error(`${oldType.value} is not compatible with type ${stoType.value} given`); } } /** * Method used to update regions of an array (vector or matrix). The should only be used when update an specific * possition since it will only update the required addresses. * @param {string} id the variable id to be updated * @param {IStoreValue} sto_value the value to be used in the update process * @param {number} line the line address of the vector/matrix * @param {number} column the matrix column, which can be undefined */ updateStoreArray (id:string, sto_value: IStoreValue, line:number, column?:number): Store { if (!this.store.has(id)) { if (this.nextStore != null) { this.nextStore.updateStoreArray(id, sto_value, line, column); 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 instanceof StoreObjectArray) { if(sto_value instanceof ArrayStoreValue) { // this must be a vector or matrix line update const actual_values = sto_value.get(); if(oldObj.isVector && sto_value.isVector()) { for(let i = 0;i < sto_value.lines; i += 1) { const val = actual_values[i] oldObj.setAt(val, i, undefined); } } else if(!oldObj.isVector && column == null && sto_value.isVector()) { for(let i = 0;i < oldObj.columns; i += 1) { const val = actual_values[i] oldObj.setAt(val, line, i); } } else { // TODO: better error message throw new Error(`Attempting to assign an invalid value to array ${id}`); } } else { if(!oldObj.isVector && column == null) { // TODO: better error message throw new Error(`Attempting to assign an invalid value to array ${id}`); } oldObj.setAt(sto_value as StoreValue, line, column); } } else { throw new Error("Cannot update a non-array variable using updateStoreArray"); } // const oldType = oldObj.type; // const stoType = sto_value.type; // // TODO: better error message // throw new Error(`${oldType.value} is not compatible with type ${stoType.value} given`); return this; } } /** * Inserts a new variable into the Store. This method should be used when declaring a new variable, * including the special return variable $. * @param id variable id * @param stoValue the value to be used as the initial value of id */ 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) { newObj = new StoreObjectRef(stoValue); } else if (stoValue instanceof ArrayStoreValueRef) { newObj = new StoreObjectArrayRef(stoValue, stoValue.lines, stoValue.columns); } else if (stoValue instanceof ArrayStoreValue) { const columns = stoValue.isVector() ? 0 : stoValue.columns!; const addresses: number[] = []; const all_values = stoValue.get(); if(all_values.length > 0) { for(let i = 0; i < stoValue.get().length; i += 1) { const val = all_values[i].get(); addresses.push(Location.allocate(val)); } } else { let total = stoValue.lines; total = stoValue.isVector() ? total : total * columns; for(let i = 0; i < total; i += 1) { addresses.push(Location.allocate(null)); } } newObj = new StoreObjectArray(stoValue.type as ArrayType, stoValue.lines, columns, addresses, stoValue.isConst); } 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); } }