From b7b129ce34e59eb0ff63c10477d3ce3e6ea31a13 Mon Sep 17 00:00:00 2001 From: darnjo Date: Sun, 3 Dec 2023 13:01:50 -0800 Subject: [PATCH 1/6] #110: Migrated RCP-019 Grammar and added regex support --- .gitignore | 2 + artifacts/grammars/RCP-019/rcp019.g4 | 82 ++++++ artifacts/grammars/RCP-019/rcp019Lexer.g4 | 106 ++++++++ artifacts/grammars/RCP-019/regex.g4 | 288 ++++++++++++++++++++++ artifacts/grammars/RCP-019/regexLexer.g4 | 90 +++++++ 5 files changed, 568 insertions(+) create mode 100644 artifacts/grammars/RCP-019/rcp019.g4 create mode 100644 artifacts/grammars/RCP-019/rcp019Lexer.g4 create mode 100644 artifacts/grammars/RCP-019/regex.g4 create mode 100644 artifacts/grammars/RCP-019/regexLexer.g4 diff --git a/.gitignore b/.gitignore index 722d5e7..1cabfcc 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ +.DS_Store .vscode +.antlr diff --git a/artifacts/grammars/RCP-019/rcp019.g4 b/artifacts/grammars/RCP-019/rcp019.g4 new file mode 100644 index 0000000..a0c1a44 --- /dev/null +++ b/artifacts/grammars/RCP-019/rcp019.g4 @@ -0,0 +1,82 @@ +/* + + RESO RCP-019 Validation Expression Grammar + + By downloading this resource, you agree to the RESO EULA: https://www.reso.org/eula/ Contact + dev@reso.org if you have questions + + Taken from: https://github.com/darnjo/rcp019/blob/master/LICENSE Copyright (c) 2018 Joshua Darnell + (josh@kurotek.com) + + NOTE: This file is written with the intent of preserving the structure of the original RETS 1.9 VE + grammar so that anyone who has implemented their systems using it shouldn't have to make any + changes unless they are wanting to support the new features. In other words, all changes made to + this grammar so far have been additive. There are more optimal representations of this file that + will accept the RESO RCP-019 grammar, and as long as your parser supports an equivalent set of + symbols and expressions, then the internals of your system are for you to decide. This example + grammar should already perform reasonably well in most situations, but the parse tree will be a bit + deep (and therefore not be as performant) due to the current structure. + + + */ + +grammar rcp019; + +options { + tokenVocab = rcp019Lexer; +} + +exp: orExp | collectionExp | funcExp; + +orExp: andExp (OR andExp)*; + +andExp: notExp (AND notExp)*; + +notExp: NOT notExp | eqExp; + +eqExp: cmpExp | cmpExp (NE | EQ) cmpExp; + +cmpExp: cntExp | cntExp (LTE | GTE | LT | GT) cntExp; + +cntExp: sumExp | sumExp (CONTAINS | IN) sumExp; + +sumExp: prodExp ((PLUS | MINUS | CONCAT) prodExp)*; + +prodExp: atomExp ((ASTERISK | SLASH | MOD) atomExp)*; + +// NOTE: original VE writing had that all lists of size 1 were atomExp, not list. LIST() and SET() +// were created as a top-level item called 'collection' and should be used instead. +atomExp: LPAREN exp RPAREN | list | value; + +// this was left in for backwards compatibility with the first production rule, LPAREN exp RPAREN +list: LPAREN (exp (COMMA exp)*)? RPAREN; + +// this was previously an atomExp, but was moved to the top-level for faster parsing (10x speedup) +funcExp: func LPAREN (param (COMMA param)*)? RPAREN; + +collectionExp: (LIST | SET) LPAREN (exp (COMMA exp)*)? RPAREN + | (UNION | INTERSECTION | DIFFERENCE) LPAREN ( + collectionExp COMMA collectionExp (COMMA collectionExp)* + )? RPAREN; + +// SPECFUNC was added. LOCALFUNC could be added as well, with corresponding known local functions +func: SPECFUNC | ALPHANUM; + +param: exp; + +value: + fieldName + | specValue + | charValue + | intValue + | floatValue + | timeValue; + +fieldName: (LAST)? FIELD_NAME + | LBRACKET (LAST)? FIELD_NAME RBRACKET; + +specValue: DOT FIELD_NAME DOT; +charValue: QUOTED_TERM; +timeValue: HASH ISO_TIMESTAMP HASH; +intValue: (PLUS | MINUS)? DIGIT+; +floatValue: intValue DOT DIGIT+; \ No newline at end of file diff --git a/artifacts/grammars/RCP-019/rcp019Lexer.g4 b/artifacts/grammars/RCP-019/rcp019Lexer.g4 new file mode 100644 index 0000000..0aa984e --- /dev/null +++ b/artifacts/grammars/RCP-019/rcp019Lexer.g4 @@ -0,0 +1,106 @@ +lexer grammar rcp019Lexer; + +CONCAT: PIPE; +LPAREN: '('; +RPAREN: ')'; +SQUOTE: '\''; +QUOTE: '"'; +DOT: '.'; +ASTERISK: '*'; +SLASH: '/'; +EXCLAMATION: '!'; +DOLLAR: '$'; +CARET: '^'; +BACKSLASH: '\\'; +SINGLE_SPACE: ' '; + +OR: '.OR.'; +AND: '.AND.'; +NOT: '.NOT.'; + +EQ: '='; +NE: EXCLAMATION EQ; +LT: '<'; +LTE: LT EQ; +GT: '>'; +GTE: GT EQ; + +CONTAINS: '.CONTAINS.'; +IN: '.IN.'; +COMMA: ','; +PLUS: '+'; +MINUS: '-'; +MOD: '.MOD.'; +PIPE: '|'; +LBRACKET: '['; +RBRACKET: ']'; +HASH: '#'; + +IIF: 'IIF'; +LAST: 'LAST'; +LIST: 'LIST'; +SET: 'SET'; +DIFFERENCE: 'DIFFERENCE'; +INTERSECTION: 'INTERSECTION'; +UNION: 'UNION'; +TRUE: 'TRUE'; +FALSE: 'FALSE'; +EMPTY: 'EMPTY'; +TODAY: 'TODAY'; +NOW: 'NOW'; +ENTRY: 'ENTRY'; +OLDVALUE: 'OLDVALUE'; +USERID: 'USERID'; +USERCLASS: 'USERCLASS'; +USERLEVEL: 'USERLEVEL'; +AGENTCODE: 'AGENTCODE'; +BROKERCODE: 'BROKERCODE'; +BROKERBRANCH: 'BROKERBRANCH'; +UPDATEACTION: 'UPDATEACTION'; +ANY: 'any'; + +// special tokens +RESO_SPECIAL_TOKENS: FIELD_NAME | SPECOP; + +// TODO: dynamically fill in your FIELD_NAMEs here +FIELD_NAME: + 'ListPrice' + | 'Status' + | 'CloseDate' + | 'Bedrooms' + | 'Bathrooms'; + +SPECFUNC: IIF; + +SPECOP: + EMPTY + | TRUE + | FALSE + | TODAY + | NOW + | ENTRY + | OLDVALUE + | USERID + | USERCLASS + | USERLEVEL + | AGENTCODE + | BROKERCODE + | BROKERBRANCH + | UPDATEACTION + | ANY; + +ISO_TIMESTAMP: '##TODO##'; +ISO_DATE: '#TODO#'; + +ALPHA: ('a' ..'z' | 'A' ..'Z'); +DIGIT: ('0' ..'9'); + +ALPHANUM: ALPHA (ALPHA | DIGIT)*; + +QUOTED_TERM: QUOTE (~[\\"])*? QUOTE | SQUOTE (~[\\'])*? SQUOTE; + +//added support for c++ style comments +SLASH_STAR_COMMENT: '/*' .+? '*/' -> skip; +SLASH_SLASH_COMMENT: '//' .+? ('\n' | EOF) -> skip; + +WS: [ \t\n\r]+ -> skip; \ No newline at end of file diff --git a/artifacts/grammars/RCP-019/regex.g4 b/artifacts/grammars/RCP-019/regex.g4 new file mode 100644 index 0000000..52df772 --- /dev/null +++ b/artifacts/grammars/RCP-019/regex.g4 @@ -0,0 +1,288 @@ +/* + * Perl-Compatible Regular Expression Grammer + * + * Based on http://www.pcre.org/pcre.txt (REVISION Last updated: 14 June 2021) + * Additional modifications by darnjo (josh@darnjo.com) + * + * Copyright (c) 2014-2023 by Bart Kiers + * + * The MIT license. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Project : PCRE Parser, an ANTLR 4 grammar for PCRE Developed by : Bart Kiers, bart@big-o.nl Also + * see : https://github.com/bkiers/pcre-parser + */ +grammar regex; + +options { + tokenVocab = regexLexer; +} + +pcre: alternation? EOF; + +alternation: expr ( '|' expr?)*; + +expr: element+; + +element: atom quantifier?; + +atom: + option_setting + | backtracking_control + | callout + | capture + | atomic_group + | lookaround + | backreference + | subroutine_reference + | conditional_pattern + | comment + | character + | character_type + | character_class + | posix_character_class + | letter + | DIGIT + | anchor + | match_point_reset + | quoting + | other; + +capture: + '(' ( + alternation + | '?' ( + '<' name '>' alternation + | '\'' name '\'' alternation + | 'P' '<' name '>' alternation + | (option_setting_flag+ ( '-' option_setting_flag+)?)? ':' alternation + | '|' alternation + ) + ) ')'; + +atomic_group: '(' '?' '>' alternation ')'; + +lookaround: + '(' '?' ('=' | '!' | '<' '=' | '<' '!') alternation ')'; + +backreference: + '\\' ( + 'g' digits + | 'g' '{' '-'? digits '}' + | 'g' '{' name '}' + | 'k' '<' name '>' + | 'k' '\'' name '\'' + | 'k' '{' name '}' + ) + | '(' '?' 'P' '=' name ')'; + +subroutine_reference: + '(' '?' ('R' | ( '+' | '-')? digits | '&' name | 'P' '>' name) ')' + | '\\' 'g' ( + '<' name '>' + | '\'' name '\'' + | '<' ( '+' | '-')? digits '>' + | '\'' ( '+' | '-')? digits '\'' + ); + +conditional_pattern: + '(' '?' ( + '(' ( + ( '+' | '-')? digits + | '<' name '>' + | '\'' name '\'' + | 'R' digits? + | 'R' '&' name + | name + ) ')' + | callout + | lookaround + ) expr ('|' no_pattern = expr)? ')'; + +comment: '(' '?' '#' ~')'+ ')'; + +quantifier: ('?' | '*' | '+') (possessive = '+' | lazy = '?')? + | '{' from = digits (',' to = digits?)? '}' ( + possessive = '+' + | lazy = '?' + )?; + +option_setting: + '(' ( + '*' ( + utf ( '8' | '1' '6' | '3' '2')? + | ucp + | no_auto_possess + | no_start_opt + | newline_conventions + | limit_match '=' digits + | limit_recursion '=' digits + | bsr_anycrlf + | bsr_unicode + ) + | '?' ( + option_setting_flag+ ('-' option_setting_flag+)? + | '-' option_setting_flag+ + ) + ) ')'; + +option_setting_flag: 'i' | 'J' | 'm' | 's' | 'U' | 'x'; + +backtracking_control: + '(' '*' ( + accept + | fail + | mark? ':' name + | commit + | prune ( ':' name)? + | skip ( ':' name)? + | then ( ':' name)? + ) ')'; + +callout: '(' '?' 'C' digits? ')'; + +newline_conventions: cr | lf | crlf | anycrlf | any; + +character: + '\\' ( + 'a' + | 'c' . + | 'e' + | 'f' + | 'n' + | 'r' + | 't' + | DIGIT (DIGIT DIGIT?)? // can also be a backreference + | 'o' '{' DIGIT DIGIT DIGIT+ '}' + | 'x' hex hex + | 'x' '{' hex hex hex+ '}' + | 'u' hex hex hex hex ( hex hex hex hex)? + ); + +character_type: + '.' + | '\\' ( + 'C' + | 'd' + | 'D' + | 'h' + | 'H' + | 'N' + | 'p' '{' '^'? name '&'? '}' + | 'P' '{' name '&'? '}' + | 'p' letter letter? + | 'R' + | 's' + | 'S' + | 'v' + | 'V' + | 'w' + | 'W' + | 'X' + ); + +character_class: + '[' negate = '^'? ']' character_class_atom* ']' + | '[' negate = '^'? character_class_atom+ ']'; + +character_class_atom: + character_class_range + | posix_character_class + | character + | character_type + | '\\' . + | ~( '\\' | ']'); + +character_class_range: + character_class_range_atom '-' character_class_range_atom; + +character_class_range_atom: character | ~']'; + +posix_character_class: '[:' negate = '^'? letters ':]'; + +anchor: '\\' ( 'b' | 'B' | 'A' | 'z' | 'Z' | 'G') | '^' | '$'; + +match_point_reset: '\\' 'K'; + +quoting: '\\' . | '\\' 'Q' .*? '\\' 'E'; + +// Helper rules +digits: DIGIT+; + +hex: + DIGIT + | 'a' + | 'b' + | 'c' + | 'd' + | 'e' + | 'f' + | 'A' + | 'B' + | 'C' + | 'D' + | 'E' + | 'F'; +letters: letter+; + +letter: ALPHA | '_'; + +name: letter (letter | DIGIT)*; + +other: + '}' + | ']' + | ',' + | '-' + | '_' + | '=' + | '&' + | '<' + | '>' + | '\'' + | ':' + | '#' + | '!' + | OTHER; + +utf: 'U' 'T' 'F'; +ucp: 'U' 'C' 'P'; +no_auto_possess: + 'N' 'O' '_' 'A' 'U' 'T' 'O' '_' 'P' 'O' 'S' 'S' 'E' 'S' 'S'; +no_start_opt: 'N' 'O' '_' 'S' 'T' 'A' 'R' 'T' '_' 'O' 'P' 'T'; +cr: 'C' 'R'; +lf: 'L' 'F'; +crlf: 'C' 'R' 'L' 'F'; +anycrlf: 'A' 'N' 'Y' 'C' 'R' 'L' 'F'; +any: 'A' 'N' 'Y'; +limit_match: 'L' 'I' 'M' 'I' 'T' '_' 'M' 'A' 'T' 'C' 'H'; +limit_recursion: + 'L' 'I' 'M' 'I' 'T' '_' 'R' 'E' 'C' 'U' 'R' 'S' 'I' 'O' 'N'; +bsr_anycrlf: 'B' 'S' 'R' '_' 'A' 'N' 'Y' 'C' 'R' 'L' 'F'; +bsr_unicode: 'B' 'S' 'R' '_' 'U' 'N' 'I' 'C' 'O' 'D' 'E'; +accept: 'A' 'C' 'C' 'E' 'P' 'T'; +fail: 'F' ( 'A' 'I' 'L')?; +mark: 'M' 'A' 'R' 'K'; +commit: 'C' 'O' 'M' 'M' 'I' 'T'; +prune: 'P' 'R' 'U' 'N' 'E'; +skip: 'S' 'K' 'I' 'P'; +then: 'T' 'H' 'E' 'N'; + +OTHER: .; + +ALPHA: ('a' ..'z' | 'A' ..'Z'); +DIGIT: ('0' ..'9'); \ No newline at end of file diff --git a/artifacts/grammars/RCP-019/regexLexer.g4 b/artifacts/grammars/RCP-019/regexLexer.g4 new file mode 100644 index 0000000..8bb9e77 --- /dev/null +++ b/artifacts/grammars/RCP-019/regexLexer.g4 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2014-2023 by Bart Kiers + * + * The MIT license. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Project : PCRE Parser, an ANTLR 4 grammar for PCRE + * Developed by : Bart Kiers, bart@big-o.nl + * Also see : https://github.com/bkiers/pcre-parser + * + * Based on http://www.pcre.org/pcre.txt + * (REVISION Last updated: 14 June 2021) + */ +lexer grammar regexLexer; + +/// \ general escape character with several uses +BSlash : '\\'; + +/// $ assert end of string (or line, in multiline mode) +Dollar : '$'; + +/// . match any character except newline (by default) +Dot : '.'; + +/// [ start character class definition +OBrack : '['; + +/// ^ assert start of string (or line, in multiline mode) +Caret : '^'; + +/// | start of alternative branch +Pipe : '|'; + +/// ? extends the meaning of (, also 0 or 1 quantifier.txt, also quantifier.txt minimizer +QMark : '?'; + +/// * 0 or more quantifier.txt +Star : '*'; + +/// + 1 or more quantifier.txt, also "possessive quantifier.txt" +Plus : '+'; + +/// { start min/max quantifier.txt +OBrace : '{'; + +CBrace : '}'; + +/// ( start subpattern +OPar : '('; + +/// ) end subpattern +CPar : ')'; + +/// ] terminates the character class +CBrack : ']'; + +OPosixBrack : '[:'; +CPosixBrack : ':]'; + +Comma : ','; +Dash : '-'; +UScore : '_'; +Eq : '='; +Amp : '&'; +Lt : '<'; +Gt : '>'; +Quote : '\''; +Col : ':'; +Hash : '#'; +Excl : '!'; From 6ff77ce329186eec21f14f9dfbb2f9bd7f5c67c0 Mon Sep 17 00:00:00 2001 From: darnjo Date: Mon, 4 Dec 2023 10:18:30 -0800 Subject: [PATCH 2/6] #110: Updated grammar and rewrote / streamlined proposal --- artifacts/grammars/RCP-019/rcp019.g4 | 17 +- artifacts/grammars/RCP-019/rcp019Lexer.g4 | 67 +++-- artifacts/grammars/RCP-019/regex.g4 | 288 --------------------- artifacts/grammars/RCP-019/regexLexer.g4 | 90 ------- web-api-validation-expression.md | 293 ++++++++++++++++++---- 5 files changed, 292 insertions(+), 463 deletions(-) delete mode 100644 artifacts/grammars/RCP-019/regex.g4 delete mode 100644 artifacts/grammars/RCP-019/regexLexer.g4 diff --git a/artifacts/grammars/RCP-019/rcp019.g4 b/artifacts/grammars/RCP-019/rcp019.g4 index a0c1a44..fec1e57 100644 --- a/artifacts/grammars/RCP-019/rcp019.g4 +++ b/artifacts/grammars/RCP-019/rcp019.g4 @@ -1,5 +1,4 @@ /* - RESO RCP-019 Validation Expression Grammar By downloading this resource, you agree to the RESO EULA: https://www.reso.org/eula/ Contact @@ -16,12 +15,12 @@ symbols and expressions, then the internals of your system are for you to decide. This example grammar should already perform reasonably well in most situations, but the parse tree will be a bit deep (and therefore not be as performant) due to the current structure. - - */ grammar rcp019; +// TODO: import https://github.com/antlr/grammars-v4/blob/master/pcre/PCRE.g4 + options { tokenVocab = rcp019Lexer; } @@ -46,10 +45,10 @@ prodExp: atomExp ((ASTERISK | SLASH | MOD) atomExp)*; // NOTE: original VE writing had that all lists of size 1 were atomExp, not list. LIST() and SET() // were created as a top-level item called 'collection' and should be used instead. -atomExp: LPAREN exp RPAREN | list | value; +atomExp: LPAREN exp RPAREN | listExp | value; // this was left in for backwards compatibility with the first production rule, LPAREN exp RPAREN -list: LPAREN (exp (COMMA exp)*)? RPAREN; +listExp: LPAREN (exp (COMMA exp)*)? RPAREN; // this was previously an atomExp, but was moved to the top-level for faster parsing (10x speedup) funcExp: func LPAREN (param (COMMA param)*)? RPAREN; @@ -59,9 +58,6 @@ collectionExp: (LIST | SET) LPAREN (exp (COMMA exp)*)? RPAREN collectionExp COMMA collectionExp (COMMA collectionExp)* )? RPAREN; -// SPECFUNC was added. LOCALFUNC could be added as well, with corresponding known local functions -func: SPECFUNC | ALPHANUM; - param: exp; value: @@ -79,4 +75,7 @@ specValue: DOT FIELD_NAME DOT; charValue: QUOTED_TERM; timeValue: HASH ISO_TIMESTAMP HASH; intValue: (PLUS | MINUS)? DIGIT+; -floatValue: intValue DOT DIGIT+; \ No newline at end of file +floatValue: intValue DOT DIGIT+; + +// SPECFUNC was added. LOCALFUNC could be added as well, with corresponding known local functions +func: SPECFUNC | ALPHANUM; \ No newline at end of file diff --git a/artifacts/grammars/RCP-019/rcp019Lexer.g4 b/artifacts/grammars/RCP-019/rcp019Lexer.g4 index 0aa984e..ae9aed4 100644 --- a/artifacts/grammars/RCP-019/rcp019Lexer.g4 +++ b/artifacts/grammars/RCP-019/rcp019Lexer.g4 @@ -1,5 +1,9 @@ lexer grammar rcp019Lexer; +options { + tokenVocab = rcp019MetadataLexer; +} + CONCAT: PIPE; LPAREN: '('; RPAREN: ')'; @@ -13,6 +17,11 @@ DOLLAR: '$'; CARET: '^'; BACKSLASH: '\\'; SINGLE_SPACE: ' '; +COLON: ':'; +PIPE: '|'; +LBRACKET: '['; +RBRACKET: ']'; +HASH: '#'; OR: '.OR.'; AND: '.AND.'; @@ -31,12 +40,9 @@ COMMA: ','; PLUS: '+'; MINUS: '-'; MOD: '.MOD.'; -PIPE: '|'; -LBRACKET: '['; -RBRACKET: ']'; -HASH: '#'; IIF: 'IIF'; +MATCH: 'MATCH'; LAST: 'LAST'; LIST: 'LIST'; SET: 'SET'; @@ -46,23 +52,29 @@ UNION: 'UNION'; TRUE: 'TRUE'; FALSE: 'FALSE'; EMPTY: 'EMPTY'; +NULL: 'NULL'; TODAY: 'TODAY'; NOW: 'NOW'; ENTRY: 'ENTRY'; OLDVALUE: 'OLDVALUE'; -USERID: 'USERID'; -USERCLASS: 'USERCLASS'; -USERLEVEL: 'USERLEVEL'; -AGENTCODE: 'AGENTCODE'; -BROKERCODE: 'BROKERCODE'; -BROKERBRANCH: 'BROKERBRANCH'; UPDATEACTION: 'UPDATEACTION'; ANY: 'any'; +// See: Data Dictionary Member and Office Resources +MEMBER_LOGIN_ID: 'MEMBER_LOGIN_ID'; +MEMBER_MLS_SECURITY_CLASS: 'MEMBER_MLS_SECURITY_CLASS'; +MEMBER_TYPE: 'MEMBER_TYPE'; +MEMBER_MLS_ID: 'MEMBER_MLS_ID'; +OFFICE_BROKER_MLS_ID: 'OFFICE_BROKER_MLS_ID'; +OFFICE_MLS_ID: 'OFFICE_MLS_ID'; + +ALPHA: ('a' ..'z' | 'A' ..'Z'); +DIGIT: ('0' ..'9'); + // special tokens RESO_SPECIAL_TOKENS: FIELD_NAME | SPECOP; -// TODO: dynamically fill in your FIELD_NAMEs here +// TODO: Dynamically fill in your FIELD_NAMEs here FIELD_NAME: 'ListPrice' | 'Status' @@ -70,7 +82,7 @@ FIELD_NAME: | 'Bedrooms' | 'Bathrooms'; -SPECFUNC: IIF; +SPECFUNC: IIF | MATCH; SPECOP: EMPTY @@ -80,25 +92,32 @@ SPECOP: | NOW | ENTRY | OLDVALUE - | USERID - | USERCLASS - | USERLEVEL - | AGENTCODE - | BROKERCODE - | BROKERBRANCH + | MEMBER_LOGIN_ID + | MEMBER_MLS_SECURITY_CLASS + | MEMBER_TYPE + | MEMBER_MLS_ID + | OFFICE_BROKER_MLS_ID + | OFFICE_MLS_ID | UPDATEACTION | ANY; -ISO_TIMESTAMP: '##TODO##'; -ISO_DATE: '#TODO#'; - -ALPHA: ('a' ..'z' | 'A' ..'Z'); -DIGIT: ('0' ..'9'); - ALPHANUM: ALPHA (ALPHA | DIGIT)*; QUOTED_TERM: QUOTE (~[\\"])*? QUOTE | SQUOTE (~[\\'])*? SQUOTE; +// #2023-12-04T06:12:24.00Z# #2023-12-04T06:12:24.00-7:00# #2023-12-04T06:12:24.00+7:00# +ISO_TIMESTAMP: + HASH ISO_DATE 'T' [0-23] COLON [0-59] COLON [0-59] DOT ( + DIGIT + )* ('Z' | (PLUS | MINUS) [0-12] COLON [0-59]) HASH; + +// #2023-12-04# +ISO_DATE: HASH YEAR '-' MONTH '-' DAY HASH; + +YEAR: DIGIT DIGIT DIGIT DIGIT; +MONTH: [0-12]; +DAY: [0-3] DIGIT; + //added support for c++ style comments SLASH_STAR_COMMENT: '/*' .+? '*/' -> skip; SLASH_SLASH_COMMENT: '//' .+? ('\n' | EOF) -> skip; diff --git a/artifacts/grammars/RCP-019/regex.g4 b/artifacts/grammars/RCP-019/regex.g4 deleted file mode 100644 index 52df772..0000000 --- a/artifacts/grammars/RCP-019/regex.g4 +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Perl-Compatible Regular Expression Grammer - * - * Based on http://www.pcre.org/pcre.txt (REVISION Last updated: 14 June 2021) - * Additional modifications by darnjo (josh@darnjo.com) - * - * Copyright (c) 2014-2023 by Bart Kiers - * - * The MIT license. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, publish, distribute, - * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT - * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * Project : PCRE Parser, an ANTLR 4 grammar for PCRE Developed by : Bart Kiers, bart@big-o.nl Also - * see : https://github.com/bkiers/pcre-parser - */ -grammar regex; - -options { - tokenVocab = regexLexer; -} - -pcre: alternation? EOF; - -alternation: expr ( '|' expr?)*; - -expr: element+; - -element: atom quantifier?; - -atom: - option_setting - | backtracking_control - | callout - | capture - | atomic_group - | lookaround - | backreference - | subroutine_reference - | conditional_pattern - | comment - | character - | character_type - | character_class - | posix_character_class - | letter - | DIGIT - | anchor - | match_point_reset - | quoting - | other; - -capture: - '(' ( - alternation - | '?' ( - '<' name '>' alternation - | '\'' name '\'' alternation - | 'P' '<' name '>' alternation - | (option_setting_flag+ ( '-' option_setting_flag+)?)? ':' alternation - | '|' alternation - ) - ) ')'; - -atomic_group: '(' '?' '>' alternation ')'; - -lookaround: - '(' '?' ('=' | '!' | '<' '=' | '<' '!') alternation ')'; - -backreference: - '\\' ( - 'g' digits - | 'g' '{' '-'? digits '}' - | 'g' '{' name '}' - | 'k' '<' name '>' - | 'k' '\'' name '\'' - | 'k' '{' name '}' - ) - | '(' '?' 'P' '=' name ')'; - -subroutine_reference: - '(' '?' ('R' | ( '+' | '-')? digits | '&' name | 'P' '>' name) ')' - | '\\' 'g' ( - '<' name '>' - | '\'' name '\'' - | '<' ( '+' | '-')? digits '>' - | '\'' ( '+' | '-')? digits '\'' - ); - -conditional_pattern: - '(' '?' ( - '(' ( - ( '+' | '-')? digits - | '<' name '>' - | '\'' name '\'' - | 'R' digits? - | 'R' '&' name - | name - ) ')' - | callout - | lookaround - ) expr ('|' no_pattern = expr)? ')'; - -comment: '(' '?' '#' ~')'+ ')'; - -quantifier: ('?' | '*' | '+') (possessive = '+' | lazy = '?')? - | '{' from = digits (',' to = digits?)? '}' ( - possessive = '+' - | lazy = '?' - )?; - -option_setting: - '(' ( - '*' ( - utf ( '8' | '1' '6' | '3' '2')? - | ucp - | no_auto_possess - | no_start_opt - | newline_conventions - | limit_match '=' digits - | limit_recursion '=' digits - | bsr_anycrlf - | bsr_unicode - ) - | '?' ( - option_setting_flag+ ('-' option_setting_flag+)? - | '-' option_setting_flag+ - ) - ) ')'; - -option_setting_flag: 'i' | 'J' | 'm' | 's' | 'U' | 'x'; - -backtracking_control: - '(' '*' ( - accept - | fail - | mark? ':' name - | commit - | prune ( ':' name)? - | skip ( ':' name)? - | then ( ':' name)? - ) ')'; - -callout: '(' '?' 'C' digits? ')'; - -newline_conventions: cr | lf | crlf | anycrlf | any; - -character: - '\\' ( - 'a' - | 'c' . - | 'e' - | 'f' - | 'n' - | 'r' - | 't' - | DIGIT (DIGIT DIGIT?)? // can also be a backreference - | 'o' '{' DIGIT DIGIT DIGIT+ '}' - | 'x' hex hex - | 'x' '{' hex hex hex+ '}' - | 'u' hex hex hex hex ( hex hex hex hex)? - ); - -character_type: - '.' - | '\\' ( - 'C' - | 'd' - | 'D' - | 'h' - | 'H' - | 'N' - | 'p' '{' '^'? name '&'? '}' - | 'P' '{' name '&'? '}' - | 'p' letter letter? - | 'R' - | 's' - | 'S' - | 'v' - | 'V' - | 'w' - | 'W' - | 'X' - ); - -character_class: - '[' negate = '^'? ']' character_class_atom* ']' - | '[' negate = '^'? character_class_atom+ ']'; - -character_class_atom: - character_class_range - | posix_character_class - | character - | character_type - | '\\' . - | ~( '\\' | ']'); - -character_class_range: - character_class_range_atom '-' character_class_range_atom; - -character_class_range_atom: character | ~']'; - -posix_character_class: '[:' negate = '^'? letters ':]'; - -anchor: '\\' ( 'b' | 'B' | 'A' | 'z' | 'Z' | 'G') | '^' | '$'; - -match_point_reset: '\\' 'K'; - -quoting: '\\' . | '\\' 'Q' .*? '\\' 'E'; - -// Helper rules -digits: DIGIT+; - -hex: - DIGIT - | 'a' - | 'b' - | 'c' - | 'd' - | 'e' - | 'f' - | 'A' - | 'B' - | 'C' - | 'D' - | 'E' - | 'F'; -letters: letter+; - -letter: ALPHA | '_'; - -name: letter (letter | DIGIT)*; - -other: - '}' - | ']' - | ',' - | '-' - | '_' - | '=' - | '&' - | '<' - | '>' - | '\'' - | ':' - | '#' - | '!' - | OTHER; - -utf: 'U' 'T' 'F'; -ucp: 'U' 'C' 'P'; -no_auto_possess: - 'N' 'O' '_' 'A' 'U' 'T' 'O' '_' 'P' 'O' 'S' 'S' 'E' 'S' 'S'; -no_start_opt: 'N' 'O' '_' 'S' 'T' 'A' 'R' 'T' '_' 'O' 'P' 'T'; -cr: 'C' 'R'; -lf: 'L' 'F'; -crlf: 'C' 'R' 'L' 'F'; -anycrlf: 'A' 'N' 'Y' 'C' 'R' 'L' 'F'; -any: 'A' 'N' 'Y'; -limit_match: 'L' 'I' 'M' 'I' 'T' '_' 'M' 'A' 'T' 'C' 'H'; -limit_recursion: - 'L' 'I' 'M' 'I' 'T' '_' 'R' 'E' 'C' 'U' 'R' 'S' 'I' 'O' 'N'; -bsr_anycrlf: 'B' 'S' 'R' '_' 'A' 'N' 'Y' 'C' 'R' 'L' 'F'; -bsr_unicode: 'B' 'S' 'R' '_' 'U' 'N' 'I' 'C' 'O' 'D' 'E'; -accept: 'A' 'C' 'C' 'E' 'P' 'T'; -fail: 'F' ( 'A' 'I' 'L')?; -mark: 'M' 'A' 'R' 'K'; -commit: 'C' 'O' 'M' 'M' 'I' 'T'; -prune: 'P' 'R' 'U' 'N' 'E'; -skip: 'S' 'K' 'I' 'P'; -then: 'T' 'H' 'E' 'N'; - -OTHER: .; - -ALPHA: ('a' ..'z' | 'A' ..'Z'); -DIGIT: ('0' ..'9'); \ No newline at end of file diff --git a/artifacts/grammars/RCP-019/regexLexer.g4 b/artifacts/grammars/RCP-019/regexLexer.g4 deleted file mode 100644 index 8bb9e77..0000000 --- a/artifacts/grammars/RCP-019/regexLexer.g4 +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2014-2023 by Bart Kiers - * - * The MIT license. - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * Project : PCRE Parser, an ANTLR 4 grammar for PCRE - * Developed by : Bart Kiers, bart@big-o.nl - * Also see : https://github.com/bkiers/pcre-parser - * - * Based on http://www.pcre.org/pcre.txt - * (REVISION Last updated: 14 June 2021) - */ -lexer grammar regexLexer; - -/// \ general escape character with several uses -BSlash : '\\'; - -/// $ assert end of string (or line, in multiline mode) -Dollar : '$'; - -/// . match any character except newline (by default) -Dot : '.'; - -/// [ start character class definition -OBrack : '['; - -/// ^ assert start of string (or line, in multiline mode) -Caret : '^'; - -/// | start of alternative branch -Pipe : '|'; - -/// ? extends the meaning of (, also 0 or 1 quantifier.txt, also quantifier.txt minimizer -QMark : '?'; - -/// * 0 or more quantifier.txt -Star : '*'; - -/// + 1 or more quantifier.txt, also "possessive quantifier.txt" -Plus : '+'; - -/// { start min/max quantifier.txt -OBrace : '{'; - -CBrace : '}'; - -/// ( start subpattern -OPar : '('; - -/// ) end subpattern -CPar : ')'; - -/// ] terminates the character class -CBrack : ']'; - -OPosixBrack : '[:'; -CPosixBrack : ':]'; - -Comma : ','; -Dash : '-'; -UScore : '_'; -Eq : '='; -Amp : '&'; -Lt : '<'; -Gt : '>'; -Quote : '\''; -Col : ':'; -Hash : '#'; -Excl : '!'; diff --git a/web-api-validation-expression.md b/web-api-validation-expression.md index febb2a5..e9ff9a5 100644 --- a/web-api-validation-expression.md +++ b/web-api-validation-expression.md @@ -1,98 +1,287 @@ # RESO Validation Expressions -| **RCP** | 19 | +| **RCP** | 019 | | :--- | :--- | -| **Version** | 19.1 | -| **Authors** | [Joshua Darnell](https://github.com/darnjo) ([RESO](mailto:josh@reso.org))
Paul Stusiak ([Falcon Technologies](mailto:pstusiak@falcontechnologies.com)) | +| **Version** | **1.0.0** | +| **Authors** | [Joshua Darnell](mailto:josh@kurotek.com)
[Paul Stusiak](mailto:pstusiak@falcontechnologies.com) | | **Specification** | [**LINK TO RCP**](./web-api-validation-expression.md) | | **Status** | **DRAFT** | -| **Date Approved** | April 2018 | -| **Dependencies** | [Validation Expression grammar](https://github.com/darnjo/rcp019)
[Data Dictionary 1.7+](./data-dictionary.md)
[Web API 2.0.0+](./web-api-core.md)
| -| **Related Links** | [DD Wiki 1.7](https://ddwiki.reso.org/display/DDW17/RESO+Data+Dictionary+1.7)
[Data Dictionary Spreadsheet](https://docs.google.com/spreadsheets/d/1_59Iqr7AQ51rEFa7p0ND-YhJjEru8gY-D_HM1yy5c6w/edit?usp=sharing)
| +| **Date Ratified** | April 2018 | +| **Dependencies** | [Data Dictionary 1.7+](./data-dictionary.md)
[Validation Expression Grammar](./artifacts/grammars/RCP-019/rcp019.g4) | +| **Related Links** | [DD Wiki 1.7](https://ddwiki.reso.org/display/DDW17/RESO+Data+Dictionary+1.7)
[Data Dictionary Spreadsheet](https://docs.google.com/spreadsheets/d/1_59Iqr7AQ51rEFa7p0ND-YhJjEru8gY-D_HM1yy5c6w/edit?usp=sharing) | -RESO Validation Expressions build on the RETS Validation Expression grammar to provide a familiar, intermediate representation of computable business rules. The RCP-019 proposal consists of a Rules resource for transport and a grammar. +

-[**RCP-019 Proposal in PDF format**](https://github.com/RESOStandards/reso-transport-specifications/files/8384860/RESOWebAPIRCP-RCP-WEBAPI-019ValidationExpressionintheWebAPI-300322-2353.pdf) +# RESO End User License Agreement (EULA) +This End User License Agreement (the "EULA") is entered into by and between the Real Estate Standards Organization ("RESO") and the person or entity ("End User") that is downloading or otherwise obtaining the product associated with this EULA ("RESO Product"). This EULA governs End Users use of the RESO Product and End User agrees to the terms of this EULA by downloading or otherwise obtaining or using the RESO Product. + +
+ +# Table of Contents +- [Introduction](#introduction) +- [Section 1: Purpose](#section-1-purpose) +- [Section 2: Specification](#section-2-specification) +- [Section 3: Certification](#section-3-certification) +- [Section 4: Contributors](#section-4-contributors) +- [Section 5: References](#section-5-references) +- [Section 6: Appendices](#section-6-appendices) +- [Section 7: License](#section-7-license) + +
+ +# Introduction +Business rules are an important, but challenging, topic and come in many forms. There are validation rules (like requiring list price being greater than 0) and process rules (such as requiring a contract date when a listing is sold). There are also display rules, such as additional elements being shown and/or required based on the current state of a record. + +These rules come into play whenever a record is created, updated, or deleted. Display rules can also apply to readonly access to that data in an API or UI. + +The goal of this specification is to provide a machine-executable business rules grammar, as well as a transport mechanism to convey rules. Another goal is that the rules themselves be human friendly. + +Executable business rules also provide a way to document a system's rules in a way that's both vendor and technology agnostic. + +
+ +# Section 1: Purpose +The ability to express and exchange machine-executable business rules has several uses. For example, providing immediate feedback to users when adding a record to a system from their own frontend of choice, without calling back to a vendor system for validation. They can also be used in back-office systems so they can push records back to an MLS or other system for publication (disconnected edit). + +The RESO Web API is built on OData, which does not provide machine-executable rules at this time. However, this is something RETS previously offered. + +After much discussion and several onsite meetings during 2016-2018, the community felt the best direction was to carry the work done in RETS forward to the RESO Web API. It had a few issues that needed to be addressed, as well as a need for a transport mechanism, but the grammar presented in this specification should mostly have backwards compatibility with what had come before for those who might have implemented it. + +While there is still more work to do, such as the addition of a scope resolution operator to support rules for expanded data elements and new functions and special operations, it will be addressed in a future version. + +
+ +# Section 2: Specification +This specification consists of the following: +* [Rules grammar](#section-212-validation-expression-bnf) expressed in [BNF](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form) +* [Rules Resource](https://ddwiki.reso.org/display/DDW17/Rules+Resource) to provide transport of business rules -# Rules Resource -The [Rules Resource](https://ddwiki.reso.org/display/DDW17/Rules+Resource) has been added to Data Dictionary 1.7+ to provide a transport mechanism. +## Section 2.1: Validation Expression Language -# Rules Grammar -The RESO Validation Expression grammar assumes that rules are ordered sequentially, and will be process in order when dependencies require it. Not all rules are dependent on each other. Both independent and dependent rules can be parallelized, but care must be taken in the latter case to avoid data hazards (out of order operations). +The [RESO Validation Expression Grammar](./artifacts/grammars/RCP-019/rcp019.g4) is used in conjunction with a data structure of `FieldName`, `RuleAction`, `RuleExpression` tuples to provide an ordered list of instructions that can be used to convey business rules in a system independent way. -The Validation Expression grammar is not Turing complete by default. It requires the use of custom functions to iterate or recurse over a potentially unbounded collection of items. In other words, rule sets are always guaranteed to terminate. This is a good thing. +These rules are intended to be written from the perspective of the business object being upserted, such as a listing record in the Property Resource, and would be run as-needed before the client sends the record back to the system using the [RESO Add/Edit](./web-api-add-edit.md) specification. -Some examples from the current [RCP-019 grammar](https://github.com/darnjo/rcp019), which uses the [ANTLR4 Format](https://www.antlr.org/): +For a client running the rules locally, making a change to the StandardStatus field would trigger rules relevant for that field (along with any related rules). -### Basic Examples +Expressions a given upsert process MUST be evaluated in the order that they appear in the [RuleOrder](https://ddwiki.reso.org/display/DDW17/RuleOrder+Field) field. -### Arithmetic Operators +While all rules may be executed each time data changes, clients SHOULD try and optimize execution so that only rules related to a given change are fired rather than all of them. There are a number of techniques that can be used to accomplish this, such as [instruction scheduling](https://en.wikipedia.org/wiki/Instruction_scheduling) or the [Rete algorithm](https://en.wikipedia.org/wiki/Rete_algorithm). + +Rule optimization is beyond the scope of this document, but it's helpful that there are existing techniques to address it. + +### Section 2.1.1: Actions + +The following actions are defined by this specification: + +| Keyword | Type | Purpose | +| ------- | ------- | ------- | +| **ACCEPT** | `BOOLEAN` | If the expression is true, the field value is considered accepted without further testing. Immediately following SET expressions MUST be executed. If the expression is false, following validation expressions MUST be executed. If the expression is ERROR (evaluation failed) in client, the client SHOULD act as if the field was accepted, allowing the server to make the final decision. | +| **REJECT** | `BOOLEAN` | If the expression is true, the field value is considered rejected without further testing. Subsequent SET expressions MUST NOT be evaluated. If the expression is false, following validation expressions MUST be executed. If the expression is ERROR, evaluation failed in client, the client SHOULD act as if the field was accepted, allowing the server to make the final decision. | +| **WARNING** | `BOOLEAN` | If the expression is true, the client should show a warning message to the user, and if the warning is okayed by the user, include a Warning-Response in the UPDATE request. If the user does not okay the warning, the field is considered rejected and following SET validation expressions MUST NOT be evaluated.

If the expression is false, the following validation expressions MUST be evaluated. | +| **SET** | `TYPEOF(Exp)` | The following expression is evaluated and the result stored in the designated field. | +| **SET_DEFAULT** | `TYPEOF(Exp)` | This expression MUST be executed ONLY when a NEW record is created. Supersedes the default value as indicated in the Update Metadata. | +| **SET_REQUIRED** | `BOOLEAN` | Expressions of this type are designed to evaluate an expression and set the field that the rule is applied on to Required if the expression returns true and to Non Required if the expression returns false. | +| **SET_READ_ONLY** | `BOOLEAN` | Expressions of this type are designed to evaluate an expression and set the field that the rule is being applied on to Read Only if the expression returns true and to updatable if the expression returns false.

The client application is expected to lock the value of the field the rule is being executed on to the value at the time the SET_REQUIRED expression is evaluated. | +| **RESTRICT_PICKLIST** | `LIST(CHAR)` | Expressions of this type are designed to return one or more lookup values that are to be removed from the lookup list that is defined for the field the rule is being executed on. This is always the entire set of values to remove from the lookup.

In other words, if this returns a blank list or `.EMPTY.`, the entire set of lookup values is to be displayed.

The value of this expression MUST be a `LIST`, rather than `Exp`.

All members of the list MUST be of the same data type as that of the field the rule is being executed on. | +| **SET_PICKLIST** | `LIST(CHAR)` | Expressions of this type are designed to return one or more lookup values that are to be used in the lookup list defined for the field the rule is being executed on.

The value of this expression MUST be a `LIST`, rather than `Exp`.

Every member of the list MUST exist in the lookup list as defined in the metadata for the field the rule is being executed on. | +| **SET_DISPLAY** | `BOOLEAN` | Expressions of this type are designed to allow a client to make fields visible or invisible based on the evaluation of an expression. The result of this expression has no effect on whether a field is READ ONLY or not. | + +### Section 2.1.2: Validation Expressions + +There are two files that define the Validation Expressions grammar: +* [ANTLR 4 grammar](./artifacts/grammars/RCP-019/rcp019.g4) - Contains [non-terminal symbols](https://en.wikipedia.org/wiki/Terminal_and_nonterminal_symbols#Nonterminal_symbols) +* [ANTLR 4 lexer](./artifacts/grammars//RCP-019/rcp019Lexer.g4) - Contains [terminal symbols](https://en.wikipedia.org/wiki/Terminal_and_nonterminal_symbols#Terminal_symbols) + +#### Basic Examples + +##### Arithmetic Operators `3 + 5` `1 * 3 + 2 - 5` -### Bracketed RETSNAMEs -`[ListPrice] > 0` +##### FIELD_NAME +To get current value of any field, use the Data Dictionary name: `ListPrice` -### RCP61 added support for unbracketed RETSNAMEs. This also works for DICTNAMEs. -`ListPrice > 0` +##### Bracketed FIELD_NAMEs +Supported for backwards compatibility with RETS: +`[ListPrice]` -### To get current value of any field, just use the `DICTNAME` of that field -`ListPrice` +##### Simple Comparisons +`ListPrice > 0` -### Access the last value of a field +##### Access the Last Value of a Field +`LAST Status != 'Sold'` -`LAST Status != "Sold"` +##### Field Change Detection +Returns true if ListPrice has changed. -### Field change detection `ListPrice != LAST ListPrice` -### Function calls are also supported +##### Function Calls `foo()` -`foo ('bar', 'baz', 42)` +`foo('bar', 'baz', 42)` -_Cannot use any RETSNAMEs, DICTNAMEs, or reserved words_ +_Functions MUST NOT use any FIELD_NAMEs or reserved words._ -### Lists of expressions +##### Lists of Expressions `(ListPrice, Status, 3+5, LAST Status, (1, 2, 3))` -### Supports complex expressions with grouping -`ListPrice > 5.01 .AND. (1, 2, 3) .CONTAINS. 3 .OR. (Status .IN. ('Active', 'Pending') .AND. .USERLEVEL. != "Admin")` +##### Complex Expressions with Grouping +`ListPrice > 5.01 .AND. (1, 2, 3) .CONTAINS. 3 .OR. (Status .IN. ('Active', 'Pending') .AND. .MEMBER_MLS_SECURITY_CLASS. != 'Admin')` -### Which parses differently than -`ListPrice > 5.01 .AND. (1, 2, 3) .CONTAINS. 3 .OR. Status .IN. ('Active', 'Pending') .AND. .USERLEVEL. != "Admin"` +##### Which parses differently than +`ListPrice > 5.01 .AND. (1, 2, 3) .CONTAINS. 3 .OR. Status .IN. ('Active', 'Pending') .AND. .MEMBER_MLS_SECURITY_CLASS. != 'Admin'` -
- -## Collection Support -RCP-019.1 added support for collections, which include `LIST()` and `SET()`. +##### Collection Support +This proposal adds support for `LIST()` and `SET()`. These functions take 0 or more items. -Backwards compatibility with the previous list syntax of `()` in RCP-019 has been preserved. - | Expression | Result | Comments | | :--- | :--- | :--- | -|`LIST(1, 2, 2, 3)`|`LIST(1, 2, 2, 3`| Duplicate items are _preserved_.| -|`SET(1, 2, 2, 3)`|`SET(1, 2, 3)`| Duplicate items are _removed_.| +|`LIST(1, 2, 2, 3)`|`LIST(1, 2, 2, 3`| Duplicate items are _preserved_| +|`SET(1, 2, 2, 3)`|`SET(1, 2, 3)`| Duplicate items are _removed_|
-## DIFFERENCE(), INTERSECTION(), and UNION() -RCP-019.1 also added support for `DIFFERENCE()`, `INTERSECTION()`, and `UNION()`, -which are intended to produce types in the following manner: +##### List and Set Difference, Intersection, and Union +This proposal also adds support for the following collection operators: +* `DIFFERENCE()` +* `INTERSECTION()` +* `UNION()` + +Which are intended to produce types in the following manner: * Operations on _homogeneous_ collections of `LIST()` or `SET()` should - return `LIST()` or `SET()`, respectively. For example, - _`UNION(LIST('a', 'b', 3+5), LIST(6))`_ should return _`LIST('a', 'b', 6, 8)`_. - * Operations on _heterogenous_ collections of `LIST()` or `SET()`, for - instance _`DIFFERENCE(LIST(1, 2, 3), SET(3))`_ should return _`LIST()`_. + return `LIST()` or `SET()`, respectively. + * Example: `UNION(LIST('a', 'b', 3+5), LIST(6))` should return `LIST('a', 'b', 6, 8)` + * Operations on _heterogenous_ collections of `LIST()` or `SET()` should return `LIST()` + * Example: `DIFFERENCE(LIST(1, 2, 3), SET(3))` should return `LIST(1, 2)` -These special functions require at least two arguments of type `LIST()` or `SET()`: +These special functions require _at least two_ arguments of type `LIST()` or `SET()`: | Expression | Result | Comments | | :--- | :--- | :--- | - |`DIFFERENCE(LIST(1, 2, 3), LIST(1, 2))`|`LIST(3)`|Collection operators require two or more `LIST()` or `SET()` arguments.| - |`UNION(LIST(1, 2), SET(3))` |`LIST(1, 2, 3)`|Arguments of type `LIST()` are converted to `SET()`. | - |`INTERSECTION(SET(DIFFERENCE(LIST(1, 2, 3), SET(1))), SET(2))`|`SET(2)`|Since the return type of `collection` operators is `SET()` or `LIST()`, they can be composed.| + | `DIFFERENCE(LIST(1, 2, 3), LIST(1, 2))` | `LIST(3)` |Collection operators require two or more `LIST()` or `SET()` arguments.| + | `UNION(LIST(1, 2), SET(3))` | `LIST(1, 2, 3)` | Arguments of type `LIST()` are converted to `SET()` | + | `INTERSECTION(SET(DIFFERENCE(LIST(1, 2, 3), SET(1))), SET(2))` | `SET(2)` |Since the return type of `collection` operators is `SET()` or `LIST()`, they can be composed + +## Section 2.2: Rules Resource +The Validation Expression grammar itself is not enough to compute rules. Additional information is needed. + +The [Rules Resource](https://ddwiki.reso.org/display/DDW17/Rules+Resource) has been created in Data Dictionary 1.7+ to transport business rules. + +At a minimum, these consist of the following: +* Field: A standard or local field to effect with the outcome of _expression_ +* Action: One of the Actions in [Section 2.1.1](#section-211-actions) +* Expression: The Validation Expression using the grammar [defined in Section 2.1.2](#section-212-validation-expressions) + +### Example: Update PurchaseContractDate when Listing is Closed + +| Field | Action | Expression | +| --------------------------- | -------- | ---------- | +| **PurchaseContractDate** | `SET` | `IIF(LAST StandardStatus != 'Closed' .AND. StandardStatus = 'Closed', #2023-12-04#, NULL)` + +It's also important that each rule contains human-friendly messages describing the outcome, when appropriate. + +## Rule Ordering +Rule ordering is important. Dependent rules MUST be executed in the order appropriate for their dependencies. + +Consider the basic operations: + +``` +a = 1 +b = 1 + a +c = a + b +``` + +If `a = 1` isn't executed first, then the second instruction will throw an error and the output won't be available to the assignment of `c`. + +However, if we had another expression `d = 0`, it could be executed in any order since there are no dependencies on any of the other items. + +The Rule Resource includes a [`RuleOrder`](https://ddwiki.reso.org/display/DDW17/RuleOrder+Field) property so ordering may be preserved. + +## Rule Versioning +Sometimes it might be necessary to communicate that a given rule has changed. + +The Rules Resource providers the [Rule Version](https://ddwiki.reso.org/display/DDW17/RuleVersion+Field) for these scenarios. + +## Filtering by FieldName and RuleAction +Consider the case where a client is interested in rules that might reject changes to ListPrice. + +These can be retrieved using the following request to the Rules Resource: + +**REQUEST** +``` +GET /Rules?$select=RuleKey,FieldName,RuleAction,RuleExpression,RuleWarningText,RuleVersion&$filter=FieldName eq 'ListPrice' and RuleAction eq 'REJECT' +``` + +**RESPONSE** +```json +{ + "@odata.context": "/Rules?$select=RuleKey,FieldName,RuleAction,RuleExpression,RuleWarningText,RuleVersion&$filter=FieldName eq 'ListPrice' and RuleAction eq 'REJECT'", + "value": [{ + "RuleKey": "abc123", + "FieldName": "ListPrice", + "RuleAction:": "REJECT", + "RuleExpression": "ListPrice GT LAST ListPrice * 2", + "RuleWarningText": "ListPrice was greater than two times the original list price. Are you sure?", + "RuleVersion": "1.234" + }] +} +``` + +
+ +# Section 3: Certification +Testing rules consists of the following steps: +* Retrieve rules from the [Rules Resource](https://ddwiki.reso.org/display/DDW17/Rules+Resource) or having them provided in [RESO Common Format](./reso-common-format.md), and validating they're in the correct format +* Parse the [Validation Expression grammar](./artifacts/grammars/RCP-019/rcp019.g4) to make sure it's correct + * Will generate a parser from the grammar and host it as a service + * Add more human-friendly error messages +* Make local changes to a record and validate both success and failure scenarios + * Those being tested will provide several tests for both scenarios, along with the expected output + * Testing tools will run the rule set and ensure the output matches +* Test that client can send record to test server with validation rules failing with initial values, then succeeding with known-good values, and the record being upserted correctly in that system (Optional) + +Certification tools will be developed so a user can perform all these steps in an interactive manner through both a CLI or UI. + +

+ +# Section 4. Contributors +This document was rewritten from the original by [Joshua Darnell](mailto:josh@kurotek.com), and originally written by Joshua Darnell and [Paul Stusiak](mailto:pstusiak@falcontechnologies.com). + +Thanks to the following contributors for their help with this project: + +Parts of this proposal are based on the RETS Validation Expression language as expressed in RETS 1.9 and RCP 61. Thanks to Libor Viktorin, Mark Sleeman, Sergio Del Rio, and Paul Stusiak for that work. Thanks to Rob Larson for his collaboration on the Rules Resource proposal. + +

+ +# Section 5: References + +Please see the following references for more information regarding topics covered in this document: +* [Rules Resource [DD WIki]](https://ddwiki.reso.org/display/DDW17/Rules+Resource) +* [Original RCP-019 Proposal [Confluence - Login Required]](https://reso.atlassian.net/wiki/spaces/RESOWebAPIRCP/pages/2250178749/RCP+-+WEBAPI-019+Validation+Expression+in+the+WebAPI) +* [PDF of original RCP-019 Proposal](https://github.com/RESOStandards/reso-transport-specifications/files/8384860/RESOWebAPIRCP-RCP-WEBAPI-019ValidationExpressionintheWebAPI-300322-2353.pdf) +* [RESO Validation Expression Grammar](./artifacts/grammars/RCP-019/rcp019.g4) +* [RESO Common Format](./reso-common-format.md) +* [Formal Grammar [Wikipedia]](https://en.wikipedia.org/wiki/Formal_grammar) +* [Backus-Naur form (BNF) [Wikipedia]](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form) +* [ANTLR 4 Documentation](https://www.antlr.org/) + + + +

+ +# Section 6: Appendices + +None at this time. + +

+ +# Section 7: License +This document is covered by the [RESO EULA](https://www.reso.org/eula/). + +Please [contact RESO](mailto:info@reso.org) if you have any questions. From a42f419cde3dafcf3bb6e7735e7476d0a57c2d73 Mon Sep 17 00:00:00 2001 From: Joshua Darnell Date: Mon, 4 Dec 2023 10:19:56 -0800 Subject: [PATCH 3/6] Updated status date --- web-api-validation-expression.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web-api-validation-expression.md b/web-api-validation-expression.md index e9ff9a5..d7ff9c4 100644 --- a/web-api-validation-expression.md +++ b/web-api-validation-expression.md @@ -6,7 +6,7 @@ | **Authors** | [Joshua Darnell](mailto:josh@kurotek.com)
[Paul Stusiak](mailto:pstusiak@falcontechnologies.com) | | **Specification** | [**LINK TO RCP**](./web-api-validation-expression.md) | | **Status** | **DRAFT** | -| **Date Ratified** | April 2018 | +| **Status Date** | April 2018 | | **Dependencies** | [Data Dictionary 1.7+](./data-dictionary.md)
[Validation Expression Grammar](./artifacts/grammars/RCP-019/rcp019.g4) | | **Related Links** | [DD Wiki 1.7](https://ddwiki.reso.org/display/DDW17/RESO+Data+Dictionary+1.7)
[Data Dictionary Spreadsheet](https://docs.google.com/spreadsheets/d/1_59Iqr7AQ51rEFa7p0ND-YhJjEru8gY-D_HM1yy5c6w/edit?usp=sharing) | From 1439e58e58e762c9e54d842207640fa1b4eb82af Mon Sep 17 00:00:00 2001 From: Joshua Darnell Date: Mon, 4 Dec 2023 11:02:58 -0800 Subject: [PATCH 4/6] Removed external metadata lexer --- artifacts/grammars/RCP-019/rcp019Lexer.g4 | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/artifacts/grammars/RCP-019/rcp019Lexer.g4 b/artifacts/grammars/RCP-019/rcp019Lexer.g4 index ae9aed4..add9b59 100644 --- a/artifacts/grammars/RCP-019/rcp019Lexer.g4 +++ b/artifacts/grammars/RCP-019/rcp019Lexer.g4 @@ -1,9 +1,5 @@ lexer grammar rcp019Lexer; -options { - tokenVocab = rcp019MetadataLexer; -} - CONCAT: PIPE; LPAREN: '('; RPAREN: ')'; @@ -122,4 +118,4 @@ DAY: [0-3] DIGIT; SLASH_STAR_COMMENT: '/*' .+? '*/' -> skip; SLASH_SLASH_COMMENT: '//' .+? ('\n' | EOF) -> skip; -WS: [ \t\n\r]+ -> skip; \ No newline at end of file +WS: [ \t\n\r]+ -> skip; From cb7a3ca816f578fe567f208e217c94dfea71bccd Mon Sep 17 00:00:00 2001 From: darnjo Date: Fri, 15 Dec 2023 09:15:10 -0800 Subject: [PATCH 5/6] #110: Remvode g4 files --- artifacts/grammars/RCP-019/rcp019.g4 | 81 --------------- artifacts/grammars/RCP-019/rcp019Lexer.g4 | 121 ---------------------- 2 files changed, 202 deletions(-) delete mode 100644 artifacts/grammars/RCP-019/rcp019.g4 delete mode 100644 artifacts/grammars/RCP-019/rcp019Lexer.g4 diff --git a/artifacts/grammars/RCP-019/rcp019.g4 b/artifacts/grammars/RCP-019/rcp019.g4 deleted file mode 100644 index fec1e57..0000000 --- a/artifacts/grammars/RCP-019/rcp019.g4 +++ /dev/null @@ -1,81 +0,0 @@ -/* - RESO RCP-019 Validation Expression Grammar - - By downloading this resource, you agree to the RESO EULA: https://www.reso.org/eula/ Contact - dev@reso.org if you have questions - - Taken from: https://github.com/darnjo/rcp019/blob/master/LICENSE Copyright (c) 2018 Joshua Darnell - (josh@kurotek.com) - - NOTE: This file is written with the intent of preserving the structure of the original RETS 1.9 VE - grammar so that anyone who has implemented their systems using it shouldn't have to make any - changes unless they are wanting to support the new features. In other words, all changes made to - this grammar so far have been additive. There are more optimal representations of this file that - will accept the RESO RCP-019 grammar, and as long as your parser supports an equivalent set of - symbols and expressions, then the internals of your system are for you to decide. This example - grammar should already perform reasonably well in most situations, but the parse tree will be a bit - deep (and therefore not be as performant) due to the current structure. - */ - -grammar rcp019; - -// TODO: import https://github.com/antlr/grammars-v4/blob/master/pcre/PCRE.g4 - -options { - tokenVocab = rcp019Lexer; -} - -exp: orExp | collectionExp | funcExp; - -orExp: andExp (OR andExp)*; - -andExp: notExp (AND notExp)*; - -notExp: NOT notExp | eqExp; - -eqExp: cmpExp | cmpExp (NE | EQ) cmpExp; - -cmpExp: cntExp | cntExp (LTE | GTE | LT | GT) cntExp; - -cntExp: sumExp | sumExp (CONTAINS | IN) sumExp; - -sumExp: prodExp ((PLUS | MINUS | CONCAT) prodExp)*; - -prodExp: atomExp ((ASTERISK | SLASH | MOD) atomExp)*; - -// NOTE: original VE writing had that all lists of size 1 were atomExp, not list. LIST() and SET() -// were created as a top-level item called 'collection' and should be used instead. -atomExp: LPAREN exp RPAREN | listExp | value; - -// this was left in for backwards compatibility with the first production rule, LPAREN exp RPAREN -listExp: LPAREN (exp (COMMA exp)*)? RPAREN; - -// this was previously an atomExp, but was moved to the top-level for faster parsing (10x speedup) -funcExp: func LPAREN (param (COMMA param)*)? RPAREN; - -collectionExp: (LIST | SET) LPAREN (exp (COMMA exp)*)? RPAREN - | (UNION | INTERSECTION | DIFFERENCE) LPAREN ( - collectionExp COMMA collectionExp (COMMA collectionExp)* - )? RPAREN; - -param: exp; - -value: - fieldName - | specValue - | charValue - | intValue - | floatValue - | timeValue; - -fieldName: (LAST)? FIELD_NAME - | LBRACKET (LAST)? FIELD_NAME RBRACKET; - -specValue: DOT FIELD_NAME DOT; -charValue: QUOTED_TERM; -timeValue: HASH ISO_TIMESTAMP HASH; -intValue: (PLUS | MINUS)? DIGIT+; -floatValue: intValue DOT DIGIT+; - -// SPECFUNC was added. LOCALFUNC could be added as well, with corresponding known local functions -func: SPECFUNC | ALPHANUM; \ No newline at end of file diff --git a/artifacts/grammars/RCP-019/rcp019Lexer.g4 b/artifacts/grammars/RCP-019/rcp019Lexer.g4 deleted file mode 100644 index add9b59..0000000 --- a/artifacts/grammars/RCP-019/rcp019Lexer.g4 +++ /dev/null @@ -1,121 +0,0 @@ -lexer grammar rcp019Lexer; - -CONCAT: PIPE; -LPAREN: '('; -RPAREN: ')'; -SQUOTE: '\''; -QUOTE: '"'; -DOT: '.'; -ASTERISK: '*'; -SLASH: '/'; -EXCLAMATION: '!'; -DOLLAR: '$'; -CARET: '^'; -BACKSLASH: '\\'; -SINGLE_SPACE: ' '; -COLON: ':'; -PIPE: '|'; -LBRACKET: '['; -RBRACKET: ']'; -HASH: '#'; - -OR: '.OR.'; -AND: '.AND.'; -NOT: '.NOT.'; - -EQ: '='; -NE: EXCLAMATION EQ; -LT: '<'; -LTE: LT EQ; -GT: '>'; -GTE: GT EQ; - -CONTAINS: '.CONTAINS.'; -IN: '.IN.'; -COMMA: ','; -PLUS: '+'; -MINUS: '-'; -MOD: '.MOD.'; - -IIF: 'IIF'; -MATCH: 'MATCH'; -LAST: 'LAST'; -LIST: 'LIST'; -SET: 'SET'; -DIFFERENCE: 'DIFFERENCE'; -INTERSECTION: 'INTERSECTION'; -UNION: 'UNION'; -TRUE: 'TRUE'; -FALSE: 'FALSE'; -EMPTY: 'EMPTY'; -NULL: 'NULL'; -TODAY: 'TODAY'; -NOW: 'NOW'; -ENTRY: 'ENTRY'; -OLDVALUE: 'OLDVALUE'; -UPDATEACTION: 'UPDATEACTION'; -ANY: 'any'; - -// See: Data Dictionary Member and Office Resources -MEMBER_LOGIN_ID: 'MEMBER_LOGIN_ID'; -MEMBER_MLS_SECURITY_CLASS: 'MEMBER_MLS_SECURITY_CLASS'; -MEMBER_TYPE: 'MEMBER_TYPE'; -MEMBER_MLS_ID: 'MEMBER_MLS_ID'; -OFFICE_BROKER_MLS_ID: 'OFFICE_BROKER_MLS_ID'; -OFFICE_MLS_ID: 'OFFICE_MLS_ID'; - -ALPHA: ('a' ..'z' | 'A' ..'Z'); -DIGIT: ('0' ..'9'); - -// special tokens -RESO_SPECIAL_TOKENS: FIELD_NAME | SPECOP; - -// TODO: Dynamically fill in your FIELD_NAMEs here -FIELD_NAME: - 'ListPrice' - | 'Status' - | 'CloseDate' - | 'Bedrooms' - | 'Bathrooms'; - -SPECFUNC: IIF | MATCH; - -SPECOP: - EMPTY - | TRUE - | FALSE - | TODAY - | NOW - | ENTRY - | OLDVALUE - | MEMBER_LOGIN_ID - | MEMBER_MLS_SECURITY_CLASS - | MEMBER_TYPE - | MEMBER_MLS_ID - | OFFICE_BROKER_MLS_ID - | OFFICE_MLS_ID - | UPDATEACTION - | ANY; - -ALPHANUM: ALPHA (ALPHA | DIGIT)*; - -QUOTED_TERM: QUOTE (~[\\"])*? QUOTE | SQUOTE (~[\\'])*? SQUOTE; - -// #2023-12-04T06:12:24.00Z# #2023-12-04T06:12:24.00-7:00# #2023-12-04T06:12:24.00+7:00# -ISO_TIMESTAMP: - HASH ISO_DATE 'T' [0-23] COLON [0-59] COLON [0-59] DOT ( - DIGIT - )* ('Z' | (PLUS | MINUS) [0-12] COLON [0-59]) HASH; - -// #2023-12-04# -ISO_DATE: HASH YEAR '-' MONTH '-' DAY HASH; - -YEAR: DIGIT DIGIT DIGIT DIGIT; -MONTH: [0-12]; -DAY: [0-3] DIGIT; - -//added support for c++ style comments -SLASH_STAR_COMMENT: '/*' .+? '*/' -> skip; -SLASH_SLASH_COMMENT: '//' .+? ('\n' | EOF) -> skip; - -WS: [ \t\n\r]+ -> skip; From 3c20496be615f1a0c65de78cd8e6c22d24f21fb7 Mon Sep 17 00:00:00 2001 From: darnjo Date: Fri, 15 Dec 2023 10:30:39 -0800 Subject: [PATCH 6/6] #110: Added section on special functions and operators --- web-api-validation-expression.md | 93 +++++++++++++++++++++++++++++--- 1 file changed, 87 insertions(+), 6 deletions(-) diff --git a/web-api-validation-expression.md b/web-api-validation-expression.md index d7ff9c4..d7d4f73 100644 --- a/web-api-validation-expression.md +++ b/web-api-validation-expression.md @@ -186,7 +186,7 @@ At a minimum, these consist of the following: It's also important that each rule contains human-friendly messages describing the outcome, when appropriate. -## Rule Ordering +## Section 2.3: Rule Ordering Rule ordering is important. Dependent rules MUST be executed in the order appropriate for their dependencies. Consider the basic operations: @@ -203,12 +203,12 @@ However, if we had another expression `d = 0`, it could be executed in any order The Rule Resource includes a [`RuleOrder`](https://ddwiki.reso.org/display/DDW17/RuleOrder+Field) property so ordering may be preserved. -## Rule Versioning +## Section 2.4: Rule Versioning Sometimes it might be necessary to communicate that a given rule has changed. The Rules Resource providers the [Rule Version](https://ddwiki.reso.org/display/DDW17/RuleVersion+Field) for these scenarios. -## Filtering by FieldName and RuleAction +## Section 2.5: Filtering by FieldName and RuleAction Consider the case where a client is interested in rules that might reject changes to ListPrice. These can be retrieved using the following request to the Rules Resource: @@ -250,7 +250,7 @@ Certification tools will be developed so a user can perform all these steps in a

-# Section 4. Contributors +# Section 4: Contributors This document was rewritten from the original by [Joshua Darnell](mailto:josh@kurotek.com), and originally written by Joshua Darnell and [Paul Stusiak](mailto:pstusiak@falcontechnologies.com). Thanks to the following contributors for their help with this project: @@ -272,12 +272,93 @@ Please see the following references for more information regarding topics covere * [ANTLR 4 Documentation](https://www.antlr.org/) -

# Section 6: Appendices -None at this time. +## 6.1: List of Functions + +| Function | Parameter Types | Type | Comments | +| -------- | --------------- | ---- | -------- | +| `BOOL` | { `BOOLEAN`, `CHAR` } | `BOOLEAN` | | +| `CHAR` | { `Exp`, `TYPEOF(Exp) NE FLOAT` } | `CHAR` | | +| `CHARF` | `FLOAT`, `INT` | `CHAR` | The `CHARF` function converts a Float number, and in the second parameter specifies how many decimal digits MUST appear after the point. | +| `TIME` | { `TIME`, `CHAR`, `DATE` } | `TIME` | | +| `DATE` | { `TIME`, `CHAR` } | `DATE` | | +| `INT` | { `INT`, `FLOAT`, `BOOL`, `CHAR` } | `INT` | | +| `FLOAT` | { `INT`, `FLOAT`, `BOOL`, `CHAR` } | `FLOAT` | | +| `SUBSTR` | `CHAR`, `INT`, `INT` | `CHAR` | The `SUBSTR` function returns a substring of its first parameter. Second parameter is a starting position of the substring, third parameter is the ending position of the substring. Positions are 1-based. | +| `STRLEN` | `CHAR` | `INT` | The `STRLEN` function returns the length if its parameter. | +| `LOWER` | `CHAR` | `CHAR` | The `LOWER` function returns its parameter lower-cased. | +| `UPPER` | `CHAR` | `CHAR` | The `UPPER` function returns its parameter upper-cased. | +| `IIF` | `BOOLEAN`, `Exp`, `Exp` | `TYPEOF (Exp)` | The `IIF` function returns the value of its second parameter if the first parameter evaluates to true, or the value of its third parameter otherwise. Types of second and third parameter must be same, and it is the type of the result. These parameters are also known as `CondExp`, `TrueExp`, and `FalseExp`, respectively. | +| `YEAR` | `TIME` | `INT` | | +| `MONTH` | `TIME` | `INT` | | +| `DAY` | `TIME` | `INT` | | +| `WEEKDAY` | `TIME` | `INT` | | +| `TYPEOF` | `Exp` | `CHAR` | The input parameter, `Exp`, can be any valid expression in the grammar. This function will return a `CHAR` representation of the given expression's type, one of: { `BOOLEAN`, `CHAR`, `FLOAT`, `INT`, `TIME` } | +| `MATCH` | `CHAR`, `Exp` | `BOOLEAN` | Takes a Regular Expression regex as a `CHAR` and an expression `Exp`, and returns True if expression matches regex and False otherwise. | + +
+ +**Notes** +1. The `BOOL`, `CHAR`, `TIME`, `DATE`, `INT` and `FLOAT` functions are used just to change a type of expression. Note that any of these functions may fail (return an ERROR value) if the parameter can not be converted to the appropriate type. + +2. In conversion from `BOOLEAN` to `INT` or `FLOAT`, `.TRUE.` is converted to "1" and `.FALSE.` is converted to "0". Casting `FLOAT` to `INT` discards the fractional part. + +3. When converting to `CHAR`, `BOOL` values are represented as "0" and "1", `TIME` and `DATE` values are represented using ISO 8601 format, `INT` values are represented with no leading zeros. + +4. When converting from `CHAR` to `BOOL`, values "0", "1", "YES", "NO", "TRUE" and "FALSE" (no matter what the case) MUST be understood. + +5. When converting from `CHAR` to `TIME`, any ISO 8601 compliant format must be understood. Leading and trailing `#` MUST be removed before conversion. + +6. When converting from `CHAR` to `INT` or `FLOAT`, usual formats MUST be understood. Scientific format (with exponent) MUST NOT be understood. `FLOAT` numbers with empty integral part ( .5, -.4) MUST be understood as long as there is at least one digit after the decimal point. + +7. The `YEAR`, `MONTH`, `DAY` and `WEEKDAY` parse the date part of `TIME` value. They return values ranging from 1 to the appropriate maximum. `WEEKDAY` returns 1 for Sunday, 2 for Monday etc. + +8. Other functions may be defined later (`HOUR` and `MINUTE` are first candidates). If a server uses a function the client does not recognize, the client MUST evaluate it as ERROR. + +9. A Validation Expression may have Operations applied to the parameters. The Operators may be applied on certain types that determine the result type. The input types and resulting output type is defined in the Validation Expression List of Operators Table (below). + +
+ +## 6.2: List of Operators + +| Operator | Left Operand | Right Operand | Result | Meaning | +| -------- | ------------ | ------------- | ------ | ------- | +| `.MOD.` | `INT` | `INT` | `INT` | Arithmetic MODULO operation | +| `/`, `*` | `INT` | `INT` | `INT` | Integer division and multiplication | +| `/`, `*` | `INT` | `FLOAT` | `FLOAT` | Division and multiplication | +| `/`, `*` | `FLOAT` | `INT` | `FLOAT` | Division and multiplication | +| `/`, `*` | `FLOAT` | `FLOAT` | `FLOAT` | Division and multiplication | +| `+`, `-` | `FLOAT` | `FLOAT` | `FLOAT` | Addition and subtraction | +| `+`, `-` | `FLOAT` | `FLOAT` | `FLOAT` | Addition and subtraction | +| `+`, `-` | `FLOAT` | `FLOAT` | `FLOAT` | Addition and subtraction | +| `+`, `-` | `FLOAT` | `FLOAT` | `FLOAT` | Addition and subtraction | +| `+` | `FLOAT` | `TIME` | `TIME` | Time shift | +| `+` | `TIME` | `FLOAT` | `TIME` | Time shift | +| `-` | `TIME` | `FLOAT` | `TIME` | Time shift | +| `-` | `TIME` | `TIME` | `FLOAT` | Time shift | +| `\|\|` | `CHAR` | `CHAR` | `CHAR` | String concatenation | +| `.CONTAINS.` | `CHAR` | `CHAR` | `BOOLEAN` | String containment. The operation is TRUE if the left operand contains the right operand as a substring anywhere within it. | +| `.IN.` | Any | List of operands, all of the same type as the left operand | `BOOLEAN` | List inclusion. The operation is TRUE if the left operand is equal to any member of the list. | +| `.AND.` | `BOOLEAN` | `BOOLEAN` | `BOOLEAN` | A Boolean operator that takes two Boolean operands, and whose value is TRUE if and only if both of its operands are TRUE. | +| `.OR.` | `BOOLEAN` | `BOOLEAN` | `BOOLEAN` | A Boolean operator that takes two Boolean operands, and whose value is TRUE if either of its operands is TRUE. | +| `.NOT.` | `BOOLEAN` | `BOOLEAN` | `BOOLEAN` | A Boolean operator that takes a single Boolean operand and returns its inverse. | +| `=`, `!=` | Any | Same as left | `BOOLEAN` | Equality | +| `<`, `>`, `<=` , `>=` | `INT`, `FLOAT` | `INT`, `FLOAT` | `BOOLEAN` | Numeric comparison | +| `<`, `>`, `<=`, `>=` | `TIME` | `TIME` | `BOOLEAN` | Date and time comparison | +| `<`, `>`, `<=`, `>=` | `BOOLEAN` | `BOOLEAN` | `BOOLEAN` | Boolean comparison (TRUE > FALSE) | + +
+ +**Notes** +1. Arithmetic operations between dates use number of days as the `FLOAT` parameter (or result); e.g. 0.25 represents a time span of 6 hours. + +2. Any operation with an ERROR argument MUST evaluate to ERROR. An EMPTY value may be compared (=,!=) against any value. + +3. Appropriate casting functions (`BOOL`, `CHAR`, `TIME`, `INT`, `FLOAT`) MUST be applied to the parameters. If a function or an operator is applied to a data type different than shown in the above tables, the expression MUST evaluate to ERROR. +