Skip to content

Commit ee1cd41

Browse files
authored
refact: refactor Scope to use arena-based StrRef keys (#25)
1 parent 3adad53 commit ee1cd41

14 files changed

Lines changed: 175 additions & 191 deletions

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ thiserror = "2"
1616
nom = "8"
1717
nom_locate = "5"
1818
unicase = "2"
19-
case_insensitive_hashmap = "1"
2019

2120
[dependencies.serde]
2221
version = "1"

src/arena.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use rustc_hash::{FxBuildHasher, FxHashMap};
44
use serde::Serialize;
55
use std::collections::hash_map::Entry;
66
use std::hash::BuildHasher;
7+
use unicase::Ascii;
78

89
/// An arena-based allocator for interning strings.
910
///
@@ -32,6 +33,24 @@ impl StringArena {
3233
}
3334
}
3435

36+
/// Interns a string using case-insensitive hashing.
37+
///
38+
/// Two strings that differ only in ASCII case will resolve to the same [`StrRef`].
39+
/// The original casing of the first insertion is preserved.
40+
pub fn alloc_no_case(&mut self, value: &str) -> StrRef {
41+
let hash = Ascii::new(value);
42+
match self.cache.entry(self.hasher.hash_one(hash)) {
43+
Entry::Occupied(entry) => *entry.get(),
44+
Entry::Vacant(entry) => {
45+
let key = StrRef(self.slots.len());
46+
entry.insert(key);
47+
self.slots.push(value.to_owned());
48+
49+
key
50+
}
51+
}
52+
}
53+
3554
/// Retrieves the string associated with the given [`StrRef`].
3655
pub fn get(&self, key: StrRef) -> &str {
3756
&self.slots[key.0]

src/ast.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -219,10 +219,10 @@ pub enum Value {
219219
///
220220
/// # Examples
221221
/// in `FROM e IN events`, `e` is the binding.
222-
#[derive(Debug, Clone, Serialize)]
222+
#[derive(Debug, Clone, Copy, Serialize)]
223223
pub struct Binding {
224224
/// Name attached to a source of events
225-
pub name: String,
225+
pub name: StrRef,
226226
/// Position in the source code where that binding was introduced
227227
pub pos: Pos,
228228
}
@@ -254,9 +254,9 @@ pub struct Source<A> {
254254
#[derive(Debug, Clone, Serialize)]
255255
pub enum SourceKind<A> {
256256
/// Named source (identifier)
257-
Name(String),
257+
Name(StrRef),
258258
/// Subject pattern (string literal used as event subject pattern)
259-
Subject(String),
259+
Subject(StrRef),
260260
/// Nested subquery
261261
Subquery(Box<Query<A>>),
262262
}

src/lib.rs

Lines changed: 42 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,10 @@ impl SessionBuilder {
195195
) -> Self {
196196
if test {
197197
let builder = args.into();
198+
let name = self.arena.strings.alloc_no_case(name);
198199
let args = self.arena.types.alloc_args(builder.args);
199200

200-
self.options.default_scope.entries.insert(
201+
self.options.default_scope.declare(
201202
name,
202203
Type::App {
203204
args: FunArgs {
@@ -252,9 +253,10 @@ impl SessionBuilder {
252253
) -> Self {
253254
if test {
254255
let builder = args.into();
256+
let name = self.arena.strings.alloc_no_case(name);
255257
let args = self.arena.types.alloc_args(builder.args);
256258

257-
self.options.default_scope.entries.insert(
259+
self.options.default_scope.declare(
258260
name,
259261
Type::App {
260262
args: FunArgs {
@@ -343,64 +345,64 @@ impl SessionBuilder {
343345
/// `declare_func` and `declare_agg_func` for all standard library functions,
344346
/// and `declare_event_type` for the default event structure.
345347
pub fn use_stdlib(self) -> Self {
346-
self.declare_func("ABS", &[Type::Number], Type::Number)
347-
.declare_func("CEIL", &[Type::Number], Type::Number)
348-
.declare_func("FLOOR", &[Type::Number], Type::Number)
349-
.declare_func("ROUND", &[Type::Number], Type::Number)
350-
.declare_func("COS", &[Type::Number], Type::Number)
351-
.declare_func("EXP", &[Type::Number], Type::Number)
352-
.declare_func("POW", &[Type::Number, Type::Number], Type::Number)
353-
.declare_func("SQRT", &[Type::Number], Type::Number)
354-
.declare_func("RAND", &[], Type::Number)
355-
.declare_func("PI", &[Type::Number], Type::Number)
356-
.declare_func("LOWER", &[Type::String], Type::String)
357-
.declare_func("UPPER", &[Type::String], Type::String)
358-
.declare_func("TRIM", &[Type::String], Type::String)
359-
.declare_func("LTRIM", &[Type::String], Type::String)
360-
.declare_func("RTRIM", &[Type::String], Type::String)
361-
.declare_func("LEN", &[Type::String], Type::Number)
362-
.declare_func("INSTR", &[Type::String], Type::Number)
348+
self.declare_func("abs", &[Type::Number], Type::Number)
349+
.declare_func("ceil", &[Type::Number], Type::Number)
350+
.declare_func("floor", &[Type::Number], Type::Number)
351+
.declare_func("round", &[Type::Number], Type::Number)
352+
.declare_func("cos", &[Type::Number], Type::Number)
353+
.declare_func("exp", &[Type::Number], Type::Number)
354+
.declare_func("pow", &[Type::Number, Type::Number], Type::Number)
355+
.declare_func("sqrt", &[Type::Number], Type::Number)
356+
.declare_func("rand", &[], Type::Number)
357+
.declare_func("pi", &[Type::Number], Type::Number)
358+
.declare_func("lower", &[Type::String], Type::String)
359+
.declare_func("upper", &[Type::String], Type::String)
360+
.declare_func("trim", &[Type::String], Type::String)
361+
.declare_func("ltrim", &[Type::String], Type::String)
362+
.declare_func("rtrim", &[Type::String], Type::String)
363+
.declare_func("len", &[Type::String], Type::Number)
364+
.declare_func("instr", &[Type::String], Type::Number)
363365
.declare_func(
364-
"SUBSTRING",
366+
"substring",
365367
&[Type::String, Type::Number, Type::Number],
366368
Type::String,
367369
)
368370
.declare_func(
369-
"REPLACE",
371+
"replace",
370372
&[Type::String, Type::String, Type::String],
371373
Type::String,
372374
)
373-
.declare_func("STARTSWITH", &[Type::String, Type::String], Type::Bool)
374-
.declare_func("ENDSWITH", &[Type::String, Type::String], Type::Bool)
375-
.declare_func("NOW", &[], Type::DateTime)
376-
.declare_func("YEAR", &[Type::Date], Type::Number)
377-
.declare_func("MONTH", &[Type::Date], Type::Number)
378-
.declare_func("DAY", &[Type::Date], Type::Number)
379-
.declare_func("HOUR", &[Type::Time], Type::Number)
380-
.declare_func("MINUTE", &[Type::Time], Type::Number)
381-
.declare_func("SECOND", &[Type::Time], Type::Number)
382-
.declare_func("WEEKDAY", &[Type::Date], Type::Number)
375+
.declare_func("startswith", &[Type::String, Type::String], Type::Bool)
376+
.declare_func("endswith", &[Type::String, Type::String], Type::Bool)
377+
.declare_func("now", &[], Type::DateTime)
378+
.declare_func("year", &[Type::Date], Type::Number)
379+
.declare_func("month", &[Type::Date], Type::Number)
380+
.declare_func("day", &[Type::Date], Type::Number)
381+
.declare_func("hour", &[Type::Time], Type::Number)
382+
.declare_func("minute", &[Type::Time], Type::Number)
383+
.declare_func("second", &[Type::Time], Type::Number)
384+
.declare_func("weekday", &[Type::Date], Type::Number)
383385
.declare_func(
384386
"IF",
385387
&[Type::Bool, Type::Unspecified, Type::Unspecified],
386388
Type::Unspecified,
387389
)
388390
.declare_agg_func(
389-
"COUNT",
391+
"count",
390392
FunArgsBuilder {
391393
args: &[Type::Bool],
392394
required: 0,
393395
},
394396
Type::Number,
395397
)
396-
.declare_agg_func("SUM", &[Type::Number], Type::Number)
397-
.declare_agg_func("AVG", &[Type::Number], Type::Number)
398-
.declare_agg_func("MIN", &[Type::Number], Type::Number)
399-
.declare_agg_func("MAX", &[Type::Number], Type::Number)
400-
.declare_agg_func("MEDIAN", &[Type::Number], Type::Number)
401-
.declare_agg_func("STDDEV", &[Type::Number], Type::Number)
402-
.declare_agg_func("VARIANCE", &[Type::Number], Type::Number)
403-
.declare_agg_func("UNIQUE", &[Type::Unspecified], Type::Unspecified)
398+
.declare_agg_func("sum", &[Type::Number], Type::Number)
399+
.declare_agg_func("avg", &[Type::Number], Type::Number)
400+
.declare_agg_func("min", &[Type::Number], Type::Number)
401+
.declare_agg_func("max", &[Type::Number], Type::Number)
402+
.declare_agg_func("median", &[Type::Number], Type::Number)
403+
.declare_agg_func("stddev", &[Type::Number], Type::Number)
404+
.declare_agg_func("variance", &[Type::Number], Type::Number)
405+
.declare_agg_func("unique", &[Type::Unspecified], Type::Unspecified)
404406
.declare_event_type()
405407
.record()
406408
.prop("specversion", Type::String)

src/parser.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ impl<'a> Parser<'a> {
8282
fn parse_source_kind(&mut self) -> ParseResult<SourceKind<Raw>> {
8383
let token = self.shift();
8484
match token.sym {
85-
Sym::Id(id) => Ok(SourceKind::Name(id.to_owned())),
86-
Sym::String(sub) => Ok(SourceKind::Subject(sub.to_owned())),
85+
Sym::Id(id) => Ok(SourceKind::Name(self.arena.strings.alloc_no_case(id))),
86+
Sym::String(sub) => Ok(SourceKind::Subject(self.arena.strings.alloc(sub))),
8787
Sym::Symbol(Symbol::OpenParen) => {
8888
let query = self.parse_query()?;
8989
expect_symbol(self.shift(), Symbol::CloseParen)?;
@@ -103,8 +103,10 @@ impl<'a> Parser<'a> {
103103

104104
let token = self.shift();
105105
let binding = if let Sym::Id(name) = token.sym {
106+
let name = self.arena.strings.alloc_no_case(name);
107+
106108
Binding {
107-
name: name.to_owned(),
109+
name,
108110
pos: token.into(),
109111
}
110112
} else {
@@ -264,13 +266,13 @@ impl<'a> Parser<'a> {
264266
let args = self.arena.exprs.alloc_vec(args);
265267

266268
Value::App(App {
267-
func: self.arena.strings.alloc(name),
269+
func: self.arena.strings.alloc_no_case(name),
268270
args,
269271
})
270272
} else if matches!(self.peek().sym, Sym::Symbol(Symbol::Dot)) {
271273
self.shift();
272274
let attrs = token.into();
273-
let name = self.arena.strings.alloc(name);
275+
let name = self.arena.strings.alloc_no_case(name);
274276
let mut access = Access {
275277
target: self.arena.exprs.alloc(attrs, Value::Id(name)),
276278
field: self.parse_ident()?,
@@ -286,7 +288,7 @@ impl<'a> Parser<'a> {
286288

287289
Value::Access(access)
288290
} else {
289-
Value::Id(self.arena.strings.alloc(name))
291+
Value::Id(self.arena.strings.alloc_no_case(name))
290292
}
291293
}
292294

src/tests/analysis.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,7 @@ fn test_typecheck_datetime_contravariance_1() {
207207
let event_type = session.options.event_type_info;
208208
let mut analysis = session.analysis();
209209

210-
analysis
211-
.scope_mut()
212-
.entries
213-
.insert("e".to_string(), event_type);
210+
analysis.test_declare("e", event_type);
214211

215212
// `e.time` is a `Type::DateTime` but it will typecheck if a `Type::Date` is expected
216213
insta::assert_yaml_snapshot!(analysis.analyze_expr(

src/tests/mod.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::arena::Arena;
2-
use crate::ast::{Binding, Limit, Order, Query};
2+
use crate::ast::{Limit, Order, Query};
33
use crate::prelude::{Type, Typed};
44
use crate::token::Operator;
5-
use crate::{Attrs, ExprRef, Raw, SourceKind, Value};
5+
use crate::{Attrs, ExprRef, Pos, Raw, SourceKind, Value};
66
use ordered_float::OrderedFloat;
77
use serde::Serialize;
88
use std::collections::BTreeMap;
@@ -86,10 +86,16 @@ pub struct QueryView<A> {
8686

8787
#[derive(Debug, Serialize)]
8888
pub struct SourceView<A> {
89-
pub binding: Binding,
89+
pub binding: BindingView,
9090
pub kind: SourceKindView<A>,
9191
}
9292

93+
#[derive(Debug, Serialize)]
94+
pub struct BindingView {
95+
pub name: String,
96+
pub pos: Pos,
97+
}
98+
9399
#[derive(Debug, Serialize)]
94100
pub enum SourceKindView<A> {
95101
Name(String),
@@ -184,10 +190,17 @@ impl<A> Query<A> {
184190
.sources
185191
.into_iter()
186192
.map(|s| SourceView {
187-
binding: s.binding.clone(),
193+
binding: BindingView {
194+
name: arena.strings.get(s.binding.name).to_owned(),
195+
pos: s.binding.pos,
196+
},
188197
kind: match s.kind {
189-
SourceKind::Name(name) => SourceKindView::Name(name),
190-
SourceKind::Subject(subject) => SourceKindView::Subject(subject),
198+
SourceKind::Name(name) => {
199+
SourceKindView::Name(arena.strings.get(name).to_owned())
200+
}
201+
SourceKind::Subject(subject) => {
202+
SourceKindView::Subject(arena.strings.get(subject).to_owned())
203+
}
191204
SourceKind::Subquery(subquery) => {
192205
SourceKindView::Subquery(Box::new(subquery.view(arena)))
193206
}

src/tests/snapshots/eventql_parser__tests__analysis__accept_valid_having_clause.snap

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
source: src/tests/analysis.rs
3-
expression: "query.and_then(|q|\n{\n q.run_static_analysis(&arena, &Default::default()).map(|q| q.view(&arena))\n})"
3+
expression: "query.and_then(|q|\n{ session.run_static_analysis(q).map(|q| q.view(&session.arena)) })"
44
---
55
Ok:
66
attrs:
@@ -54,7 +54,7 @@ Ok:
5454
col: 35
5555
value:
5656
App:
57-
func: AVG
57+
func: avg
5858
args:
5959
- attrs:
6060
pos:
@@ -107,7 +107,7 @@ Ok:
107107
col: 17
108108
value:
109109
App:
110-
func: UNIQUE
110+
func: unique
111111
args:
112112
- attrs:
113113
pos:
@@ -143,7 +143,7 @@ Ok:
143143
col: 14
144144
value:
145145
App:
146-
func: AVG
146+
func: avg
147147
args:
148148
- attrs:
149149
pos:

src/tests/snapshots/eventql_parser__tests__analysis__analyze_accept_group_by_with_agg_rec.snap

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
source: src/tests/analysis.rs
3-
expression: "query.and_then(|q|\n{\n q.run_static_analysis(&arena, &Default::default()).map(|q| q.view(&arena))\n})"
3+
expression: "query.and_then(|q|\n{ session.run_static_analysis(q).map(|q| q.view(&session.arena)) })"
44
---
55
Ok:
66
attrs:
@@ -62,7 +62,7 @@ Ok:
6262
col: 14
6363
value:
6464
App:
65-
func: UNIQUE
65+
func: unique
6666
args:
6767
- attrs:
6868
pos:
@@ -98,7 +98,7 @@ Ok:
9898
col: 8
9999
value:
100100
App:
101-
func: AVG
101+
func: avg
102102
args:
103103
- attrs:
104104
pos:

src/tests/snapshots/eventql_parser__tests__analysis__analyze_accept_group_by_with_order_by_with_agg.snap

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
source: src/tests/analysis.rs
3-
expression: "query.and_then(|q|\n{\n q.run_static_analysis(&arena, &Default::default()).map(|q| q.view(&arena))\n})"
3+
expression: "query.and_then(|q|\n{ session.run_static_analysis(q).map(|q| q.view(&session.arena)) })"
44
---
55
Ok:
66
attrs:
@@ -49,7 +49,7 @@ Ok:
4949
col: 10
5050
value:
5151
App:
52-
func: AVG
52+
func: avg
5353
args:
5454
- attrs:
5555
pos:
@@ -82,7 +82,7 @@ Ok:
8282
col: 14
8383
value:
8484
App:
85-
func: AVG
85+
func: avg
8686
args:
8787
- attrs:
8888
pos:

0 commit comments

Comments
 (0)