@@ -21,6 +21,210 @@ export interface SourceLocation {
2121 endCol : number ;
2222}
2323
24+ /**
25+ * O(1) variable lookup table that wraps a flat ModelicaVariable[] array with
26+ * Map-based indices. Three access patterns are supported:
27+ *
28+ * 1. **Exact name**: `get(name)`, `has(name)`
29+ * 2. **Array root prefix**: `getArrayElements("x")` returns all `x[1]`, `x[2,3]`, …
30+ * 3. **Encoded suffix**: `getEncoded("y")` returns the variable whose name is `\0…\0y`
31+ *
32+ * The backing array preserves insertion order for serialization and test output.
33+ */
34+ export class SymbolTable {
35+ /** Ordered backing array — preserved for serialization / iteration. */
36+ private _items : ModelicaVariable [ ] = [ ] ;
37+
38+ /** Exact name → variable. */
39+ private _byName = new Map < string , ModelicaVariable > ( ) ;
40+
41+ /** Array root name → indexed element variables (e.g. "x" → [x[1], x[2], …]). */
42+ private _byArrayRoot = new Map < string , ModelicaVariable [ ] > ( ) ;
43+
44+ /** Decoded suffix → encoded variable (for `\0prefix\0suffix` naming). */
45+ private _byEncodedSuffix = new Map < string , ModelicaVariable > ( ) ;
46+
47+ // ── Exact-match lookups ──
48+
49+ get ( name : string ) : ModelicaVariable | undefined {
50+ return this . _byName . get ( name ) ;
51+ }
52+
53+ has ( name : string ) : boolean {
54+ return this . _byName . has ( name ) ;
55+ }
56+
57+ // ── Array-prefix lookups ──
58+
59+ getArrayElements ( baseName : string ) : ModelicaVariable [ ] {
60+ return this . _byArrayRoot . get ( baseName ) ?? [ ] ;
61+ }
62+
63+ hasArrayElements ( baseName : string ) : boolean {
64+ return this . _byArrayRoot . has ( baseName ) ;
65+ }
66+
67+ // ── Encoded (\0) variable lookups ──
68+
69+ getEncoded ( decodedName : string ) : ModelicaVariable | undefined {
70+ return this . _byEncodedSuffix . get ( decodedName ) ;
71+ }
72+
73+ // ── Mutation ──
74+
75+ push ( variable : ModelicaVariable ) : void {
76+ this . _items . push ( variable ) ;
77+ this . _indexVariable ( variable ) ;
78+ }
79+
80+ remove ( variable : ModelicaVariable ) : void {
81+ const idx = this . _items . indexOf ( variable ) ;
82+ if ( idx >= 0 ) {
83+ this . _items . splice ( idx , 1 ) ;
84+ this . _deindexVariable ( variable ) ;
85+ }
86+ }
87+
88+ bulkRemove ( varSet : Set < ModelicaVariable > ) : void {
89+ this . _items = this . _items . filter ( ( v ) => ! varSet . has ( v ) ) ;
90+ // Rebuild indices fully — cheaper than N individual deindex calls
91+ this . _rebuildIndices ( ) ;
92+ }
93+
94+ // ── Array-compatible interface ──
95+
96+ get length ( ) : number {
97+ return this . _items . length ;
98+ }
99+
100+ /** Direct indexed access. */
101+ at ( index : number ) : ModelicaVariable | undefined {
102+ return this . _items [ index ] ;
103+ }
104+
105+ /** Return the backing array (read-only view for iteration/serialization). */
106+ toArray ( ) : readonly ModelicaVariable [ ] {
107+ return this . _items ;
108+ }
109+
110+ /** Replace the entire contents (used when bulk-assigning `dae.variables = filtered`). */
111+ replaceAll ( items : ModelicaVariable [ ] ) : void {
112+ this . _items = items ;
113+ this . _rebuildIndices ( ) ;
114+ }
115+
116+ // Delegate array-like methods to the backing array
117+ filter ( predicate : ( v : ModelicaVariable , i : number , arr : ModelicaVariable [ ] ) => boolean ) : ModelicaVariable [ ] {
118+ return this . _items . filter ( predicate ) ;
119+ }
120+
121+ some ( predicate : ( v : ModelicaVariable , i : number , arr : ModelicaVariable [ ] ) => boolean ) : boolean {
122+ return this . _items . some ( predicate ) ;
123+ }
124+
125+ find ( predicate : ( v : ModelicaVariable , i : number , arr : ModelicaVariable [ ] ) => boolean ) : ModelicaVariable | undefined {
126+ return this . _items . find ( predicate ) ;
127+ }
128+
129+ findIndex ( predicate : ( v : ModelicaVariable , i : number , arr : ModelicaVariable [ ] ) => boolean ) : number {
130+ return this . _items . findIndex ( predicate ) ;
131+ }
132+
133+ forEach ( callbackfn : ( value : ModelicaVariable , index : number , array : ModelicaVariable [ ] ) => void ) : void {
134+ this . _items . forEach ( callbackfn ) ;
135+ }
136+
137+ map < T > ( callbackfn : ( value : ModelicaVariable , index : number , array : ModelicaVariable [ ] ) => T ) : T [ ] {
138+ return this . _items . map ( callbackfn ) ;
139+ }
140+
141+ every ( predicate : ( v : ModelicaVariable , i : number , arr : ModelicaVariable [ ] ) => boolean ) : boolean {
142+ return this . _items . every ( predicate ) ;
143+ }
144+
145+ slice ( start ?: number , end ?: number ) : ModelicaVariable [ ] {
146+ return this . _items . slice ( start , end ) ;
147+ }
148+
149+ sort ( compareFn : ( a : ModelicaVariable , b : ModelicaVariable ) => number ) : this {
150+ this . _items . sort ( compareFn ) ;
151+ return this ;
152+ }
153+
154+ splice ( start : number , deleteCount : number ) : ModelicaVariable [ ] {
155+ const removed = this . _items . splice ( start , deleteCount ) ;
156+ for ( const v of removed ) this . _deindexVariable ( v ) ;
157+ return removed ;
158+ }
159+
160+ indexOf ( variable : ModelicaVariable ) : number {
161+ return this . _items . indexOf ( variable ) ;
162+ }
163+
164+ [ Symbol . iterator ] ( ) : IterableIterator < ModelicaVariable > {
165+ return this . _items [ Symbol . iterator ] ( ) ;
166+ }
167+
168+ // ── Private index management ──
169+
170+ private _indexVariable ( v : ModelicaVariable ) : void {
171+ this . _byName . set ( v . name , v ) ;
172+
173+ // Array root index: "foo[1,2]" → root "foo"
174+ const bracketIdx = v . name . indexOf ( "[" ) ;
175+ if ( bracketIdx > 0 ) {
176+ const root = v . name . substring ( 0 , bracketIdx ) ;
177+ let arr = this . _byArrayRoot . get ( root ) ;
178+ if ( ! arr ) {
179+ arr = [ ] ;
180+ this . _byArrayRoot . set ( root , arr ) ;
181+ }
182+ arr . push ( v ) ;
183+ }
184+
185+ // Encoded variable index: "\0prefix\0suffix" → suffix
186+ if ( v . name . startsWith ( "\0" ) ) {
187+ const lastNull = v . name . lastIndexOf ( "\0" ) ;
188+ if ( lastNull > 0 ) {
189+ const suffix = v . name . substring ( lastNull + 1 ) ;
190+ this . _byEncodedSuffix . set ( suffix , v ) ;
191+ }
192+ }
193+ }
194+
195+ private _deindexVariable ( v : ModelicaVariable ) : void {
196+ this . _byName . delete ( v . name ) ;
197+
198+ const bracketIdx = v . name . indexOf ( "[" ) ;
199+ if ( bracketIdx > 0 ) {
200+ const root = v . name . substring ( 0 , bracketIdx ) ;
201+ const arr = this . _byArrayRoot . get ( root ) ;
202+ if ( arr ) {
203+ const idx = arr . indexOf ( v ) ;
204+ if ( idx >= 0 ) arr . splice ( idx , 1 ) ;
205+ if ( arr . length === 0 ) this . _byArrayRoot . delete ( root ) ;
206+ }
207+ }
208+
209+ if ( v . name . startsWith ( "\0" ) ) {
210+ const lastNull = v . name . lastIndexOf ( "\0" ) ;
211+ if ( lastNull > 0 ) {
212+ const suffix = v . name . substring ( lastNull + 1 ) ;
213+ this . _byEncodedSuffix . delete ( suffix ) ;
214+ }
215+ }
216+ }
217+
218+ private _rebuildIndices ( ) : void {
219+ this . _byName . clear ( ) ;
220+ this . _byArrayRoot . clear ( ) ;
221+ this . _byEncodedSuffix . clear ( ) ;
222+ for ( const v of this . _items ) {
223+ this . _indexVariable ( v ) ;
224+ }
225+ }
226+ }
227+
24228export class ModelicaDAE {
25229 name : string ;
26230 description : string | null ;
@@ -35,7 +239,7 @@ export class ModelicaDAE {
35239 initialAlgorithms : ModelicaStatement [ ] [ ] = [ ] ;
36240 /** Algebraic loops (SCCs) detected during flattening. */
37241 algebraicLoops : { variables : string [ ] ; equations : ModelicaEquation [ ] } [ ] = [ ] ;
38- variables : ModelicaVariable [ ] = [ ] ;
242+ variables : SymbolTable = new SymbolTable ( ) ;
39243 stateMachines : ModelicaStateMachine [ ] = [ ] ;
40244 /** Clock partitions identified by the synchronous clock inference pass. */
41245 clockPartitions : ModelicaClockPartition [ ] = [ ] ;
@@ -138,17 +342,17 @@ export class ModelicaDAE {
138342 */
139343 get stateVariables ( ) : ModelicaRealVariable [ ] {
140344 return this . variables . filter (
141- ( v ) : v is ModelicaRealVariable => v instanceof ModelicaRealVariable && v . variability === null ,
142- ) ;
345+ ( v ) => v instanceof ModelicaRealVariable && v . variability === null ,
346+ ) as ModelicaRealVariable [ ] ;
143347 }
144348
145349 /**
146350 * Identify derivative variables — those whose names match `der(...)`.
147351 */
148352 get derivativeVariables ( ) : ModelicaRealVariable [ ] {
149353 return this . variables . filter (
150- ( v ) : v is ModelicaRealVariable => v instanceof ModelicaRealVariable && v . name . startsWith ( "der(" ) ,
151- ) ;
354+ ( v ) => v instanceof ModelicaRealVariable && v . name . startsWith ( "der(" ) ,
355+ ) as ModelicaRealVariable [ ] ;
152356 }
153357
154358 /**
0 commit comments