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
open Ast

(*
--# Generate a sequence of integers
--#
--# Creates a list of integers from start to end, optionally with a step size.
--#
--# @name seq
--# @param start :: Int (Optional) Starting value. Defaults to 1.
--# @param end :: Int (Optional) Ending value. Defaults to start if by is provided.
--# @param by :: Int (Optional) Step size. Defaults to 1 or -1.
--# @return :: List[Int] List of integers.
--# @example
--#   seq(5)
--#   seq(1, 5)
--#   seq(start = 1, end = 10, by = 2)
--#   -- Returns = [1, 3, 5, 7, 9]
--# @family core
--# @export
*)
let register env =
  Env.add "seq"
    (Ast.make_builtin_named ~name:"seq" ~variadic:true 0 (fun named_args _env ->
      let get_named k = List.find_map (fun (nk, v) -> if nk = Some k then Some v else None) named_args in
      let positional = List.filter_map (fun (k, v) -> if k = None then Some v else None) named_args in

      let as_int v = 
        match v with 
        | Ast.VInt i -> i 
        | _ -> raise (Failure "Function `seq` arguments must be Int.")
      in

      try
        let start_int, end_int_opt =
          match get_named "start", get_named "end", positional with
          | Some s, Some e, _ -> (as_int s, Some (as_int e))
          | Some s, None, _ -> (as_int s, None)
          | None, Some e, [s] -> (as_int s, Some (as_int e))
          | None, Some e, [] -> (1, Some (as_int e))
          | None, None, [e] -> (1, Some (as_int e))
          | None, None, s::e::_ -> (as_int s, Some (as_int e))
          | None, None, [] -> (1, None)
          | _, _, _ -> (1, None)
        in
        
        let by_int_opt = 
          match get_named "by" with
          | Some b -> Some (as_int b)
          | None -> match positional with
                    | _::_::b::_ -> Some (as_int b)
                    | _ -> None
        in
        
        let by_int = match by_int_opt with
          | Some b -> b
          | None -> match end_int_opt with Some e -> if e < start_int then -1 else 1 | None -> 1
        in
        
        let end_int = match end_int_opt with
          | Some e -> e
          | None -> start_int
        in

        if by_int = 0 then Error.make_error Ast.ValueError "Function `seq` cannot have `by` = 0." else
        let steps = 
          if (end_int > start_int && by_int < 0) || (end_int < start_int && by_int > 0) then 0
          else ((end_int - start_int) / by_int) + 1
        in
        let items = List.init (max 0 steps) (fun i -> (None, Ast.VInt (start_int + i * by_int))) in
        Ast.VList items
      with Failure msg -> Error.type_error msg
    ))
    env