symbolic mathematics engine in OCaml with differentiation, integration, simplification, and numerical methods
17

Configure Feed

Select the types of activity you want to include in your feed.

leibniz / lib / lexer.ml
4.0 kB 112 lines
1type position = { line : int; col : int; offset : int } 2 3type token = 4 | Num of float 5 | Var of string 6 | Ident of string 7 | Plus | Minus | Star | Slash | Caret 8 | LParen | RParen 9 | Comma 10 | EOF 11 12type token_with_pos = { token : token; start_pos : position; end_pos : position } 13 14let is_digit c = c >= '0' && c <= '9' 15let is_alpha c = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') 16let is_alphanum c = is_alpha c || is_digit c || c = '_' 17 18let needs_implicit_mult tok1 tok2 = 19 match (tok1, tok2) with 20 | (Num _ | Var _ | RParen), (Num _ | Var _ | LParen | Ident _) -> true 21 | _ -> false 22 23let tokenize str = 24 let len = String.length str in 25 let line = ref 1 in 26 let col = ref 1 in 27 28 let rec aux i acc = 29 if i >= len then 30 let pos = { line = !line; col = !col; offset = i } in 31 List.rev ({ token = EOF; start_pos = pos; end_pos = pos } :: acc) 32 else 33 let start_pos = { line = !line; col = !col; offset = i } in 34 match str.[i] with 35 | ' ' | '\t' -> 36 col := !col + 1; 37 aux (i + 1) acc 38 | '\n' -> 39 line := !line + 1; 40 col := 1; 41 aux (i + 1) acc 42 | '+' -> 43 col := !col + 1; 44 let end_pos = { line = !line; col = !col; offset = i + 1 } in 45 aux (i + 1) ({ token = Plus; start_pos; end_pos } :: acc) 46 | '-' -> 47 col := !col + 1; 48 let end_pos = { line = !line; col = !col; offset = i + 1 } in 49 aux (i + 1) ({ token = Minus; start_pos; end_pos } :: acc) 50 | '*' -> 51 col := !col + 1; 52 let end_pos = { line = !line; col = !col; offset = i + 1 } in 53 aux (i + 1) ({ token = Star; start_pos; end_pos } :: acc) 54 | '/' -> 55 col := !col + 1; 56 let end_pos = { line = !line; col = !col; offset = i + 1 } in 57 aux (i + 1) ({ token = Slash; start_pos; end_pos } :: acc) 58 | '^' -> 59 col := !col + 1; 60 let end_pos = { line = !line; col = !col; offset = i + 1 } in 61 aux (i + 1) ({ token = Caret; start_pos; end_pos } :: acc) 62 | '(' -> 63 col := !col + 1; 64 let end_pos = { line = !line; col = !col; offset = i + 1 } in 65 aux (i + 1) ({ token = LParen; start_pos; end_pos } :: acc) 66 | ')' -> 67 col := !col + 1; 68 let end_pos = { line = !line; col = !col; offset = i + 1 } in 69 aux (i + 1) ({ token = RParen; start_pos; end_pos } :: acc) 70 | ',' -> 71 col := !col + 1; 72 let end_pos = { line = !line; col = !col; offset = i + 1 } in 73 aux (i + 1) ({ token = Comma; start_pos; end_pos } :: acc) 74 | c when is_digit c || c = '.' -> 75 let j = ref (i + 1) in 76 while !j < len && (is_digit str.[!j] || str.[!j] = '.') do 77 incr j 78 done; 79 let num_str = String.sub str i (!j - i) in 80 col := !col + (!j - i); 81 let end_pos = { line = !line; col = !col; offset = !j } in 82 aux !j ({ token = Num (float_of_string num_str); start_pos; end_pos } :: acc) 83 | c when is_alpha c -> 84 let j = ref (i + 1) in 85 while !j < len && is_alphanum str.[!j] do 86 incr j 87 done; 88 let id = String.sub str i (!j - i) in 89 col := !col + (!j - i); 90 let end_pos = { line = !line; col = !col; offset = !j } in 91 let tok = if !j < len && str.[!j] = '(' then Ident id else Var id in 92 aux !j ({ token = tok; start_pos; end_pos } :: acc) 93 | c -> 94 failwith (Printf.sprintf "unexpected character: %c at line %d, column %d" 95 c !line !col) 96 in 97 98 let tokens = aux 0 [] in 99 100 let rec insert_implicit_mult = function 101 | [] -> [] 102 | [t] -> [t] 103 | t1 :: t2 :: rest -> 104 if needs_implicit_mult t1.token t2.token then 105 let mult_pos = t2.start_pos in 106 t1 :: { token = Star; start_pos = mult_pos; end_pos = mult_pos } :: 107 insert_implicit_mult (t2 :: rest) 108 else 109 t1 :: insert_implicit_mult (t2 :: rest) 110 in 111 112 insert_implicit_mult tokens