Skip to content

Commit

Permalink
feat: Implement benchmark function
Browse files Browse the repository at this point in the history
  • Loading branch information
AmrDeveloper committed Oct 3, 2024
1 parent c0ca9bd commit 330fa60
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 11 deletions.
20 changes: 20 additions & 0 deletions crates/gitql-ast/src/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub enum ExpressionKind {
Logical,
Bitwise,
Call,
BenchmarkCall,
Between,
Case,
In,
Expand Down Expand Up @@ -497,6 +498,25 @@ impl Expression for CallExpression {
}
}

pub struct BenchmarkExpression {
pub expression: Box<dyn Expression>,
pub count: Box<dyn Expression>,
}

impl Expression for BenchmarkExpression {
fn kind(&self) -> ExpressionKind {
ExpressionKind::BenchmarkCall
}

fn expr_type(&self, _scope: &Environment) -> DataType {
DataType::Integer
}

fn as_any(&self) -> &dyn Any {
self
}
}

pub struct BetweenExpression {
pub value: Box<dyn Expression>,
pub range_start: Box<dyn Expression>,
Expand Down
21 changes: 21 additions & 0 deletions crates/gitql-engine/src/engine_evaluator.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use gitql_ast::expression::ArithmeticExpression;
use gitql_ast::expression::ArrayExpression;
use gitql_ast::expression::AssignmentExpression;
use gitql_ast::expression::BenchmarkExpression;
use gitql_ast::expression::BetweenExpression;
use gitql_ast::expression::BitwiseExpression;
use gitql_ast::expression::BooleanExpression;
Expand Down Expand Up @@ -189,6 +190,13 @@ pub fn evaluate_expression(
.unwrap();
evaluate_call(env, expr, titles, object)
}
BenchmarkCall => {
let expr = expression
.as_any()
.downcast_ref::<BenchmarkExpression>()
.unwrap();
evaluate_benchmark_call(env, expr, titles, object)
}
Between => {
let expr = expression
.as_any()
Expand Down Expand Up @@ -698,6 +706,19 @@ fn evaluate_call(
Ok(function(&arguments))
}

fn evaluate_benchmark_call(
env: &mut Environment,
expr: &BenchmarkExpression,
titles: &[String],
object: &Vec<Value>,
) -> Result<Value, String> {
let number_of_execution = evaluate_expression(env, &expr.count, titles, object)?;
for _ in 0..number_of_execution.as_int() {
evaluate_expression(env, &expr.expression, titles, object)?;
}
Ok(Value::Integer(0))
}

fn evaluate_between(
env: &mut Environment,
expr: &BetweenExpression,
Expand Down
57 changes: 57 additions & 0 deletions crates/gitql-parser/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2778,6 +2778,7 @@ fn parse_primary_expression(
TokenKind::LeftBracket => parse_array_value_expression(context, env, tokens, position),
TokenKind::LeftParen => parse_group_expression(context, env, tokens, position),
TokenKind::Case => parse_case_expression(context, env, tokens, position),
TokenKind::Benchmark => parse_benchmark_call_expression(context, env, tokens, position),
TokenKind::String => {
*position += 1;
Ok(Box::new(StringExpression {
Expand Down Expand Up @@ -2984,6 +2985,62 @@ fn parse_group_expression(
Ok(expression)
}

fn parse_benchmark_call_expression(
context: &mut ParserContext,
env: &mut Environment,
tokens: &[Token],
position: &mut usize,
) -> Result<Box<dyn Expression>, Box<Diagnostic>> {
// Consume `BENCHMARK` token
*position += 1;

if *position >= tokens.len() || tokens[*position].kind != TokenKind::LeftParen {
return Err(Diagnostic::error("Expect `(` after `Benchmark` keyword")
.with_location(get_safe_location(tokens, *position))
.add_help("Try to add '(' right after `Benchmark` keyword")
.as_boxed());
}

// Consume `(` token
*position += 1;

let count = parse_expression(context, env, tokens, position)?;
if !count.expr_type(env).is_int() {
return Err(
Diagnostic::error("Benchmark expect first argument to be integer")
.with_location(get_safe_location(tokens, *position))
.add_help("Try to integer value as first argument, eg: `Benchmark(10, 1 + 1)`")
.as_boxed(),
);
}

if *position >= tokens.len() || tokens[*position].kind != TokenKind::Comma {
return Err(
Diagnostic::error("Expect `,` after Benchmark first argument value")
.with_location(get_safe_location(tokens, *position))
.add_help("Make sure you passed two arguments to the Benchmark function")
.as_boxed(),
);
}

// Consume `,` token
*position += 1;

let expression = parse_expression(context, env, tokens, position)?;

if *position >= tokens.len() || tokens[*position].kind != TokenKind::RightParen {
return Err(Diagnostic::error("Expect `)` after `Benchmark` arguments")
.with_location(get_safe_location(tokens, *position))
.add_help("Try to add ')` after `Benchmark` arguments")
.as_boxed());
}

// Consume `)` token
*position += 1;

Ok(Box::new(BenchmarkExpression { expression, count }))
}

fn parse_case_expression(
context: &mut ParserContext,
env: &mut Environment,
Expand Down
4 changes: 4 additions & 0 deletions crates/gitql-parser/src/tokenizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ pub enum TokenKind {
Show,
RegExp,

Benchmark,

Join,
Left,
Right,
Expand Down Expand Up @@ -1159,6 +1161,8 @@ fn resolve_symbol_kind(literal: String) -> TokenKind {
"show" => TokenKind::Show,
"regexp" => TokenKind::RegExp,

"benchmark" => TokenKind::Benchmark,

// Select into
"into" => TokenKind::Into,
"outfile" => TokenKind::Outfile,
Expand Down
22 changes: 11 additions & 11 deletions docs/functions/other.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
### General functions

| Name | Parameters | Return | Description |
| --------- | ---------------- | ------- | ------------------------------------------------------------------------------ |
| ISNULL | ANY | Boolean | Return TRUE if the argument type is null. |
| ISNUMERIC | ANY | Boolean | Return TRUE if the argument type is number. |
| TYPEOF | ANY | Text | Return the argument type name. |
| GREATEST | ANY, Any, ...Any | Any | Return the greatest value from list of values |
| LEAST | ANY, Any, ...Any | Any | Return the smallest value from list of values |
| UUID | | Text | Return a Universal Unique Identifier |
| IF | Boolean, T, T | T | Return second argument if the condition is TRUE otherwise return last argument |
| IFNULL | T, T | T | Return second argument if first one is null, otherwise return first one |

| Name | Parameters | Return | Description |
| --------- | ------------------- | ------- | ------------------------------------------------------------------------------ |
| ISNULL | ANY | Boolean | Return TRUE if the argument type is null. |
| ISNUMERIC | ANY | Boolean | Return TRUE if the argument type is number. |
| TYPEOF | ANY | Text | Return the argument type name. |
| GREATEST | ANY, Any, ...Any | Any | Return the greatest value from list of values |
| LEAST | ANY, Any, ...Any | Any | Return the smallest value from list of values |
| UUID | | Text | Return a Universal Unique Identifier |
| IF | Boolean, T, T | T | Return second argument if the condition is TRUE otherwise return last argument |
| IFNULL | T, T | T | Return second argument if first one is null, otherwise return first one |
| BENCHMARK | Integer, Expression | Int(0) | Execute the expression n times and return 0 |

0 comments on commit 330fa60

Please sign in to comment.