feat: initial BaraDB — multimodal database engine in Nim

- LSM-Tree storage engine with WAL, bloom filter, MemTable
- BaraQL query language: lexer (80+ tokens), recursive descent parser, AST
- Vector engine: HNSW + IVF-PQ indexes, 4 distance metrics
- Graph engine: adjacency list, BFS/DFS, Dijkstra, PageRank
- Full-Text Search: inverted index, BM25 ranking, stemming, stop words
- Type system: 17 types (int/float/string/uuid/json/vector/...)
- Async TCP server
- 21 passing tests
This commit is contained in:
2026-05-06 00:22:12 +03:00
commit 6935889877
18 changed files with 2676 additions and 0 deletions
+245
View File
@@ -0,0 +1,245 @@
## BaraQL Parser — recursive descent parser
import std/strutils
import lexer
import ast
type
Parser* = object
tokens: seq[Token]
pos: int
proc newParser*(tokens: seq[Token]): Parser =
Parser(tokens: tokens, pos: 0)
proc peek(p: Parser): Token =
if p.pos < p.tokens.len:
return p.tokens[p.pos]
Token(kind: tkEof)
proc advance(p: var Parser): Token =
result = p.tokens[p.pos]
inc p.pos
proc expect(p: var Parser, kind: TokenKind): Token =
let tok = p.advance()
if tok.kind != kind:
raise newException(ValueError,
"Expected " & $kind & " but got " & $tok.kind & " at line " & $tok.line)
return tok
proc match(p: var Parser, kind: TokenKind): bool =
if p.peek().kind == kind:
discard p.advance()
return true
return false
proc parseExpr(p: var Parser): Node
proc parseSelect(p: var Parser): Node
proc parsePrimary(p: var Parser): Node =
let tok = p.peek()
case tok.kind
of tkIntLit:
discard p.advance()
Node(kind: nkIntLit, intVal: parseInt(tok.value), line: tok.line, col: tok.col)
of tkFloatLit:
discard p.advance()
Node(kind: nkFloatLit, floatVal: parseFloat(tok.value), line: tok.line, col: tok.col)
of tkStringLit:
discard p.advance()
Node(kind: nkStringLit, strVal: tok.value, line: tok.line, col: tok.col)
of tkBoolLit:
discard p.advance()
Node(kind: nkBoolLit, boolVal: tok.value == "true", line: tok.line, col: tok.col)
of tkNull:
discard p.advance()
Node(kind: nkNullLit, line: tok.line, col: tok.col)
of tkIdent:
discard p.advance()
Node(kind: nkIdent, identName: tok.value, line: tok.line, col: tok.col)
of tkLParen:
discard p.advance()
let expr = p.parseExpr()
discard p.expect(tkRParen)
expr
of tkLBracket:
discard p.advance()
var elems: seq[Node] = @[]
if p.peek().kind != tkRBracket:
elems.add(p.parseExpr())
while p.match(tkComma):
elems.add(p.parseExpr())
discard p.expect(tkRBracket)
Node(kind: nkArrayLit, arrayElems: elems, line: tok.line, col: tok.col)
of tkSelect:
p.parseSelect()
of tkExists:
discard p.advance()
discard p.expect(tkLParen)
let sub = p.parseSelect()
discard p.expect(tkRParen)
Node(kind: nkExists, existsExpr: sub, line: tok.line, col: tok.col)
of tkNot:
discard p.advance()
let operand = p.parsePrimary()
Node(kind: nkUnaryOp, unOp: ukNot, unOperand: operand, line: tok.line, col: tok.col)
of tkMinus:
discard p.advance()
let operand = p.parsePrimary()
Node(kind: nkUnaryOp, unOp: ukNeg, unOperand: operand, line: tok.line, col: tok.col)
of tkCount:
discard p.advance()
discard p.expect(tkLParen)
var args: seq[Node] = @[]
if p.peek().kind != tkRParen:
args.add(p.parseExpr())
discard p.expect(tkRParen)
Node(kind: nkFuncCall, funcName: "count", funcArgs: args, line: tok.line, col: tok.col)
else:
discard p.advance()
Node(kind: nkNullLit, line: tok.line, col: tok.col)
proc parseMulDiv(p: var Parser): Node =
result = p.parsePrimary()
while p.peek().kind in {tkStar, tkSlash, tkPercent, tkFloorDiv}:
let op = case p.peek().kind
of tkStar: bkMul
of tkSlash: bkDiv
of tkPercent: bkMod
of tkFloorDiv: bkFloorDiv
else: bkMul
let tok = p.advance()
let right = p.parsePrimary()
result = Node(kind: nkBinOp, binOp: op, binLeft: result, binRight: right,
line: tok.line, col: tok.col)
proc parseAddSub(p: var Parser): Node =
result = p.parseMulDiv()
while p.peek().kind in {tkPlus, tkMinus, tkConcat}:
let op = case p.peek().kind
of tkPlus: bkAdd
of tkMinus: bkSub
of tkConcat: bkConcat
else: bkAdd
let tok = p.advance()
let right = p.parseMulDiv()
result = Node(kind: nkBinOp, binOp: op, binLeft: result, binRight: right,
line: tok.line, col: tok.col)
proc parseComparison(p: var Parser): Node =
result = p.parseAddSub()
while p.peek().kind in {tkEq, tkNotEq, tkLt, tkLtEq, tkGt, tkGtEq}:
let op = case p.peek().kind
of tkEq: bkEq
of tkNotEq: bkNotEq
of tkLt: bkLt
of tkLtEq: bkLtEq
of tkGt: bkGt
of tkGtEq: bkGtEq
else: bkEq
let tok = p.advance()
let right = p.parseAddSub()
result = Node(kind: nkBinOp, binOp: op, binLeft: result, binRight: right,
line: tok.line, col: tok.col)
proc parseNot(p: var Parser): Node =
if p.peek().kind == tkNot:
let tok = p.advance()
let operand = p.parseComparison()
return Node(kind: nkUnaryOp, unOp: ukNot, unOperand: operand,
line: tok.line, col: tok.col)
return p.parseComparison()
proc parseAnd(p: var Parser): Node =
result = p.parseNot()
while p.peek().kind == tkAnd:
let tok = p.advance()
let right = p.parseNot()
result = Node(kind: nkBinOp, binOp: bkAnd, binLeft: result, binRight: right,
line: tok.line, col: tok.col)
proc parseOr(p: var Parser): Node =
result = p.parseAnd()
while p.peek().kind == tkOr:
let tok = p.advance()
let right = p.parseAnd()
result = Node(kind: nkBinOp, binOp: bkOr, binLeft: result, binRight: right,
line: tok.line, col: tok.col)
proc parseExpr(p: var Parser): Node =
return p.parseOr()
proc parseSelect(p: var Parser): Node =
let tok = p.expect(tkSelect)
result = Node(kind: nkSelect, line: tok.line, col: tok.col)
if p.peek().kind == tkDistinct:
discard p.advance()
result.selDistinct = true
result.selResult = @[]
result.selResult.add(p.parseExpr())
while p.match(tkComma):
result.selResult.add(p.parseExpr())
if p.match(tkFrom):
let tableTok = p.expect(tkIdent)
var alias = ""
if p.match(tkAs):
alias = p.expect(tkIdent).value
elif p.peek().kind == tkIdent:
alias = p.advance().value
result.selFrom = Node(kind: nkFrom, fromTable: tableTok.value,
fromAlias: alias, line: tableTok.line, col: tableTok.col)
if p.match(tkWhere):
result.selWhere = Node(kind: nkWhere, whereExpr: p.parseExpr())
if p.match(tkLimit):
result.selLimit = Node(kind: nkLimit, limitExpr: p.parseExpr())
if p.match(tkOffset):
result.selOffset = Node(kind: nkOffset, offsetExpr: p.parseExpr())
proc parseInsert(p: var Parser): Node =
let tok = p.expect(tkInsert)
let target = p.expect(tkIdent).value
result = Node(kind: nkInsert, insTarget: target, line: tok.line, col: tok.col)
proc parseUpdate(p: var Parser): Node =
let tok = p.expect(tkUpdate)
let target = p.expect(tkIdent).value
result = Node(kind: nkUpdate, updTarget: target, line: tok.line, col: tok.col)
proc parseDelete(p: var Parser): Node =
let tok = p.expect(tkDelete)
let target = p.expect(tkIdent).value
result = Node(kind: nkDelete, delTarget: target, line: tok.line, col: tok.col)
proc parseCreateType(p: var Parser): Node =
let tok = p.expect(tkCreate)
discard p.expect(tkType)
let name = p.expect(tkIdent).value
result = Node(kind: nkCreateType, ctName: name, line: tok.line, col: tok.col)
proc parseStatement*(p: var Parser): Node =
case p.peek().kind
of tkSelect: p.parseSelect()
of tkInsert: p.parseInsert()
of tkUpdate: p.parseUpdate()
of tkDelete: p.parseDelete()
of tkCreate: p.parseCreateType()
else:
let tok = p.advance()
Node(kind: nkNullLit, line: tok.line, col: tok.col)
proc parse*(tokens: seq[Token]): Node =
var parser = newParser(tokens)
result = Node(kind: nkStatementList)
while parser.peek().kind != tkEof:
result.stmts.add(parser.parseStatement())
discard parser.match(tkSemicolon)
proc parse*(input: string): Node =
let tokens = tokenize(input)
parse(tokens)