A typed in-memory key-value store with a custom language, lexer, parser, and interpreter written in OCaml. Features a custom linked-list storage engine with support for multiple data types.
asdf-kvs-demo-hd.mov
Build the project:
dune buildRun the interactive REPL:
dune exec replRun the demo program:
dune exec mainStart the TCP server:
./start_server.sh
# or directly
dune exec serverConnect a TCP client:
./start_client.sh
# or directly
dune exec clientRun tests:
dune runtestTest TCP server/client:
./test_tcp.shcreate "store_name"; # Create a new store
use "store_name"; # Select a store for operations
unselect "store_name"; # Unselect the current store
drop "store_name"; # Delete a store
set "key" <type> "value"; # Set a typed key-value pair (requires selected store)
get "key"; # Get a value by key (requires selected store)
dump "store_name"; # Display all keys in a store
save "filename"; # Save store to file (stub - not yet implemented)
load "filename"; # Load store from file (stub - not yet implemented)
i32- 32-bit signed integersi64- 64-bit signed integersf32- 32-bit floating pointf64- 64-bit floating pointstring- text values
- Type System - Strongly typed values with support for integers, floats, and strings
- Custom Storage Engine - Linked-list based storage implementation
- TCP Server/Client - Multi-client TCP server with networked access
- Multiple stores - Create and manage multiple independent key-value stores
- Store selection - Select a store to operate on using
usecommand - Nested expressions - Commands can be nested, e.g.,
set get "x" string "y" - Multiple statements - Separate commands with semicolons
- Interactive REPL - Beautiful colored REPL with helpful prompts
The project consists of several layers:
- Lexer (
src/lexer.ml) - Tokenizes input text into tokens - Parser (
src/parser.ml) - Parses tokens into an abstract syntax tree (AST) - Interpreter (
src/interpreter.ml) - Evaluates the AST and executes commands - Storage (
src/storage.ml) - Custom linked-list storage engine with typed data support - Server (
src/server.ml) - Multi-threaded TCP server for networked access - Client (
src/client.ml) - TCP client for connecting to remote server - REPL (
src/repl.ml) - Interactive command-line interface - Main (
src/main.ml) - Demo program
Expressions:
create <expr>- Create a storeuse <expr>- Select a store (internally mapped to SELECT)unselect <expr>- Unselect a storedrop <expr>- Drop a storeset <expr> <type> <expr>- Set a typed key-value pairget <expr>- Get a valuedump <expr>- Dump store contentssave <expr>- Save to file (stub)load <expr>- Load from file (stub)
Types:
i32- 32-bit signed integeri64- 64-bit signed integerf32- 32-bit floating pointf64- 64-bit floating pointstring- text value
Values:
- String literals:
"text" - Names: unquoted identifiers
Statements:
- Multiple expressions separated by
;
asdf-kvs includes a multi-threaded TCP server that allows multiple clients to connect and share the same key-value store.
./start_server.sh
# Server starts on 127.0.0.1:9090The server:
- Accepts multiple concurrent client connections
- Maintains shared state across all clients
- Uses threads to handle each client independently
- Supports all commands available in the REPL
# Terminal 1
./start_client.sh
# Terminal 2
./start_client.sh
# Terminal 3 (custom host/port)
./start_client.sh 127.0.0.1 9090# Client 1
create "shared_db"
use "shared_db"
set "key1" string "value1"
# Client 2 (connects to same server)
use "shared_db"
get "key1" # Returns "value1"
set "key2" i32 "42"
# Client 1
dump "shared_db" # Shows both key1 and key2
create "users";
use "users";
set "alice" string "admin";
set "bob" string "user";
get "alice";
dump "users";
create "mydb";
use "mydb";
set "age" i32 "30";
set "timestamp" i64 "1234567890";
set "pi" f32 "3.14159";
set "e" f64 "2.71828";
set "name" string "Alice";
dump "mydb";
create "store";
use "store";
set "key1" string "value1";
set get "key1" string "value2";
This sets the value of the key returned by get "key1" to "value2".
create "users";
create "products";
use "users";
set "alice" string "admin";
use "products";
set "laptop" f64 "999.99";
dump "users";
dump "products";
The storage engine uses a custom linked-list implementation with the following characteristics:
- Type-safe storage: Each value is stored with its specific type (i32, i64, f32, f64, or string)
- O(n) operations: Linear time complexity for get, set, and delete operations
- Modular design: Easy to swap with other implementations (hash table, B-tree, etc.)
- Independent instances: Each store is a separate linked list instance
val create : unit -> t
val set : t -> string -> data -> unit
val get : t -> string -> data option
val delete : t -> string -> bool
val exists : t -> string -> bool
val size : t -> int
val clear : t -> unit
val dump : t -> unit- ✅ In-memory storage with multiple stores
- ✅ Custom linked-list storage engine
- ✅ Type system with 5 data types (i32, i64, f32, f64, string)
- ✅ Lexer and parser for custom language
- ✅ Interpreter for command execution
- ✅ Interactive REPL with colors and nice UI
- ✅ Nested expressions
- ✅ Multi-threaded TCP server
- ✅ TCP client for remote connections
- ✅ Shared state across multiple clients
- ⏳ File persistence (save/load commands are stubs)
- ⏳ Write Ahead Log
- ⏳ Concurrency control (currently no locking)
- ⏳ Benchmarking
- ⏳ Authentication and security
- ⏳ B-tree or other advanced storage structures
- ⏳ Indexing and query optimization
- ⏳ So many things...