Skip to content

Commit

Permalink
optimise parser and fix prioity order
Browse files Browse the repository at this point in the history
  • Loading branch information
SerCeMan committed Feb 4, 2023
1 parent a12684e commit cc5a82c
Show file tree
Hide file tree
Showing 9 changed files with 360 additions and 172 deletions.
65 changes: 36 additions & 29 deletions src/main/grammars/CfRules.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -65,45 +65,52 @@ SourceUnit ::= Expression

Expression ::=
// Comparison expressions
EqExpression
| NeExpression
| LtExpression
| LteExpression
| GtExpression
| GteExpression
| ContainsExpression
| MatchesExpression
| InExpression
ComparisonGroup
| LookupGroup
// Logical expressions
| ParenExpression
| NotExpression
| AndExpression
| XorExpression
| OrExpression
// Primitive expressions
| ArrayAccessExpression
| XorExpression
| AndExpression
| NotExpression
// Primitive Expression
| ParenExpression
| PrimitiveGroup

private ComparisonGroup ::= ComparisonExpression

private LookupGroup ::=
ContainsExpression
| MatchesExpression
| InExpression

private PrimitiveGroup ::=
ArrayAccessExpression
| FunctionExpression
| StandardFieldExpression


ParenExpression ::= '(' Expression ')' {pin=3}
NotExpression ::= ('not' | '!') Expression {pin=1}
AndExpression ::= Expression ('and' | '&&') Expression {pin=2}
XorExpression ::= Expression ('xor' | '^^') Expression {pin=2}
OrExpression ::= Expression ('or' | '||') Expression {pin=2}
ParenExpression ::= '(' Expression ')' {pin=1}
NotExpression ::= ('not' | '!') Expression
AndExpression ::= Expression ('and' | '&&') Expression
XorExpression ::= Expression ('xor' | '^^') Expression
OrExpression ::= Expression ('or' | '||') Expression

private PrimitiveExpression ::=
ParenExpression
| ArrayAccessExpression
| FunctionExpression
| StandardFieldExpression

EqExpression ::= PrimitiveExpression ('eq' | '==') Value {pin=2}
NeExpression ::= PrimitiveExpression ('ne' | '!=') Value {pin=2}
LtExpression ::= PrimitiveExpression ('lt' | '<') Value {pin=2}
LteExpression ::= PrimitiveExpression ('le' | '<=') Value {pin=2}
GtExpression ::= PrimitiveExpression ('gt' | '>') Value {pin=2}
GteExpression ::= PrimitiveExpression ('ge' | '>=') Value {pin=2}
ComparisonExpression ::=
PrimitiveExpression
(
'eq' | '=='
| 'ne' | '!='
| 'lt' | '<'
| 'le' | '<='
| 'gt' | '>'
| 'ge' | '>='
)
Value {pin=2}

ContainsExpression ::= PrimitiveExpression 'contains' StringLiteral {pin=2}
MatchesExpression ::= PrimitiveExpression ('matches' | '~') StringLiteral {pin=2}
Expand All @@ -117,12 +124,12 @@ StandardFieldExpression ::= identifier ('.' identifier)*

// allow multiple index accesses as I'm not sure how to parse nested ArrayAccessExpression
// without infinite recursion
ArrayAccessExpression ::= (ParenExpression | FunctionExpression | StandardFieldExpression) IndexAccess+ {pin=2}
IndexAccess ::= '[' ('*' | NumberLiteral | StringLiteral) ']'{pin=1}
ArrayAccessExpression ::= (ParenExpression | FunctionExpression | StandardFieldExpression) IndexAccess IndexAccess* {pin=2}

// Values

SetValue ::= '{' Value* '}'
SetValue ::= '{' Value* '}' {pin=1}
ListValue ::= listLiteral

Value ::=
Expand Down
28 changes: 15 additions & 13 deletions src/test/kotlin/me/serce/cfrules/lang/parsingTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,24 @@ import com.intellij.lang.LanguageExtensionPoint
import com.intellij.testFramework.ParsingTestCase

abstract class CfRulesParsingTestBase(baseDir: String) :
ParsingTestCase(baseDir, "cfrule", true, CfRulesParserDefinition()) {
ParsingTestCase(baseDir, "cfrule", true, CfRulesParserDefinition()) {

override fun setUp() {
super.setUp()
CoreApplicationEnvironment.registerExtensionPoint(
application.extensionArea,
"com.intellij.lang.braceMatcher",
LanguageExtensionPoint::class.java
)
}
override fun setUp() {
super.setUp()
CoreApplicationEnvironment.registerExtensionPoint(
application.extensionArea,
"com.intellij.lang.braceMatcher",
LanguageExtensionPoint::class.java
)
}

override fun getTestDataPath() = "src/test/resources"
override fun getTestDataPath() = "src/test/resources"
}

class CfRulesParsingTest : CfRulesParsingTestBase("fixtures/parser") {
fun testComments() = doTest(true)
fun testSample() = doTest(true)
fun testIPv6() = doTest(true)
fun testComments() = doTest(true)
fun testFunctionCall() = doTest(true)
fun testIPv6() = doTest(true)
fun testPriority() = doTest(true)
fun testSample() = doTest(true)
}
4 changes: 2 additions & 2 deletions src/test/resources/fixtures/parser/comments.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Cloudflare Rules File
PsiComment(COMMENT)('# example')
PsiWhiteSpace('\n')
CfRulesEqExpressionImpl(EQ_EXPRESSION)
CfRulesComparisonExpressionImpl(COMPARISON_EXPRESSION)
CfRulesStandardFieldExpressionImpl(STANDARD_FIELD_EXPRESSION)
PsiElement(identifier)('http')
PsiElement(.)('.')
Expand All @@ -14,4 +14,4 @@ Cloudflare Rules File
PsiElement(==)('==')
PsiWhiteSpace(' ')
CfRulesValueImpl(VALUE)
PsiElement(stringLiteral)('"section=539061&expand=comments"')
PsiElement(stringLiteral)('"section=539061&expand=comments"')
11 changes: 11 additions & 0 deletions src/test/resources/fixtures/parser/functionCall.cfrule
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
is_timed_hmac_valid_v0(
"mysecretkey",
concat(
http.request.uri,
http.request.headers["timestamp"][0],
"-",
http.request.headers["mac"][0]),
100000,
http.request.timestamp.sec,
0
)
87 changes: 87 additions & 0 deletions src/test/resources/fixtures/parser/functionCall.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
Cloudflare Rules File
CfRulesFunctionExpressionImpl(FUNCTION_EXPRESSION)
PsiElement(identifier)('is_timed_hmac_valid_v0')
PsiElement(()('(')
PsiWhiteSpace('\n ')
CfRulesFunctionArgumentImpl(FUNCTION_ARGUMENT)
CfRulesValueImpl(VALUE)
PsiElement(stringLiteral)('"mysecretkey"')
PsiElement(,)(',')
PsiWhiteSpace('\n ')
CfRulesFunctionArgumentImpl(FUNCTION_ARGUMENT)
CfRulesFunctionExpressionImpl(FUNCTION_EXPRESSION)
PsiElement(identifier)('concat')
PsiElement(()('(')
PsiWhiteSpace('\n ')
CfRulesFunctionArgumentImpl(FUNCTION_ARGUMENT)
CfRulesStandardFieldExpressionImpl(STANDARD_FIELD_EXPRESSION)
PsiElement(identifier)('http')
PsiElement(.)('.')
PsiElement(identifier)('request')
PsiElement(.)('.')
PsiElement(identifier)('uri')
PsiElement(,)(',')
PsiWhiteSpace('\n ')
CfRulesFunctionArgumentImpl(FUNCTION_ARGUMENT)
CfRulesArrayAccessExpressionImpl(ARRAY_ACCESS_EXPRESSION)
CfRulesStandardFieldExpressionImpl(STANDARD_FIELD_EXPRESSION)
PsiElement(identifier)('http')
PsiElement(.)('.')
PsiElement(identifier)('request')
PsiElement(.)('.')
PsiElement(identifier)('headers')
CfRulesIndexAccessImpl(INDEX_ACCESS)
PsiElement([)('[')
PsiElement(stringLiteral)('"timestamp"')
PsiElement(])(']')
CfRulesIndexAccessImpl(INDEX_ACCESS)
PsiElement([)('[')
PsiElement(numberLiteral)('0')
PsiElement(])(']')
PsiElement(,)(',')
PsiWhiteSpace('\n ')
CfRulesFunctionArgumentImpl(FUNCTION_ARGUMENT)
CfRulesValueImpl(VALUE)
PsiElement(stringLiteral)('"-"')
PsiElement(,)(',')
PsiWhiteSpace('\n ')
CfRulesFunctionArgumentImpl(FUNCTION_ARGUMENT)
CfRulesArrayAccessExpressionImpl(ARRAY_ACCESS_EXPRESSION)
CfRulesStandardFieldExpressionImpl(STANDARD_FIELD_EXPRESSION)
PsiElement(identifier)('http')
PsiElement(.)('.')
PsiElement(identifier)('request')
PsiElement(.)('.')
PsiElement(identifier)('headers')
CfRulesIndexAccessImpl(INDEX_ACCESS)
PsiElement([)('[')
PsiElement(stringLiteral)('"mac"')
PsiElement(])(']')
CfRulesIndexAccessImpl(INDEX_ACCESS)
PsiElement([)('[')
PsiElement(numberLiteral)('0')
PsiElement(])(']')
PsiElement())(')')
PsiElement(,)(',')
PsiWhiteSpace('\n ')
CfRulesFunctionArgumentImpl(FUNCTION_ARGUMENT)
CfRulesValueImpl(VALUE)
PsiElement(numberLiteral)('100000')
PsiElement(,)(',')
PsiWhiteSpace('\n ')
CfRulesFunctionArgumentImpl(FUNCTION_ARGUMENT)
CfRulesStandardFieldExpressionImpl(STANDARD_FIELD_EXPRESSION)
PsiElement(identifier)('http')
PsiElement(.)('.')
PsiElement(identifier)('request')
PsiElement(.)('.')
PsiElement(identifier)('timestamp')
PsiElement(.)('.')
PsiElement(identifier)('sec')
PsiElement(,)(',')
PsiWhiteSpace('\n ')
CfRulesFunctionArgumentImpl(FUNCTION_ARGUMENT)
CfRulesValueImpl(VALUE)
PsiElement(numberLiteral)('0')
PsiWhiteSpace('\n')
PsiElement())(')')
82 changes: 41 additions & 41 deletions src/test/resources/fixtures/parser/iPv6.txt
Original file line number Diff line number Diff line change
@@ -1,36 +1,22 @@
Cloudflare Rules File
CfRulesAndExpressionImpl(AND_EXPRESSION)
CfRulesOrExpressionImpl(OR_EXPRESSION)
CfRulesOrExpressionImpl(OR_EXPRESSION)
CfRulesOrExpressionImpl(OR_EXPRESSION)
CfRulesOrExpressionImpl(OR_EXPRESSION)
CfRulesOrExpressionImpl(OR_EXPRESSION)
CfRulesEqExpressionImpl(EQ_EXPRESSION)
CfRulesStandardFieldExpressionImpl(STANDARD_FIELD_EXPRESSION)
PsiElement(identifier)('ip')
PsiElement(.)('.')
PsiElement(identifier)('src')
PsiWhiteSpace(' ')
PsiElement(==)('==')
PsiWhiteSpace(' ')
CfRulesValueImpl(VALUE)
PsiElement(ipv6Cidr)('2001:0DB8:ABCD:0012:0000:0000:0000:0000')
PsiWhiteSpace('\n')
PsiElement(or)('or')
CfRulesComparisonExpressionImpl(COMPARISON_EXPRESSION)
CfRulesStandardFieldExpressionImpl(STANDARD_FIELD_EXPRESSION)
PsiElement(identifier)('ip')
PsiElement(.)('.')
PsiElement(identifier)('src')
PsiWhiteSpace(' ')
PsiElement(==)('==')
PsiWhiteSpace(' ')
CfRulesEqExpressionImpl(EQ_EXPRESSION)
CfRulesStandardFieldExpressionImpl(STANDARD_FIELD_EXPRESSION)
PsiElement(identifier)('ip')
PsiElement(.)('.')
PsiElement(identifier)('src')
PsiWhiteSpace(' ')
PsiElement(==)('==')
PsiWhiteSpace(' ')
CfRulesValueImpl(VALUE)
PsiElement(ipv6Cidr)('2001:DB8:ABCD:12::')
CfRulesValueImpl(VALUE)
PsiElement(ipv6Cidr)('2001:0DB8:ABCD:0012:0000:0000:0000:0000')
PsiWhiteSpace('\n')
PsiElement(or)('or')
PsiWhiteSpace(' ')
CfRulesEqExpressionImpl(EQ_EXPRESSION)
CfRulesComparisonExpressionImpl(COMPARISON_EXPRESSION)
CfRulesStandardFieldExpressionImpl(STANDARD_FIELD_EXPRESSION)
PsiElement(identifier)('ip')
PsiElement(.)('.')
Expand All @@ -39,29 +25,43 @@ Cloudflare Rules File
PsiElement(==)('==')
PsiWhiteSpace(' ')
CfRulesValueImpl(VALUE)
PsiElement(ipv6Cidr)('0:0:0:0:0:0:0:1')
PsiElement(ipv6Cidr)('2001:DB8:ABCD:12::')
PsiWhiteSpace('\n')
PsiElement(or)('or')
PsiWhiteSpace(' ')
CfRulesInExpressionImpl(IN_EXPRESSION)
CfRulesComparisonExpressionImpl(COMPARISON_EXPRESSION)
CfRulesStandardFieldExpressionImpl(STANDARD_FIELD_EXPRESSION)
PsiElement(identifier)('ip')
PsiElement(.)('.')
PsiElement(identifier)('src')
PsiWhiteSpace(' ')
PsiElement(in)('in')
PsiElement(==)('==')
PsiWhiteSpace(' ')
CfRulesSetValueImpl(SET_VALUE)
PsiElement({)('{')
PsiWhiteSpace(' ')
CfRulesValueImpl(VALUE)
PsiElement(ipv6Cidr)('2001:db8:abcd:0012::0/64')
PsiWhiteSpace(' ')
PsiElement(})('}')
CfRulesValueImpl(VALUE)
PsiElement(ipv6Cidr)('0:0:0:0:0:0:0:1')
PsiWhiteSpace('\n')
PsiElement(or)('or')
PsiWhiteSpace(' ')
CfRulesEqExpressionImpl(EQ_EXPRESSION)
CfRulesInExpressionImpl(IN_EXPRESSION)
CfRulesStandardFieldExpressionImpl(STANDARD_FIELD_EXPRESSION)
PsiElement(identifier)('ip')
PsiElement(.)('.')
PsiElement(identifier)('src')
PsiWhiteSpace(' ')
PsiElement(in)('in')
PsiWhiteSpace(' ')
CfRulesSetValueImpl(SET_VALUE)
PsiElement({)('{')
PsiWhiteSpace(' ')
CfRulesValueImpl(VALUE)
PsiElement(ipv6Cidr)('2001:db8:abcd:0012::0/64')
PsiWhiteSpace(' ')
PsiElement(})('}')
PsiWhiteSpace('\n')
PsiElement(or)('or')
PsiWhiteSpace(' ')
CfRulesAndExpressionImpl(AND_EXPRESSION)
CfRulesComparisonExpressionImpl(COMPARISON_EXPRESSION)
CfRulesStandardFieldExpressionImpl(STANDARD_FIELD_EXPRESSION)
PsiElement(identifier)('ip')
PsiElement(.)('.')
Expand All @@ -71,8 +71,8 @@ Cloudflare Rules File
PsiWhiteSpace(' ')
CfRulesValueImpl(VALUE)
PsiElement(ipv6Cidr)('::1')
PsiWhiteSpace('\n')
PsiElement(and)('and')
PsiWhiteSpace(' ')
CfRulesStandardFieldExpressionImpl(STANDARD_FIELD_EXPRESSION)
PsiElement(identifier)('ssl')
PsiWhiteSpace('\n')
PsiElement(and)('and')
PsiWhiteSpace(' ')
CfRulesStandardFieldExpressionImpl(STANDARD_FIELD_EXPRESSION)
PsiElement(identifier)('ssl')
15 changes: 15 additions & 0 deletions src/test/resources/fixtures/parser/priority.cfrule
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
http.cookie1
and
http.cookie2
or
http.cookie3
and
not http.cookie4
and
http.cookie5
and (
http.cookie6
or (
http.cookie7
)
)
Loading

0 comments on commit cc5a82c

Please sign in to comment.