2024-08-14 14:15:01 +02:00
|
|
|
let ( let* ) = Result.bind
|
|
|
|
|
|
2024-08-12 16:31:28 +02:00
|
|
|
open Error
|
|
|
|
|
open Expr
|
|
|
|
|
open Lexer
|
2024-08-25 02:12:51 +02:00
|
|
|
open Stmt
|
2024-08-12 16:31:28 +02:00
|
|
|
|
2024-08-25 02:12:51 +02:00
|
|
|
type parse_result = (stmt_node list, parser_error list) result
|
2024-08-28 17:29:36 +02:00
|
|
|
type state = { tokens : token list ref; is_in_loop : bool; is_in_fun : bool }
|
2024-08-25 02:12:51 +02:00
|
|
|
type stmt_result = (stmt_node, parser_error) result
|
2024-08-17 13:32:59 +02:00
|
|
|
type expr_result = (expr_node, parser_error) result
|
2024-08-12 16:31:28 +02:00
|
|
|
|
2024-08-28 22:41:00 +02:00
|
|
|
let make_state tokens = { tokens; is_in_loop = false; is_in_fun = false }
|
2024-08-27 20:32:05 +02:00
|
|
|
|
2024-08-27 18:01:34 +02:00
|
|
|
let with_is_in_loop (f : state -> 'a) (state : state) : 'a =
|
2024-08-27 18:25:12 +02:00
|
|
|
let new_state = { state with is_in_loop = true } in
|
|
|
|
|
let result = f new_state in
|
2024-08-27 19:00:11 +02:00
|
|
|
(* state.tokens <- new_state.tokens; *)
|
2024-08-27 18:25:12 +02:00
|
|
|
result
|
2024-08-27 02:46:17 +02:00
|
|
|
|
2024-08-28 17:29:36 +02:00
|
|
|
let with_is_in_fun (f : state -> 'a) (state : state) : 'a =
|
|
|
|
|
let new_state = { state with is_in_fun = true; is_in_loop = false } in
|
|
|
|
|
let result = f new_state in
|
|
|
|
|
(* state.tokens <- new_state.tokens; *)
|
|
|
|
|
result
|
|
|
|
|
|
2024-08-27 18:01:34 +02:00
|
|
|
let is_at_end state =
|
2024-08-27 19:00:11 +02:00
|
|
|
assert (not (List.is_empty !(state.tokens)));
|
|
|
|
|
(List.hd !(state.tokens)).token_type = Eof
|
2024-08-27 18:01:34 +02:00
|
|
|
|
2024-08-27 19:00:11 +02:00
|
|
|
(* let advance state = state.tokens <- List.tl state.tokens *)
|
|
|
|
|
let advance state = state.tokens := List.tl !(state.tokens)
|
|
|
|
|
let peek state = List.hd !(state.tokens)
|
|
|
|
|
let peek_tt (state : state) : token_type = (peek state).token_type
|
|
|
|
|
let cur_pos state = (peek state).pos
|
2024-08-14 14:15:01 +02:00
|
|
|
|
|
|
|
|
let next state =
|
2024-08-27 19:00:11 +02:00
|
|
|
let token = peek state in
|
2024-08-14 14:15:01 +02:00
|
|
|
advance state;
|
|
|
|
|
token
|
|
|
|
|
|
|
|
|
|
let advance_if state tt =
|
2024-08-26 01:58:03 +02:00
|
|
|
if peek_tt state = tt then (
|
2024-08-14 14:15:01 +02:00
|
|
|
advance state;
|
|
|
|
|
true)
|
|
|
|
|
else false
|
|
|
|
|
|
2024-08-25 02:12:51 +02:00
|
|
|
let consume state tt =
|
2024-08-27 20:32:05 +02:00
|
|
|
if advance_if state tt then Ok ()
|
2024-08-25 02:12:51 +02:00
|
|
|
else
|
2024-08-26 01:58:03 +02:00
|
|
|
let pos = cur_pos state in
|
|
|
|
|
let tt' = peek_tt state in
|
|
|
|
|
let msg = Printf.sprintf "Expected %s, but got %s" (show_token_type tt) (show_token_type tt') in
|
2024-08-26 17:26:59 +02:00
|
|
|
ParserError.make pos msg |> Result.error
|
2024-08-26 01:58:03 +02:00
|
|
|
|
|
|
|
|
let consume_identifier state =
|
|
|
|
|
match peek_tt state with
|
|
|
|
|
| Identifier name ->
|
|
|
|
|
advance state;
|
|
|
|
|
Ok name
|
|
|
|
|
| tt ->
|
|
|
|
|
let pos = cur_pos state in
|
2024-08-26 17:26:59 +02:00
|
|
|
let msg = Printf.sprintf "Expected identifier, but got %s" (show_token_type tt) in
|
|
|
|
|
ParserError.make pos msg |> Result.error
|
2024-08-25 02:12:51 +02:00
|
|
|
|
2024-08-14 14:15:01 +02:00
|
|
|
let matches state tts =
|
2024-08-26 01:58:03 +02:00
|
|
|
let f = ( = ) (peek_tt state) in
|
2024-08-14 14:15:01 +02:00
|
|
|
Array.fold_left (fun acc tt -> acc || f tt) false tts
|
|
|
|
|
|
2024-08-28 17:29:36 +02:00
|
|
|
let collect_chain (tts : token_type array) (collector : state -> ('a, parser_error) result)
|
|
|
|
|
(state : state) : (('a * token) list, parser_error) result =
|
|
|
|
|
let rec collect_chain_rec (acc : ('a * token) list) =
|
2024-08-14 14:15:01 +02:00
|
|
|
if (not (is_at_end state)) && matches state tts then
|
|
|
|
|
let token = next state in
|
2024-08-28 17:29:36 +02:00
|
|
|
let* expr = collector state in
|
2024-08-14 14:15:01 +02:00
|
|
|
let acc = (expr, token) :: acc in
|
|
|
|
|
collect_chain_rec acc
|
|
|
|
|
else Ok acc
|
|
|
|
|
in
|
2024-08-27 18:01:34 +02:00
|
|
|
collect_chain_rec [] |> Result.map List.rev
|
2024-08-14 14:15:01 +02:00
|
|
|
|
2024-08-27 18:01:34 +02:00
|
|
|
let primary (state : state) : expr_result =
|
2024-08-17 13:32:59 +02:00
|
|
|
let pos = cur_pos state in
|
2024-08-25 02:12:51 +02:00
|
|
|
match peek_tt state with
|
2024-08-17 13:32:59 +02:00
|
|
|
| Number x ->
|
|
|
|
|
advance state;
|
2024-08-26 17:26:59 +02:00
|
|
|
make_number pos x |> Result.ok
|
2024-08-17 13:32:59 +02:00
|
|
|
| String s ->
|
|
|
|
|
advance state;
|
2024-08-26 17:26:59 +02:00
|
|
|
make_string pos s |> Result.ok
|
2024-08-17 13:32:59 +02:00
|
|
|
| True ->
|
|
|
|
|
advance state;
|
2024-08-26 17:26:59 +02:00
|
|
|
make_bool pos true |> Result.ok
|
2024-08-17 13:32:59 +02:00
|
|
|
| False ->
|
|
|
|
|
advance state;
|
2024-08-26 17:26:59 +02:00
|
|
|
make_bool pos false |> Result.ok
|
2024-08-17 13:32:59 +02:00
|
|
|
| Nil ->
|
|
|
|
|
advance state;
|
2024-08-26 17:26:59 +02:00
|
|
|
make_nil pos |> Result.ok
|
2024-08-26 01:58:03 +02:00
|
|
|
| Identifier name ->
|
|
|
|
|
advance state;
|
2024-08-26 17:26:59 +02:00
|
|
|
make_variable pos name |> Result.ok
|
2024-08-14 14:15:01 +02:00
|
|
|
| tt ->
|
2024-08-26 01:58:03 +02:00
|
|
|
advance state;
|
|
|
|
|
let msg = Printf.sprintf "Expected valid expression, got %s instead" (show_token_type tt) in
|
2024-08-14 14:15:01 +02:00
|
|
|
Error { msg; pos }
|
|
|
|
|
|
2024-08-27 18:01:34 +02:00
|
|
|
let rec grouping (state : state) : expr_result =
|
2024-08-14 14:15:01 +02:00
|
|
|
if matches state [| LeftParen |] then (
|
|
|
|
|
advance state;
|
|
|
|
|
let* expr = expression state in
|
2024-08-17 13:32:59 +02:00
|
|
|
if advance_if state RightParen then Ok expr (* expect a ) here *)
|
2024-08-14 12:36:36 +02:00
|
|
|
else
|
2024-08-14 14:15:01 +02:00
|
|
|
let pos = cur_pos state in
|
2024-08-25 02:12:51 +02:00
|
|
|
let tt = peek_tt state in
|
2024-08-14 14:15:01 +02:00
|
|
|
let msg = Printf.sprintf "Expected RightParen, got %s instead" (show_token_type tt) in
|
|
|
|
|
Error { pos; msg })
|
|
|
|
|
else primary state
|
|
|
|
|
|
2024-08-27 20:32:05 +02:00
|
|
|
and call (state : state) : expr_result =
|
|
|
|
|
let* expr = grouping state in
|
|
|
|
|
let pos = cur_pos state in
|
2024-08-28 22:41:00 +02:00
|
|
|
let rec parse_calls_rec expr =
|
|
|
|
|
if advance_if state LeftParen then
|
|
|
|
|
let* args =
|
|
|
|
|
if peek_tt state = RightParen then Ok []
|
|
|
|
|
else
|
|
|
|
|
let* first_arg = expression state in
|
|
|
|
|
let* exprs_tokens = collect_chain [| Comma |] expression state in
|
|
|
|
|
let other_args = List.map fst exprs_tokens in
|
|
|
|
|
let args = first_arg :: other_args in
|
|
|
|
|
Ok args
|
|
|
|
|
in
|
|
|
|
|
if List.length args >= 255 then
|
|
|
|
|
let msg = "Can't call with more than 255 arguments" in
|
|
|
|
|
ParserError.make pos msg |> Result.error
|
2024-08-27 20:32:05 +02:00
|
|
|
else
|
2024-08-28 22:41:00 +02:00
|
|
|
let* () = consume state RightParen in
|
|
|
|
|
let expr = make_call pos expr args in
|
|
|
|
|
parse_calls_rec expr
|
|
|
|
|
else Ok expr
|
|
|
|
|
in
|
|
|
|
|
parse_calls_rec expr
|
2024-08-27 20:32:05 +02:00
|
|
|
|
2024-08-27 18:01:34 +02:00
|
|
|
and neg_not (state : state) : expr_result =
|
2024-08-14 14:15:01 +02:00
|
|
|
if matches state [| Bang; Minus |] then
|
|
|
|
|
let token = next state in
|
2024-08-17 13:32:59 +02:00
|
|
|
let pos = token.pos in
|
2024-08-14 14:15:01 +02:00
|
|
|
let* expr = neg_not state in
|
|
|
|
|
let op =
|
|
|
|
|
match token.token_type with
|
|
|
|
|
| Bang -> Not
|
|
|
|
|
| Minus -> Neg
|
|
|
|
|
| _ -> assert false (* should only be here if tt is - ! *)
|
2024-08-12 16:31:28 +02:00
|
|
|
in
|
2024-08-17 13:32:59 +02:00
|
|
|
let expr = make_unary pos op expr in
|
2024-08-14 14:15:01 +02:00
|
|
|
Ok expr
|
2024-08-27 20:32:05 +02:00
|
|
|
else call state
|
2024-08-14 14:15:01 +02:00
|
|
|
|
2024-08-27 18:01:34 +02:00
|
|
|
and mul_or_div (state : state) : expr_result =
|
2024-08-14 14:15:01 +02:00
|
|
|
let* expr = neg_not state in
|
2024-08-28 17:29:36 +02:00
|
|
|
let* exprs_tokens = collect_chain [| Star; Slash |] neg_not state in
|
2024-08-25 02:12:51 +02:00
|
|
|
let f acc (expr, (token : token)) =
|
2024-08-17 13:32:59 +02:00
|
|
|
let pos = token.pos in
|
2024-08-14 14:15:01 +02:00
|
|
|
let op : binary_op =
|
|
|
|
|
match token.token_type with
|
|
|
|
|
| Star -> Mul
|
|
|
|
|
| Slash -> Div
|
|
|
|
|
| _ -> assert false (* should only be here if tt is * / *)
|
2024-08-12 18:28:21 +02:00
|
|
|
in
|
2024-08-17 13:32:59 +02:00
|
|
|
make_binary pos op acc expr
|
2024-08-14 14:15:01 +02:00
|
|
|
in
|
2024-08-26 17:26:59 +02:00
|
|
|
let expr = List.fold_left f expr exprs_tokens in
|
2024-08-14 14:15:01 +02:00
|
|
|
Ok expr
|
|
|
|
|
|
2024-08-27 18:01:34 +02:00
|
|
|
and sum_or_diff (state : state) : expr_result =
|
2024-08-14 14:15:01 +02:00
|
|
|
let* expr = mul_or_div state in
|
2024-08-28 17:29:36 +02:00
|
|
|
let* exprs_tokens = collect_chain [| Plus; Minus |] mul_or_div state in
|
2024-08-25 02:12:51 +02:00
|
|
|
let f acc (expr, (token : token)) =
|
2024-08-17 13:32:59 +02:00
|
|
|
let pos = token.pos in
|
2024-08-14 14:15:01 +02:00
|
|
|
let op : binary_op =
|
|
|
|
|
match token.token_type with
|
|
|
|
|
| Plus -> Plus
|
|
|
|
|
| Minus -> Minus
|
|
|
|
|
| _ -> assert false (* should only be here if tt is + - *)
|
2024-08-12 18:28:21 +02:00
|
|
|
in
|
2024-08-17 13:32:59 +02:00
|
|
|
make_binary pos op acc expr
|
2024-08-14 14:15:01 +02:00
|
|
|
in
|
2024-08-26 17:26:59 +02:00
|
|
|
let expr = List.fold_left f expr exprs_tokens in
|
2024-08-14 14:15:01 +02:00
|
|
|
Ok expr
|
|
|
|
|
|
2024-08-27 18:01:34 +02:00
|
|
|
and inequality (state : state) : expr_result =
|
2024-08-14 14:15:01 +02:00
|
|
|
(* TODO: maybe rework to only have Less and Greater as ops; performance? *)
|
|
|
|
|
let* expr = sum_or_diff state in
|
|
|
|
|
let* exprs_tokens =
|
2024-08-28 17:29:36 +02:00
|
|
|
collect_chain [| Greater; GreaterEqual; Less; LessEqual |] sum_or_diff state
|
2024-08-14 14:15:01 +02:00
|
|
|
in
|
2024-08-25 02:12:51 +02:00
|
|
|
let f acc (expr, (token : token)) =
|
2024-08-17 13:32:59 +02:00
|
|
|
let pos = token.pos in
|
2024-08-14 14:15:01 +02:00
|
|
|
let (op : binary_op) =
|
2024-08-12 18:28:21 +02:00
|
|
|
match token.token_type with
|
2024-08-14 14:15:01 +02:00
|
|
|
| Greater -> Greater
|
|
|
|
|
| Less -> Less
|
|
|
|
|
| GreaterEqual -> GreaterEqual
|
|
|
|
|
| LessEqual -> LessEqual
|
|
|
|
|
| _ -> assert false (* should only be here if tt is > < >= <= *)
|
2024-08-12 18:28:21 +02:00
|
|
|
in
|
2024-08-17 13:32:59 +02:00
|
|
|
make_binary pos op acc expr
|
2024-08-14 14:15:01 +02:00
|
|
|
in
|
2024-08-26 17:26:59 +02:00
|
|
|
let expr = List.fold_left f expr exprs_tokens in
|
2024-08-14 14:15:01 +02:00
|
|
|
Ok expr
|
|
|
|
|
|
2024-08-27 18:01:34 +02:00
|
|
|
and equality (state : state) : expr_result =
|
2024-08-14 14:15:01 +02:00
|
|
|
let* expr = inequality state in
|
2024-08-28 17:29:36 +02:00
|
|
|
let* exprs_tokens = collect_chain [| EqualEqual; BangEqual |] inequality state in
|
2024-08-25 02:12:51 +02:00
|
|
|
let f acc (expr, (token : token)) =
|
2024-08-17 13:32:59 +02:00
|
|
|
let pos = token.pos in
|
2024-08-14 14:15:01 +02:00
|
|
|
match token.token_type with
|
2024-08-17 13:32:59 +02:00
|
|
|
| EqualEqual -> make_binary pos Equal acc expr
|
2024-08-14 14:15:01 +02:00
|
|
|
| BangEqual ->
|
2024-08-17 13:32:59 +02:00
|
|
|
let expr = make_binary pos Equal acc expr in
|
|
|
|
|
make_unary pos Not expr
|
2024-08-14 14:15:01 +02:00
|
|
|
| _ -> assert false (* should only be here if tt is == != *)
|
|
|
|
|
in
|
2024-08-26 17:26:59 +02:00
|
|
|
let expr = List.fold_left f expr exprs_tokens in
|
2024-08-14 14:15:01 +02:00
|
|
|
Ok expr
|
2024-08-12 16:31:28 +02:00
|
|
|
|
2024-08-27 18:01:34 +02:00
|
|
|
and logical_and (state : state) : expr_result =
|
2024-08-26 01:58:03 +02:00
|
|
|
let* expr = equality state in
|
2024-08-28 17:29:36 +02:00
|
|
|
let* exprs_tokens = collect_chain [| And |] equality state in
|
2024-08-26 17:26:59 +02:00
|
|
|
let f acc (expr, (token : token)) =
|
|
|
|
|
let pos = token.pos in
|
|
|
|
|
assert (token.token_type = And);
|
|
|
|
|
make_logical pos And acc expr
|
|
|
|
|
in
|
|
|
|
|
let expr = List.fold_left f expr exprs_tokens in
|
|
|
|
|
Ok expr
|
|
|
|
|
|
2024-08-27 18:01:34 +02:00
|
|
|
and logical_or (state : state) : expr_result =
|
2024-08-26 17:26:59 +02:00
|
|
|
let* expr = logical_and state in
|
2024-08-28 17:29:36 +02:00
|
|
|
let* exprs_tokens = collect_chain [| Or |] logical_and state in
|
2024-08-26 17:26:59 +02:00
|
|
|
let f acc (expr, (token : token)) =
|
|
|
|
|
let pos = token.pos in
|
|
|
|
|
assert (token.token_type = Or);
|
|
|
|
|
make_logical pos Or acc expr
|
|
|
|
|
in
|
|
|
|
|
let expr = List.fold_left f expr exprs_tokens in
|
|
|
|
|
Ok expr
|
|
|
|
|
|
2024-08-27 18:01:34 +02:00
|
|
|
and assignment (state : state) : expr_result =
|
2024-08-26 17:26:59 +02:00
|
|
|
let* expr = logical_or state in
|
2024-08-26 01:58:03 +02:00
|
|
|
if Equal = peek_tt state then
|
|
|
|
|
let pos = (next state).pos in
|
|
|
|
|
let* rhs = assignment state in
|
|
|
|
|
match expr.expr with
|
2024-08-26 17:26:59 +02:00
|
|
|
| Variable name -> make_assignment pos name rhs |> Result.ok
|
2024-08-26 01:58:03 +02:00
|
|
|
| _ ->
|
|
|
|
|
let msg = "Invalid assignment target" in
|
2024-08-26 17:26:59 +02:00
|
|
|
ParserError.make pos msg |> Result.error
|
2024-08-26 01:58:03 +02:00
|
|
|
else Ok expr
|
|
|
|
|
|
2024-08-27 18:01:34 +02:00
|
|
|
and expression (state : state) : expr_result = assignment state
|
2024-08-26 01:58:03 +02:00
|
|
|
|
2024-08-27 18:01:34 +02:00
|
|
|
let rec block (state : state) : stmt_result =
|
2024-08-26 01:58:03 +02:00
|
|
|
let pos = cur_pos state in
|
2024-08-27 20:32:05 +02:00
|
|
|
let* () = consume state LeftBrace in
|
2024-08-26 01:58:03 +02:00
|
|
|
let rec collect_stmts state =
|
|
|
|
|
if is_at_end state then
|
|
|
|
|
let msg = "Unterminated block" in
|
2024-08-26 17:26:59 +02:00
|
|
|
ParserError.make pos msg |> Result.error
|
2024-08-26 01:58:03 +02:00
|
|
|
else
|
|
|
|
|
match peek_tt state with
|
|
|
|
|
| RightBrace -> Ok []
|
|
|
|
|
| _ ->
|
|
|
|
|
let* stmt = declaration state in
|
|
|
|
|
let* tail = collect_stmts state in
|
|
|
|
|
Ok (stmt :: tail)
|
|
|
|
|
in
|
|
|
|
|
let* stmts = collect_stmts state in
|
2024-08-27 20:32:05 +02:00
|
|
|
let* () = consume state RightBrace in
|
2024-08-26 17:26:59 +02:00
|
|
|
make_block pos stmts |> Result.ok
|
|
|
|
|
|
2024-08-27 18:01:34 +02:00
|
|
|
and if_then_else (state : state) : stmt_result =
|
2024-08-26 17:26:59 +02:00
|
|
|
let pos = cur_pos state in
|
2024-08-27 20:32:05 +02:00
|
|
|
let* () = consume state If in
|
|
|
|
|
let* () = consume state LeftParen in
|
2024-08-26 17:26:59 +02:00
|
|
|
let* cond = expression state in
|
2024-08-27 20:32:05 +02:00
|
|
|
let* () = consume state RightParen in
|
2024-08-26 17:26:59 +02:00
|
|
|
let* then_ = statement state in
|
|
|
|
|
let* (else_ : stmt_node option) =
|
|
|
|
|
if advance_if state Else then statement state |> Result.map Option.some else Ok None
|
|
|
|
|
in
|
|
|
|
|
make_if pos cond then_ else_ |> Result.ok
|
2024-08-14 14:15:01 +02:00
|
|
|
|
2024-08-27 18:01:34 +02:00
|
|
|
and while_loop (state : state) : stmt_result =
|
2024-08-27 01:57:47 +02:00
|
|
|
let pos = cur_pos state in
|
2024-08-27 20:32:05 +02:00
|
|
|
let* () = consume state While in
|
|
|
|
|
let* () = consume state LeftParen in
|
2024-08-27 01:57:47 +02:00
|
|
|
let* cond = expression state in
|
2024-08-27 20:32:05 +02:00
|
|
|
let* () = consume state RightParen in
|
2024-08-27 02:46:17 +02:00
|
|
|
let* body = with_is_in_loop statement state in
|
2024-08-27 01:57:47 +02:00
|
|
|
make_while pos cond body |> Result.ok
|
|
|
|
|
|
2024-08-27 18:01:34 +02:00
|
|
|
and for_loop (state : state) : stmt_result =
|
2024-08-27 17:15:35 +02:00
|
|
|
let pos = cur_pos state in
|
2024-08-27 20:32:05 +02:00
|
|
|
let* () = consume state For in
|
|
|
|
|
let* () = consume state LeftParen in
|
2024-08-27 01:57:47 +02:00
|
|
|
let* init =
|
|
|
|
|
match peek_tt state with
|
|
|
|
|
| Semicolon ->
|
|
|
|
|
advance state;
|
|
|
|
|
Ok None
|
|
|
|
|
| Var -> var_declaration state |> Result.map Option.some
|
|
|
|
|
| _ -> expr_stmt state |> Result.map Option.some
|
|
|
|
|
in
|
|
|
|
|
let* cond =
|
|
|
|
|
match peek_tt state with
|
2024-08-27 17:15:35 +02:00
|
|
|
| Semicolon -> Ok None
|
|
|
|
|
| _ -> expression state |> Result.map Option.some
|
2024-08-27 01:57:47 +02:00
|
|
|
in
|
|
|
|
|
(* expression has no final semicolon, so we need to consume it *)
|
2024-08-27 20:32:05 +02:00
|
|
|
let* () = consume state Semicolon in
|
2024-08-27 01:57:47 +02:00
|
|
|
let* update =
|
|
|
|
|
match peek_tt state with
|
|
|
|
|
| RightParen -> Ok None
|
|
|
|
|
| _ -> expression state |> Result.map Option.some
|
|
|
|
|
in
|
2024-08-27 20:32:05 +02:00
|
|
|
let* () = consume state RightParen in
|
2024-08-27 02:46:17 +02:00
|
|
|
let* body = with_is_in_loop statement state in
|
2024-08-27 17:15:35 +02:00
|
|
|
make_for pos init cond update body |> Result.ok
|
2024-08-27 01:57:47 +02:00
|
|
|
|
2024-08-27 18:01:34 +02:00
|
|
|
and expr_stmt (state : state) : stmt_result =
|
2024-08-27 01:57:47 +02:00
|
|
|
let pos = cur_pos state in
|
|
|
|
|
let* expr = expression state in
|
2024-08-27 20:32:05 +02:00
|
|
|
let* () = consume state Semicolon in
|
2024-08-27 01:57:47 +02:00
|
|
|
let stmt = make_expr_stmt pos expr in
|
|
|
|
|
Ok stmt
|
|
|
|
|
|
2024-08-27 18:01:34 +02:00
|
|
|
and statement (state : state) : stmt_result =
|
2024-08-25 02:12:51 +02:00
|
|
|
let pos = cur_pos state in
|
|
|
|
|
match peek_tt state with
|
2024-08-27 02:46:17 +02:00
|
|
|
| Break ->
|
2024-08-28 22:41:00 +02:00
|
|
|
if not state.is_in_loop then (
|
|
|
|
|
advance state;
|
|
|
|
|
let msg = "Can use break only in loops" in
|
|
|
|
|
ParserError.make pos msg |> Result.error)
|
|
|
|
|
else (
|
2024-08-27 02:46:17 +02:00
|
|
|
advance state;
|
2024-08-27 20:32:05 +02:00
|
|
|
let* () = consume state Semicolon in
|
2024-08-27 02:46:17 +02:00
|
|
|
make_break pos |> Result.ok)
|
2024-08-27 17:15:35 +02:00
|
|
|
| Continue ->
|
2024-08-28 22:41:00 +02:00
|
|
|
if not state.is_in_loop then (
|
|
|
|
|
advance state;
|
|
|
|
|
let msg = "Can use continue only in loops" in
|
|
|
|
|
ParserError.make pos msg |> Result.error)
|
|
|
|
|
else (
|
2024-08-27 17:15:35 +02:00
|
|
|
advance state;
|
2024-08-27 20:32:05 +02:00
|
|
|
let* () = consume state Semicolon in
|
2024-08-27 17:15:35 +02:00
|
|
|
make_continue pos |> Result.ok)
|
2024-08-28 22:41:00 +02:00
|
|
|
| Return ->
|
|
|
|
|
if not state.is_in_fun then (
|
|
|
|
|
advance state;
|
|
|
|
|
let msg = "Can use return only in functions" in
|
|
|
|
|
ParserError.make pos msg |> Result.error)
|
|
|
|
|
else (
|
|
|
|
|
advance state;
|
|
|
|
|
let* expr =
|
|
|
|
|
if peek_tt state = Semicolon then make_nil pos |> Result.ok else expression state
|
|
|
|
|
in
|
|
|
|
|
let* () = consume state Semicolon in
|
|
|
|
|
make_return pos expr |> Result.ok)
|
2024-08-25 02:12:51 +02:00
|
|
|
| Print ->
|
|
|
|
|
advance state;
|
|
|
|
|
let* expr = expression state in
|
2024-08-27 20:32:05 +02:00
|
|
|
let* () = consume state Semicolon in
|
2024-08-25 02:12:51 +02:00
|
|
|
let stmt = make_print pos expr in
|
|
|
|
|
Ok stmt
|
2024-08-26 01:58:03 +02:00
|
|
|
| LeftBrace -> block state
|
2024-08-26 17:26:59 +02:00
|
|
|
| If -> if_then_else state
|
2024-08-27 01:57:47 +02:00
|
|
|
| While -> while_loop state
|
|
|
|
|
| For -> for_loop state
|
|
|
|
|
| _ -> expr_stmt state
|
2024-08-26 01:58:03 +02:00
|
|
|
|
2024-08-27 18:01:34 +02:00
|
|
|
and var_declaration (state : state) : stmt_result =
|
2024-08-26 01:58:03 +02:00
|
|
|
let pos = cur_pos state in
|
|
|
|
|
(* consume var token *)
|
2024-08-28 17:29:36 +02:00
|
|
|
let* () = consume state Var in
|
2024-08-26 01:58:03 +02:00
|
|
|
let* name = consume_identifier state in
|
|
|
|
|
let* init =
|
|
|
|
|
if Equal = peek_tt state then
|
|
|
|
|
(* found =, parsing initialiser *)
|
2024-08-27 20:32:05 +02:00
|
|
|
let* () = consume state Equal in
|
2024-08-26 01:58:03 +02:00
|
|
|
let* init = expression state in
|
|
|
|
|
Ok (Some init)
|
|
|
|
|
else (* no initialiser, default to nil *)
|
|
|
|
|
Ok None
|
|
|
|
|
in
|
2024-08-27 20:32:05 +02:00
|
|
|
let* () = consume state Semicolon in
|
2024-08-26 17:26:59 +02:00
|
|
|
make_var_decl pos name init |> Result.ok
|
2024-08-26 01:58:03 +02:00
|
|
|
|
2024-08-28 17:29:36 +02:00
|
|
|
and fun_declaration (state : state) : stmt_result =
|
|
|
|
|
let pos = cur_pos state in
|
|
|
|
|
let* () = consume state Fun in
|
|
|
|
|
let* name = consume_identifier state in
|
|
|
|
|
let* () = consume state LeftParen in
|
|
|
|
|
let* arg_names =
|
|
|
|
|
if peek_tt state = RightParen then Ok []
|
|
|
|
|
else
|
|
|
|
|
let* first_arg = consume_identifier state in
|
|
|
|
|
let* exprs_tokens = collect_chain [| Comma |] consume_identifier state in
|
|
|
|
|
let other_args = List.map fst exprs_tokens in
|
|
|
|
|
let args = first_arg :: other_args in
|
|
|
|
|
Ok args
|
|
|
|
|
in
|
|
|
|
|
if List.length arg_names >= 255 then
|
|
|
|
|
let msg = Printf.sprintf "Function %s can't have more than 255 arguments" name in
|
|
|
|
|
ParserError.make pos msg |> Result.error
|
|
|
|
|
else
|
|
|
|
|
let* () = consume state RightParen in
|
|
|
|
|
let* body = with_is_in_fun block state in
|
|
|
|
|
make_fun_decl pos name arg_names body |> Result.ok
|
|
|
|
|
|
2024-08-27 18:01:34 +02:00
|
|
|
and declaration (state : state) : stmt_result =
|
2024-08-28 17:29:36 +02:00
|
|
|
match peek_tt state with
|
|
|
|
|
| Var -> var_declaration state
|
|
|
|
|
| Fun -> fun_declaration state
|
|
|
|
|
| _ -> statement state
|
2024-08-25 02:12:51 +02:00
|
|
|
|
2024-08-27 18:01:34 +02:00
|
|
|
let rec synchronise (state : state) =
|
2024-08-25 02:12:51 +02:00
|
|
|
match peek_tt state with
|
2024-08-14 14:15:01 +02:00
|
|
|
| Semicolon -> advance state
|
2024-08-28 22:41:00 +02:00
|
|
|
| Break | Class | Continue | Fun | Var | For | If | While | Print | Return | Eof -> ()
|
2024-08-14 14:15:01 +02:00
|
|
|
| _ ->
|
|
|
|
|
advance state;
|
|
|
|
|
synchronise state
|
2024-08-12 16:31:28 +02:00
|
|
|
|
2024-08-27 18:01:34 +02:00
|
|
|
let rec parse_impl (state : state) : parse_result =
|
2024-08-26 01:58:03 +02:00
|
|
|
if peek_tt state = Eof then Ok []
|
|
|
|
|
else
|
|
|
|
|
let result = declaration state in
|
|
|
|
|
match result with
|
|
|
|
|
| Ok stmt ->
|
|
|
|
|
let* stmts = parse_impl state in
|
|
|
|
|
Ok (stmt :: stmts)
|
|
|
|
|
| Error e -> (
|
|
|
|
|
synchronise state;
|
|
|
|
|
if peek_tt state = Eof then Error [ e ]
|
|
|
|
|
else
|
|
|
|
|
let tail_result = parse_impl state in
|
|
|
|
|
match tail_result with Ok _ -> Error [ e ] | Error es -> Error (e :: es))
|
2024-08-25 03:07:18 +02:00
|
|
|
|
|
|
|
|
let parse (tokens : token list) : parse_result =
|
|
|
|
|
(* filter out all the comment tokens *)
|
|
|
|
|
let tokens =
|
|
|
|
|
List.filter (fun tok -> match tok.token_type with Comment _ -> false | _ -> true) tokens
|
|
|
|
|
in
|
2024-08-27 19:00:11 +02:00
|
|
|
let state = make_state (ref tokens) in
|
2024-08-25 03:07:18 +02:00
|
|
|
parse_impl state
|