Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/lykia-rs/lykiadb into featu…
Browse files Browse the repository at this point in the history
…re/planner
  • Loading branch information
can-keklik committed Sep 29, 2024
2 parents af82b8b + 35da50b commit 4f8c26c
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 117 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[workspace]
resolver= "2"
members = ["lykiadb-server", "lykiadb-shell", "lykiadb-lang", "lykiadb-playground", "lykiadb-connect"]
members = ["lykiadb-server", "lykiadb-shell", "lykiadb-lang", "lykiadb-connect"]
edition = "2021"

[profile.release]
Expand Down
29 changes: 13 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,35 +13,36 @@
</div>

<p align="center">
<img alt="LykiaDB logo" height="200" src="https://vcankeklik.com/assets/img/logo.svg?v=051223">
<img alt="LykiaDB logo" height="200" src="https://raw.githubusercontent.com/lykia-rs/lykiadb/refs/heads/main/assets/logo.svg">
</p>


Lykia is a toy multi-model database basically written for educational purposes.
Lykia is a document database management system built for educational purposes. The famous book, Crafting Interpreters, was the main source of inspiration for the project. It turned into a database, though.

## Overview
- Written in Rust
- A weird scripting and query language, combination of JavaScript and SQL. Built based on the language "Lox" which is explained in the famous book, Crafting Interpreters.
- 100% safe Rust, #BlazinglyFast
- A weird scripting and query language, combination of JavaScript and SQL. The language is a separate module thus can be used without the database.
- A subset of JSON data types in both scripting language itself and storage
- In-disk and in-memory storage
- ACID compliance
- Replication

## Roadmap

- [x] Core scripting language
- [x] Core scripting language + DML/DDL SQL
- [x] A minimal standard library
- [x] DML/DDL SQL
- [x] Event loop, client-server communication
- [x] Minimal playground app
- [x] Runtime
- [x] Playground app
- [ ] Query binding and planning (in progress)
- [ ] Bitcask storage engine
- [ ] LSM storage engine (based on [mini-lsm](https://github.com/lykia-rs/mini-lsm))
- [ ] MVCC for transaction management (based on [mini-lsm](https://github.com/lykia-rs/mini-lsm))
- [ ] Plan optimization
-----------------------------------------
- [ ] LSM storage engine (based on [mini-lsm](https://github.com/lykia-rs/mini-lsm))

## More ambitious roadmap:

- [ ] Basic replication with Raft


## Getting Started
To use Lykia, you can download the latest release from the GitHub releases page.

Expand All @@ -55,12 +56,8 @@ Run the shell:
```shell
$ cargo run --release --bin lykiadb-shell lykiadb-shell/examples/fib.ly
```
Run the playground:

```shell
$ cd lykiadb-playground
$ pnpm dev
```
For playground, please visit [lykia-rs/playground](https://github.com/lykia-rs/playground)

## License
Lykia is licensed under the Apache License, Version 2.0. See LICENSE for the full license text.
78 changes: 78 additions & 0 deletions assets/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 6 additions & 5 deletions lykiadb-server/src/engine/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ use super::error::ExecutionError;

use crate::plan::planner::Planner;
use crate::util::{alloc_shared, Shared};
use crate::value::callable::{Callable, CallableKind, Function, Stateful};
use crate::value::environment::{EnvId, Environment};
use crate::value::types::{eval_binary, Function, Stateful, RV};
use crate::value::types::{eval_binary, RV};

use std::sync::Arc;
use std::vec;
Expand Down Expand Up @@ -410,12 +411,12 @@ impl VisitorMut<RV, HaltReason> for Interpreter {
} => {
let eval = self.visit_expr(callee)?;

if let RV::Callable(arity, callable) = eval {
if arity.is_some() && arity.unwrap() != args.len() {
if let RV::Callable(callable) = eval {
if callable.arity.is_some() && callable.arity.unwrap() != args.len() {
return Err(HaltReason::Error(
InterpretError::ArityMismatch {
span: *span,
expected: arity.unwrap(),
expected: callable.arity.unwrap(),
found: args.len(),
}
.into(),
Expand Down Expand Up @@ -465,7 +466,7 @@ impl VisitorMut<RV, HaltReason> for Interpreter {
closure: self.env,
};

let callable = RV::Callable(Some(parameters.len()), fun.into());
let callable = RV::Callable(Callable::new(Some(parameters.len()), CallableKind::Generic, fun.into()));

if name.is_some() {
// TODO(vck): Callable shouldn't be cloned here
Expand Down
32 changes: 19 additions & 13 deletions lykiadb-server/src/engine/stdlib/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
use std::sync::Arc;

use rustc_hash::FxHashMap;

use crate::{
util::{alloc_shared, Shared},
value::types::{Function, RV},
value::{callable::{Callable, CallableKind, Function}, types::RV},
};

use self::{
Expand All @@ -30,40 +28,48 @@ pub fn stdlib(out: Option<Shared<Output>>) -> FxHashMap<String, RV> {

benchmark_namespace.insert(
"fib".to_owned(),
RV::Callable(Some(1), Arc::new(Function::Lambda { function: nt_fib })),
RV::Callable(Callable::new(Some(1), CallableKind::Generic, Function::Lambda { function: nt_fib })),
);

json_namespace.insert(
"stringify".to_owned(),
RV::Callable(
Some(1),
Arc::new(Function::Lambda {
function: nt_json_encode,
}),
Callable::new(
Some(1),
CallableKind::Generic,
Function::Lambda {
function: nt_json_encode,
}
)
),
);

json_namespace.insert(
"parse".to_owned(),
RV::Callable(
RV::Callable(Callable::new(
Some(1),
Arc::new(Function::Lambda {
CallableKind::Generic,
Function::Lambda {
function: nt_json_decode,
}),
),
);

time_namespace.insert(
"clock".to_owned(),
RV::Callable(Some(0), Arc::new(Function::Lambda { function: nt_clock })),
RV::Callable(Callable::new(
Some(0),
CallableKind::Generic,
Function::Lambda { function: nt_clock }
)),
);

if out.is_some() {
let mut test_namespace = FxHashMap::default();

test_namespace.insert(
"out".to_owned(),
RV::Callable(None, Arc::new(Function::Stateful(out.unwrap().clone()))),
RV::Callable(Callable::new(None, CallableKind::Generic, Function::Stateful(out.unwrap().clone()))),
);

std.insert(
Expand All @@ -80,7 +86,7 @@ pub fn stdlib(out: Option<Shared<Output>>) -> FxHashMap<String, RV> {
std.insert("Time".to_owned(), RV::Object(alloc_shared(time_namespace)));
std.insert(
"print".to_owned(),
RV::Callable(None, Arc::new(Function::Lambda { function: nt_print })),
RV::Callable(Callable::new(None, CallableKind::Generic, Function::Lambda { function: nt_print })),
);

std
Expand Down
86 changes: 86 additions & 0 deletions lykiadb-server/src/value/callable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use std::sync::Arc;
use std::fmt::{Debug, Display, Formatter};
use lykiadb_lang::ast::stmt::Stmt;
use crate::{engine::interpreter::{HaltReason, Interpreter}, util::Shared};
use super::environment::EnvId;
use super::types::RV;

#[derive(Debug, Clone)]
pub enum CallableKind {
Generic,
Aggregator,
}

#[derive(Clone, Debug)]
pub struct Callable {
pub arity: Option<usize>,
pub kind: CallableKind,
pub function: Arc<Function>,
}

impl Callable {
pub fn new(arity: Option<usize>, call_type: CallableKind, function: Function) -> Self {
Callable {
arity,
kind: call_type,
function: Arc::new(function),
}
}

pub fn call(&self, interpreter: &mut Interpreter, arguments: &[RV]) -> Result<RV, HaltReason> {
match self.function.as_ref() {
Function::Stateful(stateful) => stateful.write().unwrap().call(interpreter, arguments),
Function::Lambda { function } => function(interpreter, arguments),
Function::UserDefined {
name: _,
parameters,
closure,
body,
} => interpreter.user_fn_call(body, *closure, parameters, arguments),
}
}
}

pub trait Stateful {
fn call(&mut self, interpreter: &mut Interpreter, rv: &[RV]) -> Result<RV, HaltReason>;
}

#[derive(Clone)]
pub enum Function {
Lambda {
function: fn(&mut Interpreter, &[RV]) -> Result<RV, HaltReason>,
},
Stateful(Shared<dyn Stateful + Send + Sync>),
UserDefined {
name: String,
parameters: Vec<String>,
closure: EnvId,
body: Arc<Vec<Stmt>>,
},
}

impl Function {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Function::Stateful(_) | Function::Lambda { function: _ } => write!(f, "<native_fn>"),
Function::UserDefined {
name,
parameters: _,
closure: _,
body: _,
} => write!(f, "{}", name),
}
}
}

impl Debug for Function {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.fmt(f)
}
}

impl Display for Function {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.fmt(f)
}
}
1 change: 1 addition & 0 deletions lykiadb-server/src/value/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod environment;
pub mod types;
pub mod callable;
Loading

0 comments on commit 4f8c26c

Please sign in to comment.