about summary refs log tree commit diff
path: root/users/Profpatsch/tree-sitter.nix
blob: 099fa2d5b7b9d233f9233c2b185d89d4c0d766f5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
{ depot, pkgs, lib, ... }:

let
  bins = depot.nix.getBins pkgs.coreutils [ "head" "printf" "cat" ]
      // depot.nix.getBins pkgs.ncurses [ "tput" ]
      // depot.nix.getBins pkgs.bc [ "bc" ]
      // depot.nix.getBins pkgs.ocamlPackages.sexp [ "sexp" ];

  print-ast = depot.users.Profpatsch.writers.rustSimple {
    name = "print-ast";
    dependencies = with depot.users.Profpatsch.rust-crates; [
      libloading
      tree-sitter
    ];
  } ''
    extern crate libloading;
    extern crate tree_sitter;
    use std::mem;
    use std::io::{Read};
    use libloading::{Library, Symbol};
    use tree_sitter::{Language, Parser};

    /// Load the shared lib FILE and return the language under SYMBOL-NAME.
    /// Inspired by the rust source of emacs-tree-sitter.
    fn _load_language(file: String, symbol_name: String) -> Result<Language, libloading::Error> {
        let lib = Library::new(file)?;
        let tree_sitter_lang: Symbol<'_, unsafe extern "C" fn() -> _> =
            unsafe { lib.get(symbol_name.as_bytes())? };
        let language: Language = unsafe { tree_sitter_lang() };
        // Avoid segmentation fault by not unloading the lib, as language is a static piece of data.
        // TODO: Attach an Rc<Library> to Language instead.
        mem::forget(lib);
        Ok(language)
    }

    fn main() {
      let mut args = std::env::args();
      let so = args.nth(1).unwrap();
      let symbol_name = args.nth(0).unwrap();
      let file = args.nth(0).unwrap();
      let mut parser = Parser::new();
      let lang = _load_language(so, symbol_name).unwrap();
      parser.set_language(lang).unwrap();
      let bytes = std::fs::read(&file).unwrap();
      print!("{}", parser.parse(&bytes, None).unwrap().root_node().to_sexp());
    }


  '';

  tree-sitter-nix = buildTreeSitterGrammar {
    language = "tree-sitter-nix";
    source = pkgs.fetchFromGitHub {
      owner = "cstrahan";
      repo = "tree-sitter-nix";
      rev = "791b5ff0e4f0da358cbb941788b78d436a2ca621";
      sha256 = "1y5b3wh3fcmbgq8r2i97likzfp1zp02m58zacw5a1cjqs5raqz66";
    };
  };

  watch-file-modified = depot.users.Profpatsch.writers.rustSimple {
    name = "watch-file-modified";
    dependencies = [
      depot.users.Profpatsch.rust-crates.inotify
      depot.users.Profpatsch.netstring.rust-netstring
    ];
  } ''
    extern crate inotify;
    extern crate netstring;
    use inotify::{EventMask, WatchMask, Inotify};
    use std::io::Write;

    fn main() {
        let mut inotify = Inotify::init()
            .expect("Failed to initialize inotify");

        let file = std::env::args().nth(1).unwrap();

        let file_watch = inotify
            .add_watch(
                &file,
                WatchMask::MODIFY
            )
            .expect("Failed to add inotify watch");

        let mut buffer = [0u8; 4096];
        loop {
            let events = inotify
                .read_events_blocking(&mut buffer)
                .expect("Failed to read inotify events");

            for event in events {
                if event.wd == file_watch {
                  std::io::stdout().write(&netstring::to_netstring(file.as_bytes()));
                  std::io::stdout().flush();
                }
            }
        }
    }

  '';

  # clear screen and set LINES and COLUMNS to terminal height & width
  clear-screen = depot.nix.writeExecline "clear-screen" {} [
    "if" [ bins.tput "clear" ]
    "backtick" "-in" "LINES" [ bins.tput "lines" ]
    "backtick" "-in" "COLUMNS" [ bins.tput "cols" ]
    "$@"
  ];

  print-nix-file = depot.nix.writeExecline "print-nix-file" { readNArgs = 1; } [
    "pipeline" [ print-ast "${tree-sitter-nix}/parser" "tree_sitter_nix" "$1" ]
    "pipeline" [ bins.sexp "print" ]
    clear-screen
    "importas" "-ui" "lines" "LINES"
    "backtick" "-in" "ls" [
      "pipeline"
        # when you pull out bc to decrement an integer it’s time to switch to python lol
        [ bins.printf "x=%s; --x\n" "$lines" ]
        bins.bc
    ]
    "importas" "-ui" "l" "ls"
    bins.head "-n\${l}"
  ];

  print-nix-file-on-update = depot.nix.writeExecline "print-nix-file-on-update" { readNArgs = 1; } [
    "if" [ print-nix-file "$1" ]
    "pipeline" [ watch-file-modified "$1" ]
    "forstdin" "-d" "" "file"
    "importas" "file" "file"
    print-nix-file "$file"
  ];

  # copied from nixpkgs
  buildTreeSitterGrammar =
      {
        # language name
        language
        # source for the language grammar
      , source
      }:

      pkgs.stdenv.mkDerivation {

        pname = "${language}-grammar";
        inherit (pkgs.tree-sitter) version;

        src = source;

        buildInputs = [ pkgs.tree-sitter ];

        dontUnpack = true;
        configurePhase= ":";
        buildPhase = ''
          runHook preBuild
          scanner_cc="$src/src/scanner.cc"
          if [ ! -f "$scanner_cc" ]; then
            scanner_cc=""
          fi
          $CXX -I$src/src/ -c $scanner_cc
          $CC -I$src/src/ -shared -o parser -Os  scanner.o $src/src/parser.c -lstdc++
          runHook postBuild
        '';
        installPhase = ''
          runHook preInstall
          mkdir $out
          mv parser $out/
          runHook postInstall
        '';
      };

in {
  inherit
    print-ast
    tree-sitter-nix
    print-nix-file-on-update
    watch-file-modified
    ;
}