Expressions

From KynetxDocs

Jump to: navigation, search

Contents

Literals

The following literals can be used within KRL.

Booleans

The boolean values are true and false

Numbers

KRL supports integer and real values. A negative number is created by prepending the minus sign (-) to a number.

Strings

String are created by enclosing characters in double quote characters (").

Arrays

Arrays are created by enclosing comma-delimited expressions in square brackets like so:

["a", "b", "c"]

Hashes

Hashes are creating by enclosing comma-delimted name-value pairs in curly braces like so:

{"foo" : "bar", "fizz" : 3, "flop" : [1, 2, 3]}


Extended Quoting

Some KRL statements and expressions make use of extended quotes which begin with << and end the >> For example:

pre {
  
   somevar = <<
<p class="announcement">This is <em>some</em> HTML.<br/>
<a href="http://www.google.com">Search Google</a>
</p>
>>;

}

Extended quotes allow multiple line passages that contain the " symbol to be entered as variables. Values inside the extended quotes are treated as strings.

Inside the string, variables can be referenced using the #{<varname>} syntax for simple templating. These variables are interpreted in the browser, not on the server.

Regular Expressions

Regular expressions are delimited by the slash character ("/") and most closely follow the conventions for Perl regular expressions.

Simple Predicates

Several built-in operators allow testing for equality and inequality.

For numbers: <, >, <=, >=, ==, and !=

For strings: eq, neq, and like. like takes a regular expression as its second argument and returns true if it matches the string given as it's first argument.

Arguments can be any valid expression.

The following are all valid tests for equality:

c == 5

page:var("city") eq "Blackfoot"

"Orem" neq location:city()

weather:curr_temp() < 90

location:city() + ", WA" eq city

5 * (weather:curr_temp() - 32) / 9 < 0 

Predicate Expressions

Predicate expressions are created by joining simple predicates with conjunction (&&) and disjunction (||) in rules. The not operator can also be applied to negate the value of a predicate expression.

  • Negation is done using the keyword not
  • Expressions can parenthesized for grouping.

The following are examples of valid predicates:

city("Orem") || city("Provo")

not(city("Orem") || city("Provo"))

state("UT") && not city("Provo")


Intrinsic Predicates

The following predicates are built into KRL. If you think of others we ought to have, just ask.

The predicates are grouped by data source.

Location Predicates

city(<arg>)</dt>
returns true when <arg> matches the customer city. </dd>

outside_city(<arg>) </dt>
returns true when <arg> does not match the customer city. </dd>

state(<arg>) </dt>
returns true when <arg> matches the customer state. </dd>

outside_state(<arg>) </dt>
returns true when <arg> does not match the customer state. </dd>

country(<arg>) </dt>
returns true when <arg> matches the customer county. <arg> must be a valid two-digit country code. Use GB for United Kingdom. </dd>

outside_country(<arg>) </dt>
returns true when <arg> does not match the customer country. <arg> must be a valid two-digit country code. Use GB for United Kingdom. </dd>

international() </dt>
returns true when the customer country is not 'US'. </dd>

Media Market Predicates

media_market_rank_grater_than(<arg>)</dt>
returns true when media market rank is greater then <arg>. </dd>

media_market_rank_less_than(<arg>)</dt>
returns true when media market rank is less then <arg>. </dd>

dma_is(<arg>)</dt>
returns true when <arg> is your DMA Media Market Code. </dd>

Demographic Predicates

The data used by this data source is from the 2000 US Census. Demographic data is only available for US households at present. For median income, it is useful to keep this table of median income distributions by zipcode in mind:

Income RangeNumber of ZipsPercent
$0 - $100001270.40%
$10000 - $2000012563.91%
$20000 - $30000691621.55%
$30000 - $400001156436.03%
$40000 - $50000624719.46%
$50000 - $6000029339.14%
$60000 - $7000014844.62%
$70000 - $800007112.22%
$80000 - $900003851.20%
$90000 - $1000001860.58%
$100000 - $1100001320.41%
$110000 - $120000610.19%
$120000 - $130000350.11%
$130000 - $140000140.04%
$140000 - $150000150.05%
$150000 - $160000130.04%
$160000 - $17000020.01%
$170000 - $18000030.01%
$180000 - $19000020.01%
$190000 - $20000010.00%
Above $200000110.03%

Note that less than 20% of US zip codes have a median income above $50000. Also note that the median income of many work locations is lower than you might think, so you might want to combine the median income predicates with the at_home predicate (to be implemented).

A zip code is classified as "urban" if more than 70% of total population lives in an urban setting and "rural" if more than 70% of total population lives in an rural setting. Note that with this definition, "not urban" isn't the same as "rural". The classification "mixed" applied to zip codes that are neither urban nor rural. The following table shows the overall number of zip codes and percentages classified as rural, urban, or mixed:

TypeNumber of ZipsPercent
Urban1013130.53%
Rural1849655.74%
Mixed455313.72%

Note that even though the majority of zip codes are rural, according to the classification given above, 75% of the population lives in an urban setting as shown in the following table:

TypePopulationPercent
Urban21446118675.19%
Rural3312200411.61%
Mixed3764732613.20%


median_income_above(<arg>)</dt>
returns true when the median income of the zip code the customer is in is greater than the number given in <arg> </dd>

median_income_below(<arg>)</dt>
returns true when the median income of the zip code the custom is in is less than the number given in <arg> </dd>

median_income_between(<low>,<high>)</dt>
returns true when the median income of the zip code the custom is in is between <low> and <high> </dd>

urban()</dt>
returns true when the customer is in an urban location. </dd>

rural()</dt>
returns true when the customer is in an rural location. </dd>

Weather Predicates

All predicates are relative to local weather. That is weather where the user is.

warmer_than(<arg>) </dt>
Is the current temperature warmer than <arg> </dd>

colder_than(<arg>) </dt>
Is the current temperature colder than <arg> </dd>

tomorrow_cond(<arg>) </dt>
Is <arg> the condition code for tomorrow's weather? </dd>

today_showers() </dt>
Is rain forecast today? </dd>

tomorrow_showers() </dt>
Is rain forecast tomorrow? </dd>

today_cloudy() </dt>
Is cloudy weather forecast for today? </dd>

tomorrow_cloudy() </dt>
Is cloudy weather forecast for tomorrow? </dd>

today_snow() </dt>
Is snow forecast for today/ </dd>

tomorrow_snow() </dt>
Is snow forecast for tomorrow? </dd>

today_windy() </dt>
Is wind forecast for today/ </dd>

tomorrow_windy() </dt>
Is wind forecast for tomorrow? </dd>

today_sunny() </dt>
Is sunny weather forecast for today/ </dd>

tomorrow_sunny() </dt>
Is sunny weather forecast for tomorrow? </dd>

Time Predicates

All of the following predicates are referenced to the user's local time. All times are 24-hour clock.

timezone(<arg>) </dt>
<arg> is the name of a timezone in a timezone code like "America/Denver" </dd>

daytime() </dt>
Is it daytime (between sunrise and sunset)? </dd>

nighttime() </dt>
Is it nighttime (between sunset and sunrise)? </dd>

morning() </dt>
Between 0600 and 1200 </dd>

afternoon() </dt>
Between 1200 and 1700 </dd>

evening() </dt>
Between 1700 and 2000 </dd>

night() </dt>
Between 2000 and 2400 </dd>

lunch_time() </dt>
Between 1130 and 1300 </dd>

late_morning() </dt>
Between 1000 and 1200 </dd>

early_afternoon() </dt>
Between 1200 and 1500 </dd>

late_afternoon() </dt>
Between 1500 and 1700 </dd>

time_between(<start_hour>, <start_minute>, <end_hour>, <end_minute>) </dt>
Local time is between the times given. </dd>

date_between(<start_month>, <start_day>, <start_year>, <end_month>, <end_day>, <end_year>) </dt>
Local time is between the dates given (midnight) </dd>

date_start(<start_month>, <start_day>, <start_year>) </dt>
Date is on or after the indicated month, day, and year </dd>

day_of_week(<day name>) </dt>
Checks if the current day of the week has the name (capitalized) given as an argument </dd>

today_is(<sun>, <mon>, <tue>, <wed>, <thur>, <fri>, <sat>) </dt>
Each argument is a 1 or 0 indicating whether to chek if today is that day. </dd>

weekday() </dt>
is today a weekday? </dd>

weekend() </dt>
Is today a weekend? </dd>

</dt>

</dd>

Economic Indicator Predicates

These are experimental and mostly for demonstration purposes. Stock quotes are not guaranteed to be accurate and are delayed by 20 minutes. They should not be used as the basis for a financial transaction or decision.

djia_up_more_than(<arg>) </dt>

</dd>

djia_down_more_than(<arg>) </dt>

</dd>

Referer Predicates

search_engine_referer() </dt>
Is referer a search engine? </dd>

referer_domain(<arg>) </dt>
Is the referer domain equal to <arg> </dd>

remote_referer() </dt>
Did the user come to this page from another Web site? Note: only domain names are checked, not ports or protocols. </dd>

local_referer() </dt>
Did the user come to this page from within the same Web site? Note: only domain names are checked, not ports or protocols. </dd>

Location Predicates

is_ie()</dt>
returns true when the browser type is Internet Explorer. </dd>

is_firefox()</dt>
returns true when the browser type is Firefox. </dd>

Misc Predicates

truth()</dt>
returns true unconditionally</dd>

Arithmetic Expressions

KRL supports the standard arithmetic operators: +, -, *, and / using infix notation. Parentheses are used to group expressions when normal precedence rules are to be overridden:

pre {
  x = y + x;
  a = a * (b + c);
  p = q / 4;
}

String Expressions

KRL supports string concatenation using the + symbol:

pre {
  x = "Hello " + "World!";
}

Conditional Expressions

KRL provides support for conditional expressions with the following syntax:

<pred> => <expr> | <expr> 

These can be nested to produce:

<pred0> => <expr0> |
<pred1> => <expr1> |
<pred2> => <expr2> |
 ...
          | <exprn> 

Because of the limitations of the recursive descent parser, most predicates will need to be enclosed in parentheses:

pre {
  z = (x > y) => y | 3; 
} 

Data Sources

Datasource declarations take the following form:

<var> = <source>:<function>(<args>);

There are two kinds of data sources: intrinsic and user-defined.

Intrinsic Datasources

The following describes data sources available in KRL.

Weather

The <source> for weather is weather.

The following functions are available:

  • curr_temp - current temperature
  • curr_cond - current conditions
  • curr_cond_code - the Yahoo! code for the current condition
  • tomorrow_low - tomorrow's forecast low temperature
  • tomorrow_high - tomorrow's forecast high temperature
  • tomorrow_cond - tomorrow's forecast condition
  • tomorrow_cond_code - tomorrow's forecast Yahoo! condition code

None of these take arguments.

Location

The <source> for location is location.

The following functions are available:

  • country_code
  • country_name
  • region - this is the state in the US
  • city
  • postal_code - this is the zip code in the US
  • latitude
  • longitude
  • dma_code
  • area_code

None of these functions takes arguments.

Referer

The <source> for referer is referer.

The following functions are available:

  • search_terms - returns the search terms if the referer was a search engine.

None of these functions takes arguments.

Media Market

The <source> for media market is mediamarket.

The following functions are available:

  • name - media market name.
  • rank - rank in the US of your media market.
  • household - number of households with TVs in your market.

None of these functions takes arguments.

Stocks

The <source> for stock is stocks.

The following functions are available:

  • last
  • change
  • open
  • high
  • low
  • volume
  • previous_close
  • name

The argument to each of these functions is the stock symbol.

Page

The <source> for page data sources is page.

The following functions are available:

  • var - retrieve the value of a variable set on the page (see Page Variables for more information)
  • env - retrieve the value of the page environment
  • url - retrieve the values for components of the calling page's URL.

The argument to each of these functions is the page or environement variable name as a string.

page:env

The following arguments are valid for page:env:

  • caller - the URL of the page on which the ruleset if evaluating
  • ip - IP number of the client
  • referer - the URL of the refering page to the caller
  • rid - the ruleset ID
  • rule_version - version number of the ruleset. Note that these are monotonic, but not sequential.
  • title - the page title of the calling page
  • txn_id - the transaction ID of this ruleset evaluation

page:url

The following arguments are valid for page:url:

  • protocol - the protocol scheme (e.g. http, https, etc.)
  • hostname - the complete hostname (e.g. www.windley.com)
  • domain - the domain portion of the hostname (e.g. windley.com)
  • tld - the top-level domain of the hostname (e.g. com, org, net, etc.)
  • port - the port (defaults to 80 if unspecified)
  • path - the path portion of the URL (e.g. /archives/2008/07)
  • query - the query string, if present (e.g. ?vxx=_ADJKLD&q=foo)

The declarations

h = page:url("hostname");
d = page:url("domain");
q = page:url("query");

executed as a result of a call from a page with the url http://www.windley.com/archives/2008/07?q=foo would result in h having the value www.windley.com, d having the value windley.com, and q having the value q=foo.

page:param

page:param takes a single argument: the name of the parameter. It returns the value of that parameter. Page parameters are set in the KNS_config object by the endpoint.

So, if the KNS_config object contained the following:

var KOBJ_config ={
   "rids"  : ["ruleset_1","ruleset_2"],
   "ruleset_1:ex1" : "3",
   "ruleset_2:ex2" : "5",
};

then the following declaration

n = page:param("ex1")

would result in n having the value of 3.

UserAgent

The <source> for page data sources is useragent.

The following functions are available:

  • language - natural language version of language
  • language_code - ISO code (e.g. "en" for English) of language
  • browser_name - name of the browser (e.g. "Internet Explorer", "Firefox", etc.)
  • browser_version - complete version string
  • browser_version_major - part preceding "." in version string
  • browser_version_minor - part following "." in version string
  • os - operating system
  • os_type - general type of OS ("Linux")
  • os_version - version of OS

Here's an example:

pre {
  browser = useragent:browser_name();
}
notify("Browser", "You're using " + browser);

User Defined Data Sources

As described in the documentation for the global declarations, users can define data sources. They are queried in the pre declarations to produce a complex data structure. For example, if a data source named library_search had been declared, it could be queried like so:

pre {
  book_data = datasource:library_search("q="+isbn);
}

The datasource takes a single parameter of either a string or a hash.

  • If the parameter is a string, then it is concatenated with the URL root given in the datasource declaration without modification. What you supply here, when appended to the datasource root URL must result in exactly the URL that you intend to call.
  • If the parameter is a hash, then the has is turned into a properly formatted HTTP QUERY string with names 7 values delimited by an equals sign (=) to create a name-value pair and each name-value pair delimited by an ampersand (&).

If you intend to use the results of the query in a Javascript expression to be executed on the server, note that the value of the right hand side is stored in KOBJ['data'][lhs] where lhs is the left hand side of the declaration. Note that because a ruleset might execute with any other ruleset, variable names should be chosen to avoid name clashes.

Complex data can be queried and used using the pick operator.

Persistent Variables

Note: only entity persistents are currently supported by the production KNS system.

There are two types of persistent variables:

  • Entity variables are used to record persisten data about individuals interacting with rules. Entity variables are identified by the domain ent.
  • Application variables are used to record persistent data about the application or ruleset. Application variables are identified by the domain app.

Persistent variables take three forms:

  • flags
  • counters
  • trails

Flags store boolean values. Counters store (and, obviously count and test) numeric values. Trails keep track of page visits and other state information. Trails are limited storing 20 places.

As an example, the following rule uses a counter to fire an action when an individual has visited a collection of web pages (anything in the archive directory) more than twice in the last three days:

  rule frequent_archive_visitor is active {
    select using "/archives/\d+/\d+/" setting ()

    pre {
      c = ent:archive_pages;
    }

    if ent:archive_pages > 2 within 3 days then {
      alert("You win the prize!  You've seen " + c + " pages from the archives!")
    }

    fired {
      clear ent:archive_pages;
    } else {
      ent:archive_pages += 1 from 1;
    }
  }

The following rule will create a counter of all the visitors to the archive pages and replace an page element with an id of page_count with the current count:

  rule count_archive_visitors is active {
    select using "/archives/\d+/\d+/" setting ()

    pre {
      c = app:visitor_count;
      res = <<
<div id="page_count">#{c}</div>
 >>
    }

    replace("#page_count", res)

    fired {
      app:visitor_count += 1 from 1;
    }
  }

Persistent variables can be used in expressions as part of a prelude section in a rule, in predicates as part of a conditional action, and in mutator statements as part of a postlude or callback.

Using Persistents in Preludes

Flags and counters can be used inside prelude expressions like any other variable. You cannot use them inside extended quotations as template variables. If the persistent variable does not exist before it is used, the default value is 0. Note that variable references for uninitialized persistent variables are not typed and so it is treated as a counter.

The following example shows the use of a persistent variable in a prelude statement:


pre {
 c = ent:archive_pages;
 d = << 
This is a page that has been accessed #{c} times
>>
}

Persistent trails have special operators for accessing individual places in the trail.

  • The history function takes an expression and a persistent variable. If the expression evaluates to n then the history function will return the n_th place on the trail. The most recent place is at history location 0.
  • The current function takes a persistent variable. The current function performs the same operation as the history function with a location of 0. That is, it returns the most recent place on the trail.

Using Persistents in Predicates

For most uses in predicates, persistents are accessed in the same way as in the prelude. So, the following action will fire when the persisent variable ent:archive_pages is greater than 3:

if ent:archive_pages > 3 then
   notify(...)

Persistent flags and counters can also be tested with an associated timeframe. For example, the following action will fire when persisent variable ent:archive_pages is greater than 3 and the last time it as set was within the last 2 hours:

if ent:archive_pages > 3 within 2 hours then
   notify(...)

The syntax of timeframe limited predicates is

PVAR (<= | >= | < | > | == | !=) EXPR within EXPR 
   (years| months | weeks| days| hours | minutes| seconds)

where PVAR is a persistent flag or counter and EXPR is any valid KRL expression.

Persistent trails can be tested using the seen predicate. There are two forms.

The first form asks whether a regular expression has been seen within an optional timeframe (specified exactly as above). So you can say:

if seen "/archive/2006" in ent:my_trail then 
   notify(...)

or

if seen "/archive/2006" in ent:my_trail within 3 days then 
   notify(...)

The first arguments is a string that is interpreted as a regular expression.

The second form tests whether a place in a trail matching the first regular expression comes before or after a place in the same trail matching a second regular expression. This example

if seen "/archive/2006" before "/archive/2007" in ent:my_trail then 
  notify(...)

would fire the action when a place matching the regular expression "/archive/2006" was placed on the trail before a place matching the regular expression "/archive/2007".

Mutating Persistents

Persistent variables, to be useful, must be mutated, or changed permanently. The following statements mutate persistent varible:

  • clear PVAR - clears (sets to nil) the persistent variable PVAR
  • set PVAR - set (sets to true) the persistent variable PVAR making it a flag.
  • PVAR (+=|-=) EXPR from EXPR - increments (or decrements) the persistent variable PVAR by the value given by the first expression. If the value is null when the statement executes, the value of the second expression is used to initialize the persistent.
  • mark PVAR - mark the trail using the current caller URL as the place.
  • mark PVAR with EXPR - mark the trail using the value of the expression as the place.
  • forget STRING in PVAR - forget the place in the trail marked by the string when interpreted as a regular expression.

Persistent variable mutator statements can appear in postlude sections or callback sections of rules.

Built-In Packages and Functions

The syntax for using a built-in function is

<package-name>:<function-name>(<arg0>...<argn>)

Math

The package name is math. The following functions are available:

  • random(<num>) - generate a random number. The value is between 0 and the number given as an argument.
pre {
  r = math:random(999)
}

would generate a random number between 0 and 999 and bind it to r.

Page IDs

You can use the contents of elements on the page with a specific CSS ID using the page ID delclaration. For example, the following comment will make the contents of the element with the ID itemnum available as the variable item_no.

item_no = page:id("itemnum")

Note that page IDs cannot be used in predicates since the values are not available on the server, but only on the client. They can be used in declarations and as arguments to actions.

Operators

Operators can be applied to expressions. The syntax puts the operator following a period after the expression in a post-fix notation like so

store.pick("$..book[0,1]")

For now, the only way to chain operators is to fully parenthesize the expression like so:

(store.pick("$..book[0,1]")).length()

instead of

store.pick("$..book[0,1]").length()

as

Coerce objects of one type to be an object of another. For example, the following constructs a string and then turns it into a regular expression that can be used by the replace operator.

("/q=" + q + "/i").as("regexp")


The following coercions are allowed:

str num regexp array hash
str * * *
num * *
regexp * *
array *
hash *

Unsupported coercions will produce a warning and return the element with it's type unchanged.

length

Return the length of an array.


pick

The pick operator is used to pick out portions of a JSON data structure. Pick can be applied to any valid expression that returns JSON. Complex expressions should be contained in parentheses.

The pick operator takes a single string as an argument that represents a JSONPath expression that will select some portion of the JSON in the target expression (before the period).

For example, suppose that book_data contains the following JSON:

{"responseHeader":{
   "status":0,
   "QTime":0,
   "params":{
      "q":"0316160202",
      "wt":"json"}},
 "response":{
   "numFound":1,
   "start":0,
   "docs":[
      {"isbn":"0316160202",
       "title":"Eclipse",
       "url":"http://library.minlib.net/search/i?SEARCH=0316160202"}]}}

The following table shows some sample JSON picks on this data and the results

Expression Result
book_data.pick("$..docs[0].url") http://library.minlib.net/search/i?SEARCH=0316160202
book_data.pick("$..docs[0].title") "Eclipse"
book_data.pick("$..numFound") 1

JSONPath always returns a "collection" or array. That is less than helpful in many circumstances inside KRL, so the pick operator will reduce single element arrays to just the singleton member.

Note: if you're trying to match data with a period (.) in the name, the period will be interpreted as a JSONPath operator unless you escape it with a backslash like so:

bar.pick("$..www\.kynetx\.com[0].text")

replace

The replace operator replaces the portion of a string matching a regular expression with another string.

str_expr.replace(/regexp/, "str")
  • The target (before the period) must be a string.
  • The first argument must be a regular expression. The i and g operators (for case insensativity and global replacement respectively) are supported.
  • The second argument is a string to use for replacement. The $n syntax for replacement of captured text from the regular expression is supported.

The following examples show how replace can be used. Suppose that my_str contains the string "This is a string" and my_url contains the string 'http://www.amazon.com/gp/products/123456789/':

my_str.replace(/is/,"ese") // returns 'These is a string'

my_str.replace(/is/g,"ese") // returns 'These ese a string'

my_str.replace(/this/,"do you want a") // returns 'This is a string' (no change)

my_str.replace(/this/i,"do you want a") // returns 'do you want a is a string'

my_str.replace(/Th(is)/,"Nothing $1") // returns 'Nothing is is a string'

my_url.replace(/http:\/\/([A-Za-z0-9.-]+)\/.*/,"$1") // returns 'www.amazon.com'

Note that this does not mutate the original string, but rather returns a new string that is built by making the substitution in the original.

Personal tools