## ----closure-example-----------------------------------------------------
power <- function(exponent) {
  function(x) {
    x ^ exponent
  }
}
square <- power(2)
square(2)
cube <- power(3)
cube(2)

## ---- succeed------------------------------------------------------------
succeed <- function(string) {
  return(function(nextString) {
    return(list(result = string, leftover=nextString))
  })
}
succeed("1") ("abc")

## ---- item---------------------------------------------------------------
item <- function(...){
  return(function(string){
    if(length(string)==0){return(NULL)}
    return (if(string=="") list() else list(result=substr(string, 1, 1), leftover=substring(string, 2)))
  })
}
item() ("abc")

## ---- satisfy------------------------------------------------------------
satisfy <- function(p) {
  return(function(string) {
    if (length(string)==0) {
      return(list())
    }
    else if (string==""){
      return(list())
    }
    else {
      result_ = list(result=substr(string, 1, 1), leftover=substring(string, 2))
      if (p(result_$result)) {
        return(succeed(result_$result)(result_$leftover))
      }
      else{
        return(list())
      }
    }    
  })
}
satisfy(function(x) {x == "a"}) ("abc")

## ---- literal------------------------------------------------------------
literal <- function(char) {
  satisfy(function(x){return(x==char)})
}
literal("a") ("abc")

## ---- alt----------------------------------------------------------------
alt <- function(p1, p2) {
  return(function(string){
    result <- p1 (string)
    if(!is.null(result$leftover)) {return(result)}
    else{
      return(p2 (string))
    }
  })
}
`%alt%` <- alt
(item() %alt% succeed("2"))("abcdef")
alt(item(), succeed("2")) ("abcdef")

## ---- then---------------------------------------------------------------
then <- function(p1, p2) {
  return(function(string) {
    result <- p1 (string)
    if (length(result) == 0) {
      return (list())
    }
    else {
      result_ <- p2 (result$leftover)
      if (length(result_$leftover) == 0 || is.null(result_$leftover)) {return(list())}
      return(list(result=append(list(result$result), result_$result), leftover=result_$leftover))
    }
  })
}
`%then%` <- then
(literal("a") %then% literal("b")) ("abc")

## ---- using--------------------------------------------------------------
using <- function(p, f) {
  return(function(string) {
    result <- p (string) 
    if(length(result) == 0) {return(list())}
    return(list(result=f(result$result),
                leftover=result$leftover))
  })
}
`%using%` <- using
(item() %using% function(x) {as.numeric(x) + 100}) ("1abc")

## ---- many---------------------------------------------------------------
many <- function(p) {
  return(function(string) {
    ((p %then% many(p)) %alt% succeed(NULL)) (string)
  })
}
many(literal("1")) ("111223")

## ---- some---------------------------------------------------------------
some <- function(p) {
  return(function(string){
    (p %then% many(p)) (string)
  })
}
some(literal("a"))("aaabbc")

## ---- derived------------------------------------------------------------
Digit <- function(...) {satisfy(function(x) {return(!!length(grep("[0-9]", x)))})}
Lower <- function(...) {satisfy(function(x) {return(!!length(grep("[a-z]", x)))})}
Upper <- function(...) satisfy(function(x) {return(!!length(grep("[A-Z]", x)))})
Alpha <- function(...) satisfy(function(x) {return(!!length(grep("[A-Za-z]", x)))})
AlphaNum <- function(...) satisfy(function(x) {return(!!length(grep("[A-Za-z0-9]", x)))})
SpaceCheck <- function(...) satisfy(function(x) {return(!!length(grep("\\s", x)))})

## ---- String-------------------------------------------------------------
String <- function(string) {
  if (string=="") {
    return (succeed(NULL))
  }
  else {
    result_=substr(string, 1, 1)
    leftover_=substring(string, 2)
    return((literal(result_) %then% 
            String(leftover_)) %using% 
             function(x) {paste(unlist(c(x)), collapse="")})
  }
}
String("123")("123 abc")

## ---- ident--------------------------------------------------------------
ident <- function() {(many(AlphaNum()) %using%
          function(x) paste0(unlist(c(x)), collapse=""))}
nat <- function() {
  some(Digit()) %using%
  function(x) {paste(unlist(c(x)), collapse="")}
}
space <- function() {
  many(SpaceCheck()) %using%
  function(x) {return("")}
}
ident() ("var1 = 123")
nat() ("123456")

## ---- token--------------------------------------------------------------
token <- function(p) {
  space() %then%
    p %then%
    space() %using%
    function(x) {return(unlist(c(x))[2])}
}
token(ident()) ("   var1   ")

## ---- identifier---------------------------------------------------------
identifier <- function(...) {token(ident())}
natural <- function(...) {token(nat())}
symbol <- function(xs) {token(String(xs))}
identifier() ("   var1   ")

## ---- arith--------------------------------------------------------------
expr <- ((term %then% 
            symbol("+") %then%
            expr %using% function(x) {
              print(unlist(c(x)))
              return(sum(as.numeric(unlist(c(x))[c(1,3)])))
            }) %alt% 
          (term %then% 
            symbol("-") %then%
            expr %using% function(x) {
              print(unlist(c(x)))
              return(Reduce("-", as.numeric(unlist(c(x))[c(1,3)])))
            }) %alt% term)


term <- ((factor %then% 
            symbol("*") %then%
              term %using% function(x) {
                print(unlist(c(x)))
                return(prod(as.numeric(unlist(c(x))[c(1,3)])))
              }) %alt% 
         (factor %then% 
           symbol("/") %then%
           term %using% function(x) {
             print(unlist(c(x)))
             return(Reduce("/", as.numeric(unlist(c(x))[c(1,3)])))
          }) %alt% factor)

factor <- ((symbol("(") %then%
            expr %then%
            symbol(")") %using% function(x){
              print(unlist(c(x)))
              return(as.numeric(unlist(c(x))[2]))
            }) %alt% natural())

## ---- exp----------------------------------------------------------------
expr("2+(4-1)*3")

