mirror of
https://github.com/MorizzG/MLox.git
synced 2025-12-06 04:22:41 +00:00
moved for loop to separate Stmt and added continue
This commit is contained in:
parent
957783a926
commit
be28dad67e
6 changed files with 113 additions and 61 deletions
17
lib/error.ml
17
lib/error.ml
|
|
@ -21,20 +21,21 @@ module ParserError = struct
|
||||||
Printf.printf "ParserError at line %d, column %d: %s\n" e.pos.line e.pos.col e.msg
|
Printf.printf "ParserError at line %d, column %d: %s\n" e.pos.line e.pos.col e.msg
|
||||||
end
|
end
|
||||||
|
|
||||||
type runtime_error = { pos : code_pos; msg : string; is_break : bool }
|
(* type runtime_error = { pos : code_pos; msg : string; type_ : runtime_error_type } *)
|
||||||
|
type runtime_error = Error of { pos : code_pos; msg : string } | Break | Continue
|
||||||
|
|
||||||
module RuntimeError = struct
|
module RuntimeError = struct
|
||||||
type t = parser_error
|
type t = parser_error
|
||||||
|
|
||||||
let make (pos : code_pos) (msg : string) : runtime_error = { pos; msg; is_break = false }
|
let make (pos : code_pos) (msg : string) : runtime_error = Error { pos; msg }
|
||||||
|
let break () : runtime_error = Break
|
||||||
let break () : runtime_error =
|
let continue () : runtime_error = Continue
|
||||||
let pos = { line = -1; col = -1 } in
|
|
||||||
let msg = "" in
|
|
||||||
{ pos; msg; is_break = true }
|
|
||||||
|
|
||||||
let print (e : runtime_error) =
|
let print (e : runtime_error) =
|
||||||
Printf.printf "RuntimeError at line %d, column %d: %s\n" e.pos.line e.pos.col e.msg
|
match e with
|
||||||
|
| Error { pos; msg } ->
|
||||||
|
Printf.printf "RuntimeError at line %d, column %d: %s\n" pos.line pos.col msg
|
||||||
|
| Break | Continue -> assert false
|
||||||
end
|
end
|
||||||
|
|
||||||
type lox_error =
|
type lox_error =
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,7 @@ let rec interpret_stmt (env : environment) (stmt_node : stmt_node) : (unit, runt
|
||||||
(* print_endline "break!";
|
(* print_endline "break!";
|
||||||
Ok () (* TODO *) *)
|
Ok () (* TODO *) *)
|
||||||
RuntimeError.break () |> Result.error
|
RuntimeError.break () |> Result.error
|
||||||
|
| Continue -> RuntimeError.continue () |> Result.error
|
||||||
| Print expr ->
|
| Print expr ->
|
||||||
let* value = interpret_expr env expr in
|
let* value = interpret_expr env expr in
|
||||||
print_endline (Value.string_of_lox_value value);
|
print_endline (Value.string_of_lox_value value);
|
||||||
|
|
@ -119,7 +120,31 @@ let rec interpret_stmt (env : environment) (stmt_node : stmt_node) : (unit, runt
|
||||||
if cond then
|
if cond then
|
||||||
let result = interpret_stmt env body in
|
let result = interpret_stmt env body in
|
||||||
match result with
|
match result with
|
||||||
| Error { is_break = true; _ } -> Ok ()
|
| Ok () | Error Continue -> interpret_stmt env stmt_node
|
||||||
|
| Error Break -> Ok ()
|
||||||
| Error e -> Error e
|
| Error e -> Error e
|
||||||
| _ -> interpret_stmt env stmt_node
|
|
||||||
else Ok ()
|
else Ok ()
|
||||||
|
| For { init; cond; update; body } ->
|
||||||
|
let* _ = init |> Option.map (interpret_stmt env) |> Option.value ~default:(Ok ()) in
|
||||||
|
let eval_cond () =
|
||||||
|
cond
|
||||||
|
|> Option.map (interpret_expr env)
|
||||||
|
|> Option.value ~default:(Ok (Value.Bool true))
|
||||||
|
|> Result.map Value.lox_value_to_bool
|
||||||
|
in
|
||||||
|
let do_update () =
|
||||||
|
update |> Option.map (interpret_expr env) |> Option.value ~default:(Ok Value.Nil)
|
||||||
|
in
|
||||||
|
let rec loop () =
|
||||||
|
let* cond = eval_cond () in
|
||||||
|
if cond then
|
||||||
|
let result = interpret_stmt env body in
|
||||||
|
match result with
|
||||||
|
| Ok () | Error Continue ->
|
||||||
|
let* _ = do_update () in
|
||||||
|
loop ()
|
||||||
|
| Error Break -> Ok ()
|
||||||
|
| Error e -> Error e
|
||||||
|
else Ok ()
|
||||||
|
in
|
||||||
|
loop ()
|
||||||
|
|
|
||||||
12
lib/lexer.ml
12
lib/lexer.ml
|
|
@ -14,7 +14,7 @@ open Error
|
||||||
| String of string
|
| String of string
|
||||||
| Number of float
|
| Number of float
|
||||||
|
|
||||||
| And | Break | Class | Else | False | Fun | For | If | Nil | Or | Print | Return | Super | This | True
|
| And | Break | Class | Continue | Else | False | Fun | For | If | Nil | Or | Print | Return | Super | This | True
|
||||||
| Var | While
|
| Var | While
|
||||||
|
|
||||||
| Comment of string
|
| Comment of string
|
||||||
|
|
@ -29,11 +29,11 @@ let keywords =
|
||||||
Hashtbl.add keywords s tt;
|
Hashtbl.add keywords s tt;
|
||||||
keywords
|
keywords
|
||||||
in
|
in
|
||||||
keywords |> insert "and" And |> insert "break" Break |> insert "class" Class |> insert "else" Else
|
keywords |> insert "and" And |> insert "break" Break |> insert "class" Class
|
||||||
|> insert "false" False |> insert "for" For |> insert "fun" Fun |> insert "if" If
|
|> insert "continue" Continue |> insert "else" Else |> insert "false" False |> insert "for" For
|
||||||
|> insert "nil" Nil |> insert "or" Or |> insert "print" Print |> insert "return" Return
|
|> insert "fun" Fun |> insert "if" If |> insert "nil" Nil |> insert "or" Or
|
||||||
|> insert "super" Super |> insert "this" This |> insert "true" True |> insert "var" Var
|
|> insert "print" Print |> insert "return" Return |> insert "super" Super |> insert "this" This
|
||||||
|> insert "while" While
|
|> insert "true" True |> insert "var" Var |> insert "while" While
|
||||||
|
|
||||||
type token = { token_type : token_type; pos : code_pos }
|
type token = { token_type : token_type; pos : code_pos }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ type token_type =
|
||||||
| And
|
| And
|
||||||
| Break
|
| Break
|
||||||
| Class
|
| Class
|
||||||
|
| Continue
|
||||||
| Else
|
| Else
|
||||||
| False
|
| False
|
||||||
| Fun
|
| Fun
|
||||||
|
|
|
||||||
|
|
@ -265,16 +265,11 @@ and while_loop (state : state ref) : stmt_result =
|
||||||
let* _ = consume state LeftParen in
|
let* _ = consume state LeftParen in
|
||||||
let* cond = expression state in
|
let* cond = expression state in
|
||||||
let* _ = consume state RightParen in
|
let* _ = consume state RightParen in
|
||||||
(* let was_in_loop = !state.is_in_loop in
|
|
||||||
state := { !state with is_in_loop = true };
|
|
||||||
let body = statement state in
|
|
||||||
state := { !state with is_in_loop = was_in_loop };
|
|
||||||
let* body = body in *)
|
|
||||||
let* body = with_is_in_loop statement state in
|
let* body = with_is_in_loop statement state in
|
||||||
make_while pos cond body |> Result.ok
|
make_while pos cond body |> Result.ok
|
||||||
|
|
||||||
and for_loop (state : state ref) : stmt_result =
|
and for_loop (state : state ref) : stmt_result =
|
||||||
let for_pos = cur_pos state in
|
let pos = cur_pos state in
|
||||||
let* _ = consume state For in
|
let* _ = consume state For in
|
||||||
let* _ = consume state LeftParen in
|
let* _ = consume state LeftParen in
|
||||||
let* init =
|
let* init =
|
||||||
|
|
@ -287,10 +282,8 @@ and for_loop (state : state ref) : stmt_result =
|
||||||
in
|
in
|
||||||
let* cond =
|
let* cond =
|
||||||
match peek_tt state with
|
match peek_tt state with
|
||||||
| Semicolon ->
|
| Semicolon -> Ok None
|
||||||
let pos = cur_pos state in
|
| _ -> expression state |> Result.map Option.some
|
||||||
Ok (make_bool pos true)
|
|
||||||
| _ -> expression state
|
|
||||||
in
|
in
|
||||||
(* expression has no final semicolon, so we need to consume it *)
|
(* expression has no final semicolon, so we need to consume it *)
|
||||||
let* _ = consume state Semicolon in
|
let* _ = consume state Semicolon in
|
||||||
|
|
@ -300,24 +293,8 @@ and for_loop (state : state ref) : stmt_result =
|
||||||
| _ -> expression state |> Result.map Option.some
|
| _ -> expression state |> Result.map Option.some
|
||||||
in
|
in
|
||||||
let* _ = consume state RightParen in
|
let* _ = consume state RightParen in
|
||||||
(* let was_in_loop = !state.is_in_loop in
|
|
||||||
state := { !state with is_in_loop = true };
|
|
||||||
let body = statement state in
|
|
||||||
state := { !state with is_in_loop = was_in_loop };
|
|
||||||
let* body = body in *)
|
|
||||||
let* body = with_is_in_loop statement state in
|
let* body = with_is_in_loop statement state in
|
||||||
let body =
|
make_for pos init cond update body |> Result.ok
|
||||||
match update with
|
|
||||||
| Some update ->
|
|
||||||
let update_stmt = make_expr_stmt update.pos update in
|
|
||||||
make_block body.pos [ body; update_stmt ]
|
|
||||||
| None -> body
|
|
||||||
in
|
|
||||||
let loop = make_while for_pos cond body in
|
|
||||||
let outer_block =
|
|
||||||
match init with Some init -> make_block for_pos [ init; loop ] | None -> loop
|
|
||||||
in
|
|
||||||
Ok outer_block
|
|
||||||
|
|
||||||
and expr_stmt (state : state ref) : stmt_result =
|
and expr_stmt (state : state ref) : stmt_result =
|
||||||
let pos = cur_pos state in
|
let pos = cur_pos state in
|
||||||
|
|
@ -337,6 +314,14 @@ and statement (state : state ref) : stmt_result =
|
||||||
else
|
else
|
||||||
let msg = "Can use break only in loops" in
|
let msg = "Can use break only in loops" in
|
||||||
ParserError.make pos msg |> Result.error
|
ParserError.make pos msg |> Result.error
|
||||||
|
| Continue ->
|
||||||
|
if !state.is_in_loop then (
|
||||||
|
advance state;
|
||||||
|
let* _ = consume state Semicolon in
|
||||||
|
make_continue pos |> Result.ok)
|
||||||
|
else
|
||||||
|
let msg = "Can use continue only in loops" in
|
||||||
|
ParserError.make pos msg |> Result.error
|
||||||
| Print ->
|
| Print ->
|
||||||
advance state;
|
advance state;
|
||||||
let* expr = expression state in
|
let* expr = expression state in
|
||||||
|
|
|
||||||
76
lib/stmt.ml
76
lib/stmt.ml
|
|
@ -1,15 +1,23 @@
|
||||||
|
open Error
|
||||||
open Expr
|
open Expr
|
||||||
|
|
||||||
type stmt =
|
type stmt =
|
||||||
| Expr of expr_node
|
| Expr of expr_node
|
||||||
| Break
|
| Break
|
||||||
|
| Continue
|
||||||
| Print of expr_node
|
| Print of expr_node
|
||||||
| VarDecl of { name : string; init : expr_node option }
|
| VarDecl of { name : string; init : expr_node option }
|
||||||
| Block of stmt_node list
|
| Block of stmt_node list
|
||||||
| If of { cond : expr_node; then_ : stmt_node; else_ : stmt_node option }
|
| If of { cond : expr_node; then_ : stmt_node; else_ : stmt_node option }
|
||||||
| While of { cond : expr_node; body : stmt_node }
|
| While of { cond : expr_node; body : stmt_node }
|
||||||
|
| For of {
|
||||||
|
init : stmt_node option;
|
||||||
|
cond : expr_node option;
|
||||||
|
update : expr_node option;
|
||||||
|
body : stmt_node;
|
||||||
|
}
|
||||||
|
|
||||||
and stmt_node = { stmt : stmt; pos : Error.code_pos }
|
and stmt_node = { stmt : stmt; pos : code_pos }
|
||||||
|
|
||||||
let rec show_stmt ?(indent = 0) stmt =
|
let rec show_stmt ?(indent = 0) stmt =
|
||||||
let indent_s = String.make indent ' ' in
|
let indent_s = String.make indent ' ' in
|
||||||
|
|
@ -18,10 +26,11 @@ let rec show_stmt ?(indent = 0) stmt =
|
||||||
match stmt with
|
match stmt with
|
||||||
| Expr expr -> indent_s ^ "Expr\n" ^ show_expr_ind expr.expr
|
| Expr expr -> indent_s ^ "Expr\n" ^ show_expr_ind expr.expr
|
||||||
| Break -> indent_s ^ "Break"
|
| Break -> indent_s ^ "Break"
|
||||||
|
| Continue -> indent_s ^ "Continue"
|
||||||
| Print expr -> indent_s ^ "Print\n" ^ show_expr_ind expr.expr
|
| Print expr -> indent_s ^ "Print\n" ^ show_expr_ind expr.expr
|
||||||
| VarDecl { name; init } -> (
|
| VarDecl { name; init } ->
|
||||||
indent_s ^ "Var " ^ name
|
let init_s = match init with Some init -> " = \n" ^ show_expr_ind init.expr | None -> "" in
|
||||||
^ match init with Some init -> " = \n" ^ show_expr_ind init.expr | None -> "")
|
indent_s ^ "Var " ^ name ^ init_s
|
||||||
| Block stmts ->
|
| Block stmts ->
|
||||||
let stmts_s =
|
let stmts_s =
|
||||||
List.fold_left (fun acc stmt -> acc ^ show_stmt_ind stmt.stmt ^ "\n") "" stmts
|
List.fold_left (fun acc stmt -> acc ^ show_stmt_ind stmt.stmt ^ "\n") "" stmts
|
||||||
|
|
@ -37,27 +46,58 @@ let rec show_stmt ?(indent = 0) stmt =
|
||||||
let cond_s = show_expr_ind ~add:4 cond.expr in
|
let cond_s = show_expr_ind ~add:4 cond.expr in
|
||||||
let body_s = show_stmt_ind ~add:4 body.stmt in
|
let body_s = show_stmt_ind ~add:4 body.stmt in
|
||||||
indent_s ^ "While\n" ^ indent_s ^ " Cond\n" ^ cond_s ^ "\n" ^ indent_s ^ " Body\n" ^ body_s
|
indent_s ^ "While\n" ^ indent_s ^ " Cond\n" ^ cond_s ^ "\n" ^ indent_s ^ " Body\n" ^ body_s
|
||||||
|
| For { init; cond; update; body } ->
|
||||||
|
(* let init_s =
|
||||||
|
match init with
|
||||||
|
| Some { stmt = init; _ } -> indent_s ^ " Init\n" ^ show_stmt_ind ~add:4 init
|
||||||
|
| None -> ""
|
||||||
|
in *)
|
||||||
|
let init_s =
|
||||||
|
init
|
||||||
|
|> Option.map (fun { stmt = init; _ } -> indent_s ^ " Init\n" ^ show_stmt_ind ~add:4 init)
|
||||||
|
|> Option.value ~default:""
|
||||||
|
in
|
||||||
|
(* let cond_s =
|
||||||
|
match cond with
|
||||||
|
| Some { expr = cond; _ } -> indent_s ^ " Cond\n" ^ show_expr_ind ~add:4 cond
|
||||||
|
| None -> "" *)
|
||||||
|
let cond_s =
|
||||||
|
cond
|
||||||
|
|> Option.map (fun { expr = cond; _ } -> indent_s ^ " Cond\n" ^ show_expr_ind ~add:4 cond)
|
||||||
|
|> Option.value ~default:""
|
||||||
|
in
|
||||||
|
(* let update_s =
|
||||||
|
match update with
|
||||||
|
| Some { expr = update; _ } -> indent_s ^ " Update\n" ^ show_expr_ind ~add:4 update
|
||||||
|
| None -> "" *)
|
||||||
|
let update_s =
|
||||||
|
update
|
||||||
|
|> Option.map (fun { expr = update; _ } ->
|
||||||
|
indent_s ^ " Update\n" ^ show_expr_ind ~add:4 update)
|
||||||
|
|> Option.value ~default:""
|
||||||
|
in
|
||||||
|
let body_s = indent_s ^ " Body\n" ^ show_stmt_ind ~add:4 body.stmt in
|
||||||
|
indent_s ^ "For\n" ^ init_s ^ cond_s ^ update_s ^ body_s
|
||||||
|
|
||||||
let show_stmt_node stmt_node = show_stmt stmt_node.stmt
|
let show_stmt_node stmt_node = show_stmt stmt_node.stmt
|
||||||
let make_stmt_node (pos : Error.code_pos) (stmt : stmt) : stmt_node = { stmt; pos }
|
let make_stmt_node (pos : code_pos) (stmt : stmt) : stmt_node = { stmt; pos }
|
||||||
|
let make_expr_stmt (pos : code_pos) (expr : expr_node) : stmt_node = Expr expr |> make_stmt_node pos
|
||||||
|
let make_break (pos : code_pos) : stmt_node = Break |> make_stmt_node pos
|
||||||
|
let make_continue (pos : code_pos) : stmt_node = Continue |> make_stmt_node pos
|
||||||
|
let make_print (pos : code_pos) (expr : expr_node) : stmt_node = Print expr |> make_stmt_node pos
|
||||||
|
|
||||||
let make_expr_stmt (pos : Error.code_pos) (expr : expr_node) : stmt_node =
|
let make_var_decl (pos : code_pos) (name : string) (init : expr_node option) =
|
||||||
Expr expr |> make_stmt_node pos
|
|
||||||
|
|
||||||
let make_break (pos : Error.code_pos) : stmt_node = Break |> make_stmt_node pos
|
|
||||||
|
|
||||||
let make_print (pos : Error.code_pos) (expr : expr_node) : stmt_node =
|
|
||||||
Print expr |> make_stmt_node pos
|
|
||||||
|
|
||||||
let make_var_decl (pos : Error.code_pos) (name : string) (init : expr_node option) =
|
|
||||||
VarDecl { name; init } |> make_stmt_node pos
|
VarDecl { name; init } |> make_stmt_node pos
|
||||||
|
|
||||||
let make_block (pos : Error.code_pos) (stmts : stmt_node list) : stmt_node =
|
let make_block (pos : code_pos) (stmts : stmt_node list) : stmt_node =
|
||||||
Block stmts |> make_stmt_node pos
|
Block stmts |> make_stmt_node pos
|
||||||
|
|
||||||
let make_if (pos : Error.code_pos) (cond : expr_node) (then_ : stmt_node) (else_ : stmt_node option)
|
let make_if (pos : code_pos) (cond : expr_node) (then_ : stmt_node) (else_ : stmt_node option) =
|
||||||
=
|
|
||||||
If { cond; then_; else_ } |> make_stmt_node pos
|
If { cond; then_; else_ } |> make_stmt_node pos
|
||||||
|
|
||||||
let make_while (pos : Error.code_pos) (cond : expr_node) (body : stmt_node) =
|
let make_while (pos : code_pos) (cond : expr_node) (body : stmt_node) =
|
||||||
While { cond; body } |> make_stmt_node pos
|
While { cond; body } |> make_stmt_node pos
|
||||||
|
|
||||||
|
let make_for (pos : code_pos) (init : stmt_node option) (cond : expr_node option)
|
||||||
|
(update : expr_node option) (body : stmt_node) =
|
||||||
|
For { init; cond; update; body } |> make_stmt_node pos
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue