Skip to content

Commit

Permalink
support comments in expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
saig0 committed Sep 25, 2015
1 parent f0fd93d commit 2a415e0
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 21 deletions.
31 changes: 19 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@ S-FEEL is a subset of FEEL that can be used for simple expressions and condition
Currently, the goal of the parser and interpreter is to support S-FEEL, so that it can used for decision tables.

## Supported Language Features
* data types: number, string, boolean, date
* data types: number, string, boolean, date, time, duration
* simple unary tests: compare operators ('<', '<=', '>', '>=', equal), interval, negation, combination of multiple tests
* qualified names

## Current Limitations
* unsupported data types: time, duration
* no simple expressions
* no simple expressions, only S-FEEL
* the input of the simple unary test should be given as context entry 'cellInput'
* limited support for types of input and qualified variables

# How to use
The parser and interpreter can be integrated as dependency or as jar which include all dependencies (size of ~ 7mb).
Make sure you have build and deploy it (locally) before. It is not deployed in a public repository yet.
The parser and interpreter can be integrated as dependency (requires Scala in classpath) or as jar which include all dependencies (size of ~ 7mb).
Make sure you have build and deploy it (locally) before. It is not deployed in a public repository yet. Or just use a built jar of the released version found on github.

## Native Way
There is a class 'FeelEngine' that can be used to parse and evaluate a given expression. This class can be called from a scala or java program.
Expand Down Expand Up @@ -87,18 +87,25 @@ Just to have a feeling how an expression can look like:

## Simple Unary Test
```
< 42
< 42 // input less than 42
(2..4)
(2..4) // input greater than 2 and less than 4
2,4
2,4 // input is 2 or 4
not(2,4)
not(2,4) // input is not 2 or 4
>= a
>= a // input is greater or equal to the value of variable 'a'
[date("2015-09-17")..date("2015-09-19")]
"good" // input is equal the string 'good'
"good"
[date("2015-09-17")..date("2015-09-19")] // input is after or equal '2015-09-17' and before or equal '2015-09-19'
]time("08:00:00")..time("16:00:00")[ // input is after '08:00:00' and before '16:00:00'
<= duration("P1D") // input is less or equal to 'P1D' (one day)
```

# Note
The parser and interpreter can be used into the [camunda DMN engine](https://github.com/camunda/camunda-engine-dmn) as alternative to the built-in FEEL script engine.
15 changes: 9 additions & 6 deletions src/main/scala/org/camunda/feel/interpreter/Context.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import org.joda.time.LocalDate
* @author Philipp Ossler
*/
case class Context(variables: Map[String, Any] = Map()) {

def input: Val = apply(Context.inputKey)

def apply(key: String): Val = variables.get(key) match {
Expand All @@ -23,15 +23,18 @@ case class Context(variables: Map[String, Any] = Map()) {
case x: Date => ValDate(x)
case x: Time => ValTime(x)
case x: Duration => ValDuration(x)
case x: java.util.Date => ValDate( LocalDate.fromDateFields(x) )
case None => ValError("no variable avaiale")
case _ => ValError(s"unsupported variable '$x'")
// extended types
case x: java.util.Date => ValDate(LocalDate.fromDateFields(x))
// unsupported values
case None => ValError("no variable available")
case null => ValError("no varibale available")
case _ => ValError(s"unsupported type of variable '$x'")
}

}

object Context {

val inputKey = "cellInput"

}
10 changes: 7 additions & 3 deletions src/main/scala/org/camunda/feel/parser/FeelParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import scala.util.parsing.combinator.JavaTokenParsers
* @ss DMN 1.0 (S.99)
*/
object FeelParser extends JavaTokenParsers {


// override to ignore comment '// ...' and '/* ... */'
protected override val whiteSpace = """(\s|//.*|(?m)/\*(\*(?!/)|[^*])*\*/)+""".r

def parseSimpleExpression(expression: String): ParseResult[Exp] = parseAll(simpleExpression, expression)

def parseSimpleUnaryTest(expression: String): ParseResult[Exp] = parseAll(simpleUnaryTests, expression)
Expand Down Expand Up @@ -85,8 +88,9 @@ object FeelParser extends JavaTokenParsers {
// 39
private def dateTimeLiternal: Parser[Exp] = ("date(" ~> stringLiteral <~ ")" ^^ { case date => ConstDate(withoutQuotes(date)) }
| "time(" ~> stringLiteral <~ ")" ^^ { case time => ConstTime(withoutQuotes(time)) }
| "duration(" ~> stringLiteral <~ ")" ^^ { case duration => ConstDuration(withoutQuotes(duration)) })
| "duration(" ~> stringLiteral <~ ")" ^^ { case duration => ConstDuration(withoutQuotes(duration)) }
| failure("illegal start of a date time literal. expect a date ('YYYY-MM-DD'), time ('hh:mm:ss') or duration ('P_Y_M_DT_H_M_S')"))

private def withoutQuotes(exp: String): String = exp.replaceAll("\"", "")

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ class ParseSimpleExpressionTest extends FlatSpec with Matchers {
parse(".2") should be(ConstNumber(.2))
}

it should "parse a string" in {

parse(""" "a" """) should be(ConstString("a"))
}

it should "parse a qualified name" in {

parse("b") should be(Ref("b"))
}

it should "parse a boolean" in {

parse("true") should be(ConstBool(true))
Expand All @@ -28,6 +38,28 @@ class ParseSimpleExpressionTest extends FlatSpec with Matchers {
parse("""date("2015-09-18")""") should be(ConstDate("2015-09-18"))
}

it should "parse a time" in {

parse("""time("10:31:10")""") should be(ConstTime("10:31:10"))
}

it should "parse a duration" in {

parse("""duration("P1D")""") should be(ConstDuration("P1D"))
}

it should "ignore an one line comment '// ...'" in {

parse("""duration("P1D") // one day""") should be(ConstDuration("P1D"))
}

it should "ignore a multi line comment '/* ... */'" in {

parse("""duration("P1DT4H") /*
one day and 4 hours
*/ """)
}

private def parse(expression: String): Exp = {
val result = FeelParser.parseSimpleExpression(expression)
result.get
Expand Down

0 comments on commit 2a415e0

Please sign in to comment.