Browse Source

Added the 'local' keyword

Chimrod 4 months ago
parent
commit
5e62a9ff37

+ 1 - 0
lib/qparser/idents.ml

@@ -90,6 +90,7 @@ let _ =
       ("LEN", FUNCTION T.Len);
       ("LET", LET);
       ("LOC", FUNCTION T.Loc);
+      ("LOCAL", LOCAL);
       ("MAINTXT", IDENT "MAINTXT");
       ("$MAINTXT", IDENT "$MAINTXT");
       ("MAX", FUNCTION T.Max);

+ 1 - 1
lib/qparser/qsp_expression.mly

@@ -89,5 +89,5 @@ unary_operator:
             (* No declaration, consider index at 0 *)
             None
         | Some other -> other in
-        Qsp_syntax.S.{ pos = $loc ; name ; index }
+        Qsp_syntax.S.{ pos = $loc ; name ; index; local = false }
     }

+ 4 - 4
lib/qparser/qsp_instruction.mly

@@ -64,21 +64,21 @@ keyword:
     | k = KEYWORD  { k }
 
 let_assignation:
-    | assignation
+    | is_local = assignation
       variable = variable
       op = assignation_operator
       value = expression 
     { 
-        let variable = Helper.variable variable
+        let variable = { (Helper.variable variable) with local = is_local } 
         and value = Analyzer.Expression.v value in
-
         Analyzer.Instruction.assign $loc variable op value
     }
 
 %inline assignation: 
     |
     | LET 
-    | SET {}
+    | SET   { false }
+    | LOCAL { true }
 
 assignation_operator:
     | EQUAL { T.Eq'  }

+ 1 - 0
lib/qparser/tokens.mly

@@ -34,6 +34,7 @@
 %token ELSE
 %token ELIF
 %token END
+%token LOCAL
 %token LET
 %token SET
 %token OBJ

+ 6 - 1
lib/syntax/S.ml

@@ -14,7 +14,12 @@ type pos = Lexing.position * Lexing.position
 (** The type pos is used to track the starting and ending position for the
     given location. *)
 
-type ('a, 'b) variable = { pos : 'a; name : string; index : 'b option }
+type ('a, 'b) variable = {
+  pos : 'a;
+  name : string;
+  index : 'b option;
+  local : bool;
+}
 (** Describe a variable, using the name in capitalized text, and an optionnal
     index.
 

+ 8 - 5
lib/syntax/check.ml

@@ -127,13 +127,13 @@ module Helper = struct
       int ->
       (S.pos, result array) S.variable ->
       (S.pos, 'a) S.variable =
-   fun typeid i { pos = var_pos; name; index } ->
+   fun typeid i { pos = var_pos; name; index; local } ->
     let index_i =
       Option.map
         (fun expression -> Option.get (get typeid (Array.get expression i)))
         index
     in
-    S.{ pos = var_pos; name; index = index_i }
+    S.{ pos = var_pos; name; index = index_i; local }
 end
 
 module Make (A : App) = struct
@@ -244,21 +244,24 @@ module Make (A : App) = struct
           R { witness = expr_witness; value })
 
     let ident : (S.pos, t) S.variable -> t =
-     fun { pos : S.pos; name : string; index : t option } ->
+     fun { pos : S.pos; name : string; index : t option; local } ->
       Array.init len ~f:(fun i ->
           let (E { module_ = (module S); expr_witness; _ }) = Array.get A.t i in
 
           match index with
           | None ->
               (* Easest case, just return the plain ident *)
-              let value = S.Expression.ident { pos; name; index = None } in
+              let value =
+                S.Expression.ident { pos; name; index = None; local }
+              in
               R { witness = expr_witness; value }
           | Some t -> (
               match get expr_witness (Array.get t i) with
               | None -> failwith "Does not match"
               | Some value_1 ->
                   let value =
-                    S.Expression.ident { pos; name; index = Some value_1 }
+                    S.Expression.ident
+                      { pos; name; index = Some value_1; local }
                   in
                   R { witness = expr_witness; value }))
 

+ 5 - 5
lib/syntax/compose.ml

@@ -12,8 +12,8 @@ module Lazier (E : S.Expression) :
   let integer : S.pos -> string -> t = fun pos i -> lazy (E.integer pos i)
 
   let ident : (S.pos, t) S.variable -> t =
-   fun { pos; name : string; index : t option } ->
-    lazy (E.ident { pos; name; index = Option.map Lazy.force index })
+   fun { pos; name : string; index : t option; local } ->
+    lazy (E.ident { pos; name; index = Option.map Lazy.force index; local })
 
   let literal : S.pos -> t T.literal list -> t =
    fun pos litts ->
@@ -82,10 +82,10 @@ module Expression (E : S.Expression) = struct
     let v : t -> t' = fun (type_of, v) -> M.v (v' type_of, v)
 
     let ident : (S.pos, t) S.variable -> t =
-     fun { pos; name : string; index : t option } ->
-      let t' = E.ident { pos; name; index = Option.map fst index } in
+     fun { pos; name : string; index : t option; local } ->
+      let t' = E.ident { pos; name; index = Option.map fst index; local } in
       let index' = Option.map (fun (e, m) -> (v' e, m)) index in
-      (t', M.ident { pos; name; index = index' } (v' t'))
+      (t', M.ident { pos; name; index = index'; local } (v' t'))
 
     let integer : S.pos -> string -> t =
      fun pos i ->

+ 12 - 6
lib/syntax/tree.ml

@@ -14,7 +14,12 @@ module Ast = struct
   type 'a literal = 'a T.literal = Text of string | Expression of 'a
   [@@deriving eq, show]
 
-  type 'a variable = { pos : 'a; name : string; index : 'a expression option }
+  type 'a variable = {
+    pos : 'a;
+    name : string;
+    index : 'a expression option;
+    local : bool;
+  }
   [@@deriving eq, show]
 
   and 'a expression =
@@ -87,7 +92,7 @@ end = struct
     | Literal (pos, l) ->
         let litt = List.map ~f:(T.map_litteral ~f:(hash f)) l in
         Hashtbl.hash (f pos, litt)
-    | Ident { pos; name; index } ->
+    | Ident { pos; name; index; _ } ->
         Hashtbl.hash (f pos, name, Option.map (hash f) index)
     | BinaryOp (pos, op, o1, o2) ->
         Hashtbl.hash (f pos, op, hash f o1, hash f o2)
@@ -113,9 +118,9 @@ end = struct
     Ast.BinaryOp (pos, op, op1, op2)
 
   let ident : (S.pos, t) S.variable -> t =
-   fun { pos; name; index } ->
+   fun { pos; name; index; local } ->
     let index = Option.map (fun i -> i) index in
-    Ast.Ident { pos; name; index }
+    Ast.Ident { pos; name; index; local }
 end
 
 module Instruction :
@@ -161,9 +166,9 @@ module Instruction :
       T.assignation_operator ->
       Expression.t' ->
       t =
-   fun pos_loc { pos; name; index } op expr ->
+   fun pos_loc { pos; name; index; local } op expr ->
     (*let index = Option.map (fun i -> fst @@ Expression.observe (i [])) index*)
-    Ast.Declaration (pos_loc, { pos; name; index }, op, expr)
+    Ast.Declaration (pos_loc, { pos; name; index; local }, op, expr)
 
   let for_ :
       S.pos ->
@@ -180,6 +185,7 @@ module Instruction :
           pos = variable.S.pos;
           name = variable.S.name;
           index = variable.S.index;
+          local = variable.S.local;
         }
     in
     Ast.For { loc; variable; start; to_; step; statements }

+ 6 - 1
lib/syntax/tree.mli

@@ -10,7 +10,12 @@ module Ast : sig
   type 'a literal = 'a T.literal = Text of string | Expression of 'a
   [@@deriving eq, show]
 
-  type 'a variable = { pos : 'a; name : string; index : 'a expression option }
+  type 'a variable = {
+    pos : 'a;
+    name : string;
+    index : 'a expression option;
+    local : bool;
+  }
   [@@deriving eq, show]
   (** A variable, used both in an expression (reference) or in a statement
       (assignation) *)

+ 1 - 0
test/literals.ml

@@ -83,6 +83,7 @@ let expression () =
                                 Tree.Ast.pos = _position;
                                 name = "VAR";
                                 index = None;
+                                local = false;
                               },
                             Tree.Ast.Integer (_position, "0") );
                         Tree.Ast.Integer (_position, "1");

+ 118 - 41
test/syntax.ml

@@ -96,7 +96,9 @@ let test_negative_numeric_expression () =
 
 let test_negative_numeric_expression2 () =
   let index = None in
-  let var = { Ast.pos = _position; name = "CURTIMESUN"; index } in
+  let var =
+    { Ast.pos = _position; name = "CURTIMESUN"; index; local = false }
+  in
   _test_instruction "-(780-CurTimeSun)"
     Ast.
       [
@@ -110,22 +112,22 @@ let test_negative_numeric_expression2 () =
 
 let test_str_variable () =
   let index = None in
-  let var = { Ast.pos = _position; name = "$VALUE"; index } in
+  let var = { Ast.pos = _position; name = "$VALUE"; index; local = false } in
   _test_instruction "$value" [ Expression (Ident var) ]
 
 let test_variable () =
   let index = None in
-  let var = { Ast.pos = _position; name = "VALUE"; index } in
+  let var = { Ast.pos = _position; name = "VALUE"; index; local = false } in
   _test_instruction "value" [ Expression (Ident var) ]
 
 let test_indexed_variable () =
   let index = Some Ast.(Integer (_position, "1")) in
-  let var = { Ast.pos = _position; name = "VALUE"; index } in
+  let var = { Ast.pos = _position; name = "VALUE"; index; local = false } in
   _test_instruction "value[1]" [ Expression (Ident var) ]
 
 let test_let_literal () =
   let index = None in
-  let var = { Ast.pos = _position; name = "VALUE"; index } in
+  let var = { Ast.pos = _position; name = "VALUE"; index; local = false } in
   _test_instruction "let value = '123'"
     Ast.
       [
@@ -133,14 +135,16 @@ let test_let_literal () =
       ]
 
 let test_set_array_append () =
-  let var = { Ast.pos = _position; name = "$VALUE"; index = None } in
+  let var =
+    { Ast.pos = _position; name = "$VALUE"; index = None; local = false }
+  in
   _test_instruction "set $value[] = ''"
     Ast.
       [ Declaration (_position, var, Eq', Literal (_position, [ T.Text "" ])) ]
 
 let test_direct_assignation () =
   let index = None in
-  let var = { Ast.pos = _position; name = "VALUE"; index } in
+  let var = { Ast.pos = _position; name = "VALUE"; index; local = false } in
   _test_instruction "value = '123'"
     Ast.
       [
@@ -149,7 +153,7 @@ let test_direct_assignation () =
 
 let test_command_assignation () =
   let index = None in
-  let st_1 = { Ast.pos = _position; name = "ST_1"; index } in
+  let st_1 = { Ast.pos = _position; name = "ST_1"; index; local = false } in
   _test_instruction "st_1 = input 'Enter the amount'"
     Ast.
       [
@@ -165,13 +169,13 @@ let test_command_assignation () =
 
 let test_assignation2 () =
   let index = None in
-  let var = { Ast.pos = _position; name = "VALUE"; index } in
+  let var = { Ast.pos = _position; name = "VALUE"; index; local = false } in
   _test_instruction "set value += 123"
     Ast.[ Declaration (_position, var, Inc, Integer (_position, "123")) ]
 
 let test_multilie_literal () =
   let index = None in
-  let var = { Ast.pos = _position; name = "VALUE"; index } in
+  let var = { Ast.pos = _position; name = "VALUE"; index; local = false } in
   _test_instruction {| 
   value = {
 $a = '123'
@@ -194,7 +198,7 @@ let test_nested_literal () =
     [
       Ast.Declaration
         ( _position,
-          { Ast.pos = _position; name = "VALUE"; index = None },
+          { Ast.pos = _position; name = "VALUE"; index = None; local = false },
           Qsp_syntax.T.Eq',
           Ast.Literal (_position, [ T.Text "\n\n      {\n\n      }\n" ]) );
     ]
@@ -228,8 +232,8 @@ pears   = 10
 |} in
 
   let index = None in
-  let apples = { Ast.pos = _position; name = "APPLES"; index }
-  and pears = { Ast.pos = _position; name = "PEARS"; index }
+  let apples = { Ast.pos = _position; name = "APPLES"; index; local = false }
+  and pears = { Ast.pos = _position; name = "PEARS"; index; local = false }
   and value_5 = Ast.Integer (_position, "5")
   and value_10 = Ast.Integer (_position, "10") in
   _test_instruction content
@@ -243,8 +247,8 @@ let test_multiline2 () =
   let content = "apples = 5 & pears = 10" in
 
   let index = None in
-  let apples = { Ast.pos = _position; name = "APPLES"; index }
-  and pears = { Ast.pos = _position; name = "PEARS"; index }
+  let apples = { Ast.pos = _position; name = "APPLES"; index; local = false }
+  and pears = { Ast.pos = _position; name = "PEARS"; index; local = false }
   and value_5 = Ast.Integer (_position, "5")
   and value_10 = Ast.Integer (_position, "10") in
   _test_instruction content
@@ -256,8 +260,8 @@ let test_multiline2 () =
 let test_equality () =
   let content = "apples = 5 = pears" in
   let index = None in
-  let apples = { Ast.pos = _position; name = "APPLES"; index }
-  and pears = { Ast.pos = _position; name = "PEARS"; index }
+  let apples = { Ast.pos = _position; name = "APPLES"; index; local = false }
+  and pears = { Ast.pos = _position; name = "PEARS"; index; local = false }
   and value_5 = Ast.Integer (_position, "5") in
   _test_instruction content
     [
@@ -270,8 +274,8 @@ let test_plus () =
 apples = 5 + pears
 |} in
   let index = None in
-  let apples = { Ast.pos = _position; name = "APPLES"; index }
-  and pears = { Ast.pos = _position; name = "PEARS"; index }
+  let apples = { Ast.pos = _position; name = "APPLES"; index; local = false }
+  and pears = { Ast.pos = _position; name = "PEARS"; index; local = false }
   and value_5 = Ast.Integer (_position, "5") in
   _test_instruction content
     [
@@ -287,7 +291,7 @@ let test_plus_litt () =
 'five'+ pears
 |} in
   let index = None in
-  let pears = { Ast.pos = _position; name = "PEARS"; index } in
+  let pears = { Ast.pos = _position; name = "PEARS"; index; local = false } in
   _test_instruction content
     [
       Ast.(
@@ -317,10 +321,16 @@ $firstName + ' ' + $lastName
                      Tree.Ast.pos = _position;
                      name = "$FIRSTNAME";
                      index = None;
+                     local = false;
                    },
                  Tree.Ast.Literal (_position, [ T.Text " " ]) ),
              Tree.Ast.Ident
-               { Tree.Ast.pos = _position; name = "$LASTNAME"; index = None } ));
+               {
+                 Tree.Ast.pos = _position;
+                 name = "$LASTNAME";
+                 index = None;
+                 local = false;
+               } ));
     ]
 
 let test_mod () =
@@ -338,7 +348,7 @@ let test_comment () = _test_instruction "! Comment" [ Comment _position ]
 
 let test_comment2 () =
   let index = None in
-  let a = { Ast.pos = _position; name = "A"; index }
+  let a = { Ast.pos = _position; name = "A"; index; local = false }
   and value_0 = Ast.Integer (_position, "0") in
   _test_instruction "a = 0 &! Comment"
     Ast.[ Declaration (_position, a, Eq', value_0); Comment _position ]
@@ -349,7 +359,7 @@ let test_comment3 () = _test_instruction {|!!1234
 (** The exclamation mark here is an operation and not a comment *)
 let test_comment4 () =
   let index = None in
-  let a = { Ast.pos = _position; name = "A"; index }
+  let a = { Ast.pos = _position; name = "A"; index; local = false }
   and value_0 = Ast.Integer (_position, "0") in
   _test_instruction "a = rand(0, 1) ! 0"
     [
@@ -373,7 +383,7 @@ let test_comment5 () =
     [
       Ast.Declaration
         ( _position,
-          { Ast.pos = _position; name = "A"; index = None },
+          { Ast.pos = _position; name = "A"; index = None; local = false },
           Qsp_syntax.T.Eq',
           Ast.Function (_position, Rand, []) );
       Ast.Comment _position;
@@ -404,8 +414,8 @@ also count}. This is still the same comment. |}
  *)
 let test_precedence () =
   let index = None in
-  let x = Ast.Ident { Ast.pos = _position; name = "X"; index }
-  and y = Ast.Ident { Ast.pos = _position; name = "Y"; index } in
+  let x = Ast.Ident { Ast.pos = _position; name = "X"; index; local = false }
+  and y = Ast.Ident { Ast.pos = _position; name = "Y"; index; local = false } in
   _test_instruction "no x = y"
     Ast.[ Expression (Op (_position, No, BinaryOp (_position, Eq, x, y))) ]
 
@@ -413,8 +423,8 @@ let test_precedence () =
     expression *)
 let test_precedence2 () =
   let index = None in
-  let x = { Ast.pos = _position; name = "X"; index }
-  and y = Ast.Ident { Ast.pos = _position; name = "Y"; index } in
+  let x = { Ast.pos = _position; name = "X"; index; local = false }
+  and y = Ast.Ident { Ast.pos = _position; name = "Y"; index; local = false } in
   _test_instruction "x = y ! 0"
     Ast.
       [
@@ -427,7 +437,8 @@ let test_precedence2 () =
 
 let test_if () =
   let index = Some Ast.(Integer (_position, "0")) in
-  let args = Ast.(Ident { pos = _position; name = "$ARGS"; index })
+  let args =
+    Ast.(Ident { pos = _position; name = "$ARGS"; index; local = false })
   and expr1 = Ast.(Literal (_position, [ T.Text "blockA" ]))
   and expr2 =
     Ast.(Expression (Literal (_position, [ T.Text "You are in block A" ])))
@@ -549,12 +560,22 @@ let test_if_inline_act () =
                     ( _position,
                       Qsp_syntax.T.Gte,
                       Ast.Ident
-                        { Ast.pos = _position; name = "HOUR"; index = None },
+                        {
+                          Ast.pos = _position;
+                          name = "HOUR";
+                          index = None;
+                          local = false;
+                        },
                       Ast.Integer (_position, "8") ) ),
               [
                 Ast.Declaration
                   ( _position,
-                    { Ast.pos = _position; name = "MINUT"; index = None },
+                    {
+                      Ast.pos = _position;
+                      name = "MINUT";
+                      index = None;
+                      local = false;
+                    },
                     Qsp_syntax.T.Inc,
                     Ast.Integer (_position, "1") );
                 Ast.Act
@@ -592,7 +613,12 @@ let test_if_multiline () =
                     ( _position,
                       Qsp_syntax.T.Gte,
                       Ast.Ident
-                        { Ast.pos = _position; name = "_HOUR"; index = None },
+                        {
+                          Ast.pos = _position;
+                          name = "_HOUR";
+                          index = None;
+                          local = false;
+                        },
                       Ast.Integer (_position, "8") ) ),
               [ Tree.Ast.Expression (Tree.Ast.Integer (_position, "1")) ] );
           elifs = [];
@@ -631,7 +657,8 @@ let test_if_inline_act2 () =
 
 let test_precedence3 () =
   let index = Some Ast.(Integer (_position, "0")) in
-  let args = Ast.(Ident { pos = _position; name = "$ARGS"; index })
+  let args =
+    Ast.(Ident { pos = _position; name = "$ARGS"; index; local = false })
   and expr1 = Ast.(Literal (_position, [ T.Text "blockA" ]))
   and expr2 =
     Ast.(Expression (Literal (_position, [ T.Text "You are in block A" ])))
@@ -667,8 +694,15 @@ let test_gt () =
       Ast.Call
         ( _position,
           Qsp_syntax.T.Goto,
-          [ Ast.Ident { Ast.pos = _position; name = "$CURLOC"; index = None } ]
-        );
+          [
+            Ast.Ident
+              {
+                Ast.pos = _position;
+                name = "$CURLOC";
+                index = None;
+                local = false;
+              };
+          ] );
     ]
 
 let test_nl () =
@@ -742,7 +776,7 @@ let test_precedence6 () =
     identifier *)
 let test_operator () =
   let index = None in
-  let a = { Ast.pos = _position; name = "A"; index }
+  let a = { Ast.pos = _position; name = "A"; index; local = false }
   and value_0 = Ast.Integer (_position, "0") in
   _test_instruction "a *0"
     Ast.[ Expression (BinaryOp (_position, Product, Ident a, value_0)) ]
@@ -866,7 +900,12 @@ let minus_operator () =
            ( _position,
              T.Minus,
              Tree.Ast.Ident
-               { Tree.Ast.pos = _position; name = "DAY"; index = None },
+               {
+                 Tree.Ast.pos = _position;
+                 name = "DAY";
+                 index = None;
+                 local = false;
+               },
              Tree.Ast.Integer (_position, "7") ));
     ]
 
@@ -876,10 +915,20 @@ let test_stattxt () =
     [
       Tree.Ast.Declaration
         ( _position,
-          { Tree.Ast.pos = _position; name = "$VALUE"; index = None },
+          {
+            Tree.Ast.pos = _position;
+            name = "$VALUE";
+            index = None;
+            local = false;
+          },
           T.Eq',
           Tree.Ast.Ident
-            { Tree.Ast.pos = _position; name = "$STATTXT"; index = None } );
+            {
+              Tree.Ast.pos = _position;
+              name = "$STATTXT";
+              index = None;
+              local = false;
+            } );
     ]
 
 let test_for_end () =
@@ -889,7 +938,13 @@ end|}
       Tree.Ast.For
         {
           loc = _position;
-          variable = { Tree.Ast.pos = _position; name = "A"; index = None };
+          variable =
+            {
+              Tree.Ast.pos = _position;
+              name = "A";
+              index = None;
+              local = false;
+            };
           start = Tree.Ast.Integer (_position, "1");
           to_ = Tree.Ast.Integer (_position, "10");
           step = None;
@@ -904,7 +959,13 @@ end|}
       Tree.Ast.For
         {
           loc = _position;
-          variable = { Tree.Ast.pos = _position; name = "A"; index = None };
+          variable =
+            {
+              Tree.Ast.pos = _position;
+              name = "A";
+              index = None;
+              local = false;
+            };
           start = Tree.Ast.Integer (_position, "1");
           to_ = Tree.Ast.Integer (_position, "10");
           step = Some (Tree.Ast.Function (_position, T.Rnd, []));
@@ -912,6 +973,21 @@ end|}
         };
     ]
 
+let test_local () =
+  _test_instruction {|local tempora = 12|}
+    [
+      Tree.Ast.Declaration
+        ( _position,
+          {
+            Tree.Ast.pos = _position;
+            name = "TEMPORA";
+            index = None;
+            local = true;
+          },
+          T.Eq',
+          Tree.Ast.Integer (_position, "12") );
+    ]
+
 let test =
   ( "Syntax",
     [
@@ -983,4 +1059,5 @@ let test =
       Alcotest.test_case "stattxt" `Quick test_stattxt;
       Alcotest.test_case "for ... end" `Quick test_for_end;
       Alcotest.test_case "for step ... end" `Quick test_for_end_with_step;
+      Alcotest.test_case "local variable" `Quick test_local;
     ] )