Skip to content

Commit

Permalink
functions
Browse files Browse the repository at this point in the history
  • Loading branch information
mnbjhu committed Jan 28, 2024
1 parent 16f8059 commit dc17294
Show file tree
Hide file tree
Showing 30 changed files with 748 additions and 54 deletions.
60 changes: 60 additions & 0 deletions history.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,63 @@ return {\n let $x = 1;\n $x;\n $x;\n};\n
return {\n let $x = 1;\n return $x;\n};\n
return {\n let $x = 1;\n let $y = 10;\n let $z = $x + $y;\n return $x;\n};\n
return {\n let $x = 5;\n let $y = 10;\n let $z = $x + $y;\n return $z;\n};\n
let $created = (create todo content { title: "test", description: "test" });\nreturn $created[0].title;\n
use ns test; use db test;
let $created = (create todo content { title: "test", description: "test" });\nreturn $created[0].title;\n
let $created = (create todo content { title: "test", description: "test" });\nreturn $created.title;\n
let $created = (create todo content { \n title: "test", \n description: "test",\n tasks: ["test1", "test2"]\n});\nreturn $created.tasks;\n
let $nested_array_of_obj = [\n [\n { test: "data1"},\n { test: "data2"}\n ],\n [\n { test: "data3"},\n { test: "data4"}\n ]\n];\n\nreturn $nested_array_of_obj.test;\n
create some:["first_id", 123];
use ns test
use db test
create some:["first_id", 123];
select * from some
select * from some:['first_id']
select * from some:['first_id', NONE]
select * from some:['first_id', 123]
create other:{test:"thing"}
create other:123
create other:thing
let $id = {test: "thing"}
return $id
select * from ""
use ns test
use db test
select * from ""
return select * from ""
select * from ""
return select * from []
select * from ""
select * from "123"
select * from 123
select * from null
select * from count()
select count() from count()
select count() from ""
select "test" as thing, * from "text"
select *, "other" as thing from "text"
select *, "other" as thing from { first: "thing" }
select *, "other" as thing from { first: "thing" }; return "hello"
create test
return create test
return create test:123
let $test = create test
return $test
let $test = create test:123
let $test = create test:1234
return $test
let $thing = "123"
return $thing
select * from test:123
create test return id
create test return value id
let $arr = [1, 2, 3];
$arr[where true]
$arr[where false]
$arr[where value > 1]
$arr[where $value > 1]
$arr[where $value]
let $arr = [{test: "thing1"}, {test:"thing2"}];
$arr[where test = "thing1"]
select * from todo;
SELECT * FROM person WHERE ->(reacted_to WHERE type='celebrate')->post;
1 change: 1 addition & 0 deletions src/ast/expr/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub enum Expression {
Object(Vec<Spanned<ObjectEntry>>),
Variable(String),
CodeBlock(Vec<Spanned<Statement>>),
Inline(Box<Spanned<Statement>>),
Call {
name: Vec<Spanned<String>>,
args: Option<Vec<Spanned<Expression>>>,
Expand Down
40 changes: 39 additions & 1 deletion src/ast/expr/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,19 @@ impl Typed for Expression {
let ty = expr.0.get_type(scope);
match &access.0.as_ref() {
Access::Property(name) => {
let mut array_nest_count = 0;
let mut ty = ty;
while let Type::Array(inner_ty) = ty {
ty = *inner_ty.clone();
array_nest_count += 1;
}
if let Type::Object(obj) = ty {
if let Some(field) = &obj.get_field(name) {
return field.ty.clone();
let mut ty = field.ty.clone();
for _ in 0..array_nest_count {
ty = Type::Array(Box::new(ty));
}
return ty;
};
};
Type::Error
Expand All @@ -60,12 +70,20 @@ impl Typed for Expression {
Expression::Object(object) => {
let mut fields = Vec::new();
for field in object {
let value = match &field.0.value {
Some(expr) => expr.0.get_type(scope),
None => Type::Any,
};
fields.push(Field {
name: field.0.key.0.clone(),
ty: match &field.0.value {
Some(expr) => expr.0.get_type(scope),
None => Type::Error,
},
is_required: match value {
Type::Option(_) => false,
_ => true,
},
});
}
Type::Object(Object { fields })
Expand All @@ -81,6 +99,26 @@ impl Typed for Expression {
}
Type::Array(Box::new(ty))
}
Expression::Call { name, args } => {
let name = name
.iter()
.map(|name| name.0.clone())
.collect::<Vec<String>>()
.join("::");
if let Some(def) = &scope.functions.get(&name) {
if let Some(args) = args {
let arg_types = args
.iter()
.map(|arg| arg.0.get_type(scope))
.collect::<Vec<Type>>();
return def.get_return_type(arg_types);
};
def.get_return_type(vec![])
} else {
Type::Error
}
}
Expression::Inline(s) => s.as_ref().0.get_type(scope),
_ => Type::Error,
}
}
Expand Down
1 change: 1 addition & 0 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ pub mod expr;
pub mod parser;
pub mod projection;
pub mod statement;
pub mod target;
pub mod type_;
7 changes: 7 additions & 0 deletions src/ast/target.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use crate::util::span::Spanned;

pub enum Target {
Table(Spanned<String>),
Record(Spanned<String>, Spanned<String>),
}

1 change: 1 addition & 0 deletions src/declarations/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ use super::type_::Type;
pub struct Field {
pub name: String,
pub ty: Type,
pub is_required: bool,
}
127 changes: 127 additions & 0 deletions src/declarations/func/count.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// r#"## `count`
//
// The count function counts the number of times that the function is called. This is useful for returning the total number of rows in a SELECT statement with a `GROUP BY` clause.
//
// ### API Definition
// ```
// count() -> 1
// ```
// If a value is given as the first argument, then this function checks whether a given value is truthy. This is useful for returning the total number of rows, which match a certain condition, in a [`SELECT`](/docs/surrealql/statements/select) statement, with a GROUP BY clause.
//
// ### API Definition
// ```
// count(any) -> number
// ```
//
// If an array is given, this function counts the number of items in the array which are truthy. If, instead, you want to count the total number of items in the given array, then use the [`array::len()`](/docs/surrealql/functions/array#len) function.
//
// ### API Definition
// ```
// count(array) -> number
// ```
// The following example shows this function, and its output, when used in a [`RETURN`](/docs/surrealql/statements/return) statement:
//
// ```
// RETURN count();
//
// 1
// ```
// ```
// RETURN count(true);
//
// 1
// ```
// ```
// RETURN count(10 > 15);
//
// 0
// ```
// ```
// RETURN count([ 1, 2, 3, null, 0, false, (15 > 10), rand::uuid() ]);
//
// 5
// ```
// The following examples show this function being used in a [`SELECT`](/docs/surrealql/statements/select) statement with a GROUP clause:
//
// ```
// SELECT count() FROM [{ age: 33 }, { age: 45 }, { age: 39 }] GROUP ALL;
//
// 3
// ```
// ```
// SELECT count(age > 35) FROM [{ age: 33 }, { age: 45 }, { age: 39 }] GROUP ALL;
//
// 2
// ```
// An advanced example of the count function can be seen below:
//
// ```
// SELECT
// country,
// count(age > 30) AS total
// FROM [
// { age: 33, country: 'GBR' },
// { age: 45, country: 'GBR' },
// { age: 39, country: 'USA' },
// { age: 29, country: 'GBR' },
// { age: 43, country: 'USA' }
// ]
// GROUP BY country;
// ```
// ```json
// [
// {
// country: 'GBR',
// total: 2
// },
// {
// country: 'USA',
// total: 2
// }
// ]
// ```"#.to_string(),
//

use std::collections::HashMap;

use crate::declarations::{
functions::{Function, GenericType},
type_::Type,
};

pub fn get_count_functions() -> HashMap<String, Function> {
let mut map: HashMap<String, Function> = HashMap::new();

map.insert(
"count".to_string(),
Function {
args: vec![],
return_type: GenericType::Named(Type::Int),
doc: Some(r#"
## Count
The count function counts the number of times that the function is called. This is useful for returning the total number of rows in a SELECT statement with a `GROUP BY` clause.
### Examples
```surqls
SELECT
count() AS total
FROM [
{ age: 33 },
{ age: 45 },
{ age: 39 }
]
GROUP ALL;
```
```json
[
{
total: 3
}
]
```
"#.to_string()),
},
);

map
}
1 change: 1 addition & 0 deletions src/declarations/func/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod count;
106 changes: 106 additions & 0 deletions src/declarations/functions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use std::{collections::HashMap, fmt::Display};

use super::{func::count::get_count_functions, type_::Type};

#[derive(Clone, Debug)]
pub enum GenericType {
Named(Type),
TypeParam { super_: Type },
GenericArray { inner_super_: Type },
GenericOption { inner_super_: Type },
}

impl Display for GenericType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GenericType::Named(ref t) => write!(f, "{}", t),
GenericType::TypeParam { super_: _ } => write!(f, "T"),
GenericType::GenericArray { inner_super_: _ } => write!(f, "array<T>"),
GenericType::GenericOption { inner_super_: _ } => write!(f, "option<T>"),
}
}
}

#[derive(Clone, Debug)]
pub struct Function {
pub args: Vec<FunctionArg>,
pub return_type: GenericType,
pub doc: Option<String>,
}

#[derive(Clone, Debug)]
pub struct FunctionArg(pub String, pub GenericType);

impl Function {
pub fn get_type(&self, arg: Type) -> Type {
match self.return_type {
GenericType::Named(ref t) => t.clone(),
GenericType::TypeParam { super_: _ } => arg,
GenericType::GenericArray { inner_super_: _ } => Type::Array(Box::new(arg)),
GenericType::GenericOption { inner_super_: _ } => Type::Option(Box::new(arg)),
}
}

pub fn get_arg_type(&self, args: Vec<Type>) -> Type {
let mut found: Option<Type> = None;
for (index, FunctionArg(_, expected)) in self.args.iter().enumerate() {
let actual = args.get(index).unwrap();
match expected {
GenericType::Named(ref t) => {}
GenericType::TypeParam { super_ } => {
let found_type = if actual.is_assignable_to(super_) {
actual.clone()
} else {
Type::Error
};
let new_type = match found {
Some(ref found_type) => found_type.get_shared_super_type(&found_type),
None => found_type,
};
let found = Some(new_type);
}
GenericType::GenericArray { inner_super_ } => {
if let Type::Array(ref t) = actual {
if t.is_assignable_to(inner_super_) {
return t.as_ref().clone();
} else {
return Type::Error;
}
}
}
GenericType::GenericOption { inner_super_ } => {
if let Type::Option(ref t) = actual {
if t.is_assignable_to(inner_super_) {
return t.as_ref().clone();
} else {
return Type::Error;
}
}
}
}
}
match found {
Some(t) => t,
None => Type::Any,
}
}

pub fn get_return_type(&self, args: Vec<Type>) -> Type {
let arg_type = self.get_arg_type(args);
self.get_type(arg_type)
}
}

impl Display for Function {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut args = vec![];
for arg in &self.args {
args.push(format!("{}: {}", arg.0, arg.1));
}
write!(f, "({}) -> {}", args.join(", "), self.return_type)
}
}

pub fn get_functions() -> HashMap<String, Function> {
return get_count_functions();
}
Loading

0 comments on commit dc17294

Please sign in to comment.