1 /*
2 * Copyright 2019-2022 Foreseeti AB <https://foreseeti.com>
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package org.mal_lang.lib;
18
19 import java.util.HashMap;
20 import java.util.Map;
21
22 /**
23 * Scope stores maps on a stack. Each map is a 'scope' of values uniquely identified by their map
24 * keys. Values from previous scopes may be retrieved with the lookup() and lookdown() methods.
25 */
26 public class Scope<T> {
27 private Map<String, T> symbols;
28 public final Scope<T> parent;
29
30 public Scope() {
31 this.symbols = new HashMap<>();
32 this.parent = null;
33 }
34
35 public Scope(Scope<T> parent) {
36 this.symbols = new HashMap<>();
37 this.parent = parent;
38 }
39
40 /**
41 * Iterates the scopes from the bottom up (back to front), and returns the value of the first map
42 * containing the key.
43 *
44 * @param key Object key
45 * @return Object associated with the first match of key, or null if not found
46 */
47 public T lookup(String key) {
48 if (symbols.containsKey(key)) {
49 return symbols.get(key);
50 } else if (parent != null) {
51 return parent.lookup(key);
52 } else {
53 return null;
54 }
55 }
56
57 /**
58 * Iterates the scope from the top down (front to back), and returns the value of the first map
59 * containing the key.
60 *
61 * @param key Object key
62 * @return Object associated with the first match of key, or null if not found
63 */
64 public T lookdown(String key) {
65 if (parent != null) {
66 var parentValue = parent.lookdown(key);
67 if (parentValue != null) {
68 return parentValue;
69 }
70 }
71 return symbols.get(key);
72 }
73
74 /**
75 * Looks only at the current scope and returns the value associated with the key.
76 *
77 * @param key Object key
78 * @return Object associated with the match of key, or null if not found
79 */
80 public T look(String key) {
81 return symbols.get(key);
82 }
83
84 public Scope<T> getScopeFor(String key) {
85 if (symbols.containsKey(key)) {
86 return this;
87 } else if (parent != null) {
88 return parent.getScopeFor(key);
89 } else {
90 return null;
91 }
92 }
93
94 /**
95 * Adds a value to the current scope (map).
96 *
97 * @param key Key associated with object
98 * @param value Value associated with the key
99 */
100 public void add(String key, T value) {
101 symbols.put(key, value);
102 }
103
104 public Map<String, T> getSymbols() {
105 return symbols;
106 }
107
108 @Override
109 public String toString() {
110 if (parent != null) {
111 return String.format("{%s, %s}", parent.toString(), String.join(", ", symbols.keySet()));
112 } else {
113 return String.format("{%s}", String.join(", ", symbols.keySet()));
114 }
115 }
116 }