├── .gitignore ├── project ├── build.properties └── plugins.sbt └── src ├── main ├── scala │ └── jaskell │ │ ├── sql │ │ ├── Not.scala │ │ ├── Or.scala │ │ ├── NotEqual.scala │ │ ├── CouldLimit.scala │ │ ├── CouldOffset.scala │ │ ├── CouldBeFrom.scala │ │ ├── CouldBeJoin.scala │ │ ├── Union.scala │ │ ├── CouldCondition.scala │ │ ├── CouldBeColumn.scala │ │ ├── CouldBeQuote.scala │ │ ├── From.scala │ │ ├── Directive.scala │ │ ├── Empty.scala │ │ ├── CouldReturning.scala │ │ ├── CouldIn.scala │ │ ├── CouldGroup.scala │ │ ├── CouldOrder.scala │ │ ├── CouldWhere.scala │ │ ├── CouldHaving.scala │ │ ├── CouldOn.scala │ │ ├── In.scala │ │ ├── Literal.scala │ │ ├── As.scala │ │ ├── Where.scala │ │ ├── CouldAs.scala │ │ ├── Text.scala │ │ ├── Order.scala │ │ ├── Group.scala │ │ ├── Having.scala │ │ ├── On.scala │ │ ├── Join.scala │ │ ├── By.scala │ │ ├── Func.scala │ │ ├── CouldFrom.scala │ │ ├── Is.scala │ │ ├── Condition.scala │ │ ├── CouldUnion.scala │ │ ├── Delete.scala │ │ ├── Quote.scala │ │ ├── Parameter.scala │ │ ├── Query.scala │ │ ├── Name.scala │ │ ├── Update.scala │ │ ├── Returning.scala │ │ ├── Expression.scala │ │ ├── CouldJoin.scala │ │ ├── Select.scala │ │ ├── Case.scala │ │ ├── Insert.scala │ │ ├── CouldBeCondition.scala │ │ ├── SQL.scala │ │ ├── With.scala │ │ └── Statement.scala │ │ ├── croupier │ │ ├── Scale.scala │ │ ├── Ranker.scala │ │ ├── Fair.scala │ │ ├── Damping.scala │ │ ├── Invert.scala │ │ ├── Poker.scala │ │ ├── LiteScaled.scala │ │ ├── Scaled.scala │ │ ├── Ranked.scala │ │ ├── BinaryScaled.scala │ │ └── BinaryRanked.scala │ │ ├── expression │ │ ├── ExpressionException.scala │ │ ├── Expression.scala │ │ ├── Quote.scala │ │ ├── N.scala │ │ ├── Sub.scala │ │ ├── Add.scala │ │ ├── Divide.scala │ │ ├── Product.scala │ │ ├── Parameter.scala │ │ ├── parsers │ │ │ ├── Num.scala │ │ │ ├── D.scala │ │ │ ├── Q.scala │ │ │ ├── A.scala │ │ │ ├── P.scala │ │ │ ├── S.scala │ │ │ ├── Param.scala │ │ │ └── Parser.scala │ │ ├── Env.scala │ │ └── Binary.scala │ │ └── parsec │ │ ├── ParsecException.scala │ │ ├── Skip1Spaces.scala │ │ ├── One.scala │ │ ├── Many1In.scala │ │ ├── ManyIn.scala │ │ ├── Skip1Whitespaces.scala │ │ ├── MkString.scala │ │ ├── SkipSpaces.scala │ │ ├── Return.scala │ │ ├── SkipWhitespaces.scala │ │ ├── EndOfLine.scala │ │ ├── UInt.scala │ │ ├── Crlf.scala │ │ ├── Fail.scala │ │ ├── Letter.scala │ │ ├── Space.scala │ │ ├── Is.scala │ │ ├── Digit.scala │ │ ├── Newline.scala │ │ ├── Ahead.scala │ │ ├── Whitespace.scala │ │ ├── Chars1In.scala │ │ ├── Eq.scala │ │ ├── CharsIn.scala │ │ ├── Decimal.scala │ │ ├── NoWhitespace.scala │ │ ├── OneOf.scala │ │ ├── Ne.scala │ │ ├── Skip.scala │ │ ├── Eof.scala │ │ ├── Skip1.scala │ │ ├── NoneOf.scala │ │ ├── Int.scala │ │ ├── EndBy1.scala │ │ ├── EndBy.scala │ │ ├── Atom.scala │ │ ├── UDecimal.scala │ │ ├── Many.scala │ │ ├── ScNumber.scala │ │ ├── Opt.scala │ │ ├── Ch.scala │ │ ├── NCh.scala │ │ ├── Count.scala │ │ ├── Attempt.scala │ │ ├── ChIn.scala │ │ ├── Choice.scala │ │ ├── Between.scala │ │ ├── ChNone.scala │ │ ├── Many1.scala │ │ ├── Find.scala │ │ ├── SepBy.scala │ │ ├── SepBy1.scala │ │ ├── Text.scala │ │ ├── SepEndBy.scala │ │ ├── ManyTill.scala │ │ ├── SepEndBy1.scala │ │ ├── Enumerate.scala │ │ └── Combinator.scala ├── scala-2 │ └── jaskell │ │ ├── batteries │ │ ├── Env.scala │ │ ├── Token.scala │ │ ├── Literal.scala │ │ ├── python │ │ │ ├── StringLiteral.scala │ │ │ └── Strings.scala │ │ ├── SimpleString.scala │ │ └── cstyle │ │ │ └── Strings.scala │ │ ├── parsec │ │ ├── Binder.scala │ │ ├── State.scala │ │ ├── CommonState.scala │ │ └── Txt.scala │ │ └── Monad.scala ├── scala-2.13 │ └── jaskell │ │ ├── parsec │ │ ├── TxtState.scala │ │ └── Parsec.scala │ │ └── croupier │ │ └── ZipScaled.scala ├── scala-2.12 │ └── jaskell │ │ ├── parsec │ │ ├── TxtState.scala │ │ └── Parsec.scala │ │ └── croupier │ │ └── ZipScaled.scala └── scala-2.11 │ └── jaskell │ ├── parsec │ ├── TxtState.scala │ └── Parsec.scala │ └── croupier │ ├── ZipScaled.scala │ └── Croupier.scala └── test ├── scala ├── jaskell │ ├── parsec │ │ ├── ManyInSpec.scala │ │ ├── SepBySpec.scala │ │ ├── ManyTillSpec.scala │ │ ├── ReturnSpec.scala │ │ ├── ManySpec.scala │ │ ├── SpaceSpec.scala │ │ ├── TextSpec.scala │ │ ├── EofSpec.scala │ │ ├── OneOfSpec.scala │ │ ├── NoneOfSpec.scala │ │ ├── LetterSpec.scala │ │ ├── CharsInSpec.scala │ │ ├── OneSpec.scala │ │ ├── IsSpec.scala │ │ ├── NewLineSpec.scala │ │ ├── FindSpec.scala │ │ ├── EqSpec.scala │ │ ├── UIntSpec.scala │ │ ├── Skip1Test.scala │ │ ├── Many1Spec.scala │ │ ├── SepBy1Spec.scala │ │ ├── SkipSpec.scala │ │ ├── BetweenSpec.scala │ │ ├── IntSpec.scala │ │ ├── AttemptSpec.scala │ │ ├── EnumerateSpec.scala │ │ ├── AheadSpec.scala │ │ └── CommonStateSpec.scala │ ├── sql │ │ ├── SelectSpec.scala │ │ ├── WithSpec.scala │ │ ├── WriteSpec.scala │ │ └── JoinSpec.scala │ └── expression │ │ └── ExpressionSpec.scala └── test │ └── queue.scala ├── scala-3 └── jaskell │ └── FutureSpec.scala ├── scala-2 └── jasekll │ └── FutureSpec.scala ├── scala-2.13 └── jaskell │ └── parsec │ └── InjectionSpec.scala ├── scala-2.12 └── jaskell │ └── parsec │ └── InjectionSpec.scala └── scala-2.11 └── jaskell └── parsec └── InjectionSpec.scala /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.log 3 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version = 1.8.0 -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.18") 2 | addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2") -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Not.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 19:36 9 | */ 10 | class Not { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Or.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 19:35 9 | */ 10 | class Or { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/NotEqual.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 19:37 9 | */ 10 | class NotEqual { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/CouldLimit.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 17:28 9 | */ 10 | trait CouldLimit { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/CouldOffset.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 17:29 9 | */ 10 | trait CouldOffset { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/CouldBeFrom.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 18:00 9 | */ 10 | trait CouldBeFrom extends Directive { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/CouldBeJoin.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 19:00 9 | */ 10 | trait CouldBeJoin extends Directive { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Union.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/21 13:41 9 | */ 10 | trait Union extends Directive with Query { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/CouldCondition.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 19:59 9 | */ 10 | trait CouldCondition extends Directive { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/croupier/Scale.scala: -------------------------------------------------------------------------------- 1 | package jaskell.croupier 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2023/03/28 19:01 9 | */ 10 | trait Scale[T] { 11 | def weight(item: T): Int 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala-2/jaskell/batteries/Env.scala: -------------------------------------------------------------------------------- 1 | package jaskell.batteries 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2022/05/06 00:16 9 | */ 10 | trait Env { 11 | def find(name: String): String 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala-2/jaskell/batteries/Token.scala: -------------------------------------------------------------------------------- 1 | package jaskell.batteries 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2022/05/06 00:15 9 | */ 10 | trait Token { 11 | def parse(env: Env): String 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/croupier/Ranker.scala: -------------------------------------------------------------------------------- 1 | package jaskell.croupier 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2023/03/28 19:01 9 | */ 10 | trait Ranker[T] { 11 | def rank(item: T): Double 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/CouldBeColumn.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/19 17:14 9 | */ 10 | trait CouldBeColumn extends Directive with CouldAs { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala-2/jaskell/parsec/Binder.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/06/12 19:03 9 | */ 10 | trait Binder[E, T, O] { 11 | def apply(value: T): Parsec[E, O] 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/CouldBeQuote.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * Could be Quote 元素可以放进 quote 中 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/19 17:07 9 | */ 10 | trait CouldBeQuote extends Directive { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/From.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 17:22 9 | */ 10 | trait From extends Directive with CouldWhere with CouldJoin with Query { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Directive.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 15:14 9 | */ 10 | trait Directive { 11 | def script: String 12 | 13 | def parameters: Seq[Parameter[_]] 14 | } 15 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/expression/ExpressionException.scala: -------------------------------------------------------------------------------- 1 | package jaskell.expression 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/06/10 13:38 9 | */ 10 | class ExpressionException(message: String) extends Exception(message) { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Empty.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/19 17:17 9 | */ 10 | class Empty extends Directive { 11 | override def script: String = "" 12 | 13 | override def parameters: Seq[Parameter[_]] = Seq.empty 14 | } 15 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/expression/Expression.scala: -------------------------------------------------------------------------------- 1 | package jaskell.expression 2 | 3 | import scala.util.Try 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2020/06/02 21:26 11 | */ 12 | trait Expression { 13 | def eval(env: Env): Try[Double] 14 | def makeAst: Expression = this 15 | } 16 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/ParsecException.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | /** 4 | * Parser throw ParsecException if failed. 5 | * 6 | * @author Mars Liu 7 | * @version 1.0.0 8 | */ 9 | class ParsecException(val status: Any, val message: String) extends Exception { 10 | override def getMessage: String = message 11 | } 12 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/CouldReturning.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/20 18:09 9 | */ 10 | trait CouldReturning extends Directive { 11 | def returning: Returning = new Returning { 12 | override val prefix: Directive = CouldReturning.this 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/CouldIn.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/19 18:30 9 | */ 10 | trait CouldIn extends Directive { 11 | def in (q: Quote): In = new In { 12 | override val prefix: Directive = CouldIn.this 13 | override val quote: Quote = q 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Skip1Spaces.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.Try 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class Skip1Spaces extends Parsec[Char, Unit] { 12 | val parsec = new Skip1[Char](new Space()) 13 | 14 | override def apply(s: State[Char]): Try[Unit] = parsec ? s 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/CouldGroup.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | import scala.collection.mutable 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2020/05/18 16:12 11 | */ 12 | trait CouldGroup extends Directive { 13 | def group: Group = new Group { 14 | override val prefix: Directive = CouldGroup.this 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/CouldOrder.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | import scala.collection.mutable 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2020/05/18 16:12 11 | */ 12 | trait CouldOrder extends Directive { 13 | def order:Order = new Order { 14 | override val prefix: Directive = CouldOrder.this 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/CouldWhere.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 17:26 9 | */ 10 | trait CouldWhere extends Directive { 11 | def where(c: Condition): Where = new Where { 12 | override val prefix: Directive = CouldWhere.this 13 | override val condition: Directive = c 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/scala-2/jaskell/batteries/Literal.scala: -------------------------------------------------------------------------------- 1 | package jaskell.batteries 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2022/05/06 00:41 9 | */ 10 | trait Literal { 11 | val prefix: String 12 | val begin: String 13 | val content: String 14 | val end: String 15 | def token: Token 16 | def literal: String = f"$prefix$begin$content$end" 17 | } 18 | -------------------------------------------------------------------------------- /src/main/scala-2/jaskell/batteries/python/StringLiteral.scala: -------------------------------------------------------------------------------- 1 | package jaskell.batteries.python 2 | 3 | import jaskell.batteries.Token 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2022/05/06 00:10 11 | */ 12 | trait StringLiteral extends Token { 13 | val prefix: String 14 | val literal: String 15 | val left: String 16 | val right: String 17 | } 18 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/CouldHaving.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/07/10 16:17 9 | */ 10 | trait CouldHaving extends Directive { 11 | def having(c: Condition): Having = new Having { 12 | override val prefix: Directive = CouldHaving.this 13 | override val condition: Directive = c 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/One.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.Try 4 | 5 | /** 6 | * One just take state.next, It maybe throw eof exception. 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class One [E] extends Parsec[E, E]{ 12 | 13 | override def apply(s: State[E]): Try[E] = s.next() 14 | } 15 | 16 | object One { 17 | def apply[E]: One[E] = new One() 18 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Many1In.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.Try 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2022/05/06 00:47 11 | */ 12 | case class Many1In[E](toggle: Set[E]) extends Parsec[E, Seq[E]]{ 13 | val parser: Parsec[E, Seq[E]] = Many1(OneOf(toggle)) 14 | override def apply(s: State[E]): Try[Seq[E]] = parser(s) 15 | } 16 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/ManyIn.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.Try 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2022/05/06 00:47 11 | */ 12 | case class ManyIn[E](toggle: Set[E]) extends Parsec[E, Seq[E]]{ 13 | val parser: Parsec[E, Seq[E]] = Many(OneOf(toggle)) 14 | override def apply(s: State[E]): Try[Seq[E]] = parser(s) 15 | } 16 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Skip1Whitespaces.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.Try 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class Skip1Whitespaces extends Parsec[Char, Unit] { 12 | 13 | val parsec = new Skip1[Char](new Whitespace()) 14 | 15 | override def apply(s: State[Char]): Try[Unit] = for {_ <- parsec ? s} yield () 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/CouldOn.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/20 11:27 9 | */ 10 | trait CouldOn extends Directive { 11 | def on(cond: Condition): On with CouldWhere = new On with CouldWhere { 12 | override val prefix: Directive = CouldOn.this 13 | override val condition: Condition = cond 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/MkString.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/06/12 19:03 9 | */ 10 | class MkString extends Binder[Char, Seq[Char], String] { 11 | override def apply(value: Seq[Char]): Parsec[Char, String] = Return(value.mkString) 12 | } 13 | 14 | object MkString { 15 | def apply(): MkString = new MkString() 16 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/expression/Quote.scala: -------------------------------------------------------------------------------- 1 | package jaskell.expression 2 | 3 | import scala.util.Try 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2020/06/02 21:27 11 | */ 12 | class Quote(val exp: Expression) extends Expression { 13 | 14 | override def eval(env: Env): Try[Double] = exp.eval(env) 15 | 16 | override def makeAst: Expression = new Quote(exp.makeAst) 17 | } 18 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/SkipSpaces.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.Try 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class SkipSpaces extends Parsec[Char, Unit] { 12 | val parsec = new Skip[Char](new Space()) 13 | 14 | override def apply(s: State[Char]): Try[Unit] = parsec ? s 15 | } 16 | 17 | object SkipSpaces { 18 | def apply(): SkipSpaces = new SkipSpaces() 19 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/In.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/19 18:26 9 | */ 10 | trait In extends Directive { 11 | val prefix: Directive 12 | val quote: Quote 13 | 14 | override def script: String = s"${prefix.script} IN ${quote.script}" 15 | 16 | override def parameters: Seq[Parameter[_]] = prefix.parameters ++ quote.parameters 17 | } 18 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/expression/N.scala: -------------------------------------------------------------------------------- 1 | package jaskell.expression 2 | 3 | import scala.util.{Success, Try} 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2020/06/02 21:27 11 | */ 12 | class N(val num: java.lang.Number) extends Expression { 13 | 14 | override def eval(env: Env): Try[Double] = Success(num.doubleValue()) 15 | } 16 | 17 | 18 | object N { 19 | def apply(n: java.lang.Number): N = new N(n) 20 | } 21 | -------------------------------------------------------------------------------- /src/main/scala-2/jaskell/batteries/SimpleString.scala: -------------------------------------------------------------------------------- 1 | package jaskell.batteries 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2022/05/06 00:44 9 | */ 10 | case class SimpleString(prefix: String, 11 | begin: String, 12 | content: String, 13 | end: String) extends Token { 14 | override def parse(env: Env): String = f"$prefix$begin$content$end" 15 | } 16 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Return.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Success, Try} 4 | 5 | /** 6 | * Return just lift a value to Parsec Parser. 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class Return[E, T](val element: T) extends Parsec[E, T] { 12 | 13 | override def apply(s: State[E]): Try[T] = Success(element) 14 | } 15 | 16 | object Return { 17 | def apply[E, T](element: T): Return[E, T] = new Return(element) 18 | } 19 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/expression/Sub.scala: -------------------------------------------------------------------------------- 1 | package jaskell.expression 2 | 3 | import scala.util.Try 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2020/06/02 21:35 11 | */ 12 | class Sub(l: Expression, r: Expression) extends Binary(l, r) { 13 | 14 | override def eval(env: Env): Try[Double] = for { 15 | lv <- left eval env 16 | rv <- right eval env 17 | } yield { 18 | lv - rv 19 | } 20 | 21 | override def priority: Int = 1 22 | } 23 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/SkipWhitespaces.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Success, Try} 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class SkipWhitespaces extends Parsec[Char, Unit] { 12 | val parsec = new Skip[Char](new Whitespace()) 13 | 14 | override def apply(s: State[Char]): Try[Unit] = for {_ <- parsec ? s} yield () 15 | } 16 | 17 | object SkipWhitespaces { 18 | def apply(): SkipWhitespaces = new SkipWhitespaces() 19 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Literal.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/19 17:17 9 | */ 10 | trait Literal extends Directive with CouldAs with CouldBeCondition with CouldBeColumn with Expression { 11 | val prefix : Directive 12 | val literal: String 13 | 14 | override def script: String = s"${prefix.script} $literal ".trim 15 | 16 | override def parameters: Seq[Parameter[_]] = prefix.parameters 17 | } 18 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/As.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 20:18 9 | */ 10 | trait As extends Directive with Expression with CouldBeColumn with CouldBeFrom with CouldBeJoin { 11 | val prefix: Directive 12 | val name: Name 13 | 14 | override def script: String = s"${prefix.script} AS ${name.script}" 15 | 16 | override def parameters: Seq[Parameter[_]] = prefix.parameters ++ name.parameters 17 | } 18 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Where.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 17:27 9 | */ 10 | trait Where extends Directive with CouldBeQuote with Query with CouldUnion { 11 | val prefix: Directive 12 | val condition: Directive 13 | 14 | override def script: String = prefix.script + " WHERE " + condition.script 15 | 16 | override def parameters: Seq[Parameter[_]] = prefix.parameters ++ condition.parameters 17 | } 18 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/EndOfLine.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.Try 4 | 5 | /** 6 | * check next content if a \n char or \r\n 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class EndOfLine extends Parsec[Char, String] { 12 | final private val parsec = Choice[Char, String](Attempt(Text("\n")), Text("\r\n")) 13 | 14 | override def apply(s: State[Char]): Try[String] = parsec ? s 15 | } 16 | 17 | object EndOfLine { 18 | def apply(): EndOfLine = new EndOfLine() 19 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/UInt.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.Try 4 | 5 | /** 6 | * Int try to parse a long as long int from state and without signed. 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class UInt extends Parsec[Char, String]{ 12 | val psc = new Many1[Char, Char](new Digit()) 13 | 14 | override def apply(s: State[Char]): Try[String] = { 15 | psc ? s map {_.mkString} 16 | } 17 | } 18 | 19 | object UInt { 20 | def apply(): UInt = new UInt() 21 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/CouldAs.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 20:16 9 | */ 10 | trait CouldAs extends Directive { 11 | def as(n: Name): As = new As { 12 | override val prefix: Directive = CouldAs.this 13 | override val name: Name = n 14 | } 15 | 16 | def as(n: String): As = new As { 17 | override val prefix: Directive = CouldAs.this 18 | override val name: Name = new Name(n) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Text.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/19 17:20 9 | */ 10 | class Text(val prefix:Directive = new Empty, val string:String) extends Directive with CouldAs 11 | with CouldBeColumn with Expression { 12 | val content: String = string.replace("'", "\\'") 13 | override def script: String = s"${prefix.script} '$content'" 14 | 15 | override def parameters: Seq[Parameter[_]] = prefix.parameters 16 | } 17 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/expression/Add.scala: -------------------------------------------------------------------------------- 1 | package jaskell.expression 2 | 3 | import scala.util.Try 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2020/06/02 21:35 11 | */ 12 | class Add(l: Expression, r: Expression) extends Binary(l, r) { 13 | override def eval(env: Env): Try[Double] = { 14 | for { 15 | lv <- left.eval(env) 16 | rv <- right.eval(env) 17 | } yield { 18 | lv + rv 19 | } 20 | } 21 | 22 | override def priority: Int = 1 23 | } 24 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/expression/Divide.scala: -------------------------------------------------------------------------------- 1 | package jaskell.expression 2 | 3 | import scala.util.Try 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2020/06/02 21:35 11 | */ 12 | class Divide(l: Expression, r: Expression) extends Binary(l, r) { 13 | 14 | override def eval(env: Env): Try[Double] = 15 | for { 16 | lv <- left.eval(env) 17 | rv <- right.eval(env) 18 | } yield { 19 | lv / rv 20 | } 21 | 22 | override def priority: Int = 2 23 | } 24 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/expression/Product.scala: -------------------------------------------------------------------------------- 1 | package jaskell.expression 2 | 3 | import scala.util.Try 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2020/06/02 21:36 11 | */ 12 | class Product(l: Expression, r: Expression) extends Binary(l, r) { 13 | 14 | override def eval(env: Env): Try[Double] = { 15 | for { 16 | lv <- left eval env 17 | rv <- right eval env 18 | } yield { 19 | lv * rv 20 | } 21 | } 22 | 23 | override def priority: Int = 2 24 | } 25 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Crlf.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.Try 4 | 5 | /** 6 | * Crlf 即 haskell parsec 的 crlf 算子,匹配 \r\n . 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class Crlf(val r: Ch = Ch('\r'), val n: Ch = Ch('\n')) extends Parsec[Char, String] { 12 | 13 | override def apply(s: State[Char]): Try[String] = { 14 | for { 15 | _ <- r ? s 16 | _ <- n ? s 17 | } yield "\r\n" 18 | } 19 | } 20 | 21 | object Crlf { 22 | def apply(): Crlf = new Crlf() 23 | } 24 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Fail.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.Try 4 | 5 | /** 6 | * Fail do nothing but failed. 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class Fail[E](val msg: String, val objects: Any*) extends Parsec[E, E] { 12 | val message: String = msg.format(objects) 13 | 14 | override def apply(s: State[E]): Try[E] = { 15 | s.trap(message) 16 | } 17 | } 18 | 19 | object Fail { 20 | def apply[E](msg: String, objects: Any*): Fail[E] = new Fail(msg, objects) 21 | 22 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/croupier/Fair.scala: -------------------------------------------------------------------------------- 1 | package jaskell.croupier 2 | 3 | import scala.util.Random 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2023/03/28 19:03 11 | */ 12 | class Fair[T](val random: Random) extends Poker[T] { 13 | override def select(cards: Seq[T]): Option[Int] = { 14 | if (cards == null || cards.isEmpty) { 15 | return None 16 | } 17 | if (cards.size == 1) { 18 | return Some(0) 19 | } 20 | Some(random.nextInt(cards.size)) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Order.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | import scala.collection.mutable 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2020/05/18 17:10 11 | */ 12 | trait Order { 13 | val prefix: Directive 14 | def by: By = { 15 | new By with CouldOrder { 16 | override val _prefix: Directive = prefix 17 | override val _operator: String = "ORDER" 18 | override val columns: mutable.ListBuffer[_ <: Directive] = new mutable.ListBuffer[Directive] 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Letter.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Success, Try} 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2020/06/12 18:53 11 | */ 12 | class Letter extends Parsec [Char, Char]{ 13 | override def apply(s: State[Char]): Try[Char] = s.next() flatMap { v => 14 | if(v.isLetter){ 15 | Success(v) 16 | } else { 17 | s.trap(s"expect a letter but get $v") 18 | } 19 | } 20 | } 21 | 22 | object Letter { 23 | def apply(): Letter = new Letter() 24 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Group.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | import scala.collection.mutable 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2020/05/18 17:10 11 | */ 12 | trait Group { 13 | val prefix: Directive 14 | 15 | def by: By = { 16 | new By with CouldOrder { 17 | override val _prefix: Directive = prefix 18 | override val _operator: String = "GROUP" 19 | override val columns: mutable.ListBuffer[_ <: Directive] = new mutable.ListBuffer[Directive] 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/expression/Parameter.scala: -------------------------------------------------------------------------------- 1 | package jaskell.expression 2 | 3 | import jaskell.parsec.Fail 4 | 5 | import scala.util.{Failure, Try} 6 | 7 | /** 8 | * TODO 9 | * 10 | * @author mars 11 | * @version 1.0.0 12 | * @since 2020/06/10 13:24 13 | */ 14 | class Parameter(val name: String) extends Expression { 15 | override def eval(env: Env): Try[Double] = { 16 | env.get(name).map(_.eval(env)) match { 17 | case Some(value) => value 18 | case _ => Failure(new ExpressionException(s"$name not found")) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Space.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Success, Try} 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class Space extends Parsec[Char, Char] { 12 | 13 | override def apply(s: State[Char]): Try[Char] = { 14 | s.next() flatMap { re => 15 | if(re.isSpaceChar) { 16 | Success(re) 17 | } else { 18 | s.trap(s"Expect $re is space.") 19 | } 20 | } 21 | } 22 | } 23 | 24 | object Space { 25 | def apply(): Space = new Space() 26 | } 27 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Is.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Success, Try} 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2020/07/23 14:18 11 | */ 12 | class Is[T](val predicate: Function[T, Boolean]) extends Parsec[T, T] { 13 | override def apply(s: State[T]): Try[T] = { 14 | s.next().flatMap(item => { 15 | if(predicate(item)) { 16 | Success(item) 17 | } else { 18 | s.trap(s"expect anything pass predicate check but get $item") 19 | } 20 | }) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Digit.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Success, Try} 4 | 5 | /** 6 | * Digit parser return char if it is a digit 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class Digit extends Parsec[Char, Char] { 12 | 13 | override def apply(s: State[Char]): Try[Char] = { 14 | s.next() flatMap { re => 15 | if (re.isDigit) { 16 | Success(re) 17 | } else { 18 | s.trap(s"Expect $re is digit.") 19 | } 20 | } 21 | } 22 | } 23 | 24 | object Digit { 25 | def apply(): Digit = new Digit() 26 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Newline.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Success, Try} 4 | 5 | /** 6 | * Newline match \n char 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class Newline extends Parsec[Char, Char]{ 12 | 13 | override def apply(s: State[Char]): Try[Char] = { 14 | s.next() flatMap { c => 15 | if(c == '\n') { 16 | Success(c) 17 | } else { 18 | s.trap(s"Expect a newline char but get $c") 19 | } 20 | } 21 | } 22 | } 23 | 24 | object Newline { 25 | def apply(): Newline = new Newline() 26 | } 27 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Having.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/07/10 16:18 9 | */ 10 | trait Having extends Directive with CouldBeQuote with Query 11 | with CouldUnion 12 | with CouldOrder 13 | with CouldLimit 14 | with CouldOffset { 15 | val prefix: Directive 16 | val condition: Directive 17 | 18 | override def script: String = prefix.script + " HAVING " + condition.script 19 | 20 | override def parameters: Seq[Parameter[_]] = prefix.parameters ++ condition.parameters 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/On.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 18:56 9 | */ 10 | trait On extends Directive with CouldJoin with CouldWhere with CouldGroup with CouldOrder 11 | with CouldLimit with CouldOffset with CouldCondition { 12 | val prefix: Directive 13 | val condition: Condition 14 | 15 | override def script: String = { 16 | prefix.script + " ON " + condition.script 17 | } 18 | 19 | override def parameters: Seq[Parameter[_]] = prefix.parameters ++ condition.parameters 20 | } 21 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/expression/parsers/Num.scala: -------------------------------------------------------------------------------- 1 | package jaskell.expression.parsers 2 | 3 | import jaskell.expression._ 4 | import jaskell.parsec.{Decimal, Parsec, ScNumber, State} 5 | 6 | import scala.util.Try 7 | 8 | /** 9 | * TODO 10 | * 11 | * @author mars 12 | * @version 1.0.0 13 | * @since 2020/06/02 21:41 14 | */ 15 | class Num extends Parsec[Char, Expression] { 16 | 17 | import jaskell.parsec.Txt.scNumber 18 | 19 | val parser: ScNumber = scNumber 20 | 21 | override def apply(s: State[Char]): Try[Expression] = { 22 | parser ? s map {n => new N(n.toDouble)} 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Ahead.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.Try 4 | 5 | /** 6 | * Ahead use a parser to parse state, return the result and rollback whatever 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class Ahead[E, T](var parser: Parsec[E, T]) extends Parsec[E, T] { 12 | 13 | override def apply(s: State[E]): Try[T] = { 14 | val tran = s.begin() 15 | val result = parser ? s 16 | s.rollback(tran) 17 | result 18 | } 19 | } 20 | 21 | object Ahead { 22 | def apply[E, T](parser: Parsec[E, T]): Ahead[E, T] = new Ahead[E, T](parser) 23 | } 24 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Whitespace.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Success, Try} 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2020/05/09 17:18 11 | */ 12 | class Whitespace extends Parsec [Char, Char]{ 13 | 14 | override def apply(s: State[Char]): Try[Char] = { 15 | s.next() flatMap { c => 16 | if(c.isWhitespace){ 17 | Success(c) 18 | } else { 19 | s.trap(s"expect a whitespace but get $c") 20 | } 21 | } 22 | } 23 | } 24 | 25 | object Whitespace { 26 | def apply(): Whitespace = new Whitespace() 27 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Join.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 18:56 9 | */ 10 | trait Join extends Directive with CouldOn with Query { 11 | val opt: String 12 | val prefix: Directive 13 | val j: CouldBeJoin 14 | 15 | override def script: String = { 16 | if(opt.isEmpty){ 17 | prefix.script + " JOIN " + j.script 18 | } else { 19 | prefix.script + s" $opt JOIN " + j.script 20 | } 21 | } 22 | 23 | override def parameters: Seq[Parameter[_]] = prefix.parameters ++ j.parameters 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/ManyInSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | /** 7 | * TODO 8 | * 9 | * @author mars 10 | * @version 1.0.0 11 | * @since 2020/05/12 22:48 12 | */ 13 | class ManyInSpec extends AnyFlatSpec with Matchers { 14 | "Simple" should "Run a simple test" in { 15 | val state = State(Seq(1, 2, 3, 9, 15, 2, 4, 8)) 16 | 17 | val parser = ManyIn(Set(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)) 18 | 19 | val re = parser ! state 20 | 21 | re should be (Seq(1, 2, 3, 9)) 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Chars1In.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.Try 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2022/05/06 00:59 11 | */ 12 | case class Chars1In(elements: String, caseSensitive: Boolean=true) extends Parsec[Char, String] { 13 | import jaskell.Monad.Implicits._ 14 | import jaskell.parsec.Parsec.Implicits._ 15 | 16 | val parser: Parsec[Char, Seq[Char]] = new Many1(new ChIn(elements, caseSensitive)) 17 | val p: Parsec[Char,String] = parser >>= (new MkString()) 18 | override def apply(s: State[Char]): Try[String] = p(s) 19 | } 20 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Eq.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Success, Try} 4 | 5 | /** 6 | * Eq return from state if get a item equals its. 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class Eq[E](val element: E) extends Parsec[E, E] { 12 | 13 | override def apply(s: State[E]): Try[E] = { 14 | s.next() flatMap { data => 15 | if(element == data) { 16 | return Success(element) 17 | } 18 | s.trap(s"expect $element at ${s.status} but $data") 19 | } 20 | } 21 | } 22 | 23 | object Eq { 24 | def apply[E](element: E): Eq[E] = new Eq(element) 25 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/CharsIn.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | 4 | import scala.util.Try 5 | 6 | /** 7 | * TODO 8 | * 9 | * @author mars 10 | * @version 1.0.0 11 | * @since 2022/05/06 00:59 12 | */ 13 | case class CharsIn(elements: String, caseSensitive: Boolean=true) extends Parsec[Char, String] { 14 | import jaskell.Monad.Implicits._ 15 | import jaskell.parsec.Parsec.Implicits._ 16 | 17 | val parser: Parsec[Char, Seq[Char]] = new Many(new ChIn(elements, caseSensitive)) 18 | val p: Parsec[Char,String] = parser >>= (new MkString()) 19 | override def apply(s: State[Char]): Try[String] = p(s) 20 | } 21 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/By.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | import scala.collection.mutable 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2020/05/18 16:52 11 | */ 12 | trait By extends Directive with CouldUnion { 13 | val _prefix: Directive 14 | val _operator: String 15 | 16 | val columns: mutable.ListBuffer[_ <: Directive] 17 | override def script: String = { 18 | _prefix.script + " " + _operator + " BY " + columns.map(_.script).mkString(", ") 19 | } 20 | 21 | override val parameters: Seq[Parameter[_]] = _prefix.parameters ++ columns.flatMap(_.parameters) 22 | } 23 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/SepBySpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | /** 7 | * TODO 8 | * 9 | * @author mars 10 | * @version 1.0.0 11 | * @since 2020/05/12 21:40 12 | */ 13 | class SepBySpec extends AnyFlatSpec with Matchers { 14 | "Basic" should "Run some basic tests" in { 15 | val state = State("hlhlhlhlhlhll") 16 | 17 | val p = SepBy(Eq('h'), Eq('l')) 18 | 19 | val re = p parse state 20 | re.size should be (6) 21 | re foreach { item => 22 | item should be ('h') 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Decimal.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.Try 4 | 5 | /** 6 | * Decimal parser parse a decimal number with signed or no. 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class Decimal extends Parsec[Char, String] { 12 | val sign: Parsec[Char, String] = new Attempt(Text("-")) <|> Return("") 13 | val udicemal = new UDecimal() 14 | 15 | override def apply(st: State[Char]): Try[String] = { 16 | for { 17 | s <- sign ? st 18 | num <- udicemal ? st 19 | } yield s + num 20 | } 21 | } 22 | 23 | object Decimal { 24 | def apply(): Decimal = new Decimal() 25 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Func.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/21 11:26 9 | */ 10 | class Func(val name:String, val args:Seq[Directive]) extends Directive with Expression { 11 | 12 | override def script: String = { 13 | val arguments = args.map(_.script).mkString(", ") 14 | s"$name($arguments)" 15 | } 16 | 17 | override def parameters: Seq[Parameter[_]] = args.flatMap(_.parameters) 18 | 19 | } 20 | 21 | object Func { 22 | def apply(name: String, parameters: Directive*): Func = { 23 | new Func(name, parameters) 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/NoWhitespace.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Success, Try} 4 | 5 | /** 6 | * NoWhitespace success if the char is't any whitespace. 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class NoWhitespace extends Parsec [Char, Char]{ 12 | override def apply(s: State[Char]): Try[Char] = { 13 | s.next() flatMap { c => 14 | if(c.isWhitespace){ 15 | s.trap(s"expect a char not whitespace but get $c") 16 | } else { 17 | Success(c) 18 | } 19 | } 20 | } 21 | } 22 | 23 | object NoWhitespace { 24 | def apply(): NoWhitespace = new NoWhitespace() 25 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/OneOf.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Success, Try} 4 | 5 | /** 6 | * OneOf success if item equals one of prepared. 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class OneOf[T](val items:Set[T]) extends Parsec[T, T] { 12 | 13 | override def apply(s: State[T]): Try[T] = { 14 | s.next() flatMap {v => { 15 | if(items.contains(v)){ 16 | Success(v) 17 | }else{ 18 | s.trap(s"expect a value in ${items} but get $v") 19 | } 20 | }} 21 | } 22 | } 23 | 24 | object OneOf { 25 | def apply[T](items: Set[T]): OneOf[T] = new OneOf(items) 26 | } 27 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/croupier/Damping.scala: -------------------------------------------------------------------------------- 1 | package jaskell.croupier 2 | 3 | import scala.util.Random 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2023/03/28 19:02 11 | */ 12 | class Damping[T](val random: Random) extends Poker[T] { 13 | override def select(cards: Seq[T]): Option[Int] = { 14 | if (cards == null || cards.isEmpty) { 15 | return None 16 | } 17 | if (cards.size == 1) { 18 | return Some(0) 19 | } 20 | 21 | val range = Math.log(cards.size + 1) 22 | val value = Math.exp(random.nextDouble() * range) 23 | Some(Math.floor(value).intValue() - 1) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/expression/Env.scala: -------------------------------------------------------------------------------- 1 | package jaskell.expression 2 | 3 | import scala.collection.mutable 4 | 5 | 6 | /** 7 | * TODO 8 | * 9 | * @author mars 10 | * @version 1.0.0 11 | * @since 2020/06/10 13:30 12 | */ 13 | class Env { 14 | val stack: mutable.HashMap[String, Expression] = mutable.HashMap() 15 | 16 | def put(name: String, value: Double): Option[Expression] = { 17 | stack.put(name, new N(value)) 18 | } 19 | 20 | def put(name: String, value: Expression): Option[Expression] = { 21 | stack.put(name, value) 22 | } 23 | 24 | def get(name: String): Option[Expression] = { 25 | stack.get(name) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Ne.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Success, Try} 4 | 5 | /** 6 | * Ne success if get item not equals the prepared one. 7 | * 8 | * @author Mars Liu 9 | * @version 1.0.0 10 | */ 11 | class Ne[E](val element: E) extends Parsec[E, E] { 12 | override def apply(s: State[E]): Try[E] = { 13 | s.next() flatMap { data => 14 | if (element != data) { 15 | Success(data) 16 | } else { 17 | s.trap(s"expect a object not $element at ${s.status} but $data") 18 | } 19 | } 20 | } 21 | } 22 | 23 | object Ne { 24 | def apply[E](element: E): Ne[E] = new Ne(element) 25 | } -------------------------------------------------------------------------------- /src/test/scala-3/jaskell/FutureSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell 2 | 3 | import org.scalatest.flatspec.AsyncFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | import scala.concurrent.Future 7 | 8 | /** 9 | * TODO 10 | * 11 | * @author mars 12 | * @version 1.0.0 13 | * @since 2021/02/06 15:58 14 | */ 15 | class FutureSpec extends AsyncFlatSpec with Matchers { 16 | import jaskell.Monad.Implicits.futureMonad 17 | 18 | val future = Future("success") *> Future(3.14) <:> {value => value * 2*2} <* Future("Right") 19 | 20 | "Pi" should "success a area of r=2 circle" in { 21 | future.map(value => value should be (3.14 * 2 * 2)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test/scala-2/jasekll/FutureSpec.scala: -------------------------------------------------------------------------------- 1 | package jasekll 2 | 3 | import org.scalatest.flatspec.AsyncFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | import scala.concurrent.Future 7 | 8 | /** 9 | * TODO 10 | * 11 | * @author mars 12 | * @version 1.0.0 13 | * @since 2021/02/06 15:58 14 | */ 15 | class FutureSpec extends AsyncFlatSpec with Matchers { 16 | import jaskell.Monad.Implicits._ 17 | 18 | val future: Future[Double] = Future("success") *> Future(3.14) <:> { value => value * 2*2} <* Future("Right") 19 | 20 | "Pi" should "success a area of r=2 circle" in { 21 | future.map(value => value should be (3.14 * 2 * 2)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/ManyTillSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | import jaskell.parsec.Combinator._ 6 | 7 | /** 8 | * TODO 9 | * 10 | * @author mars 11 | * @version 1.0.0 12 | * @since 2020/05/12 19:48 13 | */ 14 | class ManyTillSpec extends AnyFlatSpec with Matchers{ 15 | "Simple" should "Test a many till match" in { 16 | val state = State("hhhhhhlhhhll") 17 | 18 | val m = manyTill(Eq('h'), Eq('l')) 19 | 20 | val re = m parse state 21 | re.size should be (6) 22 | for(item <- re) { 23 | item should be ('h') 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/ReturnSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | /** 7 | * TODO 8 | * 9 | * @author mars 10 | * @version 1.0.0 11 | * @since 2020/05/12 21:18 12 | */ 13 | class ReturnSpec extends AnyFlatSpec with Matchers{ 14 | "Simple" should "Just pack a value" in { 15 | val state = State("Hello World") 16 | val returns: Return[Char, BigDecimal] = Return(BigDecimal("3.1415926")) 17 | val status = state.status 18 | val re = returns ! state 19 | re should be (BigDecimal("3.1415926")) 20 | state.status should be (status) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/croupier/Invert.scala: -------------------------------------------------------------------------------- 1 | package jaskell.croupier 2 | 3 | import scala.util.Random 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2023/03/28 19:02 11 | */ 12 | class Invert[T](val random: Random) extends Poker[T] { 13 | override def select(cards: Seq[T]): Option[Int] = { 14 | if (cards == null || cards.isEmpty) { 15 | return None 16 | } 17 | if (cards.size == 1) { 18 | return Some(0) 19 | } 20 | 21 | val range = Math.log(cards.size + 1) 22 | val value = Math.exp(random.nextDouble() * range) 23 | val score = cards.size - value 24 | Some(Math.floor(score).intValue() + 1) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/CouldFrom.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 16:03 9 | */ 10 | trait CouldFrom extends Directive { 11 | 12 | def from(f: CouldBeFrom): From = new From { 13 | override def script: String = CouldFrom.this.script + " FROM " + f.script 14 | 15 | override def parameters: Seq[Parameter[_]] = CouldFrom.this.parameters ++ f.parameters 16 | } 17 | 18 | def from(f: String): From = new From { 19 | override def script: String = CouldFrom.this.script + " FROM " + f 20 | 21 | override def parameters: Seq[Parameter[_]] = CouldFrom.this.parameters 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Skip.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | import scala.language.postfixOps 3 | import scala.util.{Try, Success, Failure} 4 | 5 | /** 6 | * Skip p applies the parser p zero or more times, skipping its result. 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class Skip[E](val psc: Parsec[E, _]) extends Parsec[E, Unit] { 12 | val p: Attempt[E, _] = Attempt(psc) 13 | 14 | override def apply(s: State[E]): Try[Unit] = { 15 | 16 | while (true){ 17 | if(p ? s isFailure) { 18 | return Success() 19 | } 20 | } 21 | Success() 22 | } 23 | } 24 | 25 | object Skip { 26 | def apply[E](psc: Parsec[E, _]): Skip[E] = new Skip[E](psc) 27 | } 28 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Eof.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import java.io.EOFException 4 | 5 | import scala.util.{Failure, Success, Try} 6 | 7 | /** 8 | * Eof check state if get to end, It return nothing. Eof just success or failed. 9 | * 10 | * @author mars 11 | * @version 1.0.0 12 | */ 13 | class Eof[E] extends Parsec [E, Unit]{ 14 | override def apply(s: State[E]): Try[Unit] = { 15 | s.next() match { 16 | case Success(re) => 17 | s.trap(s"exception eof but $re") 18 | case Failure(_:EOFException) => 19 | Success() 20 | case left: Failure[E] => left.asInstanceOf 21 | } 22 | } 23 | } 24 | 25 | object Eof { 26 | def apply[E](): Eof[E] = new Eof() 27 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Skip1.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Success, Try} 4 | 5 | /** 6 | * Skip1 p applies the parser p one or more times, skipping its result. 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class Skip1[E](val psc: Parsec[E, _]) extends Parsec[E, Unit] { 12 | import jaskell.parsec.Parsec.Implicits._ 13 | 14 | val skip = new Skip(psc) 15 | val parser: Parsec[E, _] = (s: State[E]) => for { 16 | _ <- psc ask s 17 | _ <- skip ask s 18 | } yield () 19 | 20 | override def apply(s: State[E]): Try[Unit] = (parser ? s).flatMap(_ => Success()) 21 | } 22 | 23 | object Skip1 { 24 | def apply[E](psc: Parsec[E, _]): Skip1[E] = new Skip1[E](psc) 25 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Is.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 19:39 9 | */ 10 | trait Is extends Directive { 11 | val prefix: Directive 12 | 13 | def NULL(): Is.Null = new Is.Null { 14 | override val prefix: Directive = this 15 | } 16 | 17 | override def script: String = prefix.script + " IS" 18 | 19 | override def parameters: Seq[Parameter[_]] = prefix.parameters 20 | } 21 | 22 | object Is { 23 | trait Null extends Condition { 24 | val prefix:Directive 25 | override def script: String = prefix.script + " NULL" 26 | 27 | override def parameters: Seq[Parameter[_]] = prefix.parameters 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/ManySpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | import jaskell.parsec.Txt._ 6 | import jaskell.parsec.Combinator._ 7 | 8 | /** 9 | * TODO 10 | * 11 | * @author mars 12 | * @version 1.0.0 13 | * @since 2020/05/12 19:40 14 | */ 15 | class ManySpec extends AnyFlatSpec with Matchers { 16 | "Simple" should "Run match 2 times" in { 17 | val state = State("HelloHello") 18 | val parser = many(text("hello", caseSensitive = false)) 19 | val re = parser.parse(state) 20 | 21 | re.size should be (2) 22 | re.head should be ("Hello") 23 | re(1) should be ("Hello") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/SpaceSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | import jaskell.parsec.Txt.space 6 | 7 | /** 8 | * TODO 9 | * 10 | * @author mars 11 | * @version 1.0.0 12 | * @since 2020/05/12 22:29 13 | */ 14 | class SpaceSpec extends AnyFlatSpec with Matchers { 15 | "Simple" should "Run a simple test" in { 16 | val state = State(" ") 17 | val s = space 18 | val a = s ! state 19 | a should be (' ') 20 | } 21 | 22 | "Fail" should "Failed when test" in { 23 | val state = State("\t"); 24 | val s = space 25 | a[ParsecException] should be thrownBy { 26 | s ! state 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/TextSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | /** 7 | * TODO 8 | * 9 | * @author mars 10 | * @version 1.0.0 11 | * @since 2020/05/12 12:35 12 | */ 13 | class TextSpec extends AnyFlatSpec with Matchers { 14 | "Simple" should "Run some simple tests" in { 15 | import Txt._ 16 | val state:State[Char] = "Hello World" 17 | for { 18 | head <- "Hello" ? state 19 | _ <- skipSpaces ? state 20 | tail <- text("world", caseSensitive = false) ask state 21 | } yield {s"$head $tail"} match { 22 | case content: String => content should be ("Hello World") 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Condition.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 19:00 9 | */ 10 | trait Condition extends Directive with CouldBeQuote { 11 | def and(that: Condition): Condition = new Condition { 12 | override def script: String = Condition.this.script + " AND " + that.script 13 | override def parameters: Seq[Parameter[_]] = Condition.this.parameters ++ that.parameters 14 | } 15 | 16 | def or(that: Condition): Condition = new Condition { 17 | override def script: String = Condition.this.script + " OR " + that.script 18 | override def parameters: Seq[Parameter[_]] = Condition.this.parameters ++ that.parameters 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/NoneOf.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Failure, Success, Try} 4 | 5 | /** 6 | * NoneOf success if get a item is none in prepared 7 | * 8 | * @author Mars Liu 9 | * @version 1.0.0 10 | */ 11 | class NoneOf[E](val items: Set[E]) extends Parsec[E, E] { 12 | 13 | override def apply(s: State[E]): Try[E] = { 14 | s.next() match { 15 | case Success(re) => 16 | if (items.contains(re)) { 17 | s.trap(s"expect a item none of $items but got $re") 18 | } else { 19 | Success(re) 20 | } 21 | case left: Failure[_] => left 22 | } 23 | } 24 | } 25 | 26 | object NoneOf { 27 | def apply[E](items: Set[E]): NoneOf[E] = new NoneOf(items) 28 | } 29 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/CouldUnion.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/21 13:42 9 | */ 10 | trait CouldUnion extends Directive with Query with CouldBeQuote { 11 | def union(qry: Query): Query = new Query { 12 | override def script: String = s"${CouldUnion.this.script} UNION ${qry.script}" 13 | 14 | override def parameters: Seq[Parameter[_]] = CouldUnion.this.parameters ++ qry.parameters 15 | } 16 | 17 | def unionAll(qry: Query): Query = new Query { 18 | override def script: String = s"${CouldUnion.this.script} UNION ALL ${qry.script}" 19 | 20 | override def parameters: Seq[Parameter[_]] = CouldUnion.this.parameters ++ qry.parameters 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/EofSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | /** 7 | * TODO 8 | * 9 | * @author mars 10 | * @version 1.0.0 11 | * @since 2020/05/12 10:02 12 | */ 13 | class EofSpec extends AnyFlatSpec with Matchers{ 14 | import Txt._ 15 | "Eof" should "Test" in { 16 | val state:State[Char] = "hello" 17 | 18 | val eof = new Eof[Char] 19 | 20 | Text("hello") apply state 21 | eof(state) 22 | } 23 | 24 | "Eof Exception" should "Throw exception" in{ 25 | val state = State("hello") 26 | 27 | val eof = new Eof[Char] 28 | 29 | a[ParsecException] should be thrownBy { 30 | eof ! state 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/OneOfSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | /** 7 | * TODO 8 | * 9 | * @author mars 10 | * @version 1.0.0 11 | * @since 2020/05/12 20:10 12 | */ 13 | class OneOfSpec extends AnyFlatSpec with Matchers{ 14 | "Simple" should "Just success" in { 15 | val state = State("hello"); 16 | 17 | val oof = OneOf(Seq('b', 'e', 'h', 'f').toSet); 18 | 19 | val c = oof ! state 20 | c should be ('h') 21 | } 22 | 23 | "Failed" should "Fail" in { 24 | val state = State("hello") 25 | val oof = OneOf(Seq('d', 'a', 't', 'e').toSet) 26 | a[ParsecException] should be thrownBy { 27 | oof ! state 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/NoneOfSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | /** 7 | * TODO 8 | * 9 | * @author mars 10 | * @version 1.0.0 11 | * @since 2020/05/12 20:02 12 | */ 13 | class NoneOfSpec extends AnyFlatSpec with Matchers { 14 | 15 | "Simple OK" should "Test success" in { 16 | val state = State("hello") 17 | val nof = NoneOf(Seq('k', 'o', 'f').toSet) 18 | val c = nof ! state 19 | c should be ('h') 20 | } 21 | 22 | "Simple Failed" should "Test failed" in { 23 | val state = State("sound") 24 | val nof = NoneOf(Seq('k', 'f', 's').toSet) 25 | a[ParsecException] should be thrownBy { 26 | nof ! state 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Int.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Failure, Success, Try} 4 | 5 | /** 6 | * Int try to parse a long as long int from state and with signed. 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class Int extends Parsec[Char, String] { 12 | val sign = new Attempt(Ch('-')) 13 | val uint = new UInt() 14 | 15 | override def apply(s: State[Char]): Try[String] = { 16 | var sb: StringBuilder = new StringBuilder() 17 | if (sign.ask(s).isSuccess) { 18 | sb += '-' 19 | } 20 | 21 | uint ? s match { 22 | case Success(value) => sb ++= value 23 | case left: Failure[_] => return left 24 | } 25 | Success(sb.toString()) 26 | } 27 | } 28 | 29 | object Int { 30 | def apply: Int = new Int() 31 | } -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/LetterSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | import scala.util.Success 7 | 8 | /** 9 | * TODO 10 | * 11 | * @author mars 12 | * @version 1.0.0 13 | * @since 2020/06/12 18:57 14 | */ 15 | class LetterSpec extends AnyFlatSpec with Matchers { 16 | 17 | import Txt._ 18 | 19 | "Letter" should "test a char is letter" in { 20 | val content: State[Char] = "a" 21 | val parser = letter 22 | 23 | parser ? content should be (Success('a')) 24 | } 25 | 26 | "no" should "test a char is letter" in { 27 | val content: State[Char] = "22" 28 | val parser = letter 29 | 30 | (parser ? content).isFailure should be (true) 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/EndBy1.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.Try 4 | 5 | /** 6 | * EndBy1 p sep parses one or more occurrences of p, separated and ended by sep. Returns a seq of values returned by p. 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class EndBy1[E, T](val parser: Parsec[E, T], val sep: Parsec[E, _]) extends Parsec[E, Seq[T]] { 12 | import jaskell.parsec.Parsec.Implicits._ 13 | 14 | val parsec: Parsec[E, Seq[T]] = new Many1((s: State[E]) => { 15 | for { 16 | re <- parser ? s 17 | _ <- sep ? s 18 | } yield {re} 19 | }) 20 | override def apply(s: State[E]): Try[Seq[T]] = parsec ? s 21 | } 22 | 23 | object EndBy1 { 24 | def apply[E, T](parser: Parsec[E, T], sep: Parsec[E, _]): EndBy1[E, T] = new EndBy1(parser, sep) 25 | } -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/CharsInSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | /** 7 | * TODO 8 | * 9 | * @author mars 10 | * @version 1.0.0 11 | * @since 2020/05/12 22:48 12 | */ 13 | class CharsInSpec extends AnyFlatSpec with Matchers { 14 | "Simple" should "Run a simple test" in { 15 | val state = State("23413214") 16 | 17 | val parser = CharsIn("1234567890") 18 | 19 | val re = parser ! state 20 | 21 | re should be ("23413214") 22 | } 23 | 24 | "Part" should "Run a match part" in { 25 | val state = State("234234abdef2342334") 26 | 27 | val parser = CharsIn("1234567890") 28 | 29 | val re = parser ! state 30 | 31 | re should be ("234234") 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Delete.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/20 19:17 9 | */ 10 | class Delete extends Directive with CouldFrom { 11 | override def script: String = s"DELETE " 12 | 13 | override def parameters: Seq[Parameter[_]] = Seq.empty 14 | 15 | override def from(f: CouldBeFrom): From with CouldWhere with Returning = new From with CouldWhere with Returning { 16 | 17 | override val prefix: Directive = Delete.this 18 | 19 | override def script: String = s"${prefix.script} FROM ${f.script}" 20 | 21 | override def parameters: Seq[Parameter[_]] = prefix.parameters ++ f.parameters 22 | } 23 | 24 | override def from(tableName: String): From = this.from(new Name(tableName) with CouldBeFrom) 25 | } 26 | -------------------------------------------------------------------------------- /src/main/scala-2/jaskell/parsec/State.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Failure, Try} 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | trait State[+E] { 12 | type Status 13 | type Tran 14 | def status: Status 15 | 16 | def begin(): Tran 17 | 18 | def begin(tran: Tran): Tran 19 | 20 | def commit(tran: Tran): Unit 21 | 22 | def rollback(tran: Tran): Unit 23 | 24 | def next(): Try[E] 25 | 26 | def trap[T](message: String): Failure[T] = { 27 | new Failure[T](new ParsecException(this.status, message)) 28 | } 29 | } 30 | 31 | object State { 32 | def apply(str: String): TxtState = new TxtState(str) 33 | 34 | def apply[T](c: Seq[T]): CommonState[T] = new CommonState[T] { 35 | override val content: Seq[T] = c 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/OneSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import java.io.EOFException 4 | 5 | import org.scalatest.flatspec.AnyFlatSpec 6 | import org.scalatest.matchers.should.Matchers 7 | import jaskell.parsec.Atom.one 8 | 9 | /** 10 | * TODO 11 | * 12 | * @author mars 13 | * @version 1.0.0 14 | * @since 2020/05/12 20:48 15 | */ 16 | class OneSpec extends AnyFlatSpec with Matchers{ 17 | "Simple" should "Match one item" in { 18 | val state = State("hello") 19 | val parser = one[Char] 20 | (parser ! state) should be ('h') 21 | (parser ! state) should be ('e') 22 | (parser ! state) should be ('l') 23 | (parser ! state) should be ('l') 24 | (parser ! state) should be ('o') 25 | a[EOFException] should be thrownBy { 26 | parser ! state 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/expression/Binary.scala: -------------------------------------------------------------------------------- 1 | package jaskell.expression 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/06/02 21:33 9 | */ 10 | abstract class Binary(var left: Expression, var right: Expression) extends Expression { 11 | def priority: Int 12 | 13 | override def makeAst: Expression = { 14 | this.left = this.left.makeAst 15 | this.right = this.right.makeAst 16 | right match { 17 | case r: Binary => 18 | if (this.priority >= r.priority) { 19 | val lx = r.left 20 | this.right = lx 21 | r.left = this 22 | r.right = r.right 23 | r 24 | } else { 25 | this 26 | } 27 | case _ => 28 | this.right = this.right 29 | this 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/EndBy.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.Try 4 | 5 | /** 6 | * EndBy p sep parses zero or more occurrences of p, separated and ended by sep. Returns a seq of values returned by p. 7 | * 8 | * @author Mars Liu 9 | * @version 1.0.0 10 | */ 11 | class EndBy[E, T](val parser: Parsec[E, T], val sep: Parsec[E, _]) extends Parsec[E, Seq[T]] { 12 | import jaskell.parsec.Parsec.Implicits._ 13 | val parsec:Parsec[E, Seq[T]] = new Many((s: State[E]) => { 14 | for { 15 | re <- parser ? s 16 | _ <- sep ? s 17 | } yield { 18 | re 19 | } 20 | }) 21 | 22 | override def apply(s: State[E]): Try[Seq[T]] = parsec ? s 23 | } 24 | 25 | object EndBy { 26 | def apply[E, T](parser: Parsec[E, T], sep: Parsec[E, _]): EndBy[E, T] = new EndBy(parser, sep) 27 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Atom.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | /** 4 | * Atom parsers, like one, eof, return and failed, equals and not equals, etc. 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | */ 9 | object Atom { 10 | def one[E]: One[E] = new One[E] 11 | 12 | def eof[E]: Eof[E] = new Eof 13 | 14 | def pack[E, T](value: T): Return[E, T] = new Return[E, T](value) 15 | 16 | def fail[E](message: String, objects: Any*): Fail[E] = new Fail[E](message, objects) 17 | 18 | def eq[E](item: E): Eq[E] = new Eq[E](item) 19 | 20 | def ne[E](item: E): Ne[E] = new Ne[E](item) 21 | 22 | def oneOf[E](data: Set[E]): OneOf[E] = new OneOf[E](data) 23 | 24 | def noneOf[E](data: Set[E]): NoneOf[E] = new NoneOf[E](data) 25 | 26 | def is[E](predicate: Function[E, Boolean]): Is[E] = new Is(predicate) 27 | } 28 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/croupier/Poker.scala: -------------------------------------------------------------------------------- 1 | package jaskell.croupier 2 | 3 | import scala.util.Random 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2023/03/28 19:04 11 | */ 12 | trait Poker[T] { 13 | def select(cards: Seq[T]): Option[Int]; 14 | } 15 | 16 | object Poker { 17 | def fair = new Fair(new Random()) 18 | 19 | def fair(random: Random) = new Fair(random) 20 | 21 | def fair(seed: Long) = new Fair(new Random(seed)) 22 | 23 | def damping = new Damping(new Random()) 24 | 25 | def damping(random: Random) = new Damping(random) 26 | 27 | def damping(seed: Long) = new Damping(new Random(seed)) 28 | 29 | def invert = new Invert(new Random()) 30 | 31 | def invert(random: Random) = new Invert(random) 32 | 33 | def invert(seed: Long) = new Invert(new Random(seed)) 34 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/expression/parsers/D.scala: -------------------------------------------------------------------------------- 1 | package jaskell.expression.parsers 2 | 3 | import jaskell.expression.{Divide, Expression} 4 | import jaskell.parsec.{Parsec, SkipWhitespaces, State} 5 | 6 | import scala.util.Try 7 | 8 | /** 9 | * TODO 10 | * 11 | * @author mars 12 | * @version 1.0.0 13 | */ 14 | class D(val prev: Expression) extends Parsec[Char, Expression] { 15 | import jaskell.Monad.Implicits._ 16 | import jaskell.parsec.Parsec.Implicits._ 17 | import jaskell.parsec.Txt.{ch, skipWhiteSpaces} 18 | 19 | val skips: SkipWhitespaces = skipWhiteSpaces 20 | val op: Parsec[Char, Unit] = skips >> ch('/') >> skips 21 | val next = new Parser 22 | 23 | override def apply(s: State[Char]): Try[Expression] = { 24 | for { 25 | _ <- op ? s 26 | exp <- next ? s 27 | } yield {new Divide(prev, exp)} 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/expression/parsers/Q.scala: -------------------------------------------------------------------------------- 1 | package jaskell.expression.parsers 2 | 3 | import jaskell.Monad.Implicits._ 4 | import jaskell.expression.{Expression, Quote} 5 | import jaskell.parsec.{Parsec, SkipWhitespaces, State} 6 | 7 | import scala.util.Try 8 | 9 | /** 10 | * TODO 11 | * 12 | * @author mars 13 | * @version 1.0.0 14 | * @since 2020/06/03 12:13 15 | */ 16 | class Q extends Parsec[Char, Expression] { 17 | import jaskell.parsec.Txt.{skipWhiteSpaces, ch} 18 | import jaskell.Monad.Implicits._ 19 | import jaskell.parsec.Parsec.Implicits._ 20 | 21 | lazy val p = new Parser 22 | val skips: SkipWhitespaces = skipWhiteSpaces 23 | val parser: Parsec[Char, Expression] = (ch('(') *> skips) *> p <* (skips *> ch(')')) 24 | 25 | override def apply(s: State[Char]): Try[Expression] = { 26 | parser ? s map {new Quote(_)} 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/IsSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | import scala.util.Success 7 | 8 | /** 9 | * TODO 10 | * 11 | * @author mars 12 | * @version 1.0.0 13 | * @since 2020/07/23 14:23 14 | */ 15 | class IsSpec extends AnyFlatSpec with Matchers { 16 | 17 | "Is" should "get item if predicate passed" in { 18 | val parser = new Is[scala.Int]({number => (number % 2) == 0}) 19 | val state = State(Seq(2)) 20 | 21 | (parser ? state) should be (Success(2)) 22 | } 23 | 24 | "IsNot" should "failed when predicate not passed" in { 25 | val parser = new Is[scala.Int](number => number % 2== 1) 26 | val state = State(Seq(2)) 27 | 28 | (parser ? state).isFailure should be (true) 29 | state.current should be (1) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Quote.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 20:09 9 | */ 10 | trait Quote extends Directive with CouldAs with CouldBeCondition with Condition { 11 | val segment: CouldBeQuote 12 | 13 | override def script: String = s"(${segment.script})" 14 | 15 | override def parameters: Seq[Parameter[_]] = segment.parameters 16 | 17 | } 18 | 19 | object Quote { 20 | def apply(condition: Condition): Quote = new Quote with Condition { 21 | override val segment: Condition = condition 22 | } 23 | 24 | def apply(qry: Query): Quote = new Quote with Query { 25 | override val segment: Query = qry 26 | } 27 | 28 | def apply(expression: Expression): Quote = new Quote with Expression { 29 | override val segment: Expression = expression 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/NewLineSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | import jaskell.parsec.Txt._ 6 | 7 | /** 8 | * TODO 9 | * 10 | * @author mars 11 | * @version 1.0.0 12 | * @since 2020/05/12 19:52 13 | */ 14 | class NewLineSpec extends AnyFlatSpec with Matchers{ 15 | "NewLine" should "Test new line" in { 16 | val state = State("\r\n"); 17 | 18 | val enter = new Newline() 19 | 20 | val c = for{ 21 | _ <- ch('\r') ask state 22 | re <- enter ask state 23 | } yield re 24 | 25 | c.isSuccess should be (true) 26 | c foreach(re => re should be ('\n')) 27 | } 28 | 29 | "CRLF" should "test \\r\\n match" in { 30 | val state = State("\r\n"); 31 | 32 | val parser = crlf 33 | 34 | parser ! state should be ("\r\n") 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/expression/parsers/A.scala: -------------------------------------------------------------------------------- 1 | package jaskell.expression.parsers 2 | 3 | import jaskell.expression.{Add, Expression} 4 | import jaskell.parsec.Txt.skipWhiteSpaces 5 | import jaskell.parsec.{Parsec, SkipWhitespaces, State} 6 | 7 | import scala.util.Try 8 | 9 | /** 10 | * TODO 11 | * 12 | * @author mars 13 | * @version 1.0.0 14 | */ 15 | class A(val prev: Expression) extends Parsec[Char, Expression] { 16 | 17 | import jaskell.Monad.Implicits._ 18 | import jaskell.parsec.Parsec.Implicits._ 19 | import jaskell.parsec.Txt.ch 20 | 21 | val skips: SkipWhitespaces = skipWhiteSpaces 22 | val op: Parsec[Char, Unit] = skips *> ch('+') *> skips 23 | 24 | lazy val next = new Parser 25 | 26 | override def apply(s: State[Char]): Try[Expression] = { 27 | for { 28 | _ <- op ? s 29 | exp <- next ? s 30 | } yield {new Add(prev, exp)} 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/FindSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | import jaskell.parsec.Txt._ 6 | import jaskell.parsec.Combinator._ 7 | 8 | /** 9 | * TODO 10 | * 11 | * @author mars 12 | * @version 1.0.0 13 | * @since 2020/05/12 18:56 14 | */ 15 | class FindSpec extends AnyFlatSpec with Matchers { 16 | "Simple" should " find token in content" in { 17 | val data = "It is a junit test case for find parsec." 18 | 19 | val parser = Find(text("find")) 20 | val re = parser parse data 21 | re should be ("find") 22 | } 23 | 24 | "Failed" should "mismatch any content" in { 25 | val data = "It is a junit test case for find parsec." 26 | val parsec = find(text("Fail")) 27 | a[ParsecException] should be thrownBy { 28 | parsec parse data 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/UDecimal.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Failure, Success, Try} 4 | 5 | /** 6 | * UDecimal parser parse a decimal number without signed 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class UDecimal extends Parsec[Char, String] { 12 | import jaskell.parsec.Parsec.Implicits._ 13 | 14 | val uint = new jaskell.parsec.UInt() 15 | val dot: Attempt[Char, Char] = Attempt(Ch('.')) 16 | val tail: Parsec[Char, String] = (s: State[Char]) => for { 17 | _ <- dot ask s 18 | re <- uint ask s 19 | } yield re 20 | 21 | override def apply(st: State[Char]): Try[String] = { 22 | uint ask st map { value => 23 | tail ? st match { 24 | case Failure(_) => value 25 | case Success(t) => s"$value.$t" 26 | } 27 | } 28 | } 29 | } 30 | 31 | object UDecimal { 32 | def apply(): UDecimal = new UDecimal() 33 | } 34 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/expression/parsers/P.scala: -------------------------------------------------------------------------------- 1 | package jaskell.expression.parsers 2 | 3 | import jaskell.expression.{Expression, Product} 4 | import jaskell.parsec.Txt.skipWhiteSpaces 5 | import jaskell.parsec.{Parsec, SkipWhitespaces, State} 6 | 7 | import scala.util.Try 8 | 9 | /** 10 | * TODO 11 | * 12 | * @author mars 13 | * @version 1.0.0 14 | * @since 2020/06/02 21:44 15 | */ 16 | class P(val prev: Expression) extends Parsec[Char, Expression] { 17 | import jaskell.Monad.Implicits._ 18 | import jaskell.parsec.Parsec.Implicits._ 19 | import jaskell.parsec.Txt.ch 20 | 21 | val skips: SkipWhitespaces = skipWhiteSpaces 22 | val op: Parsec[Char, Unit] = skips >> ch('*') >> skips 23 | val next = new Parser 24 | 25 | override def apply(s: State[Char]): Try[Expression] = { 26 | for { 27 | _ <- op ? s 28 | exp <- next ? s 29 | } yield {new Product(prev, exp)} 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Many.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.collection.mutable 4 | import scala.util.{Failure, Success, Try} 5 | 6 | /** 7 | * Many try to parse the parse more times, and collect all results into a Seq. 8 | * Many could success 0 to any times. 9 | * 10 | * @author mars 11 | */ 12 | class Many[E, T](val parsec: Parsec[E, T]) extends Parsec[E, Seq[T]] { 13 | val psc = new Attempt[E, T](parsec) 14 | 15 | override def apply(s: State[E]): Try[Seq[T]] = { 16 | val re = new mutable.ListBuffer[T] 17 | while (true) { 18 | psc ask s match { 19 | case Success(result) => re += result 20 | case Failure(_) => 21 | return Success(re.toSeq) 22 | } 23 | } 24 | s.trap("many parsec should arrived this for never") 25 | } 26 | } 27 | 28 | object Many { 29 | def apply[E, T](parsec: Parsec[E, T]): Many[E, T] = new Many[E, T](parsec) 30 | } 31 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/ScNumber.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.Try 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class ScNumber extends Parsec [Char, String]{ 12 | val decimal = new Decimal 13 | val exp: Parsec[Char, String] = Attempt(new Parsec[Char, String] { 14 | val ep: Text = Text("e", caseSensitive = false) 15 | val sp: Parsec[Char, String] = Attempt(Text("+")) <|> Attempt(Text("-")) <|> Return("") 16 | val np: UInt = new jaskell.parsec.UInt 17 | override def apply(st: State[Char]): Try[String] = { 18 | for { 19 | e <- ep ? st 20 | s <- sp ? st 21 | n <- np ? st 22 | } yield {e + s + n} 23 | } 24 | }) 25 | 26 | override def apply(s: State[Char]): Try[String] = { 27 | decimal ? s map { mantissa => 28 | exp ? s map {e => mantissa + e} getOrElse mantissa 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Opt.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Failure, Success, Try} 4 | 5 | /** 6 | * Opt x p tries to apply parser p. If p fails without consuming input, 7 | * it returns the value x, otherwise the value returned by p. 8 | * 9 | * @author Mars Liu 10 | * @version 1.0.0 11 | */ 12 | class Opt[E, T](val p: Parsec[E, T], val otherwise: T) extends Parsec[E, T] { 13 | lazy val psc = Attempt(p) 14 | 15 | override def apply(s: State[E]): Try[T] = { 16 | val before = s.status 17 | psc ? s match { 18 | case right: Success[_] => 19 | right 20 | case left: Failure[_] => 21 | val after = s.status 22 | if (before == after) { 23 | Success(otherwise) 24 | } else { 25 | left 26 | } 27 | } 28 | } 29 | } 30 | 31 | object Opt { 32 | def apply[E, T](p: Parsec[E, T], otherwise: T): Opt[E, T] = new Opt(p, otherwise) 33 | } 34 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/expression/parsers/S.scala: -------------------------------------------------------------------------------- 1 | package jaskell.expression.parsers 2 | 3 | import jaskell.Monad.Implicits._ 4 | import jaskell.expression.{Expression, Sub} 5 | import jaskell.parsec.Txt.skipWhiteSpaces 6 | import jaskell.parsec.{Parsec, SkipWhitespaces, State} 7 | 8 | import scala.util.Try 9 | 10 | /** 11 | * TODO 12 | * 13 | * @author mars 14 | * @version 1.0.0 15 | * @since 2020/06/02 21:44 16 | */ 17 | class S(val prev: Expression) extends Parsec[Char, Expression] { 18 | import jaskell.Monad.Implicits._ 19 | import jaskell.parsec.Parsec.Implicits._ 20 | 21 | import jaskell.parsec.Txt.ch 22 | val skips: SkipWhitespaces = skipWhiteSpaces 23 | val op: Parsec[Char, Unit] = skips >> ch('-') >> skips 24 | 25 | lazy val next = new Parser 26 | 27 | override def apply(s: State[Char]): Try[Expression] = { 28 | for { 29 | _ <- op ? s 30 | exp <- next ? s 31 | } yield {new Sub(prev, exp)} 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/EqSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import java.io.EOFException 4 | 5 | import org.scalatest.flatspec.AnyFlatSpec 6 | import org.scalatest.matchers.should.Matchers 7 | 8 | import scala.util.Success 9 | 10 | /** 11 | * TODO 12 | * 13 | * @author mars 14 | * @version 1.0.0 15 | * @since 2020/05/12 10:02 16 | */ 17 | class EqSpec extends AnyFlatSpec with Matchers{ 18 | "Eq" should "Test" in { 19 | val state = State("hello") 20 | 21 | val eof = new Eof[Char] 22 | 23 | (Eq('h') ? state) should be (Success('h')) 24 | (Eq('e') ! state) should be ('e') 25 | (Eq('l') ! state) should be ('l') 26 | (Eq('l') ! state) should be ('l') 27 | a[ParsecException] should be thrownBy { 28 | Attempt(Eq('l')) ! state 29 | } 30 | (Eq('o') ! state) should be ('o') 31 | eof apply state 32 | a[EOFException] should be thrownBy { 33 | Eq('o') ! state 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Ch.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Success, Try} 4 | 5 | /** 6 | * If get one char equals the Ch prepared, return it. 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class Ch(val char: Char, val caseSensitive: Boolean) extends Parsec[Char, Char] { 12 | val chr: Char = if (caseSensitive) char else char.toLower 13 | 14 | override def apply(s: State[Char]): Try[Char] = { 15 | s.next() flatMap { c => 16 | if (caseSensitive) { 17 | if (chr == c) { 18 | return Success(c) 19 | } 20 | } else { 21 | if (chr == c.toLower) { 22 | return Success(c) 23 | } 24 | } 25 | s.trap(s"expect char $char (case sensitive $caseSensitive) but get $c") 26 | } 27 | } 28 | } 29 | 30 | object Ch { 31 | def apply(char: Char, caseSensitive: Boolean): Ch = new Ch(char, caseSensitive) 32 | 33 | def apply(char: Char): Ch = new Ch(char, true) 34 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/NCh.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Success, Try} 4 | 5 | /** 6 | * NCh return if get a char not equals prepared. 7 | * 8 | * @author Mars Liu 9 | * @version 1.0.0 10 | */ 11 | class NCh(val char: Char, val caseSensitive: Boolean) extends Parsec[Char, Char] { 12 | val chr: Char = if (caseSensitive) char else char.toLower 13 | 14 | override def apply(s: State[Char]): Try[Char] = { 15 | s.next() flatMap { c => 16 | if (caseSensitive) { 17 | if (chr != c) { 18 | return Success(c) 19 | } 20 | } else { 21 | if (chr != c.toLower) { 22 | return Success(c) 23 | } 24 | } 25 | s.trap(s"expect char $char (case sensitive $caseSensitive) but get $c") 26 | } 27 | } 28 | } 29 | 30 | object NCh { 31 | def apply(char: Char, caseSensitive: Boolean): NCh = new NCh(char, caseSensitive) 32 | 33 | def apply(char: Char): NCh = new NCh(char, true) 34 | 35 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Count.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.collection.mutable 4 | import scala.util.{Failure, Success, Try} 5 | 6 | /** 7 | * count n p parses n occurrences of p. If n is smaller or equal to zero, the parser equals to return []. 8 | * Returns a list of n values returned by p. 9 | * 10 | * @author Mars Liu 11 | * @version 1.0.0 12 | */ 13 | class Count[E, T](val p: Parsec[E, T], val n: scala.Int) extends Parsec[E, Seq[T]] { 14 | 15 | override def apply(s: State[E]): Try[Seq[T]] = { 16 | if (n <= 0) { 17 | return Success(Seq.empty) 18 | } 19 | val re = mutable.ListBuffer[T]() 20 | for (_ <- 0 to n) { 21 | p ? s match { 22 | case Success(value) => re += value 23 | case Failure(error) => 24 | return Failure(error) 25 | } 26 | } 27 | Success(re.toSeq) 28 | } 29 | } 30 | 31 | object Count { 32 | def apply[E, T](p: Parsec[E, T], n: scala.Int): Count[E, T] = new Count(p, n) 33 | } -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/UIntSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | import jaskell.parsec.Txt.uInteger 6 | 7 | /** 8 | * TODO 9 | * 10 | * @author mars 11 | * @version 1.0.0 12 | * @since 2020/05/12 22:48 13 | */ 14 | class UIntSpec extends AnyFlatSpec with Matchers { 15 | "Simple" should "Run a simple test" in { 16 | val state = State("23413214") 17 | 18 | val parser = uInteger 19 | 20 | val re = parser ! state 21 | 22 | re should be ("23413214") 23 | } 24 | 25 | "Stop" should "Match digits until a letter" in { 26 | val state = State("23413a214") 27 | 28 | val parser = uInteger 29 | 30 | val re = parser ! state 31 | 32 | re should be ("23413") 33 | } 34 | 35 | "Fail" should "Failed" in { 36 | val state = State("x2344") 37 | val parser = uInteger 38 | a[ParsecException] should be thrownBy { 39 | parser ! state 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/expression/parsers/Param.scala: -------------------------------------------------------------------------------- 1 | package jaskell.expression.parsers 2 | 3 | import jaskell.Monad.Implicits._ 4 | import jaskell.expression.{Expression, Parameter} 5 | import jaskell.parsec.{Parsec, State} 6 | 7 | import scala.util.Try 8 | 9 | /** 10 | * TODO 11 | * 12 | * @author mars 13 | * @version 1.0.0 14 | * @since 2020/06/10 14:35 15 | */ 16 | class Param extends Parsec[Char, Expression] { 17 | 18 | import jaskell.parsec.Combinator._ 19 | import jaskell.parsec.Parsec.Implicits._ 20 | import jaskell.parsec.Txt._ 21 | 22 | val head: Parsec[Char, Char] = letter 23 | val tail: Parsec[Char, Seq[Char]] = many(attempt(letter) <|> attempt(digit)) 24 | val t: Parsec[Char, String] = tail >>= mkString 25 | val parser: Parsec[Char, String] = (s: State[Char]) => for { 26 | h <- head ? s 27 | tv <- t ? s 28 | } yield s"$h$tv" 29 | 30 | override def apply(s: State[Char]): Try[Expression] = { 31 | parser ? s map { 32 | new Parameter(_) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Parameter.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | import java.util 4 | import java.util.{ArrayList, List, Optional} 5 | 6 | 7 | class Parameter[T] extends Directive with Expression with CouldBeCondition { 8 | var value: Option[T] = None 9 | var key: Any = _ 10 | var _order: Int = _ 11 | val placeHolder: String = "?" 12 | 13 | def set(v: T): Unit = { 14 | value = Option[T](v) 15 | } 16 | 17 | def confirmed: Boolean = value != null 18 | 19 | 20 | def order: Int = _order 21 | 22 | def order(o: Int): Unit = { 23 | this._order = o 24 | } 25 | 26 | override def script: String = placeHolder 27 | 28 | override def parameters: Seq[Parameter[T]] = { 29 | Seq[Parameter[T]](this) 30 | } 31 | } 32 | 33 | object Parameter { 34 | 35 | def apply[T](k: Any): Parameter[T] = new Parameter[T]() { 36 | key = k 37 | value = None 38 | } 39 | 40 | def apply[T](k: Any, v: T): Parameter[T] = new Parameter[T]() { 41 | key = k 42 | value = Some(v) 43 | } 44 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/croupier/LiteScaled.scala: -------------------------------------------------------------------------------- 1 | package jaskell.croupier 2 | 3 | import scala.util.Random 4 | 5 | /** 6 | * Rand select by scale weight. 7 | * Lite scale generate a index list size is sum(cards weight). 8 | * Lite scale is simple, but maybe use too large buffer. 9 | * 10 | * @param scale the trait that get item's weight 11 | * @param random random object, auto new one if no given 12 | * @tparam T type of card 13 | */ 14 | class LiteScaled[T](scale: Scale[T], val random: Random = new Random()) extends Poker[T] { 15 | override def select(cards: Seq[T]): Option[Int] = { 16 | if (cards == null || cards.isEmpty) { 17 | return None 18 | } 19 | if (cards.size == 1) { 20 | return Some(0) 21 | } 22 | 23 | val steps: Seq[Int] = for { 24 | idx <- cards.indices 25 | w = scale.weight(cards(idx)) 26 | value <- Seq.fill(w)(idx) 27 | } yield value 28 | 29 | val top = steps.size 30 | val score = random.nextInt(top) 31 | Some(steps(score)) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/croupier/Scaled.scala: -------------------------------------------------------------------------------- 1 | package jaskell.croupier 2 | 3 | import scala.util.Random 4 | 5 | /** 6 | * Rand select by scale weight. 7 | * 8 | * @param scale the trait that get item's weight 9 | * @param random random object, auto new one if no given 10 | * @tparam T type of card 11 | */ 12 | class Scaled[T](scale: Scale[T], val random: Random = new Random()) extends Poker[T] { 13 | override def select(cards: Seq[T]): Option[Int] = { 14 | if (cards == null || cards.isEmpty) { 15 | return None 16 | } 17 | if (cards.size == 1) { 18 | return Some(0) 19 | } 20 | 21 | val steps: Seq[Int] = cards 22 | .map(scale.weight) 23 | .foldLeft(Seq[Int](0)) { (result: Seq[Int], r: Int) => 24 | result :+ (result.last + r) 25 | } 26 | val top = steps.last 27 | val score = random.nextInt(top) 28 | for (idx <- cards.indices) { 29 | if (steps(idx) <= score && steps(idx + 1) > score) { 30 | return Some(idx) 31 | } 32 | } 33 | Some(cards.size - 1) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Query.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | import java.sql.{Connection, PreparedStatement, ResultSet, SQLException} 4 | 5 | import javax.swing.text.Segment 6 | 7 | /** 8 | * TODO 9 | * 10 | * @author mars 11 | * @version 1.0.0 12 | * @since 2020/05/18 20:14 13 | */ 14 | trait Query extends Directive with CouldBeQuote with Statement { 15 | @throws[SQLException] 16 | def query(statement: PreparedStatement): ResultSet = { 17 | syncParameters(statement) 18 | statement.executeQuery 19 | } 20 | 21 | @throws[SQLException] 22 | def scalar[T](conn: Connection): Option[T] = { 23 | val statement = conn.prepareStatement(this.script) 24 | val resultSet = statement.executeQuery 25 | try { 26 | if (resultSet.next) { 27 | if (resultSet.getObject(1) != null) { 28 | return Some(resultSet.getObject(1).asInstanceOf[T]) 29 | } 30 | } 31 | None 32 | } finally { 33 | if (statement != null) statement.close() 34 | if (resultSet != null) resultSet.close() 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/croupier/Ranked.scala: -------------------------------------------------------------------------------- 1 | package jaskell.croupier 2 | 3 | import scala.util.Random 4 | 5 | /** 6 | * Rand select by rank score. 7 | * 8 | * @param ranker the trait that get item's rank 9 | * @param random random object, auto new one if no given 10 | * @tparam T type of card 11 | */ 12 | class Ranked[T](ranker: Ranker[T], val random: Random = new Random()) extends Poker[T] { 13 | override def select(cards: Seq[T]): Option[Int] = { 14 | if (cards == null || cards.isEmpty) { 15 | return None 16 | } 17 | if (cards.size == 1) { 18 | return Some(0) 19 | } 20 | 21 | val steps: Seq[Double] = cards 22 | .map(ranker.rank) 23 | .foldLeft(Seq[Double](0)) { (result: Seq[Double], r: Double) => 24 | result :+ (r + result.last) 25 | } 26 | val top = steps.last 27 | val score = random.nextDouble() * top 28 | for (idx <- cards.indices) { 29 | if (steps(idx) <= score && steps(idx + 1) > score) { 30 | return Some(idx) 31 | } 32 | } 33 | Some(cards.size - 1) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Attempt.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Failure, Success, Try} 4 | 5 | /** 6 | * The parser try p behaves like parser p, except that it pretends that it hasn't consumed any input 7 | * when an error occurs. 8 | * 9 | * This combinator is used whenever arbitrary look ahead is needed. Since it pretends that it hasn't 10 | * consumed any input when p fails, the (<|>) combinator will try its second alternative even when the 11 | * first parser failed while consuming input. 12 | * 13 | * @author Mars Liu 14 | * @version 1.0.0 15 | */ 16 | class Attempt[E, T](val parsec: Parsec[E, T]) extends Parsec[E, T] { 17 | 18 | override def apply(s: State[E]): Try[T] = { 19 | val tran = s.begin() 20 | parsec ? s match { 21 | case right: Success[T] => 22 | s commit tran 23 | right 24 | case left: Failure[T] => 25 | s rollback tran 26 | left 27 | } 28 | } 29 | } 30 | 31 | object Attempt { 32 | def apply[E, T](parsec: Parsec[E, T]): Attempt[E, T] = new Attempt(parsec) 33 | } 34 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/ChIn.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Success, Try} 4 | 5 | /** 6 | * If get a char match any of content, return it. 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class ChIn(val content: String, val caseSensitive: Boolean) extends Parsec[Char, Char] { 12 | val contentSet: Set[Char] = if (caseSensitive) content.toSet else content.toLowerCase.toSet 13 | 14 | override def apply(s: State[Char]): Try[Char] = { 15 | s.next() flatMap { c => 16 | if (caseSensitive) { 17 | if (contentSet contains c) { 18 | return Success(c) 19 | } 20 | } else { 21 | if (contentSet contains c.toLower) { 22 | return Success(c) 23 | } 24 | } 25 | s.trap(s"expect any char in $content (case sensitive $caseSensitive) but get $c") 26 | } 27 | } 28 | } 29 | 30 | object ChIn { 31 | def apply(content: String, caseSensitive: Boolean): ChIn = new ChIn(content, caseSensitive) 32 | 33 | def apply(content: String): ChIn = new ChIn(content, true) 34 | } 35 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Choice.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import java.io.EOFException 4 | 5 | import scala.util.{Failure, Success, Try} 6 | 7 | /** 8 | * Choice just the operator <|> in Haskell parsec 9 | * 10 | * @author mars 11 | * @version 1.0.0 12 | */ 13 | class Choice[E, T](val parsecs: Seq[Parsec[E, T]]) extends Parsec[E, T] { 14 | 15 | override def apply(s: State[E]): Try[T] = { 16 | var err: Throwable = null 17 | val status = s.status 18 | for (psc <- this.parsecs) { 19 | psc ? s match { 20 | case right: Success[T] => 21 | return right 22 | case Failure(error) => 23 | err = error 24 | if (status != s.status) { 25 | return Failure(error) 26 | } 27 | } 28 | } 29 | if (err == null) { 30 | s.trap("Choice Error : All parsec parsers failed.") 31 | } else { 32 | s.trap(s"Choice Error $err, stop at $status") 33 | } 34 | } 35 | } 36 | 37 | object Choice { 38 | def apply[E, T](parsecs: Parsec[E, T]*): Choice[E, T] = new Choice(parsecs) 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Between.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import jaskell.parsec 4 | 5 | import scala.util.Try 6 | 7 | /** 8 | * Between parse the open parser, and then sub parser, and then close parser, return sub parser result if success. 9 | * 10 | * @author mars 11 | * @version 1.0.0 12 | */ 13 | class Between[E, T](val open: Parsec[E, _], val close: Parsec[E, _], val parsec: Parsec[E, T]) 14 | extends Parsec[E, T] { 15 | 16 | override def apply(s: State[E]): Try[T] = { 17 | for { 18 | _ <- open ? s 19 | re <- parsec ? s 20 | _ <- close ? s 21 | } yield re 22 | } 23 | } 24 | 25 | object Between { 26 | 27 | class Btw[E, T](val open: Parsec[E, _], val close: Parsec[E, _]) { 28 | def in(parsec: Parsec[E, T]) = new Between[E, T](this.open, this.close, parsec) 29 | } 30 | 31 | def apply[E, T](open: Parsec[E, _], close: Parsec[E, _]) = new parsec.Between.Btw[E, T](open, close) 32 | 33 | def apply[E, T](open: Parsec[E, _], close: Parsec[E, _], parsec: Parsec[E, T]) = 34 | new Between[E, T](open, close, parsec) 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Name.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 16:03 9 | */ 10 | class Name(val name: String, var quoted: Boolean = false) extends Directive with Expression with CouldAs 11 | with CouldBeCondition with CouldBeColumn { 12 | override def script: String = { 13 | if (name.contains('"') || quoted) { 14 | "\"%s\"".format(script.replace("\"", "\\\"")) 15 | } else { 16 | name 17 | } 18 | } 19 | 20 | override def parameters: Seq[Parameter[_]] = { 21 | Seq.empty 22 | } 23 | 24 | def isNull: Expression with Condition = new Expression with Condition { 25 | override def script: String = s"${Name.this.script} IS NULL" 26 | 27 | override def parameters: Seq[Parameter[_]] = Name.this.parameters 28 | } 29 | 30 | def isNotNull: Expression with Condition = new Expression with Condition { 31 | override def script: String = s"${Name.this.script} IS NOT sNULL" 32 | 33 | override def parameters: Seq[Parameter[_]] = Name.this.parameters 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/ChNone.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Success, Try} 4 | 5 | /** 6 | * Char None get next char, return it if none of content chars match. 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class ChNone(val content: String, val caseSensitive: Boolean) extends Parsec[Char, Char] { 12 | val contentSet: Set[Char] = if (caseSensitive) content.toSet else content.toLowerCase.toSet 13 | 14 | override def apply(s: State[Char]): Try[Char] = { 15 | s.next() flatMap { c => 16 | if (caseSensitive) { 17 | if (!(contentSet contains c)) { 18 | return Success(c) 19 | } 20 | } else { 21 | if (!(contentSet contains c.toLower)) { 22 | return Success(c) 23 | } 24 | } 25 | s.trap(s"expect any char none of $content (case sensitive $caseSensitive) but get $c") 26 | } 27 | } 28 | } 29 | 30 | object ChNone { 31 | def apply(content: String, caseSensitive: Boolean): ChNone = new ChNone(content, caseSensitive) 32 | 33 | def apply(content: String): ChNone = new ChNone(content, true) 34 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Many1.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.collection.mutable 4 | import scala.util.{Failure, Success, Try} 5 | import scala.util.control.Breaks._ 6 | 7 | /** 8 | * Many try to parse the parse more times, and collect all results into a Seq. 9 | * Many must success once at lest 10 | * 11 | * @author Mars Liu 12 | * @version 1.0.0 13 | */ 14 | class Many1[E, T](val parsec: Parsec[E, T]) extends Parsec[E, Seq[T]] { 15 | val psc = new Attempt[E, T](parsec) 16 | 17 | override def apply(s: State[E]): Try[Seq[T]] = { 18 | val re = new mutable.ListBuffer[T] 19 | parsec ask s match { 20 | case Success(value) => re += value 21 | case Failure(e) => return Failure(e) 22 | } 23 | 24 | breakable { 25 | while (true) { 26 | psc ask s match { 27 | case Success(value) => 28 | re += value 29 | case Failure(_) => 30 | break 31 | } 32 | } 33 | } 34 | Success(re.toSeq) 35 | } 36 | } 37 | 38 | object Many1 { 39 | def apply[E, T](parsec: Parsec[E, T]): Many1[E, T] = new Many1[E, T](parsec) 40 | } 41 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Find.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import java.io.EOFException 4 | 5 | import scala.util.{Failure, Success, Try} 6 | import scala.util.control.Breaks._ 7 | /** 8 | * Find try and next util success or eof. 9 | * 10 | * @author mars 11 | * @version 1.0.0 12 | */ 13 | class Find[E, T](val psc: Parsec[E, T]) extends Parsec[E, T] { 14 | 15 | override def apply(s: State[E]): Try[T] = { 16 | var error: Exception = null 17 | breakable { 18 | while (true){ 19 | psc ? s match { 20 | case right: Success[_] => 21 | return right 22 | case Failure(err: ParsecException) => 23 | if(error == null) { 24 | error = err 25 | } 26 | case Failure(err) => 27 | if (error == null) { 28 | return Failure(err) 29 | } else { 30 | return Failure(error) 31 | } 32 | } 33 | } 34 | } 35 | s.trap("find parsec should not run to this!") 36 | } 37 | } 38 | 39 | object Find { 40 | def apply[E, T](psc: Parsec[E, T]): Find[E, T] = new Find(psc) 41 | } 42 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/Skip1Test.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | import jaskell.parsec.Combinator.skip1 7 | import jaskell.parsec.Txt.text 8 | 9 | /** 10 | * TODO 11 | * 12 | * @author mars 13 | * @version 1.0.0 14 | * @since 2020/05/12 22:13 15 | */ 16 | class Skip1Test extends AnyFlatSpec with Matchers{ 17 | "Simple" should "Just run a simple test" in { 18 | val state = State("left right left right") 19 | val parser = skip1(text("left")) 20 | parser apply state 21 | state.status should be (4) 22 | } 23 | 24 | "Status More" should "Test status after twice matches" in { 25 | val state = State("left left right left right") 26 | val parser = skip1(text("left ")) 27 | parser apply state 28 | state.status should be (10) 29 | } 30 | 31 | "Fail" should "Failed after twice matches" in { 32 | val state = State("right left right left right") 33 | val parser = skip1(text("left ")) 34 | a[ParsecException] should be thrownBy { 35 | parser ! state 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/sql/SelectSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | /** 7 | * TODO 8 | * 9 | * @author mars 10 | * @version 1.0.0 11 | * @since 2020/05/19 19:36 12 | */ 13 | class SelectSpec extends AnyFlatSpec with Matchers { 14 | import jaskell.sql.SQL._ 15 | 16 | "Basic Select" should "Test basic SQL generate" in { 17 | val s = select("f0", "f1", "f2") 18 | val j = s from "table" 19 | val query = j where (q(n("f1") > n("f0")) and q(n("f1") > n("f2"))) 20 | query.script should be 21 | ("SELECT f0, f1, f2 FROM table WHERE (f1 > f0) AND (f1 > f2)") 22 | } 23 | 24 | "Auto Comprehension " should "Test types implicits comprehension" in { 25 | val s = select("f0", "f1", "f2") 26 | val j = s from "table" leftJoin ("table2" as "data") on (n("data.id") == n("table.id")) 27 | val query = j where (n("f1") > n("f0") and n("f1") > n("f2")) 28 | query.script should be 29 | ("SELECT f0, f1, f2 FROM table LEFT JOIN table2 AS data ON data.id = table.id WHERE (f1 > f0) AND (f1 > f2)") 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Update.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | import jaskell.sql 4 | 5 | import scala.collection.mutable 6 | 7 | /** 8 | * TODO 9 | * 10 | * @author mars 11 | * @version 1.0.0 12 | * @since 2020/05/20 18:00 13 | */ 14 | class Update(val name: Name) extends Directive with CouldWhere with CouldReturning with CouldBeQuote { 15 | val pairs: mutable.ListBuffer[(Name, Expression)] = new mutable.ListBuffer[(Name, Expression)] 16 | 17 | def set(name: Name, exp: Expression): this.type = { 18 | pairs += Tuple2(name, exp) 19 | this 20 | } 21 | 22 | override def where(c: Condition): Where with CouldReturning = new Where with CouldReturning { 23 | override val prefix: Directive = Update.this 24 | override val condition: Directive = c 25 | } 26 | 27 | override def script: String = 28 | s"UPDATE ${name.script} SET " + (pairs.map({ pair => 29 | val (n, exp) = pair 30 | s"${n.script} = ${exp.script}" 31 | }) mkString ", ") 32 | 33 | override def parameters: Seq[Parameter[_]] = name.parameters ++ pairs.flatMap({ pair => 34 | val (n, exp) = pair 35 | n.parameters ++ exp.parameters 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/Many1Spec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | import jaskell.parsec.Combinator._ 6 | import jaskell.parsec.Txt._ 7 | 8 | /** 9 | * TODO 10 | * 11 | * @author mars 12 | * @version 1.0.0 13 | * @since 2020/05/12 19:22 14 | */ 15 | class Many1Spec extends AnyFlatSpec with Matchers{ 16 | "One" should "Get first char and return" in { 17 | val state = State("hello") 18 | val parser = many1(ch('h')) 19 | val re = parser(state) 20 | assert(re.isSuccess) 21 | re.foreach(ele => { 22 | ele.head should be('h') 23 | ele.size should be(1) 24 | }) 25 | } 26 | 27 | "All" should "success all content" in { 28 | val state = State("hello") 29 | val parser = many1(text("hello")) 30 | val re = parser(state) 31 | assert(re.isSuccess) 32 | re.foreach(_.head should be ("hello")) 33 | } 34 | 35 | "Failed" should "Throw error " in { 36 | val state = State("Hello") 37 | val parser = many1(ch('h')) 38 | a[ParsecException] should be thrownBy { 39 | parser ! state 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/SepBy1Spec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | import jaskell.parsec.Txt.ch 7 | /** 8 | * TODO 9 | * 10 | * @author mars 11 | * @version 1.0.0 12 | * @since 2020/05/12 21:31 13 | */ 14 | class SepBy1Spec extends AnyFlatSpec with Matchers { 15 | "Basic" should "Run some basic tests" in { 16 | val state = State("hlhlhlhlhlhll") 17 | val p = SepBy1(ch('h'), ch('l')) 18 | 19 | val re = p parse state 20 | re.size should be(6) 21 | re.foreach(item => { 22 | item should be('h') 23 | }) 24 | } 25 | 26 | "2 Times" should "Match 2 times sepBy1" in { 27 | val state = State("hlh,h.hlhlhll") 28 | val p = SepBy1(ch('h'), ch('l')) 29 | 30 | val re = p parse state 31 | re.size should be(2) 32 | re foreach { item => 33 | item should be('h') 34 | } 35 | } 36 | 37 | "Fail" should "Failed at " in { 38 | val state = State("hlh,h.hlhlhll") 39 | val p = SepBy1(ch('h'), ch('l')) 40 | p parse state 41 | a[ParsecException] should be thrownBy { 42 | p parse state 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/SepBy.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import jaskell.parsec.Parsec.Implicits._ 4 | import scala.collection.mutable 5 | import scala.util.{Failure, Success, Try} 6 | 7 | /** 8 | * sepBy p sep parses zero or more occurrences of p, separated by sep. Returns a list of values returned by p. 9 | * 10 | * @author Mars Liu 11 | * @version 1.0.0 12 | */ 13 | class SepBy[E, T](val parsec: Parsec[E, T], val by: Parsec[E, _]) extends Parsec[E, Seq[T]] { 14 | val b: Attempt[E, _] = Attempt(by) 15 | val p: Attempt[E, T] = Attempt[E, T](parsec) 16 | val psc: Parsec[E, T] = (s: State[E]) => for { 17 | _ <- b ask s 18 | re <- p ask s 19 | } yield re 20 | 21 | override def apply(s: State[E]): Try[Seq[T]] = { 22 | val re = new mutable.ListBuffer[T] 23 | p ? s map { head => 24 | re += head 25 | while (true) { 26 | psc ? s match { 27 | case Success(r) => re += r 28 | case Failure(_) => return Success(re.toSeq) 29 | } 30 | } 31 | re.toSeq 32 | } 33 | } 34 | } 35 | 36 | object SepBy { 37 | def apply[E, T](parsec: Parsec[E, T], by: Parsec[E, _]): SepBy[E, T] = new SepBy(parsec, by) 38 | } 39 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/SepBy1.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import jaskell.parsec.Parsec.Implicits._ 4 | import scala.collection.mutable 5 | import scala.util.{Failure, Success, Try} 6 | 7 | /** 8 | * SepBy1 p sep parses one or more occurrences of p, separated by sep. Returns a list of values returned by p. 9 | * 10 | * @author mars 11 | * @version 1.0.0 12 | */ 13 | class SepBy1[E, T](val parsec: Parsec[E, T], val by: Parsec[E, _]) extends Parsec[E, Seq[T]] { 14 | val b = new Attempt(by) 15 | val p = new Attempt[E, T](parsec) 16 | val psc: Parsec[E, T] = (s: State[E]) => for { 17 | _ <- b ask s 18 | re <- p ask s 19 | } yield re 20 | 21 | override def apply(s: State[E]): Try[Seq[T]] = { 22 | val re = new mutable.ListBuffer[T] 23 | parsec ? s map { head => 24 | re += head 25 | while (true) { 26 | psc ? s match { 27 | case Success(value) => re += value 28 | case Failure(_) => return Success(re.toSeq) 29 | } 30 | } 31 | return Success(re.toSeq) 32 | } 33 | } 34 | } 35 | 36 | object SepBy1 { 37 | def apply[E, T](parsec: Parsec[E, T], by: Parsec[E, _]): SepBy1[E, T] = new SepBy1(parsec, by) 38 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Text.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.util.{Failure, Success, Try} 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | class Text(val text: String, val caseSensitive: Boolean) extends Parsec[Char, String] { 12 | val content: String = if (caseSensitive) text else text.toLowerCase 13 | 14 | override def apply(s: State[Char]): Try[String] = { 15 | var idx = 0 16 | val sb: StringBuilder = new StringBuilder 17 | for(c <- this.text) { 18 | s.next() match { 19 | case Success(data) => 20 | val dataChar = if (caseSensitive) data else data.toLower 21 | if (c != dataChar) { 22 | return s.trap(s"Expect $c of $text [$idx] (case sensitive $caseSensitive) at ${s.status} but get $data") 23 | } 24 | idx += 1 25 | sb += data 26 | case Failure(error) => 27 | return Failure(error) 28 | } 29 | } 30 | Success(sb.toString()) 31 | } 32 | } 33 | 34 | object Text { 35 | def apply(text: String, caseSensitive: Boolean): Text = new Text(text, caseSensitive) 36 | 37 | def apply(text: String): Text = new Text(text, true) 38 | 39 | } -------------------------------------------------------------------------------- /src/test/scala/jaskell/sql/WithSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | /** 7 | * TODO 8 | * 9 | * @author mars 10 | * @version 1.0.0 11 | * @since 2020/05/21 13:03 12 | */ 13 | class WithSpec extends AnyFlatSpec with Matchers { 14 | import jaskell.sql.SQL._ 15 | "Basic" should "Generate a with cte " in { 16 | val script = "WITH t1 AS (SELECT n FROM t), t2 AS (SELECT m FROM t) SELECT m * n FROM t1 JOIN t2 ON t1.n != t2.m" 17 | val query = ((With(n("t1")) as q(select("n").from("t"))) and ( n("t2"), q(select("m").from("t")))) 18 | .select(c("m") * c("n")) 19 | .from("t1").join(t("t2")).on(n("t1.n") != n("t2.m")) 20 | query.script should be (script) 21 | } 22 | 23 | "Recursive" should "Generate a recursive common table expression" in { 24 | val script = "WITH RECURSIVE t(f) AS (SELECT 1 UNION SELECT f + 1 FROM t WHERE f < 100) SELECT f FROM t" 25 | val s = select (l("f")+l(1)).from("t").where(n("f") < l(100)) 26 | val query = With.recursive.as(n("t(f)"), select (l(1)).union(s)).select("f").from("t") 27 | 28 | query.script should be (script) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/SkipSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | import jaskell.parsec.Combinator.skip 6 | import jaskell.parsec.Txt.chIn 7 | 8 | /** 9 | * TODO 10 | * 11 | * @author mars 12 | * @version 1.0.0 13 | * @since 2020/05/12 22:21 14 | */ 15 | class SkipSpec extends AnyFlatSpec with Matchers{ 16 | "Once" should "Run match once" in { 17 | val state = State("hello World"); 18 | val s = skip(Eq('h')); 19 | s apply state 20 | 21 | state.status should be (1) 22 | } 23 | 24 | "Stop1" should "Stop at start" in { 25 | val state = State("hello World") 26 | val s = skip(Eq('e')) 27 | s apply state 28 | state.status should be (0) 29 | } 30 | 31 | "Spaces" should "Skip some spaces or tabs" in { 32 | val state = State("\t\t \thello World") 33 | val spaces = skip(chIn(" \t")) 34 | spaces apply state 35 | 36 | state.status should be (4) 37 | } 38 | 39 | "Nothing" should "Skip nothing" in { 40 | val state = State("\nhello World") 41 | val spaces = skip(chIn(" \t")) 42 | spaces apply state 43 | state.status should be (0) 44 | } 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/SepEndBy.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.collection.mutable 4 | import scala.util.{Failure, Success, Try} 5 | import util.control.Breaks._ 6 | 7 | /** 8 | * SepEndBy p sep parses zero or more occurrences of p, separated and optionally ended by sep, ie. 9 | * haskell style statements. Returns a seq of values returned by p. 10 | * 11 | * @author mars 12 | * @version 1.0.0 13 | */ 14 | class SepEndBy[E, T](val parser: Parsec[E, T], val sep: Parsec[E, _]) extends Parsec[E, Seq[T]] { 15 | import jaskell.parsec.Parsec.Implicits._ 16 | 17 | val separator = new Attempt(sep) 18 | val p = new Attempt(parser) 19 | val parsec: Parsec[E, Seq[T]] = (s: State[E]) => { 20 | val re = new mutable.ListBuffer[T] 21 | breakable { 22 | while (true) { 23 | if ((separator ? s).isFailure) { 24 | break 25 | } 26 | val tran = s.begin() 27 | p ? s match { 28 | case Success(value) => 29 | re += value 30 | s commit tran 31 | case Failure(_) => 32 | s rollback tran 33 | break 34 | } 35 | } 36 | } 37 | Success(re.toSeq) 38 | } 39 | 40 | override def apply(s: State[E]): Try[Seq[T]] = parsec ? s 41 | } 42 | -------------------------------------------------------------------------------- /src/main/scala-2/jaskell/parsec/CommonState.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import java.io.EOFException 4 | import scala.util.{Failure, Success, Try} 5 | 6 | /** 7 | * Common State has int status and transaction market. It can apply to Seq[T] or any serial collection. 8 | * 9 | * @author Mars Liu 10 | * @version 1.0.0 11 | */ 12 | trait CommonState[T] extends State[T] { 13 | override type Status = scala.Int 14 | override type Tran = scala.Int 15 | 16 | val content: Seq[T] 17 | var current: scala.Int = 0 18 | var tran: scala.Int = -1 19 | 20 | def next(): Try[T] = { 21 | if (content.size <= current) { 22 | Failure(new EOFException()) 23 | } else { 24 | val re = content(current) 25 | current += 1 26 | Success(re) 27 | } 28 | } 29 | 30 | def status: Status = { 31 | current 32 | } 33 | 34 | def begin(): Tran = { 35 | if (this.tran == -1) this.tran = this.current 36 | current 37 | } 38 | 39 | def begin(tran: Tran): Tran = { 40 | if (this.tran > tran) this.tran = tran 41 | this.tran 42 | } 43 | 44 | def rollback(tran: Tran): Unit = { 45 | if (this.tran == tran) this.tran = -1 46 | this.current = tran 47 | } 48 | 49 | def commit(tran: Tran): Unit = { 50 | if (this.tran == tran) this.tran = -1 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Returning.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | import scala.collection.mutable 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2020/05/20 18:08 11 | */ 12 | trait Returning extends Directive with CouldBeQuote with Query with CouldUnion { 13 | val prefix: Directive 14 | 15 | val columns: mutable.ListBuffer[CouldBeColumn] = new mutable.ListBuffer[CouldBeColumn] 16 | 17 | override def script: String = { 18 | val sb: mutable.StringBuilder = new mutable.StringBuilder("RETURNING ") 19 | sb ++= columns.map(_.script).mkString(", ") 20 | sb.mkString 21 | } 22 | 23 | override def parameters: Seq[Parameter[_]] = { 24 | val re = columns.flatMap(_.parameters).toSeq 25 | for (idx <- re.indices) { 26 | re(idx).order(idx + 1) 27 | } 28 | re 29 | } 30 | 31 | def apply(names: CouldBeColumn *) : this.type = { 32 | this.columns ++= names 33 | this 34 | } 35 | 36 | def +=(name: Any): this.type = { 37 | this.columns += { 38 | name match { 39 | case n:String => new Name(n) 40 | case c:CouldBeColumn => c 41 | }} 42 | 43 | this 44 | } 45 | 46 | def ++=(names: Seq[_]): this.type = { 47 | for (name <- names) { 48 | this += name 49 | } 50 | this 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/scala-2.13/jaskell/parsec/TxtState.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.collection.mutable 4 | /** 5 | * Txt State extends Common State trait. It special for text content analyst. 6 | * Txt state could mark a status point in which line. 7 | * 8 | * @author Mars Liu 9 | * @version 1.0.0 10 | */ 11 | class TxtState(val txt: String, val newLine:Char = '\n') extends CommonState[Char] { 12 | override val content: Seq[Char] = txt.toCharArray.toSeq 13 | val lines: scala.collection.SortedMap[scala.Int, scala.Int] = { 14 | val result = new mutable.TreeMap[scala.Int, scala.Int](); 15 | result.put(0, 0); 16 | for(index <- Range(0, txt.length)){ 17 | val c = txt.charAt(index) 18 | if(c == newLine) { 19 | val lastIndex = result.lastKey 20 | result.put(lastIndex, index); 21 | if(index < txt.length - 1) { 22 | result.put(index+1, index+1) 23 | } 24 | } 25 | } 26 | result 27 | } 28 | def lineByIndex(index: scala.Int): scala.Int = { 29 | var i = 0 30 | for(idx:scala.Int <- lines.keySet){ 31 | if(idx <= index && index <= lines(idx)) { 32 | return i 33 | } 34 | i += 1 35 | } 36 | -1 37 | } 38 | } 39 | 40 | object TxtState { 41 | def apply(txt: String, newLine: Char='\n'): TxtState = new TxtState(txt, newLine) 42 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Expression.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 15:50 9 | */ 10 | trait Expression extends Directive with CouldBeQuote with CouldIn with CouldBeColumn with CouldCondition with Condition { 11 | def +(other: Expression): Expression = new Expression { 12 | override def script: String = s"${Expression.this.script} + ${other.script}" 13 | 14 | override def parameters: Seq[Parameter[_]] = Expression.this.parameters ++ other.parameters 15 | } 16 | 17 | def -(other: Expression): Expression = new Expression { 18 | override def script: String = s"${Expression.this.script} - ${other.script}" 19 | 20 | override def parameters: Seq[Parameter[_]] = Expression.this.parameters ++ other.parameters 21 | } 22 | 23 | def *(other: Expression): Expression = new Expression { 24 | override def script: String = s"${Expression.this.script} * ${other.script}" 25 | 26 | override def parameters: Seq[Parameter[_]] = Expression.this.parameters ++ other.parameters 27 | } 28 | 29 | def /(other: Expression): Expression = new Expression { 30 | override def script: String = s"${Expression.this.script} / ${other.script}" 31 | 32 | override def parameters: Seq[Parameter[_]] = Expression.this.parameters ++ other.parameters 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/ManyTill.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import java.io.EOFException 4 | 5 | import scala.collection.mutable 6 | import scala.util.{Failure, Success, Try} 7 | 8 | /** 9 | * manyTill p end applies parser p zero or more times until parser end succeeds. Returns the list of 10 | * values returned by p. This parser can be used to scan comments. 11 | * 12 | * @author Mars Liu 13 | * @version 1.0.0 14 | */ 15 | class ManyTill[E, T, L](val parser: Parsec[E, T], val end: Parsec[E, L]) extends Parsec[E, Seq[T]] { 16 | val psc = new Attempt[E, T](parser) 17 | val till = new Attempt[E, L](end) 18 | 19 | 20 | 21 | override def apply(s: State[E]): Try[Seq[T]] = { 22 | val re = new mutable.ListBuffer[T] 23 | while (true) { 24 | till ask s match { 25 | case Success(_) => return Success(re.toSeq) 26 | case Failure(error: EOFException) => return Failure(error) 27 | case Failure(_: ParsecException) => 28 | psc ask s match { 29 | case Success(value) => re += value 30 | case Failure(e) => Failure(e) 31 | } 32 | case Failure(e) => Failure(e) 33 | } 34 | } 35 | Success(re.toSeq) 36 | } 37 | } 38 | 39 | object ManyTill { 40 | def apply[E, T, L](parser: Parsec[E, T], end: Parsec[E, L]): ManyTill[E, T, L] = new ManyTill(parser, end) 41 | } -------------------------------------------------------------------------------- /src/main/scala-2.12/jaskell/parsec/TxtState.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.collection.mutable 4 | import scala.collection.SortedMap 5 | 6 | /** 7 | * Txt State extends Common State trait. It special for text content analyst. 8 | * Txt state could mark a status point in which line. 9 | * 10 | * @author Mars Liu 11 | * @version 1.0.0 12 | */ 13 | class TxtState(val txt: String, val newLine:Char = '\n') extends CommonState[Char] { 14 | override val content: Seq[Char] = txt.toCharArray.toSeq 15 | val lines: SortedMap[scala.Int, scala.Int] = { 16 | val result = new mutable.TreeMap[scala.Int, scala.Int](); 17 | result.put(0, 0); 18 | for(index <- Range(0, txt.length)){ 19 | val c = txt.charAt(index) 20 | if(c == newLine) { 21 | val lastIndex = result.lastKey 22 | result.put(lastIndex, index); 23 | if(index < txt.length - 1) { 24 | result.put(index+1, index+1) 25 | } 26 | } 27 | } 28 | result 29 | } 30 | def lineByIndex(index: scala.Int): scala.Int = { 31 | var i = 0 32 | for(idx:scala.Int <- lines.keySet){ 33 | if(idx <= index && index <= lines(idx)) { 34 | return i 35 | } 36 | i += 1 37 | } 38 | -1 39 | } 40 | } 41 | 42 | object TxtState { 43 | def apply(txt: String, newLine: Char='\n'): TxtState = new TxtState(txt, newLine) 44 | } -------------------------------------------------------------------------------- /src/main/scala-2/jaskell/batteries/cstyle/Strings.scala: -------------------------------------------------------------------------------- 1 | package jaskell.batteries.cstyle 2 | 3 | import jaskell.Monad.Implicits._ 4 | import jaskell.parsec.Combinator.many 5 | import jaskell.parsec.Parsec.Implicits._ 6 | import jaskell.parsec.Txt.{ch, mkString} 7 | import jaskell.parsec.{Parsec, State} 8 | 9 | import scala.language.implicitConversions 10 | import scala.util.Success 11 | 12 | /** 13 | * TODO 14 | * 15 | * @author mars 16 | * @version 1.0.0 17 | * @since 2022/02/07 09:33 18 | */ 19 | object Strings { 20 | 21 | def escapedChar: Parsec[Char, Char] = ch('\\') *> { (s: State[Char]) => 22 | s.next() flatMap { 23 | case 'n' => 24 | Success('\n') 25 | case 'r' => 26 | Success('\r') 27 | case '"' => 28 | Success('"') 29 | case 't' => 30 | Success('\t') 31 | case c: Char => 32 | s.trap(f"\\$c isn't a valid escape char") 33 | } 34 | } 35 | 36 | def char: Parsec[Char, Char] = (s: State[Char]) => 37 | s.next() flatMap { 38 | case '\n' => 39 | s.trap(f"new line is invalid in a literal token, maybe use \\n?") 40 | case '"' => 41 | s.trap("stop at \"") 42 | case c: Char => 43 | Success(c) 44 | 45 | } 46 | 47 | def string: Parsec[Char, String] = 48 | ch('"') *> many(escapedChar <|> char) <* ch('"') >>= mkString 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/croupier/BinaryScaled.scala: -------------------------------------------------------------------------------- 1 | package jaskell.croupier 2 | 3 | import scala.util.Random 4 | 5 | /** 6 | * Rand select by scale weight. 7 | * 8 | * @param scale the trait that get item's weight 9 | * @param random random object, auto new one if no given 10 | * @tparam T type of card 11 | */ 12 | class BinaryScaled[T](scale: Scale[T], val random: Random = new Random()) extends Poker[T] { 13 | override def select(cards: Seq[T]): Option[Int] = { 14 | if (cards == null || cards.isEmpty) { 15 | return None 16 | } 17 | if (cards.size == 1) { 18 | return Some(0) 19 | } 20 | 21 | val steps: Seq[Int] = cards 22 | .map(scale.weight) 23 | .foldLeft(Seq[Int](0)) { (result: Seq[Int], r: Int) => 24 | result :+ (result.last + r) 25 | } 26 | val top = steps.last 27 | val score = random.nextInt(top) 28 | 29 | var idx: Int = steps.size / 2 30 | var low = 0 31 | var high = steps.size - 1 32 | while (high - low > 1) { 33 | if (steps(idx) <= score && steps(idx + 1) > score) { 34 | return Some(idx) 35 | } 36 | if (score < steps(idx)) { 37 | high = idx 38 | idx -= math.max((idx - low) / 2, 1) 39 | } else { 40 | low = idx 41 | idx += math.max((high - idx) / 2, 1) 42 | } 43 | } 44 | 45 | Some(idx) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/BetweenSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import jaskell.parsec.Txt.nch 4 | import org.scalatest.flatspec.AnyFlatSpec 5 | import org.scalatest.matchers.should.Matchers 6 | 7 | import scala.util.Success 8 | 9 | 10 | /** 11 | * Between Tester. 12 | * 13 | * @author Mars Liu 14 | * 15 | * 16 | */ 17 | class BetweenSpec extends AnyFlatSpec with Matchers { 18 | 19 | import Combinator.many 20 | import Txt.ch 21 | 22 | "Simple" should "test basic between case" in { 23 | 24 | val state = State("hello"); 25 | 26 | val bmw = Between[Char, Char]( 27 | ch('h'), 28 | ch('l'), 29 | ch('e') 30 | ); 31 | 32 | val c = bmw(state); 33 | 34 | c should be(Success('e')) 35 | } 36 | 37 | "Brackets" should "test brackets pairs" in { 38 | 39 | val state = State("[hello]"); 40 | val parser = Between( 41 | ch('['), 42 | ch(']'), 43 | many(Ne(']')) 44 | ) 45 | 46 | val re = parser(state).map(_.mkString) 47 | re should be (Success("hello")) 48 | } 49 | 50 | "Combinator" should "test typeclasses style" in { 51 | import jaskell.parsec.Combinator.BuiltIn 52 | val state = State("[hello]"); 53 | val parser = nch(']').many.between(ch('['), ch(']')) 54 | 55 | val re = parser(state).map(_.mkString) 56 | re should be (Success("hello")) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/croupier/BinaryRanked.scala: -------------------------------------------------------------------------------- 1 | package jaskell.croupier 2 | 3 | import scala.util.Random 4 | 5 | /** 6 | * Rand select by rank score. 7 | * 8 | * @param ranker the trait that get item's rank 9 | * @param random random object, auto new one if no given 10 | * @tparam T type of card 11 | */ 12 | class BinaryRanked[T](ranker: Ranker[T], val random: Random = new Random()) extends Poker[T] { 13 | override def select(cards: Seq[T]): Option[Int] = { 14 | if (cards == null || cards.isEmpty) { 15 | return None 16 | } 17 | if (cards.size == 1) { 18 | return Some(0) 19 | } 20 | 21 | val steps: Seq[Double] = cards 22 | .map(ranker.rank) 23 | .foldLeft(Seq[Double](0)) { (result: Seq[Double], r: Double) => 24 | result :+ (r + result.last) 25 | } 26 | val top = steps.last 27 | val score = random.nextDouble() * top 28 | 29 | var idx: Int = steps.size / 2 30 | var low = 0 31 | var high = steps.size - 1 32 | while (high - low > 1) { 33 | if (steps(idx) <= score && steps(idx + 1) > score) { 34 | return Some(idx) 35 | } 36 | if (score < steps(idx)) { 37 | high = idx 38 | idx -= math.max((idx - low) / 2, 1) 39 | } else { 40 | low = idx 41 | idx += math.max((high - idx) / 2, 1) 42 | } 43 | } 44 | 45 | Some(idx) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/scala-2.11/jaskell/parsec/TxtState.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.collection.JavaConverters.asScalaSetConverter 4 | 5 | 6 | /** 7 | * Txt State extends Common State trait. It special for text content analyst. 8 | * Txt state could mark a status point in which line. 9 | * 10 | * @author Mars Liu 11 | * @version 1.0.0 12 | */ 13 | class TxtState(val txt: String, val newLine:Char = '\n') extends CommonState[Char] { 14 | override val content: Seq[Char] = txt.toCharArray.toSeq 15 | val lines: java.util.SortedMap[scala.Int, scala.Int] = { 16 | val result = new java.util.TreeMap[scala.Int, scala.Int](); 17 | result.put(0, 0); 18 | for(index <- Range(0, txt.length)){ 19 | val c = txt.charAt(index) 20 | if(c == newLine) { 21 | val lastIndex = result.lastKey 22 | result.put(lastIndex, index); 23 | if(index < txt.length - 1) { 24 | result.put(index+1, index+1) 25 | } 26 | } 27 | } 28 | result 29 | } 30 | def lineByIndex(index: scala.Int): scala.Int = { 31 | var i = 0 32 | for(idx:scala.Int <- lines.keySet().asScala){ 33 | if(idx <= index && index <= lines.get(idx)) { 34 | return i 35 | } 36 | i += 1 37 | } 38 | -1 39 | } 40 | } 41 | 42 | object TxtState { 43 | def apply(txt: String, newLine: Char='\n'): TxtState = new TxtState(txt, newLine) 44 | } -------------------------------------------------------------------------------- /src/main/scala-2.12/jaskell/croupier/ZipScaled.scala: -------------------------------------------------------------------------------- 1 | package jaskell.croupier 2 | 3 | import scala.collection.mutable 4 | import scala.util.Random 5 | 6 | /** 7 | * Rand select by scale weight. Faster if very large cards 8 | * 9 | * @param scale the trait that get item's weight 10 | * @param random random object, auto new one if no given 11 | * @tparam T type of card 12 | */ 13 | class ZipScaled[T](scale: Scale[T], val random: Random = new Random()) extends Poker[T] { 14 | val scaled = new Scaled[(Int, Int)](_._2, random) 15 | val fair = new Fair[Int](random) 16 | 17 | override def select(cards: Seq[T]): Option[Int] = { 18 | if (cards == null || cards.isEmpty) { 19 | return None 20 | } 21 | if (cards.size == 1) { 22 | return Some(0) 23 | } 24 | 25 | val group = new mutable.TreeMap[Int, Seq[Int]]() 26 | val steps = cards.map(scale.weight) 27 | for (position <- steps.indices) { 28 | val w = steps(position) 29 | group.put(w, group.getOrElse(w, Seq()) :+ position) 30 | } 31 | val pairs: Seq[(Int, Int)] = (for ((weight, positions) <- group) yield (weight, weight * positions.size)).toSeq 32 | scaled.select(pairs) 33 | .map(pairs) 34 | .map({ weight => 35 | group(weight._1) 36 | }) 37 | .flatMap({ positions => 38 | val result = fair.select(positions).map(positions) 39 | result 40 | }) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/CouldJoin.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 18:57 9 | */ 10 | trait CouldJoin extends Directive { 11 | def join(jo: CouldBeJoin): Join = new Join { 12 | override val opt = "" 13 | override val prefix: Directive = CouldJoin.this 14 | override val j: CouldBeJoin = jo 15 | } 16 | 17 | def innerJoin(jo: CouldBeJoin): Join = new Join { 18 | override val opt: String = "INNER" 19 | override val prefix: Directive = CouldJoin.this 20 | override val j: CouldBeJoin = jo 21 | } 22 | 23 | def leftJoin(jo: CouldBeJoin): Join = new Join { 24 | override val opt: String = "LEFT" 25 | override val prefix: Directive = CouldJoin.this 26 | override val j: CouldBeJoin = jo 27 | } 28 | 29 | def rightJoin(jo: CouldBeJoin): Join = new Join { 30 | override val opt: String = "RIGHT" 31 | override val prefix: Directive = CouldJoin.this 32 | override val j: CouldBeJoin = jo 33 | } 34 | 35 | def crossJoin(jo: CouldBeJoin): Join = new Join { 36 | override val opt: String = "CROSS" 37 | override val prefix: Directive = CouldJoin.this 38 | override val j: CouldBeJoin = jo 39 | } 40 | 41 | def fullJoin(jo: CouldBeJoin): Join = new Join { 42 | override val opt: String = "FULL" 43 | override val prefix: Directive = CouldJoin.this 44 | override val j: CouldBeJoin = jo 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/scala-2.13/jaskell/croupier/ZipScaled.scala: -------------------------------------------------------------------------------- 1 | package jaskell.croupier 2 | 3 | import scala.collection.mutable 4 | import scala.util.Random 5 | 6 | /** 7 | * Rand select by scale weight. Faster if very large cards 8 | * 9 | * @param scale the trait that get item's weight 10 | * @param random random object, auto new one if no given 11 | * @tparam T type of card 12 | */ 13 | class ZipScaled[T](scale: Scale[T], val random: Random = new Random()) extends Poker[T] { 14 | val scaled = new Scaled[(Int, Int)](_._2, random) 15 | val fair = new Fair[Int](random) 16 | 17 | override def select(cards: Seq[_ <: T]): Option[Int] = { 18 | if (cards == null || cards.isEmpty) { 19 | return None 20 | } 21 | if (cards.size == 1) { 22 | return Some(0) 23 | } 24 | 25 | val group = new mutable.TreeMap[Int, Seq[Int]]() 26 | val steps = cards.map(scale.weight) 27 | for (position <- steps.indices) { 28 | val w = steps(position) 29 | group.put(w, group.getOrElse(w, Seq()).appended(position)) 30 | } 31 | val pairs: Seq[(Int, Int)] = 32 | (for ((weight, positions) <- group) 33 | yield (weight, weight * positions.size) 34 | ).toSeq 35 | 36 | scaled.select(pairs) 37 | .map(pairs) 38 | .map({ weight => 39 | group(weight._1) 40 | }) 41 | .flatMap({ positions => 42 | val result = fair.select(positions).map(positions) 43 | result 44 | }) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/IntSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import jaskell.parsec.Txt.uInteger 4 | import org.scalatest.flatspec.AnyFlatSpec 5 | import org.scalatest.matchers.should.Matchers 6 | 7 | /** 8 | * TODO 9 | * 10 | * @author mars 11 | * @version 1.0.0 12 | * @since 2020/05/12 22:48 13 | */ 14 | class IntSpec extends AnyFlatSpec with Matchers { 15 | "Simple" should "Run a simple test" in { 16 | val state = State("23413214") 17 | 18 | val parser = Int.apply 19 | 20 | val re = parser ! state 21 | 22 | re should be ("23413214") 23 | } 24 | 25 | "Negative Simple" should "Run a simple test" in { 26 | val state = State("-23413214") 27 | 28 | val parser = Int.apply 29 | 30 | val re = parser ! state 31 | 32 | re should be ("-23413214") 33 | } 34 | 35 | "Stop" should "Match digits until a letter" in { 36 | val state = State("23413a214") 37 | 38 | val parser = Int.apply 39 | 40 | val re = parser ! state 41 | 42 | re should be ("23413") 43 | } 44 | 45 | "Negative Stop" should "Match negative digits until a letter" in { 46 | val state = State("-23413a214") 47 | 48 | val parser = Int.apply 49 | 50 | val re = parser ! state 51 | 52 | re should be ("-23413") 53 | } 54 | 55 | "Negative Fail" should "Failed" in { 56 | val state = State("-x2344") 57 | val parser = new Int 58 | a[ParsecException] should be thrownBy { 59 | parser ! state 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/expression/parsers/Parser.scala: -------------------------------------------------------------------------------- 1 | package jaskell.expression.parsers 2 | 3 | import jaskell.expression.Expression 4 | import jaskell.parsec._ 5 | 6 | import scala.util.{Failure, Success, Try} 7 | 8 | /** 9 | * TODO 10 | * 11 | * @author mars 12 | * @version 1.0.0 13 | * @since 2020/06/02 21:45 14 | */ 15 | class Parser extends Parsec[Char, Expression] { 16 | 17 | import jaskell.parsec.Atom.eof 18 | import jaskell.parsec.Combinator.{ahead, attempt} 19 | import jaskell.parsec.Txt.{ch, skipWhiteSpaces} 20 | import jaskell.parsec.Parsec.Implicits._ 21 | 22 | val rq: Attempt[Char, Char] = attempt(ch(')')) 23 | val skips: SkipWhitespaces = skipWhiteSpaces 24 | val e: Eof[Char] = eof 25 | 26 | val end: Ahead[Char, Unit] = ahead((s: State[Char]) => { 27 | rq ? s match { 28 | case Failure(_) => 29 | e ? s 30 | case Success(_) => 31 | Success() 32 | } 33 | }) 34 | 35 | override def apply(s: State[Char]): Try[Expression] = { 36 | val np = attempt(attempt(new Num) <|> attempt(new Param) <|> new Q) 37 | 38 | np ? s flatMap { left => 39 | (for { 40 | _ <- skips ? s 41 | e <- end ? s 42 | } yield { 43 | e 44 | }) match { 45 | case Success(_) => Success(left) 46 | case Failure(_) => 47 | val next = attempt(new A(left)) <|> attempt(new S(left)) <|> attempt(new P(left)) <|> new D(left) 48 | next ? s 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/SepEndBy1.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.collection.mutable 4 | import scala.util.{Failure, Success, Try} 5 | import scala.util.control.Breaks._ 6 | import jaskell.parsec.Parsec.Implicits._ 7 | 8 | /** 9 | * SepEndBy1 p sep parses one or more occurrences of p, separated and optionally ended by sep. 10 | * Returns a list of values returned by p. 11 | * 12 | * @author mars 13 | * @version 1.0.0 14 | */ 15 | class SepEndBy1[E, T](val parser: Parsec[E, T], val sep: Parsec[E, _]) extends Parsec[E, Seq[T]] { 16 | val separator = new Attempt(sep) 17 | val p: Parsec[E, T] = Attempt(parser) 18 | val psc: Parsec[E, T] = (s: State[E]) => for { 19 | _ <- sep ask s 20 | re <- p ask s 21 | } yield re 22 | 23 | val parsec: Parsec[E, Seq[T]] = (s: State[E]) => { 24 | val re = new mutable.ListBuffer[T] 25 | 26 | parser ? s map { head => 27 | re += head 28 | breakable { 29 | while (true) { 30 | if ((separator ? s).isFailure) { 31 | break 32 | } 33 | 34 | val tran = s.begin() 35 | p ? s match { 36 | case Success(value) => 37 | re += value 38 | s commit tran 39 | case Failure(_) => 40 | s rollback tran 41 | break 42 | } 43 | } 44 | } 45 | } 46 | Success(re.toSeq) 47 | } 48 | 49 | override def apply(s: State[E]): Try[Seq[T]] = parsec ? s 50 | } 51 | -------------------------------------------------------------------------------- /src/main/scala-2.11/jaskell/croupier/ZipScaled.scala: -------------------------------------------------------------------------------- 1 | package jaskell.croupier 2 | 3 | import scala.collection.JavaConversions.mapAsScalaMap 4 | import scala.util.Random 5 | 6 | /** 7 | * Rand select by scale weight. Faster if very large cards 8 | * 9 | * @param scale the trait that get item's weight 10 | * @param random random object, auto new one if no given 11 | * @tparam T type of card 12 | */ 13 | class ZipScaled[T](scale: Scale[T], val random: Random = new Random()) extends Poker[T] { 14 | val scaled = new Scaled[(Int, Int)](new Scale[(Int, Int)] { 15 | override def weight(item: (Int, Int)): Int = item._2 16 | }, random) 17 | val fair = new Fair[Int](random) 18 | 19 | override def select(cards: Seq[T]): Option[Int] = { 20 | if (cards == null || cards.isEmpty) { 21 | return None 22 | } 23 | if (cards.size == 1) { 24 | return Some(0) 25 | } 26 | 27 | val group = new java.util.TreeMap[Int, Seq[Int]]() 28 | val steps = cards.map(scale.weight) 29 | for (position <- steps.indices) { 30 | val w = steps(position) 31 | group.put(w, group.getOrElse(w, Seq()) :+ position) 32 | } 33 | val pairs: Seq[(Int, Int)] = (for ((weight, positions) <- group) yield (weight, weight * positions.size)).toSeq 34 | scaled.select(pairs) 35 | .map(pairs) 36 | .map({ weight => 37 | group(weight._1) 38 | }) 39 | .flatMap({ positions => 40 | val result = fair.select(positions).map(positions) 41 | result 42 | }) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/AttemptSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | import scala.util.Success 7 | 8 | /** 9 | * TODO 10 | * 11 | * @author mars 12 | * @version 1.0.0 13 | * @since 2020/05/12 22:33 14 | */ 15 | class AttemptSpec extends AnyFlatSpec with Matchers { 16 | "Simple" should "Run a simple test" in { 17 | val data = Seq("Hello", "World") 18 | 19 | val state = State[String](data) 20 | val idx = state.status 21 | val tryIt = new Attempt[String, String](Eq("Hello")) 22 | 23 | val re = tryIt ! state 24 | 25 | re should be("Hello") 26 | idx should not be (state.status) 27 | } 28 | 29 | "Fail" should "Run a failed test" in { 30 | val data = Seq("Hello", "World") 31 | val state = State[String](data) 32 | val idx = state.status 33 | val tryIt = new Attempt[String, String](new Eq[String]("hello")) 34 | 35 | a[ParsecException] should be thrownBy { 36 | tryIt ! state 37 | } 38 | idx should be(state.status) 39 | 40 | val tryIti = new Attempt[String, String](Parsec[String, String] { s => 41 | s.next() flatMap { content => 42 | if (content.toLowerCase == "hello") { 43 | Success(content) 44 | } else { 45 | state.trap(f"expect a word match [Hello] and case insensitive but get [${content}]") 46 | } 47 | } 48 | }) 49 | 50 | val re = tryIti ! state 51 | re should be("Hello") 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Select.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | import scala.collection.mutable 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2020/05/18 15:49 11 | */ 12 | class Select extends Statement with CouldFrom with Query with CouldUnion { 13 | 14 | val columns: mutable.ListBuffer[CouldBeColumn] = new mutable.ListBuffer[CouldBeColumn] 15 | var isDistinct: Boolean = false 16 | 17 | override def script: String = { 18 | val sb: mutable.StringBuilder = new mutable.StringBuilder("SELECT ") 19 | if(isDistinct){ 20 | sb ++= "DISTINCT " 21 | } 22 | sb ++= columns.map(_.script).mkString(", ") 23 | sb.mkString 24 | } 25 | 26 | override def parameters: Seq[Parameter[_]] = { 27 | val re = columns.flatMap(_.parameters).toSeq 28 | for (idx <- re.indices) { 29 | re(idx).order(idx + 1) 30 | } 31 | re 32 | } 33 | 34 | def distinct: this.type = { 35 | isDistinct = true 36 | this 37 | } 38 | 39 | def apply(names: CouldBeColumn *) : this.type = { 40 | this.columns ++= names 41 | this 42 | } 43 | 44 | def +=(name: Any): this.type = { 45 | this.columns += { 46 | name match { 47 | case n:String => new Name(n) 48 | case c:CouldBeColumn => c 49 | }} 50 | 51 | this 52 | } 53 | 54 | def ++=(names: Seq[_]): this.type = { 55 | for (name <- names) { 56 | this += name 57 | } 58 | this 59 | } 60 | 61 | } 62 | 63 | 64 | object Select { 65 | 66 | trait From extends jaskell.sql.From with CouldGroup with CouldOrder 67 | with CouldLimit with CouldOffset with CouldBeJoin with CouldUnion { 68 | } 69 | 70 | } -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Case.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | import scala.collection.mutable 4 | import scala.collection.mutable.ListBuffer 5 | 6 | /** 7 | * TODO 8 | * 9 | * @author mars 10 | * @version 1.0.0 11 | * @since 2020/05/18 19:36 12 | */ 13 | class Case(val base: Expression = new Empty with Expression) extends Directive { 14 | val whens: mutable.ListBuffer[(Condition, Expression)] = new ListBuffer 15 | def when(condition: Condition, `then`: Expression): this.type = { 16 | whens += Tuple2(condition, `then`) 17 | this 18 | } 19 | 20 | def end: Directive with CouldBeColumn with Expression with CouldAs = 21 | new Directive with CouldBeColumn with Expression with CouldAs { 22 | override def script: String = { 23 | val dispatches = whens.map({pair => 24 | val (c, t) = pair 25 | s"WHEN ${c.script} THEN ${t.script}" 26 | }).mkString 27 | s"CASE ${base.script} $dispatches END" 28 | } 29 | 30 | override def parameters: Seq[Parameter[_]] = base.parameters ++ (whens flatMap {pair => 31 | val (c, t) = pair 32 | c.parameters ++ t.parameters 33 | }) 34 | } 35 | 36 | override def script: String = { 37 | val dispatches = whens.map({pair => 38 | val (c, t) = pair 39 | s"WHEN ${c.script} THEN ${t.script}" 40 | }).mkString 41 | s"CASE ${base.script} $dispatches END" 42 | } 43 | 44 | override def parameters: Seq[Parameter[_]] = base.parameters ++ (whens flatMap {pair => 45 | val (c, t) = pair 46 | c.parameters ++ t.parameters 47 | }) 48 | } 49 | 50 | object Case { 51 | def apply(base: Expression): Case = new Case(base) 52 | 53 | def apply(): Case = new Case() 54 | } 55 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/EnumerateSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import jaskell.parsec.Txt.ch 4 | import org.scalatest.flatspec.AnyFlatSpec 5 | import org.scalatest.matchers.should.Matchers 6 | 7 | /** 8 | * TODO 9 | * 10 | * @author mars 11 | * @version 1.0.0 12 | * @since 2020/05/11 10:50 13 | */ 14 | class EnumerateSpec extends AnyFlatSpec with Matchers { 15 | 16 | import Combinator.ahead 17 | import Txt.{space, text} 18 | 19 | "Simple" should "Expect match ab from abc" in { 20 | import jaskell.parsec.Combinator.enumerate 21 | 22 | val content: String = "abc" 23 | val state = State.apply(content) 24 | val parser: Enumerate[Char, Char] = enumerate(ch('a'), ch('b')) 25 | parser ! state should be(Set('a', 'b')) 26 | state.status should be(2) 27 | } 28 | 29 | "More" should "Check status get result and stop at is" in { 30 | val content: String = "rf'abcdefg'" 31 | val state = State.apply(content) 32 | val parser: Enumerate[Char, Char] = Txt.enumerate("rf") 33 | val re = parser ! state 34 | re.contains('f') should be(true) 35 | re.contains('r') should be(true) 36 | re.contains('\'') should be(false) 37 | state.status should be(2) 38 | } 39 | 40 | "Separate" should "read abc from a|b|c,d" in { 41 | val content: String = "a|b|c,d" 42 | val state = State apply content 43 | val parser = Txt.enumerate("abc", '|') 44 | 45 | val re = parser ! state 46 | re.contains('a') should be(true) 47 | re.contains('b') should be(true) 48 | re.contains('c') should be(true) 49 | re.contains('|') should be(false) 50 | re.contains(',') should be(false) 51 | re.contains('d') should be(false) 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/AheadSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | /** 7 | * TODO 8 | * 9 | * @author mars 10 | * @version 1.0.0 11 | * @since 2020/05/11 10:50 12 | */ 13 | class AheadSpec extends AnyFlatSpec with Matchers { 14 | 15 | import Txt.{text, space} 16 | import Combinator.ahead 17 | 18 | "Simple" should "Expect status stop after this" in { 19 | val content: String = "this is a string data." 20 | val state = State.apply(content) 21 | val parser = Parsec[Char, String] { s => 22 | for { 23 | re <- text("this") ? (s) 24 | _ <- ahead (text(" is")) ? (s) 25 | } yield re 26 | } 27 | parser ! state should be("this") 28 | state.status should be(4) 29 | } 30 | 31 | "Then" should "Check status get result and stop at is" in { 32 | val content: String = "this is a string data." 33 | val state = State.apply(content) 34 | val parser = Parsec[Char, String] { s => 35 | for { 36 | _ <- text("this") ? s 37 | _ <- space ? s 38 | re <- ahead (text("is")) ? s 39 | } yield re 40 | } 41 | val re = parser ! state 42 | re should be("is") 43 | state.status should be(5) 44 | } 45 | 46 | "Fail" should "throw parsec exception from parser" in { 47 | val content: String = "this is a string data." 48 | val state = State apply content 49 | val parser = Parsec[Char, String] { s => 50 | for { 51 | _ <- text("this") ? s 52 | _ <- space ? s 53 | re <- ahead(text(" is")) ? s 54 | } yield re 55 | } 56 | 57 | a[ParsecException] should be thrownBy { 58 | parser ! state 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Insert.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | import jaskell.sql.Insert.Into 4 | 5 | import scala.collection.mutable 6 | 7 | /** 8 | * TODO 9 | * 10 | * @author mars 11 | * @version 1.0.0 12 | * @since 2020/05/20 18:28 13 | */ 14 | class Insert extends Directive { 15 | def into(name: Name): Into = new Into(name) 16 | 17 | override def script: String = "" 18 | 19 | override def parameters: Seq[Parameter[_]] = Seq.empty 20 | } 21 | 22 | object Insert { 23 | 24 | class Into(val name: Name) extends Directive { 25 | val columns: mutable.ListBuffer[Name] = new mutable.ListBuffer[Name] 26 | 27 | def apply(names: Name*): this.type = { 28 | columns ++= names 29 | this 30 | } 31 | 32 | def values(expression: Expression *): Values = new Values(this, expression) 33 | 34 | def select: Select = new Select(this) 35 | 36 | override def script: String = { 37 | val cols = columns.map(_.script).mkString(","); 38 | s"INSERT INTO ${name.script}($cols)" 39 | } 40 | 41 | override def parameters: Seq[Parameter[_]] = name.parameters ++ columns.flatMap(_.parameters) 42 | } 43 | 44 | class Select(val prefix: Directive) extends jaskell.sql.Select { 45 | override def script: String = prefix.script + " " + super.script 46 | 47 | override def parameters: Seq[Parameter[_]] = prefix.parameters ++ super.parameters 48 | } 49 | 50 | class Values(val prefix: Directive, val values: Seq[Expression]) extends Directive with CouldReturning with Statement { 51 | override def script: String = { 52 | val vs = values.map(_.script).mkString(", ") 53 | s"${prefix.script} VALUES($vs)" 54 | } 55 | 56 | override def parameters: Seq[Parameter[_]] = prefix.parameters ++ values.flatMap(_.parameters) 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Enumerate.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import jaskell.Monad.Implicits.toMonad 4 | import jaskell.parsec.Parsec.Implicits.mkMonad 5 | 6 | import scala.annotation.tailrec 7 | import scala.util.{Failure, Success, Try} 8 | 9 | /** 10 | * TODO 11 | * 12 | * @author mars 13 | * @version 1.0.0 14 | * @since 2023/10/22 00:43 15 | */ 16 | class Enumerate[E, T](val parsecs: Seq[Parsec[E, T]], val sep: Parsec[E, _] = Return[E, Unit](())) extends Parsec[E, Set[T]] { 17 | 18 | override def apply(s: State[E]): Try[Set[T]] = { 19 | if (parsecs.isEmpty) { 20 | s.trap("enumerate parsers must not empty") 21 | } else { 22 | headHelper(parsecs, s) flatMap { value => 23 | val tail = parsecs.tail.map(p => sep *> p) 24 | Success(helper(tail, s) + value) 25 | } 26 | } 27 | } 28 | 29 | def by(sep: Parsec[E, _]): Enumerate[E, T] = new Enumerate[E, T](parsecs, sep) 30 | 31 | @tailrec 32 | private def headHelper(parsers: Seq[Parsec[E, T]], s: State[E]): Try[T] = { 33 | val head = parsers.head 34 | head(s) match { 35 | case Success(value) => 36 | Success(value) 37 | case Failure(error) => 38 | val tail = parsers.tail 39 | if (tail.isEmpty) { 40 | Failure(error) 41 | } else { 42 | headHelper(tail, s) 43 | } 44 | } 45 | } 46 | 47 | @tailrec 48 | private def helper(parsers: Seq[Parsec[E, T]], s: State[E], acc: Set[T] = Set()): Set[T] = { 49 | if (parsers.isEmpty) { 50 | acc 51 | } else { 52 | val head = parsers.head 53 | head(s) match { 54 | case Success(value) => 55 | helper(parsers.tail, s, acc + value) 56 | case Failure(_) => 57 | helper(parsers.tail, s, acc) 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/scala-2.13/jaskell/parsec/Parsec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import jaskell.Monad 4 | 5 | import scala.language.implicitConversions 6 | import scala.util.{Failure, Success, Try} 7 | 8 | /** 9 | * trait of Parsec parsers. 10 | * 11 | * @author mars 12 | * @version 1.0.0 13 | */ 14 | trait Parsec[E, +T] { 15 | 16 | def apply(s: State[E]): Try[T] 17 | 18 | def parse(s: State[E]): T = { 19 | ask(s) match { 20 | case Success(result) => result 21 | case Failure(error) => throw error 22 | } 23 | } 24 | 25 | def !(s: State[E]): T = { 26 | ask(s) match { 27 | case Success(result) => result 28 | case Failure(error) => throw error 29 | } 30 | } 31 | 32 | def ask(s: State[E]): Try[T] = apply(s) 33 | 34 | def ask(s: Seq[E]): Try[T] = { 35 | ask(State(s)) 36 | } 37 | 38 | def option(s: State[E]): Option[T] = { 39 | apply(s).toOption 40 | } 41 | 42 | def `<|>`[U >: T](parsec: Parsec[E, U]): Parsec[E, U] = new Choice(Seq(this, parsec)) 43 | 44 | def ``[U >: T](message: String): Parsec[E, U] = (s: State[E]) => { 45 | this ask s orElse s.trap(message) 46 | } 47 | 48 | def ?(s: State[E]): Try[T] = ask(s) 49 | 50 | def ?(s: Seq[E]): Try[T] = ask(s) 51 | 52 | } 53 | 54 | object Parsec { 55 | def apply[E, T](parser: State[E] => Try[T]): Parsec[E, T] = parser(_) 56 | 57 | object Implicits { 58 | implicit def toFlatMapper[E, T, O](binder: Binder[E, T, O]): (T) => Parsec[E, O] = binder.apply 59 | 60 | implicit def mkMonad[T]: Monad[({type P[A] = Parsec[T, A]})#P] = 61 | new Monad[({type P[A] = Parsec[T, A]})#P] { 62 | override def pure[A](element: A): Parsec[T, A] = Return(element) 63 | 64 | override def fmap[A, B](m: Parsec[T, A], f: A => B): Parsec[T, B] = m.ask(_).map(f) 65 | 66 | override def flatMap[A, B](m: Parsec[T, A], f: A => Parsec[T, B]): Parsec[T, B] = state => for { 67 | a <- m.ask(state) 68 | b <- f(a).ask(state) 69 | } yield b 70 | } 71 | } 72 | } 73 | 74 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/CouldBeCondition.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/18 19:59 9 | */ 10 | trait CouldBeCondition extends Directive { 11 | 12 | def ==(that: CouldBeCondition): Condition = new Condition { 13 | 14 | override def script: String = { 15 | CouldBeCondition.this.script + " = " + that.script 16 | } 17 | 18 | override def parameters: Seq[Parameter[_]] = CouldBeCondition.this.parameters ++ that.parameters 19 | } 20 | 21 | def !=(that: CouldBeCondition): Condition = new Condition { 22 | 23 | override def script: String = { 24 | CouldBeCondition.this.script + " != " + that.script 25 | } 26 | 27 | override def parameters: Seq[Parameter[_]] = CouldBeCondition.this.parameters ++ that.parameters 28 | } 29 | 30 | def >(that: CouldBeCondition): Condition = new Condition { 31 | 32 | override def script: String = { 33 | CouldBeCondition.this.script + " > " + that.script 34 | } 35 | 36 | override def parameters: Seq[Parameter[_]] = CouldBeCondition.this.parameters ++ that.parameters 37 | } 38 | 39 | def <(that: CouldBeCondition): Condition = new Condition { 40 | 41 | override def script: String = { 42 | CouldBeCondition.this.script + " < " + that.script 43 | } 44 | 45 | override def parameters: Seq[Parameter[_]] = CouldBeCondition.this.parameters ++ that.parameters 46 | } 47 | 48 | def >=(that: CouldBeCondition): Condition = new Condition { 49 | 50 | override def script: String = { 51 | CouldBeCondition.this.script + " >= " + that.script 52 | } 53 | 54 | override def parameters: Seq[Parameter[_]] = CouldBeCondition.this.parameters ++ that.parameters 55 | } 56 | 57 | def <=(that: CouldBeCondition): Condition = new Condition { 58 | 59 | override def script: String = { 60 | CouldBeCondition.this.script + " <= " + that.script 61 | } 62 | 63 | override def parameters: Seq[Parameter[_]] = CouldBeCondition.this.parameters ++ that.parameters 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/sql/WriteSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | import java.sql.{Connection, DriverManager, PreparedStatement} 7 | 8 | /** 9 | * TODO 10 | * 11 | * @author mars 12 | * @version 1.0.0 13 | * @since 2020/05/21 15:35 14 | */ 15 | class WriteSpec extends AnyFlatSpec with Matchers { 16 | 17 | import jaskell.sql.SQL._ 18 | 19 | val url = "jdbc:sqlite::memory:" 20 | val table: Name with CouldBeFrom = t("test") 21 | val conn: Connection = DriverManager.getConnection(url) 22 | 23 | 24 | val create: PreparedStatement = conn.prepareStatement("create table test(id integer primary key autoincrement, content text)") 25 | create.execute() 26 | create.close() 27 | 28 | val query: Statement = insert.into(table)(c("content")).values(p("data")).cache 29 | val statement: PreparedStatement = query.prepare(conn) 30 | statement.execute() 31 | 32 | "Insert" should "Test insert " in { 33 | val query = insert.into(table)(c("content")).values(p("data")).cache 34 | val statement = query.prepare(conn) 35 | 36 | for (i <- 0 until 10) { 37 | val log = f"write ${i}th log" 38 | query.setParameter("data", log) 39 | query.syncParameters(statement) 40 | statement.execute 41 | statement.getUpdateCount should be(1) 42 | } 43 | } 44 | 45 | "Update" should "Test update" in { 46 | val findIdQuery = select(max(n("id")).as("id")).from(table) 47 | val i: Option[Int] = findIdQuery.scalar[Int](conn) 48 | val id = i.get 49 | val statement = 50 | update(table).set(c("content"), p("data")).where(n("id") == p[Int]("id")) 51 | statement.setParameter("id", id) 52 | statement.setParameter("data", "rewritten") 53 | statement.execute(conn) 54 | 55 | val query = select("content").from(table).where(n("id") == l(id)) 56 | val s = query.scalar[String](conn) 57 | s should be(Some("rewritten")) 58 | } 59 | 60 | "Clean" should "Test delete " in { 61 | val statement = delete.from(table) 62 | statement.execute(conn) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/parsec/CommonStateSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import java.io.EOFException 4 | 5 | import org.scalatest.flatspec.AnyFlatSpec 6 | import org.scalatest.matchers.should.Matchers 7 | 8 | import scala.util.Success 9 | 10 | /** 11 | * TODO 12 | * 13 | * @author mars 14 | * @version 1.0.0 15 | * @since 2020/05/12 21:52 16 | */ 17 | class CommonStateSpec extends AnyFlatSpec with Matchers{ 18 | "Index" should "Test index status" in { 19 | val data = "It is a \"string\" for this unit test"; 20 | val state = State(data); 21 | while (state.status < data.length()){ 22 | val index = state.status 23 | val c = state.next(); 24 | val chr = data.charAt(index); 25 | c should be (Success(chr)) 26 | } 27 | state.next().isFailure should be (true) 28 | } 29 | 30 | "Begin" should "Test begin tran and rollback then next" in { 31 | val state = State("hello") 32 | 33 | val c = state.next() 34 | 35 | 36 | c should be (Success('h')) 37 | 38 | val a = state.begin() 39 | 40 | state.next() 41 | state.next() 42 | state.next() 43 | 44 | state rollback a 45 | 46 | val d = state.next() 47 | 48 | d should be (Success('e')) 49 | } 50 | 51 | "Commit" should "Test begin a transaction and commit" in { 52 | val state = State("hello") 53 | val tran = state.begin() 54 | val c = state.next() 55 | 56 | 57 | c should be (Success('h')) 58 | state.next() 59 | 60 | state.commit(tran); 61 | 62 | val d = state.next(); 63 | 64 | d should be (Success('l')) 65 | } 66 | 67 | "Rollback" should "Test rollback" in { 68 | val state = State("hello"); 69 | 70 | val tran = state.begin(); 71 | val c = state.next(); 72 | 73 | 74 | c should be (Success('h')) 75 | 76 | state rollback tran 77 | 78 | val d = state.next(); 79 | 80 | d should be (Success('h')) 81 | } 82 | 83 | "Next" should "Test state next method" in { 84 | val state = State("hello") 85 | 86 | 87 | val c = state.next() 88 | 89 | c should be (Success('h')) 90 | 91 | val d = state.next() 92 | 93 | d should be (Success('e')) 94 | 95 | val e = state.next() 96 | 97 | e should be (Success('l')) 98 | 99 | val f = state.next() 100 | 101 | f should be (Success('l')) 102 | val g = state.next() 103 | 104 | g should be (Success('o')) 105 | } 106 | 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/SQL.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | /** 4 | * TODO 5 | * 6 | * @author mars 7 | * @version 1.0.0 8 | * @since 2020/05/19 18:57 9 | */ 10 | object SQL { 11 | import scala.util.matching.Regex 12 | val stringRegex: Regex = "^''$".r 13 | def empty = new Empty 14 | 15 | def select: Select = new Select 16 | def insert: Insert = new Insert 17 | def update(name: Name): Update = new Update(name) 18 | def delete: Delete = new Delete 19 | 20 | def n(name: String): Name = new Name(name) 21 | def t(name: String): Name with CouldBeJoin with CouldWhere with CouldBeFrom with CouldOrder with CouldGroup = 22 | new Name(name) with CouldBeJoin with CouldWhere with CouldBeFrom with CouldOrder with CouldGroup 23 | 24 | def c(name: String): Name with CouldBeColumn = new Name(name) with CouldBeColumn 25 | def l(ltl: String): Literal = new Literal { 26 | override val prefix: Directive = empty 27 | override val literal: String = ltl 28 | } 29 | def l(ltl: Int): Literal = new Literal { 30 | override val prefix: Directive = empty 31 | override val literal: String = ltl.toString 32 | } 33 | 34 | def q(condition: Condition): Quote = new Quote with Condition { 35 | override val segment: Condition = condition 36 | } 37 | 38 | def q(qry: Query): Quote = new Quote with Query { 39 | override val segment: Query = qry 40 | } 41 | 42 | def q(expression: Expression): Quote = new Quote with Expression { 43 | override val segment: Expression = expression 44 | } 45 | 46 | def p[T](name: String): Parameter[T] = { 47 | val parameter = new Parameter[T] 48 | parameter.key = name 49 | parameter 50 | } 51 | 52 | def max(argument: Directive): Func = { 53 | Func("max", argument) 54 | } 55 | 56 | def min(argument: Directive): Func = { 57 | Func("max", argument) 58 | } 59 | 60 | def avg(argument: Directive): Func = { 61 | Func("avg", argument) 62 | } 63 | 64 | def count: Func = { 65 | Func("count", n("*")) 66 | } 67 | 68 | def count(argument: Directive): Func = { 69 | Func("count", argument) 70 | } 71 | 72 | def f(name:String, args: Directive*): Func = { 73 | Func(name, args :_*) 74 | } 75 | 76 | def `case`: Case = { 77 | new Case() 78 | } 79 | 80 | def `case`(base: Expression): Case = { 81 | new Case(base) 82 | } 83 | 84 | implicit def strToName(str:String):Expression = stringRegex.findFirstIn(str) match { 85 | case Some(txt) => new Text(string = txt) 86 | case None => new Name(str) 87 | } 88 | 89 | implicit def strsToNames(strs: Seq[String]):Seq[Expression] = strs map strToName 90 | } 91 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/With.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | import jaskell.sql.With.TableExpression 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2020/05/20 18:41 11 | */ 12 | class With(val prefix: Directive = SQL.empty, val name: Name) extends Directive { 13 | 14 | def as(quote: Quote): TableExpression = new TableExpression(prefix = With.this, name = name, quote = quote) 15 | 16 | override def script: String = s"WITH" 17 | 18 | override def parameters: Seq[Parameter[_]] = name.parameters 19 | } 20 | 21 | object With { 22 | 23 | def recursive: Recursive = new Recursive 24 | 25 | def apply(n: Name): With = new With(name = n) 26 | 27 | class Select(val prefix: Directive) extends jaskell.sql.Select { 28 | override def script: String = prefix.script + " " + super.script 29 | 30 | override def parameters: Seq[Parameter[_]] = prefix.parameters ++ super.parameters 31 | } 32 | 33 | class Insert(val prefix: Directive) extends jaskell.sql.Insert { 34 | override def script: String = prefix.script + " " + super.script 35 | 36 | override def parameters: Seq[Parameter[_]] = prefix.parameters ++ super.parameters 37 | } 38 | 39 | class Update(val prefix: Directive, name: Name) extends jaskell.sql.Update(name) { 40 | override def script: String = prefix.script + " " + super.script 41 | 42 | override def parameters: Seq[Parameter[_]] = prefix.parameters ++ super.parameters 43 | } 44 | 45 | class Delete(val prefix: Directive) extends jaskell.sql.Delete { 46 | override def script: String = prefix.script + " " + super.script 47 | 48 | override def parameters: Seq[Parameter[_]] = prefix.parameters ++ super.parameters 49 | } 50 | 51 | class TableExpression(val prefix: Directive = SQL.empty, val name: Name, val quote: Quote, val sep:String = " ") extends Directive { 52 | override def script: String = s" ${prefix.script}$sep${name.script} AS ${quote.script}".trim 53 | 54 | override def parameters: Seq[Parameter[_]] = prefix.parameters ++ name.parameters ++ quote.parameters 55 | 56 | def and(name: Name, quote: Quote): TableExpression = new TableExpression(this, name, quote, ", ") 57 | 58 | def select = new Select(this) 59 | 60 | def update(name: Name) = new Update(this, name) 61 | 62 | def delete = new Delete(this) 63 | } 64 | 65 | class Recursive extends Directive { 66 | 67 | override def script: String = s"WITH RECURSIVE" 68 | 69 | override def parameters: Seq[Parameter[_]] = Seq.empty 70 | 71 | def as(name: Name, quote: Quote): TableExpression = new TableExpression(this, name, quote) 72 | 73 | def as(name: Name, query: Query): TableExpression = new TableExpression(this, name, Quote(query)) 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/scala-2/jaskell/parsec/Txt.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import scala.language.implicitConversions 4 | 5 | /** 6 | * Functions Helper include parsers for Text 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | */ 11 | object Txt { 12 | def ch(value: Char): Ch = Ch(value) 13 | 14 | def ch(value: Char, caseSensitive: Boolean): Ch = Ch(value, caseSensitive) 15 | 16 | def nch(value: Char): NCh = NCh(value) 17 | 18 | def nch(value: Char, caseSensitive: Boolean): NCh = NCh(value, caseSensitive) 19 | 20 | def chIn(data: String): ChIn = ChIn(data) 21 | 22 | def chIn(data: String, caseSensitive: Boolean): ChIn = ChIn(data, caseSensitive) 23 | 24 | def chNone(data: String): ChNone = ChNone(data) 25 | 26 | def chNone(data: String, caseSensitive: Boolean): ChNone = ChNone(data, caseSensitive) 27 | 28 | def crlf: Crlf = new Crlf 29 | 30 | def decimal: Decimal = Decimal() 31 | 32 | def udecimal: UDecimal = new UDecimal 33 | 34 | def scNumber: ScNumber = new ScNumber 35 | 36 | def digit: Digit = new Digit 37 | 38 | def letter: Letter = new Letter 39 | 40 | def integer: Int = new Int 41 | 42 | def uInteger: UInt = new UInt 43 | 44 | def eol: EndOfLine = new EndOfLine 45 | 46 | def newline: Newline = new Newline 47 | 48 | def space: Space = new Space 49 | 50 | def whitespace: Whitespace = new Whitespace 51 | 52 | def noWhitespace: NoWhitespace = new NoWhitespace 53 | 54 | def skipSpaces: SkipSpaces = new SkipSpaces 55 | 56 | def skipWhiteSpaces: SkipWhitespaces = new SkipWhitespaces 57 | 58 | def text(value: String): Text = Text(value) 59 | 60 | def text(value: String, caseSensitive: Boolean): Text = Text(value, caseSensitive) 61 | 62 | def chars(chs: String, caseSensitive: Boolean = true): CharsIn = CharsIn(chs, caseSensitive) 63 | 64 | def enumerate(chars: String): Enumerate[Char, Char] = { 65 | val enumerates = chars.toCharArray.map(c => Ch(c)) 66 | new Enumerate(enumerates) 67 | } 68 | 69 | def enumerate(chars: String, by: Char): Enumerate[Char, Char] = { 70 | val enumerates = chars.toCharArray.map(c => Ch(c)) 71 | new Enumerate(enumerates, Ch(by)) 72 | } 73 | 74 | def enumerate(chars: String, by: String): Enumerate[Char, Char] = { 75 | val enumerates = chars.toCharArray.map(c => Ch(c)) 76 | new Enumerate(enumerates, Text(by)) 77 | } 78 | 79 | def enumerate(chars: String, by: Parsec[Char, _]): Enumerate[Char, Char] = { 80 | val enumerates = chars.toCharArray.map(c => Ch(c)) 81 | new Enumerate(enumerates, by) 82 | } 83 | 84 | def mkString: Binder[Char, Seq[Char], String] = new MkString 85 | 86 | implicit def stringToText(content: String): Parsec[Char, String] = Text(content) 87 | 88 | implicit def charToCh(ch: Char): Parsec[Char, Char] = Ch(ch) 89 | 90 | implicit def stringToState(content: String): TxtState = TxtState(content) 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/sql/Statement.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | import java.sql.{Connection, PreparedStatement, SQLException} 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author mars 9 | * @version 1.0.0 10 | * @since 2020/05/18 15:12 11 | */ 12 | trait Statement extends Directive { 13 | @throws[SQLException] 14 | def prepare(connection: Connection): PreparedStatement = { 15 | connection.prepareStatement(this.script) 16 | } 17 | 18 | @throws[SQLException] 19 | @throws[IllegalStateException] 20 | def execute(connection: Connection): Boolean = { 21 | val statement: PreparedStatement = connection.prepareStatement(this.script) 22 | try { 23 | syncParameters(statement) 24 | statement.execute() 25 | } finally { 26 | if (statement != null) statement.close() 27 | } 28 | } 29 | 30 | @throws[SQLException] 31 | @throws[IllegalStateException] 32 | def execute(statement: PreparedStatement): Boolean = { 33 | syncParameters(statement) 34 | try { 35 | statement.execute() 36 | }finally { 37 | if (statement != null) statement.close() 38 | } 39 | } 40 | 41 | @throws[IllegalArgumentException] 42 | def setParameter[T](key: Any, value: T): Statement = { 43 | var flag: Boolean = false 44 | for (parameter <- parameters) { 45 | if(parameter.key == key) { 46 | parameter.asInstanceOf[Parameter[T]].set(value) 47 | flag = true 48 | } 49 | } 50 | if (!flag) { 51 | throw new IllegalArgumentException("parameter named %s not found".format(key)) 52 | } 53 | this 54 | } 55 | 56 | @throws[SQLException] 57 | def clear(statement: PreparedStatement): Unit = { 58 | statement.clearParameters() 59 | } 60 | 61 | @throws[SQLException] 62 | def syncParameters(statement: PreparedStatement): Unit = { 63 | for (parameter <- parameters) { 64 | if (!parameter.confirmed) { 65 | throw new IllegalStateException("parameter %s has not value".format(parameter.key)) 66 | } 67 | } 68 | clear(statement) 69 | val params = parameters 70 | setOrder(params) 71 | for (parameter <- params) { //TODO: overload by parameter.valueClass 72 | statement.setObject(parameter.order, parameter.value.orNull) 73 | } 74 | } 75 | 76 | private[sql] def setOrder(parameters: Seq[Parameter[_]]): Unit = { 77 | for (i <- parameters.indices) { 78 | parameters(i).order(i + 1) 79 | } 80 | } 81 | 82 | def cache: Statement = { 83 | val self: Statement = this 84 | new Statement() { 85 | final private val _script: String = self.script 86 | final private val _parameters: Seq[Parameter[_]] = self.parameters 87 | 88 | override 89 | 90 | def script: String = { 91 | _script 92 | } 93 | 94 | override 95 | 96 | def parameters: Seq[Parameter[_]] = { 97 | _parameters 98 | } 99 | } 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/sql/JoinSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.sql 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | import java.sql.{Connection, DriverManager} 7 | 8 | /** 9 | * TODO 10 | * 11 | * @author mars 12 | * @version 1.0.0 13 | * @since 2020/05/21 11:08 14 | */ 15 | class JoinSpec extends AnyFlatSpec with Matchers { 16 | 17 | import jaskell.sql.SQL._ 18 | 19 | private val url: String = "jdbc:sqlite::memory:" 20 | private val table: String = "test" 21 | private val ist: Statement = insert.into(n(table))(c("pid"), c("content")) 22 | .values(p[Int]("pid"), p[String]("content")) 23 | Class.forName("org.sqlite.JDBC") 24 | val conn: Connection = DriverManager.getConnection(url) 25 | 26 | conn.prepareStatement( 27 | "create table test(id integer primary key autoincrement, pid integer references test(id), content text)" 28 | ).execute 29 | 30 | 31 | val ins: Statement = ist.cache 32 | val queryLastId: Query = select(f("last_insert_rowid")) 33 | var last: Int = 1 34 | ins.setParameter("content", "one line.") 35 | for (_ <- 0 until 10) { 36 | ins.setParameter("pid", last) 37 | ins.execute(conn) 38 | last = queryLastId.scalar[Int](conn).getOrElse(0) 39 | if (last == 0) { 40 | throw new IllegalStateException("can't get new id which insert") 41 | } 42 | } 43 | 44 | "Init" should "Test database init by DDL" in { 45 | val q: Query = select(count).from("test") 46 | val c = q.scalar[Int](conn) 47 | c should be(Some(10)) 48 | } 49 | 50 | 51 | "Join Self" should "Create join self SQL" in { 52 | val q: Query = select("l.id, r.id, l.content, r.content") 53 | .from(n("test").as("l")) 54 | .join(n("test").as("r")).on(l("l.id") == (l("r.pid"))).where(l("l.id") != (l("r.id"))) 55 | val statement = q.prepare(conn) 56 | val rs = q.query(statement) 57 | try while ( { 58 | rs.next 59 | }) { 60 | rs.getString(3) should be(rs.getString(4)) 61 | rs.getInt(1) + 1 should be(rs.getInt(2)) 62 | } finally { 63 | if (statement != null) statement.close() 64 | if (rs != null) rs.close() 65 | } 66 | } 67 | 68 | "LeftJoin0" should "Test left join" in { 69 | val q: Query = select("l.id, r.id, l.content, r.content").from(n("test").as("l")) 70 | .leftJoin(n("test").as("r")).on(n("l.id") == n("r.pid")).where(n("r.id").isNull) 71 | val statement = q.prepare(conn) 72 | val rs = q.query(statement) 73 | try while ( { 74 | rs.next 75 | }) { 76 | rs.getObject(2) should be(null) 77 | } finally { 78 | if (statement != null) statement.close() 79 | if (rs != null) rs.close() 80 | } 81 | } 82 | 83 | "LeftJoin1" should "Test left join" in { 84 | val q: Query = select("r.id") 85 | .from(n("test").as("l")).leftJoin(n("test") as "r").on(n("l.id") == n("r.pid")) 86 | .where(n("r.id").isNull) 87 | val re = q.scalar[Int](conn) 88 | re.isDefined should be(false) 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/main/scala/jaskell/parsec/Combinator.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | 4 | /** 5 | * Parsec Combinators 6 | * 7 | * @author mars 8 | * @version 1.0.0 9 | */ 10 | object Combinator { 11 | import jaskell.Monad.Implicits._ 12 | import jaskell.parsec.Parsec.Implicits._ 13 | 14 | def attempt[E, T](parser: Parsec[E, T]): Attempt[E, T] = { 15 | new Attempt[E, T](parser) 16 | } 17 | 18 | def ahead[E, T](parser: Parsec[E, T]): Ahead[E, T] = { 19 | new Ahead[E, T](parser) 20 | } 21 | 22 | def choice[E, T](parsers: Parsec[E, T]*): Choice[E, T] = { 23 | new Choice[E, T](parsers) 24 | } 25 | 26 | def enumerate[E, T](parsers: Parsec[E, T]*): Enumerate[E, T] = { 27 | new Enumerate[E, T](parsers) 28 | } 29 | 30 | def enumerate[E, T](parsers: Parsec[E, T]*)(by: Parsec[E, _]): Enumerate[E, T] = { 31 | new Enumerate[E, T](parsers, by) 32 | } 33 | 34 | def many[E, T](parser: Parsec[E, T]): Parsec[E, Seq[T]] = { 35 | new Many[E, T](parser) 36 | } 37 | 38 | def many1[E, T](parser: Parsec[E, T]): Parsec[E, Seq[T]] = { 39 | new Many1[E, T](parser) 40 | } 41 | 42 | def manyTill[E, T, L](parser: Parsec[E, T], end: Parsec[E, L]): ManyTill[E, T, L] = { 43 | new ManyTill[E, T, L](parser, end) 44 | } 45 | 46 | def skip[E](parser: Parsec[E, _]): Parsec[E, Unit] = { 47 | new Skip[E](parser) 48 | } 49 | 50 | def skip1[E](parser: Parsec[E, _]): Parsec[E, Unit] = { 51 | new Skip1[E](parser) 52 | } 53 | 54 | def sepBy[T, Sep, E](parser: Parsec[E, T], by: Parsec[E, Sep]): Parsec[E, Seq[T]] = { 55 | new SepBy[E, T](parser, by) 56 | } 57 | 58 | def sepBy1[E, T](parser: Parsec[E, T], by: Parsec[E, _]): Parsec[E, Seq[T]] = { 59 | new SepBy1[E, T](parser, by) 60 | } 61 | 62 | def find[E, T](parser: Parsec[E, T]): Find[E, T] = { 63 | new Find[E, T](parser) 64 | } 65 | 66 | def between[E, T](open: Parsec[E, _], close: Parsec[E, _], parser: Parsec[E, T]): Parsec[E, T] = { 67 | new Between[E, T](open, close, parser) 68 | } 69 | 70 | def chars(str: String): Parsec[Char, String] = CharsIn(str) 71 | 72 | implicit class BuiltIn[E, T](p: Parsec[E, T]) { 73 | def attempt: Parsec[E, T] = Attempt(p) 74 | 75 | def ahead: Parsec[E, T] = Ahead(p) 76 | 77 | def or(other: Parsec[E, T]): Parsec[E, T] = Attempt(p) <|> other 78 | 79 | def many: Parsec[E, Seq[T]] = Many(p) 80 | 81 | def many1: Parsec[E, Seq[T]] = Many1(p) 82 | 83 | def manyTill(end: Parsec[E, _]): Parsec[E, Seq[T]] = ManyTill(p, end) 84 | 85 | def skip: Parsec[E, Unit] = Skip(p) 86 | 87 | def skip1: Parsec[E, Unit] = Skip1(p) 88 | 89 | def sepBy(by: Parsec[E, _]): Parsec[E, Seq[T]] = SepBy(p, by) 90 | 91 | def sepBy1(by: Parsec[E, _]): Parsec[E, Seq[T]] = SepBy1(p, by) 92 | 93 | def find: Parsec[E, T] = Find(p) 94 | 95 | def between(open: Parsec[E, _], close: Parsec[E, _]): Parsec[E, T] = Between(open, close, p) 96 | 97 | def opt: Parsec[E, Option[T]] = (p >>= {v => Return(Some(v))}) <|> Return(None) 98 | 99 | 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/test/scala-2.13/jaskell/parsec/InjectionSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import org.scalatest.flatspec.AnyFlatSpec 4 | import org.scalatest.matchers.should.Matchers 5 | 6 | import scala.util.{Failure, Success} 7 | 8 | 9 | /** 10 | * TODO 11 | * 12 | * @author Mars Liu 13 | * @version 1.0.0 14 | * @since 2020/06/16 21:13 15 | */ 16 | class InjectionSpec extends AnyFlatSpec with Matchers { 17 | 18 | import jaskell.Monad.Implicits._ 19 | import jaskell.parsec.Atom.{eof, one} 20 | import jaskell.parsec.Combinator._ 21 | import jaskell.parsec.Parsec.Implicits._ 22 | import jaskell.parsec.Txt._ 23 | 24 | 25 | implicit def toParsec[E, T, P <: Parsec[E, T]](parsec: P): Parsec[E, T] = parsec.asInstanceOf[Parsec[E, T]] 26 | 27 | val escapeChar: Parsec[Char, Char] = attempt(ch('\\') >> ((s: State[Char]) => { 28 | s.next() flatMap { 29 | case 't' => Success('\t') 30 | case '\'' => Success('\'') 31 | case 'n' => Success('\n') 32 | case 'r' => Success('\r') 33 | case c@_ => Failure(new ParsecException(s.status, s"invalid escape char \\$c")) 34 | } 35 | })) 36 | val notEof: Parsec[Char, Char] = ahead(one[Char]) 37 | 38 | val oneChar: Parsec[Char, Char] = escapeChar <|> nch('\'') 39 | 40 | val contentString: Parsec[Char, String] = ch('\'') *> many(oneChar) <* ch('\'') >>= mkString 41 | 42 | val noString: Parsec[Char, String] = many1(nch('\'')) >>= mkString 43 | val content: Parsec[Char, String] = attempt(noString) <|> contentString 44 | 45 | val parser: Parsec[Char, String] = 46 | many(notEof >> content) >>= ((value: Seq[String]) => Parsec { (s: State[Char]) => 47 | for { 48 | _ <- eof ? s 49 | } yield value.mkString 50 | }) 51 | 52 | 53 | "Simple" should "match some regular content without string" in { 54 | val content: State[Char] = "a data without text content" 55 | (parser ? content) should be(Success("a data without text content")) 56 | } 57 | 58 | "SimpleString" should "match content in string literal" in { 59 | val content: State[Char] = "'a data included text content'" 60 | (parser ? content) should be(Success("a data included text content")) 61 | } 62 | 63 | "Escape" should "match content in string literal" in { 64 | val content: State[Char] = "'a data without\ttext content'" 65 | (parser ? content) should be(Success("a data without\ttext content")) 66 | } 67 | 68 | "Escape More" should "match mixed content in string literal and outer" in { 69 | val content: State[Char] = "some content included 'a data without\ttext content'" 70 | (parser ? content) should be(Success("some content included a data without\ttext content")) 71 | } 72 | 73 | "Injection" should "get a left exception because some injection attack in content" in { 74 | val content: State[Char] = "some content included 'a data without 'text content'" 75 | (parser ? content).isFailure should be(true) 76 | } 77 | 78 | "Not Close" should "get a left exception because some injection attack in content" in { 79 | val content: State[Char] = "some content included \'a data without 'text content'" 80 | (parser ? content).isFailure should be(true) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/scala-2.12/jaskell/parsec/Parsec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import jaskell.Monad 4 | 5 | import scala.language.implicitConversions 6 | import scala.util.{Failure, Success, Try} 7 | 8 | /** 9 | * trait of Parsec parsers. 10 | * 11 | * @author mars 12 | * @version 1.0.0 13 | */ 14 | trait Parsec[E, +T] { 15 | 16 | def apply(s: State[E]): Try[T] 17 | 18 | def parse(s: State[E]): T = { 19 | ask(s) match { 20 | case Success(result) => result 21 | case Failure(error) => throw error 22 | } 23 | } 24 | 25 | def !(s: State[E]): T = { 26 | ask(s) match { 27 | case Success(result) => result 28 | case Failure(error) => throw error 29 | } 30 | } 31 | 32 | def ask(s: State[E]): Try[T] = apply(s) 33 | 34 | def ask(s: Seq[E]): Try[T] = { 35 | ask(State(s)) 36 | } 37 | 38 | def option(s: State[E]): Option[T] = { 39 | apply(s).toOption 40 | } 41 | 42 | def `<|>`[U >: T](parsec: Parsec[E, U]): Parsec[E, U] = new Choice(Seq(this, parsec)) 43 | 44 | def ``[U >: T](message: String): Parsec[E, U] = (s: State[E]) => { 45 | this ask s orElse s.trap(message) 46 | } 47 | 48 | def ?(s: State[E]): Try[T] = ask(s) 49 | 50 | def ?(s: Seq[E]): Try[T] = ask(s) 51 | 52 | // Monad 53 | val self: Parsec[E, T] = this 54 | 55 | def map[B](f: T => B): Parsec[E, B] = (s: State[E]) => self(s).map(f) 56 | 57 | def <:>[B](f: T => B): Parsec[E, B] = self.map(f) 58 | 59 | def flatMap[B](f: T => Parsec[E, B]): Parsec[E, B] = (s: State[E]) => 60 | for { 61 | a <- self(s) 62 | func = f(a) 63 | x <- func(s) 64 | } yield x 65 | 66 | 67 | def liftA2[B, C](f: (T, B) => C): Parsec[E, B] => Parsec[E, C] = (p: Parsec[E, B]) => (s: State[E]) => 68 | for { 69 | a <- self(s) 70 | b <- p(s) 71 | } yield f(a, b) 72 | 73 | def <*>[B](f: T => B): Parsec[E, B] = (s: State[E]) => 74 | for { 75 | a <- self(s) 76 | } yield f(a) 77 | 78 | def *>[B](mb: Parsec[E, B]): Parsec[E, B] = for { 79 | _ <- self 80 | re <- mb 81 | } yield re 82 | 83 | def <*[_](mb: Parsec[E, _]): Parsec[E, T] = for { 84 | re <- self 85 | _ <- mb 86 | } yield re 87 | 88 | def >>=[B](f: T => Parsec[E, B]): Parsec[E, B] = flatMap(f) 89 | 90 | def >>[B](m: Parsec[E, B]): Parsec[E, B] = for { 91 | _ <- self 92 | re <- m 93 | } yield re 94 | 95 | } 96 | 97 | object Parsec { 98 | def apply[E, T](parser: State[E] => Try[T]): Parsec[E, T] = parser(_) 99 | 100 | object Implicits { 101 | implicit def toFlatMapper[E, T, O](binder: Binder[E, T, O]): (T) => Parsec[E, O] = binder.apply 102 | 103 | implicit def mkMonad: Monad[({type P[A] = Parsec[Char, A]})#P] = 104 | new Monad[({type P[A] = Parsec[Char, A]})#P] { 105 | override def pure[A](element: A): Parsec[Char, A] = Return(element) 106 | 107 | override def fmap[A, B](m: Parsec[Char, A], f: A => B): Parsec[Char, B] = m.ask(_).map(f) 108 | 109 | override def flatMap[A, B](m: Parsec[Char, A], f: A => Parsec[Char, B]): Parsec[Char, B] = state => for { 110 | a <- m.ask(state) 111 | b <- f(a).ask(state) 112 | } yield b 113 | } 114 | } 115 | } 116 | 117 | -------------------------------------------------------------------------------- /src/test/scala-2.12/jaskell/parsec/InjectionSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import jaskell.Monad.Implicits._ 4 | import org.scalatest.flatspec.AnyFlatSpec 5 | import org.scalatest.matchers.should.Matchers 6 | 7 | import scala.util.{Failure, Success} 8 | 9 | 10 | /** 11 | * TODO 12 | * 13 | * @author Mars Liu 14 | * @version 1.0.0 15 | * @since 2020/06/16 21:13 16 | */ 17 | class InjectionSpec extends AnyFlatSpec with Matchers { 18 | 19 | import jaskell.parsec.Atom.{one, eof} 20 | import jaskell.parsec.Combinator._ 21 | import jaskell.parsec.Txt._ 22 | import jaskell.parsec.Parsec.Implicits._ 23 | import jaskell.Monad.Implicits._ 24 | 25 | 26 | implicit def toParsec[E, T, P <: Parsec[E, T]](parsec: P): Parsec[E, T] = parsec.asInstanceOf[Parsec[E, T]] 27 | 28 | val escapeChar: Parsec[Char, Char] = attempt(ch('\\') >> ((s: State[Char]) => { 29 | s.next() flatMap { 30 | case 't' => Success('\t') 31 | case '\'' => Success('\'') 32 | case 'n' => Success('\n') 33 | case 'r' => Success('\r') 34 | case c@_ => Failure(new ParsecException(s.status, s"invalid escape char \\$c")) 35 | } 36 | })) 37 | val notEof: Parsec[Char, Char] = ahead(one[Char]) 38 | 39 | val oneChar: Parsec[Char, Char] = escapeChar <|> nch('\'') 40 | 41 | val contentString: Parsec[Char, String] = ch('\'') *> many(oneChar) <* ch('\'') >>= mkString 42 | 43 | val noString: Parsec[Char, String] = many1(nch('\'')) >>= mkString 44 | val content: Parsec[Char, String] = attempt(noString) <|> contentString 45 | 46 | val parser: Parsec[Char, String] = 47 | many(notEof >> content) >>= new Binder[Char, Seq[String], String] { 48 | override def apply(value: Seq[String]): Parsec[Char, String] = Parsec { (s: State[Char]) => 49 | for { 50 | _ <- eof ? s 51 | } yield value.mkString 52 | } 53 | } 54 | 55 | 56 | "Simple" should "match some regular content without string" in { 57 | val content: State[Char] = "a data without text content" 58 | (parser ? content) should be(Success("a data without text content")) 59 | } 60 | 61 | "SimpleString" should "match content in string literal" in { 62 | val content: State[Char] = "'a data included text content'" 63 | (parser ? content) should be(Success("a data included text content")) 64 | } 65 | 66 | "Escape" should "match content in string literal" in { 67 | val content: State[Char] = "'a data without\ttext content'" 68 | (parser ? content) should be(Success("a data without\ttext content")) 69 | } 70 | 71 | "Escape More" should "match mixed content in string literal and outer" in { 72 | val content: State[Char] = "some content included 'a data without\ttext content'" 73 | (parser ? content) should be(Success("some content included a data without\ttext content")) 74 | } 75 | 76 | "Injection" should "get a left exception because some injection attack in content" in { 77 | val content: State[Char] = "some content included 'a data without 'text content'" 78 | (parser ? content).isFailure should be(true) 79 | } 80 | 81 | "Not Close" should "get a left exception because some injection attack in content" in { 82 | val content: State[Char] = "some content included \'a data without 'text content'" 83 | (parser ? content).isFailure should be(true) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/test/scala-2.11/jaskell/parsec/InjectionSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import jaskell.Monad.Implicits._ 4 | import org.scalatest.flatspec.AnyFlatSpec 5 | import org.scalatest.matchers.should.Matchers 6 | 7 | import scala.util.{Failure, Success} 8 | 9 | 10 | /** 11 | * TODO 12 | * 13 | * @author Mars Liu 14 | * @version 1.0.0 15 | * @since 2020/06/16 21:13 16 | */ 17 | class InjectionSpec extends AnyFlatSpec with Matchers { 18 | 19 | import jaskell.parsec.Atom.{one, eof} 20 | import jaskell.parsec.Combinator._ 21 | import jaskell.parsec.Txt._ 22 | import jaskell.parsec.Parsec.Implicits._ 23 | import jaskell.Monad.Implicits._ 24 | 25 | 26 | implicit def toParsec[E, T, P <: Parsec[E, T]](parsec: P): Parsec[E, T] = parsec.asInstanceOf[Parsec[E, T]] 27 | 28 | val escapeChar: Parsec[Char, Char] = attempt(ch('\\') >> Parsec((s: State[Char]) => { 29 | s.next() flatMap { 30 | case 't' => Success('\t') 31 | case '\'' => Success('\'') 32 | case 'n' => Success('\n') 33 | case 'r' => Success('\r') 34 | case c@_ => Failure(new ParsecException(s.status, s"invalid escape char \\$c")) 35 | } 36 | })) 37 | val notEof: Parsec[Char, Char] = ahead(one[Char]) 38 | 39 | val oneChar: Parsec[Char, Char] = escapeChar <|> nch('\'') 40 | 41 | val contentString: Parsec[Char, String] = ch('\'') *> many(oneChar) <* ch('\'') >>= mkString 42 | 43 | val noString: Parsec[Char, String] = many1(nch('\'')) >>= mkString 44 | val content: Parsec[Char, String] = attempt(noString) <|> contentString 45 | 46 | val parser: Parsec[Char, String] = 47 | many(notEof >> content) >>= new Binder[Char, Seq[String], String] { 48 | override def apply(value: Seq[String]): Parsec[Char, String] = Parsec { (s: State[Char]) => 49 | for { 50 | _ <- eof ? s 51 | } yield value.mkString 52 | } 53 | } 54 | 55 | 56 | "Simple" should "match some regular content without string" in { 57 | val content: State[Char] = "a data without text content" 58 | (parser ? content) should be(Success("a data without text content")) 59 | } 60 | 61 | "SimpleString" should "match content in string literal" in { 62 | val content: State[Char] = "'a data included text content'" 63 | (parser ? content) should be(Success("a data included text content")) 64 | } 65 | 66 | "Escape" should "match content in string literal" in { 67 | val content: State[Char] = "'a data without\ttext content'" 68 | (parser ? content) should be(Success("a data without\ttext content")) 69 | } 70 | 71 | "Escape More" should "match mixed content in string literal and outer" in { 72 | val content: State[Char] = "some content included 'a data without\ttext content'" 73 | (parser ? content) should be(Success("some content included a data without\ttext content")) 74 | } 75 | 76 | "Injection" should "get a left exception because some injection attack in content" in { 77 | val content: State[Char] = "some content included 'a data without 'text content'" 78 | (parser ? content).isFailure should be(true) 79 | } 80 | 81 | "Not Close" should "get a left exception because some injection attack in content" in { 82 | val content: State[Char] = "some content included \'a data without 'text content'" 83 | (parser ? content).isFailure should be(true) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/scala-2/jaskell/Monad.scala: -------------------------------------------------------------------------------- 1 | package jaskell 2 | 3 | import scala.concurrent.{ExecutionContext, Future} 4 | import scala.util.{Success, Try} 5 | 6 | /** 7 | * TODO 8 | * 9 | * @author mars 10 | * @version 1.0.0 11 | * @since 2021/02/10 16:05 12 | */ 13 | trait Monad[M[_]] { 14 | def pure[A](element: A): M[A] 15 | 16 | def fmap[A, B](m: M[A], f: A => B): M[B] 17 | 18 | def flatMap[A, B](m: M[A], f: A => M[B]): M[B] 19 | 20 | def liftA2[A, B, C](f: (A, B) => C): (Monad.MonadOps[A, M], Monad.MonadOps[B, M]) => M[C] = { (ma, mb) => 21 | for { 22 | a <- ma 23 | b <- mb 24 | } yield f(a, b) 25 | } 26 | 27 | } 28 | 29 | object Monad { 30 | def apply[M[_]](implicit instance: Monad[M]): Monad[M] = instance 31 | 32 | def apply[M[_]](implicit creator: () => Monad[M]): Monad[M] = creator.apply() 33 | import Implicits._ 34 | 35 | abstract class MonadOps[A, M[_]](implicit I: Monad[M]) { 36 | def self: M[A] 37 | 38 | def map[B](f: A => B): M[B] = I.fmap(self, f) 39 | 40 | def <:>[B](f: A => B): M[B] = I.fmap(self, f) 41 | 42 | def flatMap[B](f: A => M[B]): M[B] = I.flatMap(self, f) 43 | 44 | def liftA2[B, C](f: (A, B) => C): M[B] => M[C] = m => I.liftA2(f)(self, m) 45 | 46 | def <*>[B](f: A => B): M[A] => M[B] = ma => I.fmap(ma, f) 47 | 48 | def *>[B](mb: M[B]): M[B] = for { 49 | _ <- self 50 | re <- mb 51 | } yield re 52 | 53 | def <*[_](mb: M[_]): M[A] = for { 54 | re <- self 55 | _ <- mb 56 | } yield re 57 | 58 | def >>=[B](f: A => M[B]): M[B] = flatMap(f) 59 | 60 | def >>[B](m: M[B]): M[B] = for { 61 | _ <- self 62 | re <- m 63 | } yield re 64 | 65 | } 66 | 67 | 68 | object Implicits { 69 | implicit def toMonad[A, M[_]](target: M[A])(implicit I: Monad[M]): MonadOps[A, M] = 70 | new MonadOps[A, M]() { 71 | override def self: M[A] = target 72 | } 73 | 74 | implicit val listMonad: Monad[List] = new Monad[List] { 75 | override def pure[A](element: A): List[A] = List(element) 76 | 77 | override def fmap[A, B](m: List[A], f: A => B): List[B] = m.map(f) 78 | 79 | override def flatMap[A, B](m: List[A], f: A => List[B]): List[B] = m.flatMap(f) 80 | } 81 | 82 | 83 | implicit val seqMonad: Monad[Seq] = new Monad[Seq] { 84 | override def pure[A](element: A): Seq[A] = Seq(element) 85 | 86 | override def fmap[A, B](m: Seq[A], f: A => B): Seq[B] = m.map(f) 87 | 88 | override def flatMap[A, B](m: Seq[A], f: A => Seq[B]): Seq[B] = m.flatMap(f) 89 | } 90 | 91 | implicit val tryMonad: Monad[Try] = new Monad[Try] { 92 | override def pure[A](element: A): Try[A] = Success(element) 93 | 94 | override def fmap[A, B](m: Try[A], f: A => B): Try[B] = m.map(f) 95 | 96 | override def flatMap[A, B](m: Try[A], f: A => Try[B]): Try[B] = m.flatMap(f) 97 | } 98 | 99 | implicit def toMonad(implicit ec: ExecutionContext): Monad[Future] = new Monad[Future] { 100 | override def pure[A](element: A): Future[A] = Future.successful(element) 101 | 102 | override def fmap[A, B](m: Future[A], f: A => B): Future[B] = m.map(f) 103 | 104 | override def flatMap[A, B](m: Future[A], f: A => Future[B]): Future[B] = m.flatMap(f) 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/scala-2.11/jaskell/parsec/Parsec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.parsec 2 | 3 | import jaskell.Monad 4 | 5 | import scala.language.implicitConversions 6 | import scala.util.{Failure, Success, Try} 7 | 8 | /** 9 | * trait of Parsec parsers. 10 | * 11 | * @author mars 12 | * @version 1.0.0 13 | */ 14 | trait Parsec[E, +T] { 15 | 16 | def apply(s: State[E]): Try[T] 17 | 18 | def parse(s: State[E]): T = { 19 | ask(s) match { 20 | case Success(result) => result 21 | case Failure(error) => throw error 22 | } 23 | } 24 | 25 | def !(s: State[E]): T = { 26 | ask(s) match { 27 | case Success(result) => result 28 | case Failure(error) => throw error 29 | } 30 | } 31 | 32 | def ask(s: State[E]): Try[T] = apply(s) 33 | 34 | def ask(s: Seq[E]): Try[T] = { 35 | ask(State(s)) 36 | } 37 | 38 | def option(s: State[E]): Option[T] = { 39 | apply(s).toOption 40 | } 41 | 42 | def `<|>`[U >: T](parsec: Parsec[E, U]): Parsec[E, U] = new Choice(Seq(this, parsec)) 43 | 44 | def ``[U >: T](message: String): Parsec[E, U] = Parsec((s: State[E]) => { 45 | this ask s orElse s.trap(message) 46 | }) 47 | 48 | def ?(s: State[E]): Try[T] = ask(s) 49 | 50 | def ?(s: Seq[E]): Try[T] = ask(s) 51 | 52 | // Monad 53 | val self: Parsec[E, T] = this 54 | 55 | def map[B](f: T => B): Parsec[E, B] = Parsec((s: State[E]) => self(s).map(f)) 56 | 57 | def <:>[B](f: T => B): Parsec[E, B] = self.map(f) 58 | 59 | def flatMap[B](f: T => Parsec[E, B]): Parsec[E, B] = Parsec((s: State[E]) => 60 | for { 61 | a <- self(s) 62 | func = f(a) 63 | x <- func(s) 64 | } yield x) 65 | 66 | 67 | def liftA2[B, C](f: (T, B) => C): Parsec[E, B] => Parsec[E, C] = (p: Parsec[E, B]) => Parsec((s: State[E]) => 68 | for { 69 | a <- self(s) 70 | b <- p(s) 71 | } yield f(a, b)) 72 | 73 | def <*>[B](f: T => B): Parsec[E, B] = Parsec((s: State[E]) => 74 | for { 75 | a <- self(s) 76 | } yield f(a)) 77 | 78 | def *>[B](mb: Parsec[E, B]): Parsec[E, B] = (for { 79 | _ <- self 80 | re <- mb 81 | } yield { 82 | re 83 | }) 84 | 85 | def <*[_](mb: Parsec[E, _]): Parsec[E, T] = for { 86 | re <- self 87 | _ <- mb 88 | } yield re 89 | 90 | def >>=[B](f: T => Parsec[E, B]): Parsec[E, B] = flatMap(f) 91 | 92 | def >>[B](m: Parsec[E, B]): Parsec[E, B] = for { 93 | _ <- self 94 | re <- m 95 | } yield re 96 | 97 | } 98 | 99 | object Parsec { 100 | def apply[E, T](parser: State[E] => Try[T]): Parsec[E, T] = new Parsec[E, T] { 101 | override def apply(s: State[E]): Try[T] = parser(s) 102 | } 103 | 104 | object Implicits { 105 | 106 | implicit def toParsec[E, T](parser: State[E] => Try[T]): Parsec[E, T] = Parsec(parser) 107 | 108 | implicit def toFlatMapper[E, T, O](binder: Binder[E, T, O]): (T) => Parsec[E, O] = binder.apply 109 | 110 | implicit def mkMonad: Monad[({type P[A] = Parsec[Char, A]})#P] = 111 | new Monad[({type P[A] = Parsec[Char, A]})#P] { 112 | override def pure[A](element: A): Parsec[Char, A] = Return(element) 113 | 114 | override def fmap[A, B](m: Parsec[Char, A], f: A => B): Parsec[Char, B] = Parsec((s: State[Char]) => m(s).map(f)) 115 | 116 | override def flatMap[A, B](m: Parsec[Char, A], f: A => Parsec[Char, B]): Parsec[Char, B] = Parsec(state => for { 117 | a <- m(state) 118 | b <- f(a)(state) 119 | } yield b) 120 | } 121 | } 122 | } 123 | 124 | -------------------------------------------------------------------------------- /src/test/scala/test/queue.scala: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | sealed trait Cons[+T] 4 | 5 | case class ConsT[T](head: T, tag: Int, var tail: Cons[T]) extends Cons[T] 6 | 7 | case object ConsZ extends Cons[Nothing] 8 | 9 | trait Queue[+T] { 10 | self => 11 | 12 | val size: Int 13 | val cons: Cons[T] 14 | val last: Cons[T] 15 | 16 | def isEmpty: Boolean = size <= 0 17 | 18 | def insert[G >: T](g: G): Queue[G] = { 19 | val s:Int = size + 1 20 | self.cons match { 21 | case consT: ConsT[G] => 22 | self.last match { 23 | case lastT: ConsT[G] => 24 | val newTail = ConsT(g, s, tail = ConsZ) 25 | lastT.tail = newTail 26 | new Queue[G] { 27 | override val size: Int = size 28 | override val cons: Cons[G] = consT 29 | override val last: Cons[G] = newTail 30 | } 31 | case ConsZ => 32 | val newTail = ConsT(g, s, tail = ConsZ) 33 | consT.tail = newTail 34 | new Queue[G] { 35 | override val size: Int = s 36 | override val cons: Cons[G] = consT 37 | override val last: Cons[G] = newTail 38 | } 39 | } 40 | case ConsZ => 41 | val consT = ConsT(g, s, tail = ConsZ) 42 | new Queue[G] { 43 | override val size: Int = s 44 | override val cons: Cons[G] = consT 45 | override val last: Cons[G] = consT.tail 46 | } 47 | } 48 | } 49 | 50 | def remove: (Option[T], Queue[T]) = cons match { 51 | case ConsT(head, tag, tail) if self.size > 0 => (Option(head), new Queue[T] { 52 | override val size: Int = self.size - 1 53 | override val cons: Cons[T] = tail 54 | override val last: Cons[T] = tail match { 55 | case s: ConsT[T] => self.last 56 | case _ => ConsZ 57 | } 58 | }) 59 | case _ => (Option.empty, self) 60 | } 61 | } 62 | 63 | object Queue { 64 | def empty[T]: Queue[T] = new Queue[T] { 65 | override val size: Int = 0 66 | override val cons: Cons[T] = ConsZ 67 | override val last: Cons[T] = ConsZ 68 | } 69 | } 70 | 71 | object QueueTest extends App { 72 | 73 | val ququeInt = Queue.empty[Int].insert(2).insert(5).insert(6).insert(8).insert(10) 74 | val q1 = ququeInt.remove 75 | val q2 = q1._2.remove 76 | val q3 = q2._2.remove 77 | val q4 = q3._2.remove 78 | val qq4 = q3._2.insert(85).insert(86).insert(22) 79 | val q5 = q4._2.remove 80 | val q6 = q5._2.remove 81 | val q7 = q6._2.remove 82 | assert(q1._1 == Option(2)) 83 | assert(q2._1 == Option(5)) 84 | assert(q3._1 == Option(6)) 85 | assert(q4._1 == Option(8)) 86 | assert(q5._1 == Option(10)) 87 | assert(q6._1.isEmpty) 88 | assert(q7._1.isEmpty) 89 | assert(qq4.remove._1 == Option(8)) 90 | assert(qq4.remove._2.remove._1 == Option(10)) 91 | assert(qq4.remove._2.remove._2.remove._1 == Option(85)) 92 | assert(qq4.remove._2.remove._2.remove._2.remove._1 == Option(86)) 93 | assert(qq4.remove._2.remove._2.remove._2.remove._2.remove._1 == Option(22)) 94 | assert(qq4.remove._2.remove._2.remove._2.remove._2.remove._2.remove._1.isEmpty) 95 | 96 | assert(!q1._2.isEmpty) 97 | assert(!q2._2.isEmpty) 98 | assert(!q3._2.isEmpty) 99 | assert(!q4._2.isEmpty) 100 | assert(q5._2.isEmpty) 101 | assert(q6._2.isEmpty) 102 | assert(q7._2.isEmpty) 103 | assert(!qq4.remove._2.isEmpty) 104 | assert(!qq4.remove._2.remove._2.isEmpty) 105 | assert(!qq4.remove._2.remove._2.remove._2.isEmpty) 106 | assert(!qq4.remove._2.remove._2.remove._2.remove._2.isEmpty) 107 | assert(qq4.remove._2.remove._2.remove._2.remove._2.remove._2.isEmpty) 108 | assert(qq4.remove._2.remove._2.remove._2.remove._2.remove._2.remove._2.isEmpty) 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/main/scala-2/jaskell/batteries/python/Strings.scala: -------------------------------------------------------------------------------- 1 | package jaskell.batteries.python 2 | 3 | import jaskell.Monad.Implicits._ 4 | import jaskell.parsec.Combinator.{BuiltIn, many} 5 | import jaskell.parsec.Txt.{ch, chIn, text} 6 | import jaskell.parsec.{Parsec, State} 7 | 8 | import scala.util.Success 9 | 10 | /** 11 | * TODO 12 | * 13 | * @author mars 14 | * @version 1.0.0 15 | * @since 2022/02/16 12:18 16 | */ 17 | object Strings { 18 | import jaskell.parsec.Parsec.Implicits._ 19 | 20 | def startQuote: Parsec[Char, Char] = ch('f').opt *> chIn("\"'") 21 | 22 | def startTriQuote: Parsec[Char, String] = ch('f').opt *> (text("\"\"\"") <|> text("'''")) 23 | 24 | def escapedChar: Parsec[Char, Char] = ch('\\') *> { (s: State[Char]) => 25 | s.next() flatMap { 26 | case '\'' => 27 | Success('\'') 28 | case '\"' => 29 | Success('\"') 30 | case '\\' => 31 | Success('\\') 32 | case '\r' => 33 | Success('\r') 34 | case '\n' => 35 | Success('\n') 36 | case '\t' => 37 | Success('\t') 38 | } 39 | } 40 | 41 | def char(stop: Char): Parsec[Char, Char] = { (s: State[Char]) => 42 | s.next() flatMap { c => 43 | if (c == stop) { 44 | s.trap(f"stop at $stop") 45 | } else { 46 | c match { 47 | case '\\' => 48 | s.trap("except a unescaped char but get \\") 49 | case '\n' => 50 | s.trap("except single line but get \n") 51 | case '\r' => 52 | s.trap("except single line but get \r") 53 | case char: Char => 54 | Success(char) 55 | } 56 | } 57 | } 58 | } 59 | 60 | 61 | def char(stop: String): Parsec[Char, Char] = { (s: State[Char]) => 62 | val tryIt = text(stop).attempt ? s 63 | if (tryIt.isSuccess) { 64 | s.trap(f"stop at $stop") 65 | } else { 66 | s.next() flatMap { 67 | case '\\' => 68 | s.trap("except a unescaped char but get \\") 69 | case char: Char => 70 | Success(char) 71 | } 72 | } 73 | } 74 | 75 | def rawChar(stop: Char): Parsec[Char, Char] = { (s: State[Char]) => 76 | s.next() flatMap { c => 77 | if (c == stop) { 78 | s.trap(f"stop at $stop") 79 | } else { 80 | c match { 81 | case '\n' => 82 | s.trap("except single line but get \n") 83 | case '\r' => 84 | s.trap("except single line but get \r") 85 | case char: Char => 86 | Success(char) 87 | } 88 | } 89 | } 90 | } 91 | 92 | 93 | def rawChar(stop: String): Parsec[Char, Char] = { (s: State[Char]) => 94 | val tryIt = text(stop).attempt ? s 95 | if (tryIt.isSuccess) { 96 | s.trap(f"stop at $stop") 97 | } else { 98 | s.next() 99 | } 100 | } 101 | 102 | def singleLineString:Parsec[Char, String] = { (s: State[Char]) => 103 | for { 104 | stop <- startQuote ? s 105 | content <- many(escapedChar <|> char(stop)) ? s 106 | _ <- char(stop) ? s 107 | } yield { 108 | content.mkString 109 | } 110 | } 111 | 112 | def multiLineString:Parsec[Char, String] = { (s: State[Char]) => 113 | for { 114 | stop <- startTriQuote ? s 115 | content <- many(escapedChar <|> char(stop)) ? s 116 | _ <- text(stop) ? s 117 | } yield { 118 | content.mkString 119 | } 120 | } 121 | 122 | def singleLineRawString:Parsec[Char, String] = { (s: State[Char]) => 123 | for { 124 | stop <- (ch('r') *> startQuote) ? s 125 | content <- many(rawChar(stop)) ? s 126 | _ <- char(stop) ? s 127 | } yield { 128 | content.mkString 129 | } 130 | } 131 | 132 | def multiLineRawString:Parsec[Char, String] = { (s: State[Char]) => 133 | for { 134 | stop <- (ch('r') *> startTriQuote) ? s 135 | content <- many(rawChar(stop)) ? s 136 | _ <- text(stop) ? s 137 | } yield { 138 | content.mkString 139 | } 140 | } 141 | 142 | def string: Parsec[Char, String] = multiLineString <|> singleLineString 143 | } 144 | -------------------------------------------------------------------------------- /src/test/scala/jaskell/expression/ExpressionSpec.scala: -------------------------------------------------------------------------------- 1 | package jaskell.expression 2 | 3 | import jaskell.expression.parsers.{Num, Parser} 4 | import jaskell.parsec.{State, TxtState} 5 | import org.scalatest.flatspec.AnyFlatSpec 6 | import org.scalatest.matchers.should.Matchers 7 | 8 | import scala.util.Success 9 | 10 | /** 11 | * TODO 12 | * 13 | * @author mars 14 | * @version 1.0.0 15 | * @since 2020/06/03 15:09 16 | */ 17 | class ExpressionSpec extends AnyFlatSpec with Matchers { 18 | def emptyEnv = new Env 19 | 20 | import jaskell.parsec.Txt._ 21 | val p = new Parser 22 | 23 | "Number" should "match a number" in { 24 | val content = "3.14" 25 | val p = new Num 26 | val exp = p ! content 27 | exp.eval(emptyEnv) should be(Success(3.14)) 28 | } 29 | 30 | "Basic" should "match a number" in { 31 | val content = "3.14" 32 | val exp = p ! content 33 | exp.eval(emptyEnv) should be(Success(3.14)) 34 | } 35 | 36 | "Add" should "match a add expression" in { 37 | val content = "3.14+2.53" 38 | val exp = p ! content 39 | exp.eval(emptyEnv) should be(Success(5.67)) 40 | } 41 | 42 | "Sub" should "match a sub expression" in { 43 | val content = "179- 8" 44 | val exp = p ! content 45 | exp.eval(emptyEnv) should be(Success(171)) 46 | } 47 | 48 | "Product" should "match a product expression" in { 49 | val content = "8 * -8" 50 | val exp = p parse content 51 | exp.eval(emptyEnv) should be(Success(-64)) 52 | } 53 | 54 | "Divide" should "match a divide expression" in { 55 | val st: TxtState = State("128/8") 56 | val re = p(st) 57 | re.flatMap(_.eval(emptyEnv)) should be(Success(16)) 58 | } 59 | 60 | "Quote" should "match a quoted expression" in { 61 | val st: TxtState = State("(128/8)") 62 | val re = p(st) 63 | re.flatMap(_.eval(emptyEnv)) should be(Success(16)) 64 | } 65 | 66 | "Priorities" should "compute a ploy expressio right to left" in { 67 | val st = State("7 + 15 * 3") 68 | val re = p(st) 69 | re.map(_.makeAst).flatMap(_.eval(emptyEnv)) should be(Success(52)) 70 | } 71 | 72 | "Priorities flow" should "compute a ploy expression left to right" in { 73 | val content = "5 * 3 + 7" 74 | val re = p parse content 75 | val exp = re.makeAst 76 | exp.eval(emptyEnv) should be(Success(22)) 77 | } 78 | 79 | "Ploy Quote" should "compute a ploy expression include quote" in { 80 | val content: State[Char] = "5 * (3 + 7)" 81 | val re = p ! content 82 | val exp = re.makeAst 83 | exp.eval(emptyEnv) should be(Success(50)) 84 | } 85 | 86 | "Ploy Complex" should "compute a complex ploy expression include quote" in { 87 | val content = "5 * (3 + 7) - 22.5" 88 | val re = p ! content 89 | val exp = re.makeAst 90 | exp.eval(emptyEnv) should be(Success(27.5)) 91 | } 92 | 93 | "More Complex" should "compute a complex ploy expression has double sub" in { 94 | val content = "5 * (3 + 7) - -22.5" 95 | val p = new Parser 96 | val re = p ! content 97 | val exp = re.makeAst 98 | exp.eval(emptyEnv) should be(Success(72.5)) 99 | } 100 | 101 | "Scientific" should "compute a complex ploy expression has scientific notation" in { 102 | val content = "5 * (3 + 7e2) - -22.5" 103 | val re = p ! content 104 | val exp = re.makeAst 105 | exp.eval(emptyEnv) should be(Success(3537.5)) 106 | } 107 | 108 | "Scientific More" should "compute a complex ploy expression of more scientific notation numbers" in { 109 | val content = "5 * (3E-3 + 7) - -22.5e8" 110 | val re = p ! content 111 | val exp = re.makeAst 112 | exp.eval(emptyEnv) should be(Success(2.250000035015E9)) 113 | } 114 | 115 | "Normal Compute" should "compute a normal expression" in { 116 | val content:State[Char] = "3.14 + 7 * 8 - (2 + 3)" 117 | p ? content flatMap {_.makeAst eval emptyEnv} should be (Success(54.14)) 118 | } 119 | 120 | "Parameters Compute" should "compute a parameters expression" in { 121 | val env = emptyEnv 122 | env.put("x", 13) 123 | val content:State[Char] = "3.14 + 7 * 8 - (2 + x)" 124 | p ? content flatMap {_.makeAst eval env} should be (Success(44.14)) 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /src/main/scala-2.11/jaskell/croupier/Croupier.scala: -------------------------------------------------------------------------------- 1 | package jaskell.croupier 2 | 3 | import scala.annotation.tailrec 4 | import scala.collection.mutable 5 | import scala.util.Random 6 | 7 | /** 8 | * TODO 9 | * 10 | * @author mars 11 | * @version 1.0.0 12 | * @since 2023/03/26 23:55 13 | */ 14 | class Croupier[T](val poker: Poker[T]) { 15 | def randIndex(seq: Seq[T]): Option[Int] = poker.select(seq) 16 | 17 | def randDraw(seq: Seq[T]): (Option[T], Seq[T]) = 18 | poker.select(seq) match { 19 | case Some(idx) => 20 | (Some(seq(idx)), seq.take(idx) ++ seq.drop(idx + 1)) 21 | case None => 22 | (None, seq) 23 | } 24 | 25 | def randDraw(list: mutable.ListBuffer[T]): Option[T] = 26 | poker.select(list.toSeq) 27 | .map(idx => list.remove(idx)) 28 | 29 | def randDraw(data: mutable.ListBuffer[T], size: Int): Seq[T] = { 30 | if (data == null) { 31 | Seq() 32 | } 33 | 34 | for { 35 | _ <- 0 until Math.min(data.size, size) 36 | element <- randDraw(data) 37 | } yield element 38 | } 39 | 40 | def randSelect(seq: Seq[T], size: Int): Seq[T] = { 41 | randDraw(seq, size)._1 42 | } 43 | 44 | def randSelect(seq: Seq[T]): Option[T] = randIndex(seq).map(seq) 45 | 46 | def randDraw(seq: Seq[T], size: Int): (Seq[T], Seq[T]) = { 47 | @tailrec 48 | def helper(data: Seq[T], seq: Seq[T], size: Int): (Seq[T], Seq[T]) = { 49 | if (size == 0 || seq.isEmpty) { 50 | return (data, seq) 51 | } 52 | randDraw(seq) match { 53 | case (Some(element), rest: Seq[T]) => 54 | helper(data :+ element, rest, size - 1) 55 | case (None, _) => 56 | (data, seq) 57 | } 58 | } 59 | 60 | helper(Seq(), seq, size) 61 | } 62 | } 63 | 64 | object Croupier { 65 | 66 | def fair[T]: Croupier[T] = new Croupier(new Fair(new Random())) 67 | 68 | def fair[T](random: Random): Croupier[T] = new Croupier[T](new Fair(random)) 69 | 70 | def fair[T](seed: Long): Croupier[T] = new Croupier[T](new Fair(new Random(seed))) 71 | 72 | def damping[T]: Croupier[T] = new Croupier(new Damping(new Random())) 73 | 74 | def damping[T](random: Random): Croupier[T] = new Croupier(new Damping(random)) 75 | 76 | def damping[T](seed: Long): Croupier[T] = new Croupier(new Damping(new Random(seed))) 77 | 78 | def invert[T]: Croupier[T] = new Croupier(new Invert(new Random())) 79 | 80 | def invert[T](random: Random): Croupier[T] = new Croupier[T](new Invert(random)) 81 | 82 | def invert[T](seed: Long): Croupier[T] = new Croupier[T](new Invert(new Random(seed))) 83 | 84 | def byWeight[T](scale: Scale[T]) = new Croupier[T](new Scaled[T](scale, new Random())) 85 | 86 | def byWeight[T](scale: Scale[T], random: Random) = new Croupier[T](new Scaled[T](scale, random)) 87 | 88 | def byWeight[T](scale: Scale[T], seed: Long) = new Croupier[T](new Scaled[T](scale, new Random(seed))) 89 | 90 | def byWeightLite[T](scale: Scale[T]) = new Croupier[T](new LiteScaled[T](scale, new Random())) 91 | 92 | def byWeightLite[T](scale: Scale[T], random: Random) = new Croupier[T](new LiteScaled[T](scale, random)) 93 | 94 | def byWeightLite[T](scale: Scale[T], seed: Long) = new Croupier[T](new LiteScaled[T](scale, new Random(seed))) 95 | 96 | def byWeightBinary[T](scale: Scale[T]) = new Croupier[T](new BinaryScaled[T](scale, new Random())) 97 | 98 | def byWeightBinary[T](scale: Scale[T], random: Random) = new Croupier[T](new BinaryScaled[T](scale, random)) 99 | 100 | def byWeightBinary[T](scale: Scale[T], seed: Long) = new Croupier[T](new BinaryScaled[T](scale, new Random(seed))) 101 | 102 | def byRank[T](ranker: Ranker[T]) = new Croupier[T](new Ranked[T](ranker, new Random())) 103 | 104 | def byRank[T](ranker: Ranker[T], random: Random) = new Croupier[T](new Ranked[T](ranker, random)) 105 | 106 | def byRank[T](ranker: Ranker[T], seed: Long) = new Croupier[T](new Ranked[T](ranker, new Random(seed))) 107 | 108 | def byRankBinary[T](ranker: Ranker[T]) = new Croupier[T](new BinaryRanked[T](ranker, new Random())) 109 | 110 | def byRankBinary[T](ranker: Ranker[T], random: Random) = new Croupier[T](new BinaryRanked[T](ranker, random)) 111 | 112 | def byRankBinary[T](ranker: Ranker[T], seed: Long) = new Croupier[T](new BinaryRanked[T](ranker, new Random(seed))) 113 | 114 | def byZipScaled[T](scale: Scale[T]) = new Croupier[T](new ZipScaled[T](scale, new Random())) 115 | 116 | def byZipScaled[T](scale: Scale[T], random: Random) = new Croupier[T](new ZipScaled[T](scale, random)) 117 | 118 | def byZipScaled[T](scale: Scale[T], seed: Long) = new Croupier[T](new ZipScaled[T](scale, new Random(seed))) 119 | } 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | --------------------------------------------------------------------------------