!include once #1034831 // Gazelle 22 Function Include for Testing

set flag PingV3.

scope test_leftArrowScript

sclass #Tester {
  O value = "123";
  sO staticField = "ok";
}

svoid test_leftArrowScript() {
  test_leftArrowScript_pinging();
  
  embedded S countLoop(int n) {
    ret [[ i <- 0; while lessThan i ]] + n + [[ { i <- plus i 1 }; i ]];
  }

  S ifThenElseScript = [[
    if lessThan a b { joinWithSpace "It's" "less" }
    else if greaterThan a b { joinWithSpace "It's" "greater" }
    else { joinWithSpace "They're" "equal" }
  ]];
  
  testFunctionValues(script -> leftArrowVerbose((S) script),
    // int primitive
    
    [[ 5 ]], 5,
    [[ -5 ]], -5,
    
    // function definition & call
    [[
      def double x {
        Math multiplyExact x 2
      }
      
      double 10
    ]], 20,
  
    // new object with constructor arguments
    [[ new Pair "hello" "world" ]], pair("hello", "world"),
    
    // double constant
    [[ str 1.5e2 ]], "150.0",
    
    // float constant
    [[ 1.5e2f ]], 150f,
    
    // hex constant
    [[ 0x80 ]], 0x80,
    
    // binary constant
    [[ 0B101 ]], 0B101,
    
    // [not implemented, too complicated]
    // last = result of last statement
    // [[ plus 1 2; Math addExact 5 last ]], 8,
    
    // get static field
    [[ Color black ]], Color.black,
    
    // get instance field,
    [[ tester <- new test_leftArrowScript_Tester; tester value ]], "123",
    
    // while loop
    countLoop(5), 5,
    
    // if statement
    [[ if lessThan 0 1 { "ok" } ]], "ok",
    [[ if lessThan 1 0 { "ok" } ]], null,
    
    // for each
    [[ l <- ll 2 3
       sum <- 0
       for number in l { sum <- plus sum number }
       sum
    ]], 5,
    
    // for each is also map()
    [[ for i in ll 2 3 { plus i 1 }
    ]], ll(3, 4),
    
    // for each as nested expression
    [[ joinWithSpace (for i in ll 2 3 { i }) ]], "2 3",
    
    // some bug
    [[
      img <- newImage 10 10
      r <- randomRect img 10 10
    ]], rect(0, 0, 10, 10),

    // another bug
    [[
      a <- 1
      b <- a
      a
    ]], 1,
    
    // null bug
    [[
      a <- 1
      a <- null
      a
    ]], null,
    
    // return at the end
    [[
      return 17
    ]], 17,
    
    // two returns (first wins)
    [[
      return 13
      return 22
    ]], 13,
    
    // same with "ret" for "return"
    [[
      ret 13
      ret 22
    ]], 13,
    
    // conditional return
    
    [[
      if true { return "so true" }
      return "oops"
    ]], "so true",
    
    [[
      if false { return "not true" }
      return "but this"
    ]], "but this",
    
    // if-then-else
    
    [[ a <- 1; b <- 2; ]] + ifThenElseScript, "It's less",
    [[ a <- 6; b <- 2; ]] + ifThenElseScript, "It's greater",
    [[ a <- 5.0; b <- 5.0; ]] + ifThenElseScript, "They're equal",
    
    // nested expressions
    
    [[ plus (plus 1 2) 4 ]], 7,
    [[ (plus 5 6) toString ]], "11",
    
    // nested expressions over 2 lines (it's ok to split an expression
    // over multiple lines if you at least parenthesis is still open)
    
    [[ (plus 1
         (plus 2 3)) ]], 6,
    
    // another case for nested expressions     
    [[ str (Color black) ]], str(Color.black),
    
    // bug with nested for + new + linebreak + for (phew)
    [[ (for y in ll 1 {
         new Pair 1 2
         for x in null {}
       }); 0 ]], 0,
       
    // param declares a variable
    [[ param x; x ]], null,
    
    // return with assignment
    [[ return x <- 1 ]], 1,
    
    // for pair
    [[ for pair a b in ll (pair 1 2) (pair 3 4) {
      plus a b
    } ]], ll(3, 7),
    
    // for key, value (map)
    [[ for a, b in litorderedmap 1 2 3 4 {
      plus a b
    } ]], ll(3, 7),
    
    // for index, value (list)
    [[ for index i, a in ll "a" "b" {
      concatStrings i a
    } ]], ll("0a", "1b"),
    
    // automatically convert int to long for method call
    [[ longPlus 1 2 ]], 3L,
    
    // same for constructors
    [[ new Ratio 1 ]], Ratio(1),
    
    // call a function from another function
    [[ def f1 { 1 }
       def f2 { plus (f1) 2 }
       f2 ]], 3,
    
     // return out of for loop
     [[ for x in iota 2 { return x } ]], 1,
     
     // continue loop
     [[ for x in iota 2 {
          if eq x 1 { continue }
          return "ok"
        } ]], "ok",
     
     // new without new
     [[ Pair (neg 1) (neg 2) ]], pair(-1, -2),
     
     // variable doesn't come into scope until it's initialized
     [[ neg <- neg 1; neg ]], -1,
     
     // repeat x { ... } construct
     [[ x <- 0
        repeat plus 1 2 { x <- plus x 1 }
        x ]], 3,
        
     // update outer variable from function
     [[ x <- 0
        def incIt { outer x <- plus x 1 }
        incIt
        x ]], 1,
        
      // return directly followed by }
      [[ if true { return } ]], null,
    
      // return from lambda (exits only the lambda, just like in Java)
      [[ repF 2 (IF0 -> { return 1; null }) ]], ll(1, 1),
      
      // "temp" (like in JavaX)
      [[ list <- new ArrayList
         {
           temp tempAdd list 5
           assertContains list 5
         }
         list
      ]], ll(),
     
      // "temp" test 2 (assure it handles exceptions properly)
      [[ list <- new ArrayList
         pcallF (Runnable -> {
           temp tempAdd list 5
           fail
         })
         list
      ]], ll(),
    // add simple tests here
  );
  
  // get reference to current var context with _context
  
  new GazelleV_LeftArrowScriptParser parser;
  enableScaffolding(parser);
  parser.allowTheWorld();
  new VarContext ctx;
  assertSame(ctx, parser.parse("_context").get(ctx));
  
  test_leftArrowScriptParseErrors();
  
  test_leftArrowScript_lambdas();
  
  test_leftArrowScript_binaryOperators();
  
  test_leftArrowScriptCompileErrors();

  // get static field in functionContainer
  
  parser = new GazelleV_LeftArrowScriptParser;
  parser.allowTheWorld(Tester);
  assertEqualsVerbose("ok", parser.parse("staticField")!);
  assertEqualsVerbose("ko", parser.parse("reversed staticField")!);
  
  // add more tests here
  
  print("All left-arrow script tests OK!");
}