packages feed

zeolite-lang-0.24.0.0: tests/traverse.0rt

/* -----------------------------------------------------------------------------
Copyright 2021 Kevin P. Barry

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
----------------------------------------------------------------------------- */

// Author: Kevin P. Barry [ta0kira@gmail.com]

testcase "simple traverse tests" {
  success
}

unittest simpleSum {
  Int total <- 0
  traverse (Counter.zeroIndexed(5) -> Int i) {
    total <- total+i
  }
  \ Testing.checkEquals(total, 10)
}

unittest ignoreValue {
  Int total <- 0
  traverse (Counter.zeroIndexed(5) -> _) {
    total <- total+1
  }
  \ Testing.checkEquals(total, 5)
}

unittest existingVariable {
  Int total <- 0
  Int i <- 0
  traverse (Counter.zeroIndexed(5) -> i) {
    total <- total+i
  }
  \ Testing.checkEquals(total, 10)
  \ Testing.checkEquals(i, 4)
}

unittest breakAtLimit {
  Int total <- 0
  traverse (Counter.zeroIndexed(10) -> Int i) {
    if (i >= 5) {
      break
    }
    total <- total+i
  }
  \ Testing.checkEquals(total, 10)
}

unittest continueOnOdd {
  Int total <- 0
  traverse (Counter.zeroIndexed(10) -> Int i) {
    if (i%2 == 1) {
      continue
    }
    total <- total+i
  }
  \ Testing.checkEquals(total, 20)
}

unittest withUpdate {
  Int total <- 0
  traverse (Counter.zeroIndexed(10) -> Int i) {
    if (i%2 == 1) {
      continue
    }
  } update {
    total <- total+i
  }
  \ Testing.checkEquals(total, 45)
}

unittest overwriteVisibleValue {
  Int i <- 10
  traverse (Counter.zeroIndexed(5) -> i) {
    // i shouldn't be overwritten after the last iteration.
    i <- 0
  }
  \ Testing.checkEquals(i, 0)
}

unittest overwriteLocalValue {
  traverse (Counter.zeroIndexed(5) -> Int i) {
    // i should be visible within this scope.
    i <- 0
  } update {
    // i should also be visible here.
    \ Testing.checkEquals(i, 0)
  }
}

concrete Counter {
  refines Order<Int>

  @type zeroIndexed (Int) -> (optional Counter)
}

define Counter {
  @value Int current
  @value Int max

  zeroIndexed (max) {
    if (max < 1) {
      return empty
    } else {
      return Counter{ 0, max }
    }
  }

  next () {
    if (current+1 < max) {
      current <- current+1
      return self
    } else {
      return empty
    }
  }

  get () {
    return current
  }
}


testcase "bad traverse container" {
  error
  require "Int.+Order"
}

unittest test {
  traverse (0 -> _) { }
}


testcase "bad traverse variable" {
  error
  require "String.+Int"
}

concrete Empty<|#x> {
  refines Order<#x>

  @category new<#x> () -> (optional Empty<#x>)
}

define Empty {
  new () {
    return empty
  }

  next () {
    fail("next() not implemented")
  }

  get () {
    fail("get() not implemented")
  }
}

unittest test {
  traverse (Empty:new<String>() -> Int i) { }
}


// #auto is used internally when compiling traverse.
testcase "no clash when #auto is in category" {
  success
}

unittest test {
  \ Test<String>.run()
}

concrete Test<#auto> {
  @type run () -> ()
}

define Test {
  run () {
    Int total <- 0
    traverse (Counter.new() -> Int i) {
      total <- total+i
    }
    \ Testing.checkEquals(total, 10)
  }
}

concrete Counter {
  refines Order<Int>

  @type new () -> (Counter)
}

define Counter {
  @value Int current

  new () {
    return Counter{ 0 }
  }

  next () {
    if (current+1 < 5) {
      current <- current+1
      return self
    } else {
      return empty
    }
  }

  get () {
    return current
  }
}


// #auto is used internally when compiling traverse.
testcase "no clash when #auto is in function call" {
  success
}

unittest test {
  \ Test.run<String>()
}

concrete Test {
  @type run<#auto> () -> ()
}

define Test {
  run () {
    Int total <- 0
    traverse (Counter.new() -> Int i) {
      total <- total+i
    }
    \ Testing.checkEquals(total, 10)
  }
}

concrete Counter {
  refines Order<Int>

  @type new () -> (Counter)
}

define Counter {
  @value Int current

  new () {
    return Counter{ 0 }
  }

  next () {
    if (current+1 < 5) {
      current <- current+1
      return self
    } else {
      return empty
    }
  }

  get () {
    return current
  }
}


// #auto is used internally when compiling traverse.
testcase "no clash when #auto also the inferred type" {
  success
}

unittest test {
  \ Testing.checkEquals(Test.run("message"), "message")
}

concrete Test {
  @type run<#auto> (#auto) -> (#auto)
}

define Test {
  run (x) (i) {
    i <- x
    Int total <- 0
    traverse (Repeat:times(x, 10) -> i) {
      total <- total+1
    }
    \ Testing.checkEquals(total, 10)
  }
}

concrete Repeat<|#x> {
  refines Order<#x>

  @category times<#y> (#y, Int) -> (optional Repeat<#y>)
}

define Repeat {
  @value #x value
  @value Int count

  times (value, count) {
    if (count < 1) {
      return empty
    } else {
      return Repeat<#y>{ value, count }
    }
  }

  next () {
    if (count > 1) {
      count <- count-1
      return self
    } else {
      return empty
    }
  }

  get () {
    return value
  }
}