{"action":"created","comment":{"url":"https://api.github.com/repos/binast/binjs-ref/pulls/comments/233304095","pull_request_review_id":174667144,"id":233304095,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIzMzMwNDA5NQ==","diff_hunk":"@@ -8,128 +10,321 @@ use std::rc::Rc;\n \n use range_encoding;\n \n-use binjs_shared::{ FieldName, InterfaceName };\n-\n pub type IOPath = binjs_shared::ast::Path<InterfaceName, (/* child index */ usize, /* field name */ FieldName)>;\n pub type IOPathItem = binjs_shared::ast::PathItem<InterfaceName, (/* child index */ usize, /* field name */ FieldName)>;\n \n-pub struct Entry<'a, K, T> where K: 'a, T: 'a {\n-    /// The number of entries for this key in the context.\n-    pub len: usize,\n-    pub value: std::collections::hash_map::Entry<'a, K, T>\n+mod context_information {\n+    use entropy::probabilities::SymbolInfo;\n+    use std::collections::HashMap;\n+    use std::hash::Hash;\n+\n+    /// A container for the statistics available in a given prediction context\n+    /// (a typical prediction context is a path in the AST, or a position in\n+    /// the file, etc.).\n+    ///\n+    /// This container is meant to be used in two settings:\n+    ///\n+    /// - to count the number of instances of values in a context (instantiated with `Statistics=usize`,\n+    ///     in which case the `usize` is a number of instances); or\n+    /// - once number of instances have been converted to frequency information and unique indices\n+    ///     (instantiated with `Statistics=SymbolInfo`, in which case the `SymbolInfo` contains the unique index).\n+    ///\n+    /// For this reason, all the meaningful methods of this struct are implemented only if `Statistics=usize`\n+    /// or `Statistics=SymbolInfo`.\n+    #[derive(Clone, Debug, Deserialize, Serialize)]\n+    pub struct ContextInformation<NodeValue, Statistics> where NodeValue: Eq + Hash {\n+        /// NodeValue => Statistics mapping, always valid\n+        by_value: HashMap<NodeValue, Statistics>,\n+\n+        /// usize => NodeValue mapping.\n+        ///\n+        /// This vector is populated only when `Statistics = SymbolInfo`. When that is the case,\n+        /// `by_index` is effectively the reverse mapping from `by_value` (using the\n+        /// index embedded in `SymbolInfo`).\n+        by_index: Vec<NodeValue>,\n+    }\n+    impl<NodeValue, Statistics> ContextInformation<NodeValue, Statistics> where NodeValue: Eq + Hash {\n+        pub fn new() -> Self {\n+            ContextInformation {\n+                by_value: HashMap::new(),\n+                by_index: Vec::new(),\n+            }\n+        }\n+\n+        /// Return the number of entries.\n+        pub fn len(&self) -> usize {\n+            self.by_value.len()\n+        }\n+    }\n+\n+    // Methods that make sense only when we have finished computing frequency information.\n+    impl<NodeValue> ContextInformation<NodeValue, SymbolInfo> where NodeValue: Eq + Hash {\n+        pub fn by_value(&self) -> &HashMap<NodeValue, SymbolInfo> {\n+            &self.by_value\n+        }\n+\n+        pub fn by_value_mut(&mut self) -> &mut HashMap<NodeValue, SymbolInfo> {\n+            &mut self.by_value\n+        }\n+\n+        pub fn by_index(&self, index: usize) -> Option<&NodeValue> {\n+            self.by_index.get(index)\n+        }\n+    }\n+\n+    // Methods that make sense only while we are collecting instances.\n+\n+    impl<NodeValue> ContextInformation<NodeValue, usize> where NodeValue: Eq + Hash {\n+        /// Register a value as being used in this context.\n+        pub fn add(&mut self, node_value: NodeValue) {\n+            self.by_value.entry(node_value)\n+            .and_modify(|instances| *instances += 1)\n+            .or_insert(1);\n+        }\n+    }\n+\n+    impl<NodeValue> ::entropy::probabilities::InstancesToProbabilities for ContextInformation<NodeValue, usize> where NodeValue: Clone + Eq + Hash {\n+        type AsProbabilities = ContextInformation<NodeValue, SymbolInfo>;\n+        fn instances_to_probabilities(self, _description: &str) -> ContextInformation<NodeValue, SymbolInfo> {\n+            let instances: Vec<_> = self\n+                .by_value\n+                .values()\n+                .map(|x| *x as u32)\n+                .collect();\n+\n+            let distribution = std::rc::Rc::new(std::cell::RefCell::new(range_encoding::CumulativeDistributionFrequency::new(instances).\n+                unwrap()));\n+            // FIXME: This will fail if `by_value` is empty.\n+            // FIXME: We should have a fallback distribution in case everything is empty.\n+\n+            let (by_value, by_index): (HashMap<_, _>, Vec<_>) = self.by_value\n+                .into_iter()\n+                .enumerate()\n+                .map(|(index, (value, _))| {\n+                    let for_by_value = (value.clone(), SymbolInfo {\n+                        index,\n+                        distribution: distribution.clone(),\n+                    });\n+                    let for_by_index = value;\n+                    (for_by_value, for_by_index)\n+                })\n+                .unzip();\n+            ContextInformation {\n+                by_value,\n+                by_index\n+            }\n+        }\n+    }\n }\n+use self::context_information::ContextInformation;\n \n-/// A generic predictor, associating a context and a key to a value.\n-#[derive(Debug, Default, Serialize, Deserialize)]\n-pub struct ContextPredict<C, K, T> where C: Eq + Hash + Clone, K: Eq + Hash + Clone {\n-    by_context: HashMap<C, HashMap<K, T>>,\n+/// A generic mechanism used to predict possible values in a given context (e.g.\n+/// AST path or file position) in a file.\n+///\n+/// This mechanism is meant to be used as follows:\n+///\n+/// 1. Create a new `ContextPredict<Context, NodeValue, usize>` and use `ContextPredict::add` to count the number\n+///     of instances of each possible `NodeValue` in each possible `Context`. Alternatively, load this data\n+///     from an existing dictionary.\n+/// 2. Convert the `ContextPredict<Context, NodeValue, usize>` into a `ContextPredict<Context, NodeValue, SymbolInfo>`\n+///     by calling `ContextPredict::instances_to_probabilities`.\n+/// 3. Use method `ContextPredict::<_, _, SymbolInfo>::by_node_value` and `by_node_value_mut` to get the statistics\n+///     information in a specific context for a specific node value (used for compression). This information contains\n+///     an index designed to be written to a compressed stream.\n+/// 4. Use method `ContextPredict::<_, _, SymbolInfo>::frequencies_at` to get the statistics information in a\n+///     specific context for all node values that have shown up in this context (used for decompression). This\n+///     information is used to extract an index from a compressed stream.\n+/// 5. Use method `ContextPredict::<_, _, SymbolInfo>::index_at` to get a specific node value in a specific\n+///     context from the index extracted from the compressed stream.\n+///\n+/// As most methods of this struct can only be used if `Statistics = usize` xor `Statistics = SymbolInfo`,\n+/// the implementation of these methods is only available respectively if `Statistics = usize` or if\n+/// `Statistics = SymbolInfo`.\n+///\n+/// For most use cases, you probably want one of the more specialized predictors.\n+#[derive(Clone, Debug, Default, Deserialize, Serialize)]\n+pub struct ContextPredict<Context, NodeValue, Statistics> where Context: Eq + Hash + Clone, NodeValue: Eq + Hash + Clone {\n+    by_context: HashMap<Context, ContextInformation<NodeValue, Statistics>>,\n }\n-impl<C, K, T> ContextPredict<C, K, T> where C: Eq + Hash + Clone, K: Eq + Hash + Clone {\n+impl<Context, NodeValue, Statistics> ContextPredict<Context, NodeValue, Statistics> where Context: Eq + Hash + Clone, NodeValue: Eq + Hash + Clone {\n     pub fn new() -> Self {\n         Self {\n             by_context: HashMap::new()\n         }\n     }\n-    pub fn get_mut<C2: ?Sized>(&mut self, context: &C2, key: &K) -> Option<&mut T>\n-        where\n-            C: std::borrow::Borrow<C2>,\n-            C2: Hash + Eq\n-    {\n-        let by_key = self.by_context.get_mut(context)?;\n-        by_key.get_mut(key)\n-    }\n \n-    pub fn entry(&mut self, context: C, key: &K) -> Entry<K, T> {\n-        let by_key = self.by_context.entry(context)\n-            .or_insert_with(|| HashMap::new());\n-        Entry {\n-            len: by_key.len(),\n-            value: by_key.entry(key.clone()),\n-        }\n+    /// All the contexts known to this predictor.\n+    ///\n+    /// Used mainly for debugging.\n+    pub fn contexts(&self) -> impl Iterator<Item=&Context> {\n+        self.by_context.keys()\n     }\n \n     /// The number of states in this predictor.\n     pub fn len(&self) -> usize {\n         self.by_context.values()\n-            .map(|map| map.len())\n+            .map(ContextInformation::len)\n             .sum()\n     }\n }\n \n+impl<Context, NodeValue> ContextPredict<Context, NodeValue, usize> where Context: Eq + Hash + Clone, NodeValue: Eq + Hash + Clone {\n+    /// Register a value as being used in this context.\n+    pub fn add(&mut self, context: Context, value: NodeValue) {\n+        let by_value = self.by_context.entry(context)\n+            .or_insert_with(|| ContextInformation::new());\n+        by_value.add(value)\n+    }\n+}\n+\n+impl<Context, NodeValue> ContextPredict<Context, NodeValue, SymbolInfo> where Context: Eq + Hash + Clone, NodeValue: Eq + Hash + Clone {\n+    /// Get a value by context and index.\n+    ///\n+    /// This method is only implemented when `Statistics=SymbolInfo` as the index is initialized\n+    /// by `instances_to_probabilities`. The index corresponds to the one defined in\n+    /// the `SymbolInfo`.\n+    pub fn by_index<C2: ?Sized>(&mut self, context: &C2, index: usize) -> Option<&NodeValue>\n+        where\n+            Context: std::borrow::Borrow<C2>,\n+            C2: Hash + Eq\n+    {\n+        self.by_context.get(context)?\n+            .by_index(index)\n+    }\n \n-impl<C, K> InstancesToProbabilities for ContextPredict<C, K, usize> where C: Eq + Hash + Clone, K: Eq + Hash + Clone {\n-    type AsProbabilities = ContextPredict<C, K, Symbol>;\n-    fn instances_to_probabilities(self) -> ContextPredict<C, K, Symbol> {\n+    pub fn by_value<C2: ?Sized>(&self, context: &C2, value: &NodeValue) -> Option<&SymbolInfo>\n+        where\n+            Context: std::borrow::Borrow<C2>,\n+            C2: Hash + Eq\n+    {\n+        self.by_context.get(context)?\n+            .by_value()\n+            .get(value)\n+    }\n+\n+    pub fn by_value_mut<C2: ?Sized>(&mut self, context: &C2, value: &NodeValue) -> Option<&mut SymbolInfo>\n+        where\n+            Context: std::borrow::Borrow<C2>,\n+            C2: Hash + Eq\n+    {\n+        self.by_context.get_mut(context)?\n+            .by_value_mut()\n+            .get_mut(value)\n+    }\n+}\n+\n+impl<Context, NodeValue> InstancesToProbabilities for ContextPredict<Context, NodeValue, usize> where Context: Eq + Hash + Clone + std::fmt::Debug, NodeValue: Eq + Hash + Clone {\n+    type AsProbabilities = ContextPredict<Context, NodeValue, SymbolInfo>;\n+    fn instances_to_probabilities(self, description: &str) -> ContextPredict<Context, NodeValue, SymbolInfo> {\n+        debug!(target: \"entropy\", \"Converting ContextPredict {} to probabilities\", description);\n         let by_context = self.by_context.into_iter()\n-            .map(|(context, by_key)| {\n-                let instances : Vec<_> = by_key.values()\n-                    .map(|x| *x as u32)\n-                    .collect();\n-                let distribution = Rc::new(RefCell::new(range_encoding::CumulativeDistributionFrequency::new(instances).\n-                    unwrap()));\n-                    // FIXME: This will fail if `by_key` is empty.\n-                    // FIXME: We should have a fallback distribution in case everything is empty.\n-\n-                let by_key = by_key.into_iter()\n-                    .enumerate()\n-                    .map(|(index, (key, _))| {\n-                        (key, Symbol {\n-                            index,\n-                            distribution: distribution.clone(),\n-                        })\n-                    })\n-                    .collect();\n-                // FIXME: Actually put the number of instances in `distribution`.\n-                (context, by_key)\n-            })\n+            .map(|(context, info)| (context, info.instances_to_probabilities(\"ContextInformation\")))\n             .collect();\n         ContextPredict {\n-            by_context\n+            by_context,\n         }\n     }\n }\n \n-\n-/// A predictor used to predict the probability of a symbol based on\n-/// its position in a tree. The predictor is typically customized to\n-/// limit the prediction depth, e.g. to 0 (don't use any context),\n-/// 1 (use only the parent + child index) or 2 (use parent +\n-/// grand-parent and both child indices).\n-#[derive(Debug, Default, Serialize, Deserialize)]\n-pub struct PathPredict<K, T> where K: Eq + Hash + Clone {\n-    context_predict: ContextPredict<IOPath, K, T>,\n+/// A specialized predictor used to predict possible values at a possible path in the AST.\n+///\n+/// This mechanism is meant to be used as follows:\n+///\n+/// 1. Create a new `PathPredict<NodeValue, usize>` and use `PathPredict::add` to count the number\n+///     of instances of each possible `NodeValue` in each possible path. Alternatively, load this data\n+///     from an existing dictionary.\n+/// 2. Convert the `PathPredict<NodeValue, usize>` into a `PathPredict<NodeValue, SymbolInfo>`\n+///     by calling `PathPredict::<_, usize>::instances_to_probabilities`.\n+/// 3. Use method `PathPredict::<_, SymbolInfo>::by_node_value` and `by_node_value_mut` to get the statistics\n+///     information in a specific path for a specific node value (used for compression). This information contains\n+///     an index designed to be written to a compressed stream.\n+/// 4. Use method `PathPredict::<_, SymbolInfo>::frequencies_at` to get the statistics information in a\n+///     specific path for all node values that have shown up in this path (used for decompression). This\n+///     information is used to extract an index from a compressed stream.\n+/// 5. Use method `PathPredict::<_, SymbolInfo>::index_at` to get a specific node value in a specific\n+///     path from the index extracted from the compressed stream.\n+///\n+/// As most methods of this struct can only be used if `Statistics = usize` xor `Statistics = SymbolInfo`,\n+/// the implementation of these methods is only available respectively if `Statistics = usize` or if\n+/// `Statistics = SymbolInfo`.\n+#[derive(Clone, Debug, Default, Deserialize, Serialize)]\n+pub struct PathPredict<NodeValue, Statistics> where NodeValue: Eq + Hash + Clone {\n+    context_predict: ContextPredict<IOPath, NodeValue, Statistics>,\n }\n \n \n-impl<K> InstancesToProbabilities for PathPredict<K, usize> where K: Eq + Hash + Clone {\n-    type AsProbabilities = PathPredict<K, Symbol>;\n-    fn instances_to_probabilities(self) -> PathPredict<K, Symbol> {\n+impl<NodeValue> InstancesToProbabilities for PathPredict<NodeValue, usize> where NodeValue: Eq + Hash + Clone {\n+    type AsProbabilities = PathPredict<NodeValue, SymbolInfo>;\n+    fn instances_to_probabilities(self, description: &str) -> PathPredict<NodeValue, SymbolInfo> {\n         PathPredict {\n-            context_predict: self.context_predict.instances_to_probabilities()\n+            context_predict: self.context_predict\n+                .instances_to_probabilities(description)\n         }\n     }\n }\n \n-impl<K, T> PathPredict<K, T> where K: Eq + Hash + Clone + std::fmt::Debug {\n+impl<NodeValue, Statistics> PathPredict<NodeValue, Statistics> where NodeValue: Eq + Hash + Clone + std::fmt::Debug {\n     pub fn new() -> Self {\n         PathPredict {\n             context_predict: ContextPredict::new(),\n         }\n     }\n \n-    pub fn get_mut(&mut self, tail: &[IOPathItem], key: &K) -> Option<&mut T> {\n-        self.context_predict.get_mut(tail, key)\n+    /// The number of states in this predictor.\n+    pub fn len(&self) -> usize {\n+        self.context_predict.len()\n     }\n \n-    pub fn entry(&mut self, tail: &[IOPathItem], key: &K) -> Entry<K, T> {\n-        let tail : Vec<_> = tail.iter()\n-            .cloned()\n-            .collect(); // Note: that's bound to be expensive. Ideally, we'd like to avoid most allocations here.\n-        self.context_predict.entry(tail.into(), key)\n+    /// All the paths known to this predictor.\n+    ///\n+    /// Used mainly for debugging.\n+    pub fn paths(&self) -> impl Iterator<Item=&IOPath> {\n+        self.context_predict.contexts()\n+    }\n+}\n+impl<NodeValue> PathPredict<NodeValue, usize> where NodeValue: Eq + Hash + Clone {\n+        /// Register a value as being used at this path.\n+    pub fn add(&mut self, path: &[IOPathItem], value: NodeValue) {\n+        let mut as_path = IOPath::new();\n+        as_path.extend_from_slice(path);\n+        self.context_predict.add(as_path, value);\n+    }\n+}\n+impl<NodeValue> PathPredict<NodeValue, SymbolInfo> where NodeValue: Eq + Hash + Clone {\n+    /// Get a value by path and index.","path":"crates/binjs_io/src/entropy/predict.rs","position":366,"original_position":366,"commit_id":"0a13b2fa48f038a2ea6115ecd03c5026716bdb68","original_commit_id":"0a13b2fa48f038a2ea6115ecd03c5026716bdb68","user":{"login":"arai-a","id":6299746,"node_id":"MDQ6VXNlcjYyOTk3NDY=","avatar_url":"https://avatars1.githubusercontent.com/u/6299746?v=4","gravatar_id":"","url":"https://api.github.com/users/arai-a","html_url":"https://github.com/arai-a","followers_url":"https://api.github.com/users/arai-a/followers","following_url":"https://api.github.com/users/arai-a/following{/other_user}","gists_url":"https://api.github.com/users/arai-a/gists{/gist_id}","starred_url":"https://api.github.com/users/arai-a/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/arai-a/subscriptions","organizations_url":"https://api.github.com/users/arai-a/orgs","repos_url":"https://api.github.com/users/arai-a/repos","events_url":"https://api.github.com/users/arai-a/events{/privacy}","received_events_url":"https://api.github.com/users/arai-a/received_events","type":"User","site_admin":false},"body":"\"index\" => \"Symbol's index\"","created_at":"2018-11-14T03:29:36Z","updated_at":"2018-11-14T03:39:22Z","html_url":"https://github.com/binast/binjs-ref/pull/195#discussion_r233304095","pull_request_url":"https://api.github.com/repos/binast/binjs-ref/pulls/195","author_association":"COLLABORATOR","_links":{"self":{"href":"https://api.github.com/repos/binast/binjs-ref/pulls/comments/233304095"},"html":{"href":"https://github.com/binast/binjs-ref/pull/195#discussion_r233304095"},"pull_request":{"href":"https://api.github.com/repos/binast/binjs-ref/pulls/195"}}},"pull_request":{"url":"https://api.github.com/repos/binast/binjs-ref/pulls/195","id":229357183,"node_id":"MDExOlB1bGxSZXF1ZXN0MjI5MzU3MTgz","html_url":"https://github.com/binast/binjs-ref/pull/195","diff_url":"https://github.com/binast/binjs-ref/pull/195.diff","patch_url":"https://github.com/binast/binjs-ref/pull/195.patch","issue_url":"https://api.github.com/repos/binast/binjs-ref/issues/195","number":195,"state":"open","locked":false,"title":"Towards a roundtrip with entropy + external dictionaries","user":{"login":"Yoric","id":10190,"node_id":"MDQ6VXNlcjEwMTkw","avatar_url":"https://avatars1.githubusercontent.com/u/10190?v=4","gravatar_id":"","url":"https://api.github.com/users/Yoric","html_url":"https://github.com/Yoric","followers_url":"https://api.github.com/users/Yoric/followers","following_url":"https://api.github.com/users/Yoric/following{/other_user}","gists_url":"https://api.github.com/users/Yoric/gists{/gist_id}","starred_url":"https://api.github.com/users/Yoric/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Yoric/subscriptions","organizations_url":"https://api.github.com/users/Yoric/orgs","repos_url":"https://api.github.com/users/Yoric/repos","events_url":"https://api.github.com/users/Yoric/events{/privacy}","received_events_url":"https://api.github.com/users/Yoric/received_events","type":"User","site_admin":false},"body":"While we're not entirely set to test roundtrip, we're getting there.","created_at":"2018-11-08T13:02:43Z","updated_at":"2018-11-14T03:39:22Z","closed_at":null,"merged_at":null,"merge_commit_sha":"7c48b33b809ce7917686304532ef494906fa9873","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/binast/binjs-ref/pulls/195/commits","review_comments_url":"https://api.github.com/repos/binast/binjs-ref/pulls/195/comments","review_comment_url":"https://api.github.com/repos/binast/binjs-ref/pulls/comments{/number}","comments_url":"https://api.github.com/repos/binast/binjs-ref/issues/195/comments","statuses_url":"https://api.github.com/repos/binast/binjs-ref/statuses/0a13b2fa48f038a2ea6115ecd03c5026716bdb68","head":{"label":"Yoric:entropy-with-dictionary-builder","ref":"entropy-with-dictionary-builder","sha":"0a13b2fa48f038a2ea6115ecd03c5026716bdb68","user":{"login":"Yoric","id":10190,"node_id":"MDQ6VXNlcjEwMTkw","avatar_url":"https://avatars1.githubusercontent.com/u/10190?v=4","gravatar_id":"","url":"https://api.github.com/users/Yoric","html_url":"https://github.com/Yoric","followers_url":"https://api.github.com/users/Yoric/followers","following_url":"https://api.github.com/users/Yoric/following{/other_user}","gists_url":"https://api.github.com/users/Yoric/gists{/gist_id}","starred_url":"https://api.github.com/users/Yoric/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Yoric/subscriptions","organizations_url":"https://api.github.com/users/Yoric/orgs","repos_url":"https://api.github.com/users/Yoric/repos","events_url":"https://api.github.com/users/Yoric/events{/privacy}","received_events_url":"https://api.github.com/users/Yoric/received_events","type":"User","site_admin":false},"repo":{"id":151379140,"node_id":"MDEwOlJlcG9zaXRvcnkxNTEzNzkxNDA=","name":"binjs-ref","full_name":"Yoric/binjs-ref","private":false,"owner":{"login":"Yoric","id":10190,"node_id":"MDQ6VXNlcjEwMTkw","avatar_url":"https://avatars1.githubusercontent.com/u/10190?v=4","gravatar_id":"","url":"https://api.github.com/users/Yoric","html_url":"https://github.com/Yoric","followers_url":"https://api.github.com/users/Yoric/followers","following_url":"https://api.github.com/users/Yoric/following{/other_user}","gists_url":"https://api.github.com/users/Yoric/gists{/gist_id}","starred_url":"https://api.github.com/users/Yoric/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Yoric/subscriptions","organizations_url":"https://api.github.com/users/Yoric/orgs","repos_url":"https://api.github.com/users/Yoric/repos","events_url":"https://api.github.com/users/Yoric/events{/privacy}","received_events_url":"https://api.github.com/users/Yoric/received_events","type":"User","site_admin":false},"html_url":"https://github.com/Yoric/binjs-ref","description":"Reference implementation for the JavaScript Binary AST format","fork":true,"url":"https://api.github.com/repos/Yoric/binjs-ref","forks_url":"https://api.github.com/repos/Yoric/binjs-ref/forks","keys_url":"https://api.github.com/repos/Yoric/binjs-ref/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Yoric/binjs-ref/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Yoric/binjs-ref/teams","hooks_url":"https://api.github.com/repos/Yoric/binjs-ref/hooks","issue_events_url":"https://api.github.com/repos/Yoric/binjs-ref/issues/events{/number}","events_url":"https://api.github.com/repos/Yoric/binjs-ref/events","assignees_url":"https://api.github.com/repos/Yoric/binjs-ref/assignees{/user}","branches_url":"https://api.github.com/repos/Yoric/binjs-ref/branches{/branch}","tags_url":"https://api.github.com/repos/Yoric/binjs-ref/tags","blobs_url":"https://api.github.com/repos/Yoric/binjs-ref/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Yoric/binjs-ref/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Yoric/binjs-ref/git/refs{/sha}","trees_url":"https://api.github.com/repos/Yoric/binjs-ref/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Yoric/binjs-ref/statuses/{sha}","languages_url":"https://api.github.com/repos/Yoric/binjs-ref/languages","stargazers_url":"https://api.github.com/repos/Yoric/binjs-ref/stargazers","contributors_url":"https://api.github.com/repos/Yoric/binjs-ref/contributors","subscribers_url":"https://api.github.com/repos/Yoric/binjs-ref/subscribers","subscription_url":"https://api.github.com/repos/Yoric/binjs-ref/subscription","commits_url":"https://api.github.com/repos/Yoric/binjs-ref/commits{/sha}","git_commits_url":"https://api.github.com/repos/Yoric/binjs-ref/git/commits{/sha}","comments_url":"https://api.github.com/repos/Yoric/binjs-ref/comments{/number}","issue_comment_url":"https://api.github.com/repos/Yoric/binjs-ref/issues/comments{/number}","contents_url":"https://api.github.com/repos/Yoric/binjs-ref/contents/{+path}","compare_url":"https://api.github.com/repos/Yoric/binjs-ref/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Yoric/binjs-ref/merges","archive_url":"https://api.github.com/repos/Yoric/binjs-ref/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Yoric/binjs-ref/downloads","issues_url":"https://api.github.com/repos/Yoric/binjs-ref/issues{/number}","pulls_url":"https://api.github.com/repos/Yoric/binjs-ref/pulls{/number}","milestones_url":"https://api.github.com/repos/Yoric/binjs-ref/milestones{/number}","notifications_url":"https://api.github.com/repos/Yoric/binjs-ref/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Yoric/binjs-ref/labels{/name}","releases_url":"https://api.github.com/repos/Yoric/binjs-ref/releases{/id}","deployments_url":"https://api.github.com/repos/Yoric/binjs-ref/deployments","created_at":"2018-10-03T07:56:02Z","updated_at":"2018-10-03T07:56:33Z","pushed_at":"2018-11-13T12:51:41Z","git_url":"git://github.com/Yoric/binjs-ref.git","ssh_url":"git@github.com:Yoric/binjs-ref.git","clone_url":"https://github.com/Yoric/binjs-ref.git","svn_url":"https://github.com/Yoric/binjs-ref","homepage":"https://binast.github.io/binjs-ref/binjs/index.html","size":42372,"stargazers_count":0,"watchers_count":0,"language":"Rust","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"other","name":"Other","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"binast:master","ref":"master","sha":"5a074e35634f6b848e0bc27112cd0fd87276c2f6","user":{"login":"binast","id":34548197,"node_id":"MDEyOk9yZ2FuaXphdGlvbjM0NTQ4MTk3","avatar_url":"https://avatars3.githubusercontent.com/u/34548197?v=4","gravatar_id":"","url":"https://api.github.com/users/binast","html_url":"https://github.com/binast","followers_url":"https://api.github.com/users/binast/followers","following_url":"https://api.github.com/users/binast/following{/other_user}","gists_url":"https://api.github.com/users/binast/gists{/gist_id}","starred_url":"https://api.github.com/users/binast/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/binast/subscriptions","organizations_url":"https://api.github.com/users/binast/orgs","repos_url":"https://api.github.com/users/binast/repos","events_url":"https://api.github.com/users/binast/events{/privacy}","received_events_url":"https://api.github.com/users/binast/received_events","type":"Organization","site_admin":false},"repo":{"id":90141336,"node_id":"MDEwOlJlcG9zaXRvcnk5MDE0MTMzNg==","name":"binjs-ref","full_name":"binast/binjs-ref","private":false,"owner":{"login":"binast","id":34548197,"node_id":"MDEyOk9yZ2FuaXphdGlvbjM0NTQ4MTk3","avatar_url":"https://avatars3.githubusercontent.com/u/34548197?v=4","gravatar_id":"","url":"https://api.github.com/users/binast","html_url":"https://github.com/binast","followers_url":"https://api.github.com/users/binast/followers","following_url":"https://api.github.com/users/binast/following{/other_user}","gists_url":"https://api.github.com/users/binast/gists{/gist_id}","starred_url":"https://api.github.com/users/binast/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/binast/subscriptions","organizations_url":"https://api.github.com/users/binast/orgs","repos_url":"https://api.github.com/users/binast/repos","events_url":"https://api.github.com/users/binast/events{/privacy}","received_events_url":"https://api.github.com/users/binast/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/binast/binjs-ref","description":"Reference implementation for the JavaScript Binary AST format","fork":false,"url":"https://api.github.com/repos/binast/binjs-ref","forks_url":"https://api.github.com/repos/binast/binjs-ref/forks","keys_url":"https://api.github.com/repos/binast/binjs-ref/keys{/key_id}","collaborators_url":"https://api.github.com/repos/binast/binjs-ref/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/binast/binjs-ref/teams","hooks_url":"https://api.github.com/repos/binast/binjs-ref/hooks","issue_events_url":"https://api.github.com/repos/binast/binjs-ref/issues/events{/number}","events_url":"https://api.github.com/repos/binast/binjs-ref/events","assignees_url":"https://api.github.com/repos/binast/binjs-ref/assignees{/user}","branches_url":"https://api.github.com/repos/binast/binjs-ref/branches{/branch}","tags_url":"https://api.github.com/repos/binast/binjs-ref/tags","blobs_url":"https://api.github.com/repos/binast/binjs-ref/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/binast/binjs-ref/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/binast/binjs-ref/git/refs{/sha}","trees_url":"https://api.github.com/repos/binast/binjs-ref/git/trees{/sha}","statuses_url":"https://api.github.com/repos/binast/binjs-ref/statuses/{sha}","languages_url":"https://api.github.com/repos/binast/binjs-ref/languages","stargazers_url":"https://api.github.com/repos/binast/binjs-ref/stargazers","contributors_url":"https://api.github.com/repos/binast/binjs-ref/contributors","subscribers_url":"https://api.github.com/repos/binast/binjs-ref/subscribers","subscription_url":"https://api.github.com/repos/binast/binjs-ref/subscription","commits_url":"https://api.github.com/repos/binast/binjs-ref/commits{/sha}","git_commits_url":"https://api.github.com/repos/binast/binjs-ref/git/commits{/sha}","comments_url":"https://api.github.com/repos/binast/binjs-ref/comments{/number}","issue_comment_url":"https://api.github.com/repos/binast/binjs-ref/issues/comments{/number}","contents_url":"https://api.github.com/repos/binast/binjs-ref/contents/{+path}","compare_url":"https://api.github.com/repos/binast/binjs-ref/compare/{base}...{head}","merges_url":"https://api.github.com/repos/binast/binjs-ref/merges","archive_url":"https://api.github.com/repos/binast/binjs-ref/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/binast/binjs-ref/downloads","issues_url":"https://api.github.com/repos/binast/binjs-ref/issues{/number}","pulls_url":"https://api.github.com/repos/binast/binjs-ref/pulls{/number}","milestones_url":"https://api.github.com/repos/binast/binjs-ref/milestones{/number}","notifications_url":"https://api.github.com/repos/binast/binjs-ref/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/binast/binjs-ref/labels{/name}","releases_url":"https://api.github.com/repos/binast/binjs-ref/releases{/id}","deployments_url":"https://api.github.com/repos/binast/binjs-ref/deployments","created_at":"2017-05-03T11:25:08Z","updated_at":"2018-11-14T01:27:09Z","pushed_at":"2018-11-14T01:27:03Z","git_url":"git://github.com/binast/binjs-ref.git","ssh_url":"git@github.com:binast/binjs-ref.git","clone_url":"https://github.com/binast/binjs-ref.git","svn_url":"https://github.com/binast/binjs-ref","homepage":"https://binast.github.io/binjs-ref/binjs/index.html","size":42337,"stargazers_count":62,"watchers_count":62,"language":"Rust","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":16,"mirror_url":null,"archived":false,"open_issues_count":68,"license":{"key":"other","name":"Other","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":16,"open_issues":68,"watchers":62,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/binast/binjs-ref/pulls/195"},"html":{"href":"https://github.com/binast/binjs-ref/pull/195"},"issue":{"href":"https://api.github.com/repos/binast/binjs-ref/issues/195"},"comments":{"href":"https://api.github.com/repos/binast/binjs-ref/issues/195/comments"},"review_comments":{"href":"https://api.github.com/repos/binast/binjs-ref/pulls/195/comments"},"review_comment":{"href":"https://api.github.com/repos/binast/binjs-ref/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/binast/binjs-ref/pulls/195/commits"},"statuses":{"href":"https://api.github.com/repos/binast/binjs-ref/statuses/0a13b2fa48f038a2ea6115ecd03c5026716bdb68"}},"author_association":"COLLABORATOR"}}
{"actor":{"display_login":"arai-a"}}