1use std::collections::HashSet;
2use std::path::{Path, PathBuf};
3
4use serde::Serialize;
5
6use crate::checker::Checker;
7use crate::getter::Getter;
8use crate::node::Node;
9use crate::spaces::SpaceKind;
10
11use crate::halstead::{Halstead, HalsteadMaps};
12
13use crate::dump_ops::*;
14use crate::traits::*;
15
16#[derive(Debug, Clone, Serialize)]
18pub struct Ops {
19 pub name: Option<String>,
24 pub start_line: usize,
26 pub end_line: usize,
28 pub kind: SpaceKind,
30 pub spaces: Vec<Ops>,
32 pub operands: Vec<String>,
34 pub operators: Vec<String>,
36}
37
38impl Ops {
39 fn new<T: Getter>(node: &Node, code: &[u8], kind: SpaceKind) -> Self {
40 let (start_position, end_position) = match kind {
41 SpaceKind::Unit => {
42 if node.child_count() == 0 {
43 (0, 0)
44 } else {
45 (node.start_row() + 1, node.end_row())
46 }
47 }
48 _ => (node.start_row() + 1, node.end_row() + 1),
49 };
50 Self {
51 name: T::get_func_space_name(node, code).map(|name| name.to_string()),
52 spaces: Vec::new(),
53 kind,
54 start_line: start_position,
55 end_line: end_position,
56 operators: Vec::new(),
57 operands: Vec::new(),
58 }
59 }
60
61 pub(crate) fn merge_ops(&mut self, other: &Ops) {
62 self.operands.extend_from_slice(&other.operands);
63 self.operators.extend_from_slice(&other.operators);
64 }
65}
66
67#[derive(Debug, Clone)]
68struct State<'a> {
69 ops: Ops,
70 halstead_maps: HalsteadMaps<'a>,
71 primitive_types: HashSet<String>,
72}
73
74fn compute_operators_and_operands<T: ParserTrait>(state: &mut State) {
75 state.ops.operators = state
76 .halstead_maps
77 .operators
78 .keys()
79 .filter(|k| !T::Checker::is_primitive(**k))
80 .map(|k| T::Getter::get_operator_id_as_str(*k).to_owned())
81 .collect();
82
83 let v: Vec<_> = state.primitive_types.iter().cloned().collect();
85 state.ops.operators.extend_from_slice(&v);
86 println!("{:?}", state.ops.operators);
87 println!("{:?}", state.halstead_maps.operators);
88
89 state.ops.operands = state
90 .halstead_maps
91 .operands
92 .keys()
93 .map(|k| String::from_utf8(k.to_vec()).unwrap_or_else(|_| String::from("wrong_operands")))
94 .collect();
95}
96
97fn finalize<T: ParserTrait>(state_stack: &mut Vec<State>, diff_level: usize) {
98 if state_stack.is_empty() {
99 return;
100 }
101
102 if state_stack.len() == 1 {
104 let last_state = state_stack.last_mut().unwrap();
105 compute_operators_and_operands::<T>(last_state);
107 }
108
109 for _ in 0..diff_level {
110 if state_stack.len() == 1 {
111 break;
112 } else {
113 let mut state = state_stack.pop().unwrap();
114 let last_state = state_stack.last_mut().unwrap();
115
116 compute_operators_and_operands::<T>(&mut state);
118
119 compute_operators_and_operands::<T>(last_state);
121
122 last_state.halstead_maps.merge(&state.halstead_maps);
124
125 last_state.ops.merge_ops(&state.ops);
127 last_state.ops.spaces.push(state.ops);
128 }
129 }
130}
131
132pub fn operands_and_operators<'a, T: ParserTrait>(parser: &'a T, path: &'a Path) -> Option<Ops> {
159 let code = parser.get_code();
160 let node = parser.get_root();
161 let mut cursor = node.cursor();
162 let mut stack = Vec::new();
163 let mut children = Vec::new();
164 let mut state_stack: Vec<State> = Vec::new();
165 let mut last_level = 0;
166
167 stack.push((node, 0));
168
169 while let Some((node, level)) = stack.pop() {
170 if level < last_level {
171 finalize::<T>(&mut state_stack, last_level - level);
172 last_level = level;
173 }
174
175 let kind = T::Getter::get_space_kind(&node);
176
177 let func_space = T::Checker::is_func(&node) || T::Checker::is_func_space(&node);
178
179 let new_level = if func_space {
180 let state = State {
181 ops: Ops::new::<T::Getter>(&node, code, kind),
182 halstead_maps: HalsteadMaps::new(),
183 primitive_types: HashSet::new(),
184 };
185 state_stack.push(state);
186 last_level = level + 1;
187 last_level
188 } else {
189 level
190 };
191
192 if let Some(state) = state_stack.last_mut() {
193 T::Halstead::compute(&node, code, &mut state.halstead_maps);
194 if T::Checker::is_primitive(node.kind_id()) {
195 let code = &code[node.start_byte()..node.end_byte()];
196 let primitive_string = String::from_utf8(code.to_vec())
197 .unwrap_or_else(|_| String::from("primitive_type"));
198 state.primitive_types.insert(primitive_string);
199 }
200 }
201
202 cursor.reset(&node);
203 if cursor.goto_first_child() {
204 loop {
205 children.push((cursor.node(), new_level));
206 if !cursor.goto_next_sibling() {
207 break;
208 }
209 }
210 for child in children.drain(..).rev() {
211 stack.push(child);
212 }
213 }
214 }
215
216 finalize::<T>(&mut state_stack, usize::MAX);
217
218 state_stack.pop().map(|mut state| {
219 state.ops.name = path.to_str().map(|name| name.to_string());
220 state.ops
221 })
222}
223
224#[derive(Debug)]
227pub struct OpsCfg {
228 pub path: PathBuf,
230}
231
232pub struct OpsCode {
233 _guard: (),
234}
235
236impl Callback for OpsCode {
237 type Res = std::io::Result<()>;
238 type Cfg = OpsCfg;
239
240 fn call<T: ParserTrait>(cfg: Self::Cfg, parser: &T) -> Self::Res {
241 if let Some(ops) = operands_and_operators(parser, &cfg.path) {
242 dump_ops(&ops)
243 } else {
244 Ok(())
245 }
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use std::path::PathBuf;
252
253 use crate::{LANG, get_ops};
254
255 #[inline(always)]
256 fn check_ops(
257 lang: LANG,
258 source: &str,
259 file: &str,
260 correct_operators: &mut [&str],
261 correct_operands: &mut [&str],
262 ) {
263 let path = PathBuf::from(file);
264 let mut trimmed_bytes = source.trim_end().trim_matches('\n').as_bytes().to_vec();
265 trimmed_bytes.push(b'\n');
266 let ops = get_ops(&lang, trimmed_bytes, &path, None).unwrap();
267
268 let mut operators_str: Vec<&str> = ops.operators.iter().map(AsRef::as_ref).collect();
269 let mut operands_str: Vec<&str> = ops.operands.iter().map(AsRef::as_ref).collect();
270
271 operators_str.sort_unstable();
273 correct_operators.sort_unstable();
274
275 assert_eq!(&operators_str[..], correct_operators);
276
277 operands_str.sort_unstable();
279 correct_operands.sort_unstable();
280
281 assert_eq!(&operands_str[..], correct_operands);
282 }
283
284 #[test]
285 fn python_ops() {
286 check_ops(
287 LANG::Python,
288 "if True:
289 a = 1 + 2",
290 "foo.py",
291 &mut ["if", "=", "+"],
292 &mut ["True", "a", "1", "2"],
293 );
294 }
295
296 #[test]
297 fn python_function_ops() {
298 check_ops(
299 LANG::Python,
300 "def foo():
301 def bar():
302 def toto():
303 a = 1 + 1
304 b = 2 + a
305 c = 3 + 3",
306 "foo.py",
307 &mut ["def", "=", "+"],
308 &mut ["foo", "bar", "toto", "a", "b", "c", "1", "2", "3"],
309 );
310 }
311
312 #[test]
313 fn cpp_ops() {
314 check_ops(
315 LANG::Cpp,
316 "int a, b, c;
317 float avg;
318 avg = (a + b + c) / 3;",
319 "foo.c",
320 &mut ["int", "float", "()", "=", "+", "/", ",", ";"],
321 &mut ["a", "b", "c", "avg", "3"],
322 );
323 }
324
325 #[test]
326 fn cpp_function_ops() {
327 check_ops(
328 LANG::Cpp,
329 "main()
330 {
331 int a, b, c, avg;
332 scanf(\"%d %d %d\", &a, &b, &c);
333 avg = (a + b + c) / 3;
334 printf(\"avg = %d\", avg);
335 }",
336 "foo.c",
337 &mut ["()", "{}", "int", "&", "=", "+", "/", ",", ";"],
338 &mut [
339 "main",
340 "a",
341 "b",
342 "c",
343 "avg",
344 "scanf",
345 "\"%d %d %d\"",
346 "3",
347 "printf",
348 "\"avg = %d\"",
349 ],
350 );
351 }
352
353 #[test]
354 fn rust_ops() {
355 check_ops(
356 LANG::Rust,
357 "let: usize a = 5; let b: f32 = 7.0; let c: i32 = 3;",
358 "foo.rs",
359 &mut ["let", "usize", "=", ";", "f32", "i32"],
360 &mut ["a", "b", "c", "5", "7.0", "3"],
361 );
362 }
363
364 #[test]
365 fn rust_function_ops() {
366 check_ops(
367 LANG::Rust,
368 "fn main() {
369 let a = 5; let b = 5; let c = 5;
370 let avg = (a + b + c) / 3;
371 println!(\"{}\", avg);
372 }",
373 "foo.rs",
374 &mut ["fn", "()", "{}", "let", "=", "+", "/", ";", "!", ","],
375 &mut ["main", "a", "b", "c", "avg", "5", "3", "println", "\"{}\""],
376 );
377 }
378
379 #[test]
380 fn javascript_ops() {
381 check_ops(
382 LANG::Javascript,
383 "var a, b, c, avg;
384 let x = 1;
385 a = 5; b = 5; c = 5;
386 avg = (a + b + c) / 3;
387 console.log(\"{}\", avg);",
388 "foo.js",
389 &mut ["()", "var", "let", "=", "+", "/", ",", ".", ";"],
390 &mut [
391 "a",
392 "b",
393 "c",
394 "avg",
395 "x",
396 "1",
397 "3",
398 "5",
399 "console.log",
400 "console",
401 "log",
402 "\"{}\"",
403 ],
404 );
405 }
406
407 #[test]
408 fn javascript_function_ops() {
409 check_ops(
410 LANG::Javascript,
411 "function main() {
412 var a, b, c, avg;
413 let x = 1;
414 a = 5; b = 5; c = 5;
415 avg = (a + b + c) / 3;
416 console.log(\"{}\", avg);
417 }",
418 "foo.js",
419 &mut [
420 "function", "()", "{}", "var", "let", "=", "+", "/", ",", ".", ";",
421 ],
422 &mut [
423 "main",
424 "a",
425 "b",
426 "c",
427 "avg",
428 "x",
429 "1",
430 "3",
431 "5",
432 "console.log",
433 "console",
434 "log",
435 "\"{}\"",
436 ],
437 );
438 }
439
440 #[test]
441 fn typescript_ops() {
442 check_ops(
443 LANG::Typescript,
444 "var a, b, c, avg;
445 let age: number = 32;
446 let name: string = \"John\"; let isUpdated: boolean = true;
447 a = 5; b = 5; c = 5;
448 avg = (a + b + c) / 3;
449 console.log(\"{}\", avg);",
450 "foo.ts",
451 &mut [
452 "()", "var", "let", "string", "number", "boolean", ":", "=", "+", "/", ",", ".",
453 ";",
454 ],
455 &mut [
456 "a",
457 "b",
458 "c",
459 "avg",
460 "age",
461 "name",
462 "isUpdated",
463 "32",
464 "\"John\"",
465 "true",
466 "3",
467 "5",
468 "console.log",
469 "console",
470 "log",
471 "\"{}\"",
472 ],
473 );
474 }
475
476 #[test]
477 fn typescript_function_ops() {
478 check_ops(
479 LANG::Typescript,
480 "function main() {
481 var a, b, c, avg;
482 let age: number = 32;
483 let name: string = \"John\"; let isUpdated: boolean = true;
484 a = 5; b = 5; c = 5;
485 avg = (a + b + c) / 3;
486 console.log(\"{}\", avg);
487 }",
488 "foo.ts",
489 &mut [
490 "function", "()", "{}", "var", "let", "string", "number", "boolean", ":", "=", "+",
491 "/", ",", ".", ";",
492 ],
493 &mut [
494 "main",
495 "a",
496 "b",
497 "c",
498 "avg",
499 "age",
500 "name",
501 "isUpdated",
502 "32",
503 "\"John\"",
504 "true",
505 "3",
506 "5",
507 "console.log",
508 "console",
509 "log",
510 "\"{}\"",
511 ],
512 );
513 }
514
515 #[test]
516 fn tsx_ops() {
517 check_ops(
518 LANG::Tsx,
519 "var a, b, c, avg;
520 let age: number = 32;
521 let name: string = \"John\"; let isUpdated: boolean = true;
522 a = 5; b = 5; c = 5;
523 avg = (a + b + c) / 3;
524 console.log(\"{}\", avg);",
525 "foo.ts",
526 &mut [
527 "()", "var", "let", "string", "number", "boolean", ":", "=", "+", "/", ",", ".",
528 ";",
529 ],
530 &mut [
531 "a",
532 "b",
533 "c",
534 "avg",
535 "age",
536 "name",
537 "isUpdated",
538 "32",
539 "\"John\"",
540 "true",
541 "3",
542 "5",
543 "console.log",
544 "console",
545 "log",
546 "\"{}\"",
547 ],
548 );
549 }
550
551 #[test]
552 fn tsx_function_ops() {
553 check_ops(
554 LANG::Tsx,
555 "function main() {
556 var a, b, c, avg;
557 let age: number = 32;
558 let name: string = \"John\"; let isUpdated: boolean = true;
559 a = 5; b = 5; c = 5;
560 avg = (a + b + c) / 3;
561 console.log(\"{}\", avg);
562 }",
563 "foo.ts",
564 &mut [
565 "function", "()", "{}", "var", "let", "string", "number", "boolean", ":", "=", "+",
566 "/", ",", ".", ";",
567 ],
568 &mut [
569 "main",
570 "a",
571 "b",
572 "c",
573 "avg",
574 "age",
575 "name",
576 "isUpdated",
577 "32",
578 "\"John\"",
579 "true",
580 "3",
581 "5",
582 "console.log",
583 "console",
584 "log",
585 "\"{}\"",
586 ],
587 );
588 }
589
590 #[test]
591 fn java_ops() {
592 check_ops(
593 LANG::Java,
594 "public class Main {
595 public static void main(string args[]) {
596 int a, b, c, avg;
597 a = 5; b = 5; c = 5;
598 avg = (a + b + c) / 3;
599 MessageFormat.format(\"{0}\", avg);
600 }
601 }",
602 "foo.java",
603 &mut ["{}", "void", "()", "[]", ",", ";", "int", "=", "+", "/"],
604 &mut [
605 "Main",
606 "main",
607 "args",
608 "a",
609 "b",
610 "c",
611 "avg",
612 "5",
613 "3",
614 "MessageFormat",
615 "format",
616 "\"{0}\"",
617 ],
618 );
619 }
620}