Skip to content

Commit 1cc9621

Browse files
committed
support hashtable in bytecode
1 parent 9c43320 commit 1cc9621

File tree

4 files changed

+103
-32
lines changed

4 files changed

+103
-32
lines changed

src/app.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ fn process_server_reader(reader: impl std::io::Read,
7171
break
7272
}
7373
let json_val = json::from_str(&msg)?;
74-
match bytecode::generate_bytecode_repl(&json_val) {
74+
match bytecode::generate_bytecode_repl(&json_val, bytecode::BytecodeOptions::default()) {
7575
Ok(bytecode_str) => {
7676
channel_pub.send(bytecode_str)?;
7777
},

src/bytecode.rs

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use smallvec::smallvec;
66

77

88
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
9-
enum LispObject {
9+
pub enum LispObject {
1010
Symbol(String),
1111
Keyword(String),
1212
Str(String),
@@ -139,9 +139,34 @@ impl Op {
139139
}
140140
}
141141

142+
#[derive(Clone, Copy)]
143+
pub enum ObjectType {
144+
Plist,
145+
Hashtable,
146+
// TODO: Alist,
147+
}
148+
149+
pub struct BytecodeOptions {
150+
pub object_type: ObjectType,
151+
// TODO: array_type
152+
pub null_object: LispObject,
153+
pub false_object: LispObject,
154+
}
155+
156+
impl Default for BytecodeOptions {
157+
fn default() -> Self {
158+
Self {
159+
object_type: ObjectType::Plist,
160+
null_object: LispObject::Nil,
161+
false_object: LispObject::Nil,
162+
}
163+
}
164+
}
142165

143166
// Only for generating json. Sequential execution only.
144167
struct BytecodeCompiler {
168+
options: BytecodeOptions,
169+
145170
ops: Vec<Op>,
146171
constants: BTreeMap<LispObject, (u32, u32)>, // (index, count)
147172
}
@@ -191,7 +216,7 @@ impl BytecodeCompiler {
191216
}
192217
}
193218

194-
fn compile_value_map(&mut self, map: &json::Map<String, json::Value>) {
219+
fn compile_value_map_plist(&mut self, map: &json::Map<String, json::Value>) {
195220
let list_len = map.len() * 2;
196221
// see below
197222
if list_len < (1 << 16) && list_len >= (1 << 8) {
@@ -218,12 +243,31 @@ impl BytecodeCompiler {
218243
}
219244
}
220245

221-
// current only support:
222-
// object-type: plist, null-object: nil, false-object: nil, array-type: vector
246+
fn compile_value_map_hashtable(&mut self, map: &json::Map<String, json::Value>) {
247+
self.compile_constant_op(LispObject::Symbol("make-hash-table".into()));
248+
self.compile_constant_op(LispObject::Keyword("test".into()));
249+
self.compile_constant_op(LispObject::Symbol("equal".into()));
250+
self.compile_constant_op(LispObject::Keyword("size".into()));
251+
self.compile_constant_op(LispObject::Int(map.len() as i64));
252+
self.ops.push(Op::Call(4));
253+
254+
for (key, value) in map {
255+
self.compile_constant_op(LispObject::Symbol("puthash".into()));
256+
self.compile_constant_op(LispObject::Str(key.clone()));
257+
self.compile_value(value);
258+
self.ops.push(Op::StackRef(3));
259+
self.ops.push(Op::Call(3));
260+
self.ops.push(Op::Discard);
261+
}
262+
}
263+
223264
fn compile_value(&mut self, value: &json::Value) {
224265
match value {
225-
&json::Value::Null | &json::Value::Bool(false) => {
226-
self.compile_constant_op(LispObject::Nil);
266+
&json::Value::Null => {
267+
self.compile_constant_op(self.options.null_object.clone());
268+
},
269+
&json::Value::Bool(false) => {
270+
self.compile_constant_op(self.options.false_object.clone());
227271
},
228272
&json::Value::Bool(true) => {
229273
self.compile_constant_op(LispObject::T);
@@ -242,7 +286,10 @@ impl BytecodeCompiler {
242286
self.compile_value_array(&arr);
243287
},
244288
&json::Value::Object(ref map) => {
245-
self.compile_value_map(&map);
289+
match self.options.object_type {
290+
ObjectType::Plist => self.compile_value_map_plist(&map),
291+
ObjectType::Hashtable => self.compile_value_map_hashtable(&map),
292+
};
246293
},
247294
}
248295
}
@@ -319,8 +366,9 @@ impl BytecodeCompiler {
319366
}
320367
}
321368

322-
pub fn generate_bytecode_repl(value: &json::Value) -> Result<String> {
369+
pub fn generate_bytecode_repl(value: &json::Value, options: BytecodeOptions) -> Result<String> {
323370
let mut compiler = BytecodeCompiler {
371+
options,
324372
ops: Vec::new(),
325373
constants: BTreeMap::new(),
326374
};

tests/benchmark_and_compare.template.el

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,21 @@
1919
(setq p (cddr p)))
2020
(sort res (lambda (a b) (string< (symbol-name (car a)) (symbol-name (car b)))))))
2121

22+
(defun -hashtable-to-sorted-alist (h)
23+
(let (res)
24+
(maphash (lambda (k v) (push (cons k v) res)) h)
25+
(sort res (lambda (a b) (string< (car a) (car b))))))
26+
27+
(defun -check-equal-sorted-alist (a-alist b-alist)
28+
(let ((res t))
29+
(while a-alist
30+
(unless (and (equal (caar a-alist) (caar b-alist))
31+
(json-equal (cdar a-alist) (cdar b-alist)))
32+
(setq res nil))
33+
(setq a-alist (cdr a-alist)
34+
b-alist (cdr b-alist)))
35+
res))
36+
2237
(defun json-equal (a b)
2338
(pcase a
2439
((or
@@ -36,16 +51,13 @@
3651
((pred plistp)
3752
(and (plistp b)
3853
(equal (length a) (length b))
39-
(let ((a-alist (-plist-to-sorted-alist a))
40-
(b-alist (-plist-to-sorted-alist b))
41-
(res t))
42-
(while a-alist
43-
(unless (and (equal (caar a-alist) (caar b-alist))
44-
(json-equal (cdar a-alist) (cdar b-alist)))
45-
(setq res nil))
46-
(setq a-alist (cdr a-alist)
47-
b-alist (cdr b-alist)))
48-
res)))
54+
(-check-equal-sorted-alist (-plist-to-sorted-alist a)
55+
(-plist-to-sorted-alist b))))
56+
((pred hash-table-p)
57+
(and (hash-table-p b)
58+
(equal (hash-table-count a) (hash-table-count b))
59+
(-check-equal-sorted-alist (-hashtable-to-sorted-alist a)
60+
(-hashtable-to-sorted-alist b))))
4961
(_ nil)))
5062

5163
(let ((json-str (with-temp-buffer
@@ -54,15 +66,17 @@
5466
(bytecode-str (with-temp-buffer
5567
(insert-file-contents "{}")
5668
(buffer-string)))
69+
(object-type (if (equal "{}" "plist") 'plist 'hash-table))
5770
json-val bytecode-val)
71+
(message "Object-type: %s" object-type)
5872
(unless (json-equal (setq json-val
59-
(json-parse-string json-str :object-type 'plist :null-object nil :false-object nil))
73+
(json-parse-string json-str :object-type object-type :null-object nil :false-object nil))
6074
(setq bytecode-val
6175
(funcall (read bytecode-str))))
6276
(error "NOT EQUAL!"))
6377
(message "Benchmark json-parse-string 100 times: %s"
6478
(benchmark-run 100
65-
(json-parse-string json-str :object-type 'plist :null-object nil :false-object nil)))
79+
(json-parse-string json-str :object-type object-type :null-object nil :false-object nil)))
6680
(message "Benchmark read-bytecode 100 times: %s"
6781
(benchmark-run 100
6882
(read bytecode-str)))
@@ -73,5 +87,5 @@
7387
(message "Benchmark read-lisp-data 100 times: %s"
7488
(benchmark-run 100
7589
(read lisp-str))))
76-
90+
7791
(message "PASS!"))

tests/bytecode_test.rs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@ use tempfile;
55
use emacs_lsp_booster::bytecode;
66

77

8-
fn run_one_test(json_str: &str) -> Result<()> {
8+
fn run_one_test(json_str: &str, object_type: bytecode::ObjectType) -> Result<()> {
99
let json_value: json::Value = json::from_str(json_str)?;
1010
let json_str_nowhitespaces = json_value.to_string();
11-
let bytecode = bytecode::generate_bytecode_repl(&json_value)?;
11+
let bytecode = bytecode::generate_bytecode_repl(&json_value, bytecode::BytecodeOptions {
12+
object_type: object_type.clone(),
13+
..Default::default()
14+
})?;
1215

1316
eprintln!("Json: {} bytes, Bytecode: {} bytes, ratio={}",
1417
json_str_nowhitespaces.len(), bytecode.len(),
@@ -23,7 +26,12 @@ fn run_one_test(json_str: &str) -> Result<()> {
2326

2427
let elisp_file = tmpdir.path().join("script.el");
2528
let elisp_code = format!(include_str!("./benchmark_and_compare.template.el"),
26-
json_file.display(), bytecode_file.display());
29+
json_file.display(),
30+
bytecode_file.display(),
31+
match object_type {
32+
bytecode::ObjectType::Plist => "plist",
33+
bytecode::ObjectType::Hashtable => "hashtable",
34+
});
2735
std::fs::write(&elisp_file, elisp_code.as_bytes())?;
2836

2937
let mut child = std::process::Command::new("emacs")
@@ -44,7 +52,7 @@ fn test_huge_array() {
4452
(0..100000).map(|x| json::Value::String(format!("{}", x)))
4553
.collect()
4654
);
47-
run_one_test(&value.to_string()).unwrap();
55+
run_one_test(&value.to_string(), bytecode::ObjectType::Plist).unwrap();
4856
}
4957

5058
#[test]
@@ -54,30 +62,31 @@ fn test_huge_object() {
5462
json::Value::Number(x.into())))
5563
.collect()
5664
);
57-
run_one_test(&value.to_string()).unwrap();
65+
run_one_test(&value.to_string(), bytecode::ObjectType::Plist).unwrap();
5866
}
5967

6068
#[test]
6169
fn test_completion_100k() {
62-
run_one_test(include_str!("./data/completion.json")).unwrap();
70+
run_one_test(include_str!("./data/completion.json"), bytecode::ObjectType::Plist).unwrap();
71+
run_one_test(include_str!("./data/completion.json"), bytecode::ObjectType::Hashtable).unwrap();
6372
}
6473

6574
#[test]
6675
fn test_completion_100k_2() {
67-
run_one_test(include_str!("./data/completion2.json")).unwrap();
76+
run_one_test(include_str!("./data/completion2.json"), bytecode::ObjectType::Plist).unwrap();
6877
}
6978

7079
#[test]
7180
fn test_completion_4k() {
72-
run_one_test(include_str!("./data/completion3.json")).unwrap();
81+
run_one_test(include_str!("./data/completion3.json"), bytecode::ObjectType::Plist).unwrap();
7382
}
7483

7584
#[test]
7685
fn test_diagnostics_12k() {
77-
run_one_test(include_str!("./data/publishDiagnostics.json")).unwrap();
86+
run_one_test(include_str!("./data/publishDiagnostics.json"), bytecode::ObjectType::Plist).unwrap();
7887
}
7988

8089
#[test]
8190
fn test_diagnostics_12k_2() {
82-
run_one_test(include_str!("./data/publishDiagnostics2.json")).unwrap();
91+
run_one_test(include_str!("./data/publishDiagnostics2.json"), bytecode::ObjectType::Plist).unwrap();
8392
}

0 commit comments

Comments
 (0)