Add wrapper to prevent iteration of unordered maps
diff --git a/syntax/map.rs b/syntax/map.rs
new file mode 100644
index 0000000..a3b33ed
--- /dev/null
+++ b/syntax/map.rs
@@ -0,0 +1,89 @@
+use std::borrow::Borrow;
+use std::hash::Hash;
+use std::ops::Index;
+
+pub use self::unordered::UnorderedMap;
+pub use std::collections::hash_map::Entry;
+
+mod unordered {
+    use crate::syntax::set::UnorderedSet;
+    use std::borrow::Borrow;
+    use std::collections::hash_map::{Entry, HashMap};
+    use std::hash::Hash;
+
+    // Wrapper prohibits accidentally introducing iteration over the map, which
+    // could lead to nondeterministic generated code.
+    pub struct UnorderedMap<K, V>(HashMap<K, V>);
+
+    impl<K, V> UnorderedMap<K, V> {
+        pub fn new() -> Self {
+            UnorderedMap(HashMap::new())
+        }
+    }
+
+    impl<K, V> UnorderedMap<K, V>
+    where
+        K: Hash + Eq,
+    {
+        pub fn insert(&mut self, key: K, value: V) -> Option<V> {
+            self.0.insert(key, value)
+        }
+
+        pub fn contains_key<Q>(&self, key: &Q) -> bool
+        where
+            K: Borrow<Q>,
+            Q: ?Sized + Hash + Eq,
+        {
+            self.0.contains_key(key)
+        }
+
+        pub fn get<Q>(&self, key: &Q) -> Option<&V>
+        where
+            K: Borrow<Q>,
+            Q: ?Sized + Hash + Eq,
+        {
+            self.0.get(key)
+        }
+
+        pub fn entry(&mut self, key: K) -> Entry<K, V> {
+            self.0.entry(key)
+        }
+
+        pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
+        where
+            K: Borrow<Q>,
+            Q: ?Sized + Hash + Eq,
+        {
+            self.0.remove(key)
+        }
+
+        pub fn keys(&self) -> UnorderedSet<K>
+        where
+            K: Copy,
+        {
+            let mut set = UnorderedSet::new();
+            for key in self.0.keys() {
+                set.insert(*key);
+            }
+            set
+        }
+    }
+}
+
+impl<K, V> Default for UnorderedMap<K, V> {
+    fn default() -> Self {
+        UnorderedMap::new()
+    }
+}
+
+impl<Q, K, V> Index<&Q> for UnorderedMap<K, V>
+where
+    K: Borrow<Q> + Hash + Eq,
+    Q: ?Sized + Hash + Eq,
+{
+    type Output = V;
+
+    fn index(&self, key: &Q) -> &V {
+        self.get(key).unwrap()
+    }
+}