package tiny_languages
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
Small languages from scratch: Scheme, Lisp, Smalltalk-80, Pascal, BASIC, JavaScript, HTML, CSS and more
Install
dune-project
Dependency
Authors
Maintainers
Sources
0.3.6.tar.gz
md5=7c636383d146d30ac6f2fa234a6253c8
sha512=c79f3823c5f8f57e5038eb640d487c61168b84aa07c61999d6622ef9fd0c890e2b03b4c6a7cdbbe9352a49e25dda00ac7bb14693cee8e3d7beeed251351a2af0
doc/src/tiny_languages.lang_c/Highlight_c.ml.html
Source file Highlight_c.ml
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 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307(* Claude Code * * Copyright (C) 2026 Yoann Padioleau * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License * (LGPL) as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. *) (* See Highlight_c.mli *) open Highlight_code (*****************************************************************************) (* From the tokens *) (*****************************************************************************) let control_keywords = [ "if"; "else"; "while"; "for"; "do"; "switch"; "case"; "default"; "return"; "break"; "continue"; "goto" ] let type_keywords = [ "void"; "char"; "short"; "int"; "long"; "float"; "double"; "signed"; "unsigned"; "_Bool"; "__signed__" ] (* a banner comment: /*****...*/ or //*****... *) let (t : Token_c.t) : bool = t.kind = Comment && String.length t.text >= 7 && (String.sub t.text 0 7 = "/******" || String.sub t.text 0 7 = "//*****") (* a token's category from its kind alone *) let of_kind (t : Token_c.t) : category = match t.kind with | Comment -> Comment | Keyword -> if List.mem t.text control_keywords then Keyword_control else if List.mem t.text type_keywords then Type else Keyword | Ident -> if Token_c.is_constant t.text then Constructor else Normal | Int | Float -> Number | Char | String -> String | Operator -> Operator | Punctuation -> Punctuation | Directive -> Keyword_module | Error -> Error (*****************************************************************************) (* From the tree *) (*****************************************************************************) (* claude: what the tree says of a name (Parse_c, Ast_c): C's scopes (a * parameter until its function ends, a local from its declaration to its * block's end), the fields, the definitions, the types. A token index to * its category; what is not in it keeps the first guess. *) open Ast_c (* the names in scope: parameters and locals, and their binding's token *) type env = (string * (category * int)) list (* claude: and [binds], a name's token to its binding's (a binding's to * its own), as Highlight_ml's *) let resolve (file : file) : (int, category) Hashtbl.t * (int, int) Hashtbl.t * ((int * Highlight_code.space * int) list * (int * Highlight_code.space) list) = let out = Hashtbl.create 1024 in let binds = Hashtbl.create 256 in let mark (n : name) (c : category) = if n.tok >= 0 then Hashtbl.replace out n.tok c in (* [n] bound as [c], in scope in [env] from now on *) let bound (n : name) (c : category) (env : env) : env = if n.tok >= 0 then Hashtbl.replace binds n.tok n.tok; (n.text, (c, n.tok)) :: env in let use (n : name) (b : int) = if n.tok >= 0 && b >= 0 then Hashtbl.replace binds n.tok b in (* claude: the file's top-level names (plan_codemap_naming.md, level * 2), in C's namespaces: values (functions, globals, enum constants, * macros), typedefs, struct and enum tags. C does not care in which * order, so all of them first; of several of a name, the best: a * definition (a function's body, a global) over a prototype over a * macro, and every declaration of the name bound to it *) let values = Hashtbl.create 64 and types = Hashtbl.create 16 and = Hashtbl.create 16 in let space_of tbl : Highlight_code.space = if tbl == types then Type else if tbl == tags then Tag else Value in let declared = ref [] in (* claude: and for other files (level 3): the file's definitions, with * their rank, and the names it uses that it does not define *) let defs = ref [] and refs = ref [] in let declare tbl (n : name) (rank : int) = if n.tok >= 0 then begin declared := (tbl, n) :: !declared; defs := (n.tok, space_of tbl, rank) :: !defs; match Hashtbl.find_opt tbl n.text with Some (r, _) when r >= rank -> () | _ -> Hashtbl.replace tbl n.text (rank, n.tok) end in let declare_base (t : ty) = match t with | Tstruct (Some tag, Some _) -> declare tags tag 3 | Tenum (tag, Some cs) -> Option.iter (fun n -> declare tags n 3) tag; List.iter (fun (n, _) -> declare values n 3) cs | _ -> () in List.iter (fun d -> declare values d.mname 1) file.defines; (* claude: the structs given a body here, by tag: C's habit of typedef * struct Window Window; before struct Window { ... } -- the typedef * only names the struct, what matters is its body (the author, at * principia's rio: Window's 173 uses counted on its typedef's line) *) let bodies = Hashtbl.create 16 in List.iter (function | Ifunc (sp, _, _, _) | Idecl (sp, _) -> ( match sp.base with Tstruct (Some tag, Some _) -> Hashtbl.replace bodies tag.text tag | _ -> ()) | Imacro _ -> ()) file.items; List.iter (function | Ifunc (sp, d, _, _) -> declare_base sp.base; Option.iter (fun n -> declare values n 3) d.dname | Idecl (sp, ds) -> declare_base sp.base; List.iter (fun d -> Option.iter (fun (n : name) -> match (sp.typedef, sp.base) with (* claude: typedef struct X X; with struct X's body in the * file: the type X is the struct's, its uses the body's *) | true, Tstruct (Some tag, None) when tag.text = n.text && Hashtbl.mem bodies n.text -> let body = Hashtbl.find bodies n.text in declare types body 3; use n body.tok | true, _ -> declare types n 3 | false, _ -> declare values n (match d.dty with Tfunc _ -> 2 | _ -> 3)) d.dname) ds | Imacro _ -> ()) file.items; List.iter (fun (tbl, (n : name)) -> match Hashtbl.find_opt tbl n.text with Some (_, b) -> Hashtbl.replace binds n.tok b | None -> ()) !declared; let refer tbl (n : name) = if n.tok >= 0 then match Hashtbl.find_opt tbl n.text with | Some (_, b) -> Hashtbl.replace binds n.tok b | None -> refs := (n.tok, space_of tbl) :: !refs in (* the file's constants: its enums', its #defines' without parameters *) let constants = Hashtbl.create 64 in List.iter (fun d -> if d.mparams = None then Hashtbl.replace constants d.mname.text ()) file.defines; let rec ty (env : env) (t : ty) = match t with | Tbase -> () | Tname n -> mark n Type; refer types n | Tstruct (tag, fields) -> Option.iter (fun n -> mark n (if fields = None then Type else Def_type)) tag; if fields = None then Option.iter (refer tags) tag; Option.iter (List.iter (decl env Field)) fields | Tenum (tag, cs) -> Option.iter (fun n -> mark n (if cs = None then Type else Def_type)) tag; Option.iter (List.iter (fun (n, v) -> mark n Constructor; Hashtbl.replace constants n.text (); Option.iter (expr env) v)) cs | Tptr t -> ty env t | Tarray (t, e) -> ty env t; Option.iter (expr env) e | Tfunc (t, ps) -> ty env t; List.iter (decl env Parameter) ps | Ttypeof e -> expr env e (* a declared name, as [c]; its type; its initializer *) and decl (env : env) (c : category) (d : decl) = Option.iter (fun n -> mark n c) d.dname; ty env d.dty; Option.iter (init env) d.dinit and init env (i : init) = match i with | Iexpr e -> expr env e | Ilist items -> List.iter (fun (ds, i) -> List.iter (function Dfield n -> mark n Field | Dindex e -> expr env e) ds; init env i) items and expr (env : env) (e : expr) = match e with | Econst -> () | Eident n -> ( match List.assoc_opt n.text env with | Some (c, b) -> mark n c; use n b | None -> mark n (if Hashtbl.mem constants n.text || Token_c.is_constant n.text then Constructor else Normal); refer values n) | Efield (e, n) -> expr env e; mark n Field | Ecall (f, args) -> expr env f; List.iter (expr env) args | Ecast (t, e) -> ty env t; expr env e | Etype t -> ty env t | Ecompound (t, i) -> ty env t; init env i | Estmt ss -> stmts env ss | Emisc es -> List.iter (expr env) es (* a block's statements: a declaration's names in scope after it *) and stmts (env : env) (ss : stmt list) = ignore (List.fold_left stmt env ss) and stmt (env : env) (s : stmt) : env = match s with | Sexpr e -> expr env e; env | Sdecl (sp, ds) -> ty env sp.base; List.fold_left (fun env d -> let c = if sp.typedef then Def_type else Local in (* its initializer sees it: int n = sizeof n *) let env = match d.dname with Some n when not sp.typedef -> bound n c env | _ -> env in decl env c d; env) env ds | Sblock ss -> stmts env ss; env | Sfor (first, es, body) -> let inner = match first with Some s -> stmt env s | None -> env in List.iter (expr inner) es; ignore (stmt inner body); env | Snest (es, ss) -> List.iter (expr env) es; List.iter (fun s -> ignore (stmt env s)) ss; env | Slabel (n, s) -> mark n Label; stmt env s | Sgoto n -> mark n Label; env in (* a function's parameters: its declarator's outermost Tfunc's *) let params_of (t : ty) : decl list = match t with Tfunc (_, ps) -> ps | _ -> [] in let names (ds : decl list) : env = List.fold_right (fun d env -> match d.dname with Some n -> bound n Parameter env | None -> env) ds [] in let item (it : item) = match it with | Ifunc (sp, d, kr, body) -> ty [] sp.base; decl [] Def_function d; List.iter (decl [] Parameter) kr; stmts (names (params_of d.dty) @ names kr) body | Idecl (sp, ds) -> ty [] sp.base; List.iter (fun d -> let c = if sp.typedef then Def_type else match d.dty with Tfunc _ -> Def_function | _ -> Def_value in decl [] c d) ds | Imacro e -> expr [] e in List.iter (fun d -> mark d.mname (if d.mparams = None then Def_value else Def_function); let ps = Option.value d.mparams ~default:[] in let env = List.fold_left (fun env p -> mark p Parameter; bound p Parameter env) [] ps in List.iter (fun (n : name) -> match List.assoc_opt n.text env with | Some (_, b) -> mark n Parameter; use n b | None -> ()) d.mbody) file.defines; List.iter item file.items; (out, binds, (List.rev !defs, List.rev !refs)) (* the kinds' categories, and over them what the tree says of the names; * a banner, and the title between two, a section *) let categorize_bound (toks : Token_c.t list) = let tree, binds, others = resolve (Parse_c.parse toks) in let all = Array.of_list toks in let n = Array.length all in ( Array.to_list (Array.mapi (fun i (t : Token_c.t) -> let c = match Hashtbl.find_opt tree i with | Some c when t.kind = Ident -> c | _ -> if is_banner t || (t.kind = Comment && i > 0 && is_banner all.(i - 1) && i + 1 < n && is_banner all.(i + 1)) then Comment_section else of_kind t in (t, c)) all), binds, others ) let categorize (toks : Token_c.t list) : (Token_c.t * category) list = let cats, _, _ = categorize_bound toks in cats (* claude: the headers of its own a file includes, #include "x.h" (not * <x.h>): their names *) let includes (all : Token_c.t array) : string list = let out = ref [] in Array.iteri (fun i (t : Token_c.t) -> if t.kind = Directive && String.trim (String.sub t.text 1 (String.length t.text - 1)) = "include" && i + 1 < Array.length all then let s = all.(i + 1).text in if all.(i + 1).kind = String && String.length s >= 2 && s.[0] = '"' then out := String.sub s 1 (String.length s - 2) :: !out) all; List.rev !out (* claude: rev_map and rev, and arrays, not List.map (Highlight_ml.lines * says why) *) let analyze (src : string) : analysis = let toks = Lexer_c.tokens src in let cats, binds, (defs, refs) = categorize_bound toks in let all = Array.of_list toks in let places = Array.map (fun (t : Token_c.t) -> (t.line, t.col, t.text)) all in { spans = Highlight_code.lines src (List.rev (List.rev_map (fun ((t : Token_c.t), c) -> (t.line, t.col, t.text, c)) cats)); occurrences = Highlight_code.occurrences places binds; definitions = List.rev (List.rev_map (fun (tok, space, rank) -> Highlight_code.definition places tok space rank) defs); references = List.rev (List.rev_map (fun (tok, space) -> Highlight_code.reference places tok [] space) refs); opens = []; includes = includes all; } let lines (src : string) : span list array = (analyze src).spans
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>