-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for search macro substitution
- Loading branch information
Showing
10 changed files
with
537 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import re | ||
from typing import Protocol | ||
|
||
from pydantic import BaseModel | ||
|
||
from spl_transpiler.spl_transpiler import detect_macros, parse | ||
|
||
|
||
class MacroDefinition(BaseModel): | ||
arguments: list[str] | None = None | ||
definition: str | ||
|
||
|
||
class MacroLoader(Protocol): | ||
def __getitem__(self, item: str) -> dict | MacroDefinition: | ||
pass | ||
|
||
|
||
def substitute_macros(code, macros: MacroLoader): | ||
(chunks, suffix) = detect_macros(code) | ||
query_parts = [] | ||
for prefix, macro_call in chunks: | ||
query_parts.append(prefix) | ||
|
||
macro_name = macro_call.macro_name | ||
args = macro_call.args | ||
|
||
macro_spec = MacroDefinition.model_validate(macros[macro_name]) | ||
|
||
macro_final_value = macro_spec.definition | ||
|
||
if args: | ||
if all(name is None for name, _ in args): | ||
if macro_spec.arguments is None or len(macro_spec.arguments) != len( | ||
args | ||
): | ||
raise ValueError( | ||
f"Mismatched number of arguments in macro call: expected {len(macro_spec.arguments or [])}, got {len(args)}" | ||
) | ||
args = dict(zip(macro_spec.arguments, [value for _, value in args])) | ||
elif all(name is not None for name, _ in args): | ||
if macro_spec.arguments is None or len(macro_spec.arguments) != len( | ||
args | ||
): | ||
raise ValueError( | ||
f"Mismatched number of arguments in macro call: expected {len(macro_spec.arguments or [])}, got {len(args)}" | ||
) | ||
args = dict(args) | ||
else: | ||
raise ValueError( | ||
"Mixture of named and positional arguments in macro call" | ||
) | ||
|
||
for arg_name, arg_substitute_value in args.items(): | ||
macro_final_value = re.sub( | ||
f"\\${arg_name}\\$", arg_substitute_value, macro_final_value | ||
) | ||
|
||
query_parts.append(macro_final_value) | ||
|
||
query_parts.append(suffix) | ||
return "".join(query_parts) | ||
|
||
|
||
def parse_with_macros(code: str, macros: MacroLoader, *args, **kwargs): | ||
return parse(substitute_macros(code, macros), *args, **kwargs) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
// use crate::spl::ast; | ||
use crate::spl::parser::{expr, token, ws}; | ||
use nom::bytes::complete::{tag, take_until}; | ||
use nom::character::complete::none_of; | ||
use nom::combinator::{all_consuming, into, map, opt, recognize}; | ||
use nom::multi::{many0, separated_list0}; | ||
use nom::sequence::{delimited, pair, terminated}; | ||
use nom::IResult; | ||
use pyo3::pyclass; | ||
|
||
#[derive(Debug, PartialEq, Clone, Hash)] | ||
#[pyclass(frozen, eq, hash)] | ||
pub struct MacroCall { | ||
#[pyo3(get)] | ||
pub macro_name: String, | ||
#[pyo3(get)] | ||
pub args: Vec<(Option<String>, String)>, | ||
} | ||
|
||
type ChunkedQuery<'a> = (Vec<(&'a str, MacroCall)>, &'a str); | ||
|
||
pub fn spl_macros(input: &str) -> IResult<&str, ChunkedQuery> { | ||
all_consuming(pair( | ||
many0(pair( | ||
take_until("`"), | ||
delimited( | ||
tag("`"), | ||
ws(map( | ||
pair( | ||
ws(token), | ||
opt(delimited( | ||
ws(tag("(")), | ||
separated_list0( | ||
ws(tag(",")), | ||
pair( | ||
opt(into(terminated(token, tag("=")))), | ||
into(recognize(expr)), | ||
), | ||
), | ||
ws(tag(")")), | ||
)), | ||
), | ||
|(name, args)| MacroCall { | ||
macro_name: name.to_string(), | ||
args: args.unwrap_or(Vec::new()), | ||
}, | ||
)), | ||
tag("`"), | ||
), | ||
)), | ||
recognize(many0(none_of("`"))), | ||
))(input) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn test_spl_macros_simple_no_args() { | ||
let input = r#"`foo`"#; | ||
let result = spl_macros(input).unwrap(); | ||
assert_eq!( | ||
result, | ||
( | ||
"", | ||
( | ||
vec![( | ||
"", | ||
MacroCall { | ||
macro_name: "foo".to_string(), | ||
args: Vec::new() | ||
} | ||
)], | ||
"" | ||
) | ||
) | ||
); | ||
} | ||
|
||
#[test] | ||
fn test_spl_macros_simple_with_args() { | ||
let input = r#"`foo(bar, 1, "s")`"#; | ||
let result = spl_macros(input).unwrap(); | ||
assert_eq!( | ||
result, | ||
( | ||
"", | ||
( | ||
vec![( | ||
"", | ||
MacroCall { | ||
macro_name: "foo".to_string(), | ||
args: vec![ | ||
(None, "bar".to_string()), | ||
(None, "1".to_string()), | ||
(None, "\"s\"".to_string()), | ||
] | ||
} | ||
)], | ||
"" | ||
) | ||
) | ||
); | ||
} | ||
|
||
#[test] | ||
fn test_spl_macros_simple_named_args() { | ||
let input = r#"`foo(foo=bar, baz=1)`"#; | ||
let result = spl_macros(input).unwrap(); | ||
assert_eq!( | ||
result, | ||
( | ||
"", | ||
( | ||
vec![( | ||
"", | ||
MacroCall { | ||
macro_name: "foo".to_string(), | ||
args: vec![ | ||
(Some("foo".to_string()), "bar".to_string()), | ||
(Some("baz".to_string()), "1".to_string()), | ||
] | ||
} | ||
)], | ||
"" | ||
) | ||
) | ||
); | ||
} | ||
|
||
#[test] | ||
fn test_spl_macros_multiple() { | ||
let input = r#"index=main | `foo(bar, 1, "s")` x=`f` y=3"#; | ||
let result = spl_macros(input).unwrap(); | ||
assert_eq!( | ||
result, | ||
( | ||
"", | ||
( | ||
vec![ | ||
( | ||
"index=main | ", | ||
MacroCall { | ||
macro_name: "foo".to_string(), | ||
args: vec![ | ||
(None, "bar".to_string()), | ||
(None, "1".to_string()), | ||
(None, "\"s\"".to_string()), | ||
] | ||
} | ||
), | ||
( | ||
" x=", | ||
MacroCall { | ||
macro_name: "f".to_string(), | ||
args: Vec::new(), | ||
} | ||
) | ||
], | ||
" y=3" | ||
) | ||
) | ||
); | ||
} | ||
|
||
#[test] | ||
fn test_spl_macros_quoted_backtick() { | ||
let input = r#"`foo("`")`"#; | ||
let result = spl_macros(input).unwrap(); | ||
assert_eq!( | ||
result, | ||
( | ||
"", | ||
( | ||
vec![( | ||
"", | ||
MacroCall { | ||
macro_name: "foo".to_string(), | ||
args: vec![(None, "\"`\"".to_string()),] | ||
} | ||
)], | ||
"" | ||
) | ||
) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,6 @@ | ||
pub mod ast; | ||
pub mod macros; | ||
pub mod operators; | ||
pub(crate) mod parser; | ||
pub mod python; | ||
// pub mod to_spl; |
Oops, something went wrong.