Skip to content

Commit d584263

Browse files
committed
progress
1 parent 0bb025f commit d584263

File tree

70 files changed

+4615
-287
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+4615
-287
lines changed

agentic/pretty_printer.md

Lines changed: 182 additions & 16 deletions
Large diffs are not rendered by default.

crates/pgt_pretty_print/src/nodes/a_expr.rs

Lines changed: 155 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use pgt_query::protobuf::{AExpr, AExprKind};
2+
use pgt_query::{Node, NodeEnum};
23

34
use crate::{
45
TokenKind,
@@ -31,20 +32,62 @@ pub(super) fn emit_a_expr(e: &mut EventEmitter, n: &AExpr) {
3132

3233
// Basic binary operator: left op right
3334
fn emit_aexpr_op(e: &mut EventEmitter, n: &AExpr) {
34-
if let Some(ref lexpr) = n.lexpr {
35-
super::emit_node(lexpr, e);
36-
}
37-
38-
if !n.name.is_empty() {
39-
e.space();
40-
for name in &n.name {
41-
super::emit_node(name, e);
35+
if n.name.is_empty() {
36+
if let Some(ref lexpr) = n.lexpr {
37+
super::emit_node(lexpr, e);
4238
}
43-
e.space();
39+
if let Some(ref rexpr) = n.rexpr {
40+
if n.lexpr.is_some() {
41+
e.space();
42+
}
43+
super::emit_node(rexpr, e);
44+
}
45+
return;
4446
}
4547

46-
if let Some(ref rexpr) = n.rexpr {
47-
super::emit_node(rexpr, e);
48+
let lexpr = n.lexpr.as_ref();
49+
let rexpr = n.rexpr.as_ref();
50+
51+
match (lexpr, rexpr) {
52+
(Some(lexpr), Some(rexpr)) => {
53+
super::emit_node(lexpr, e);
54+
e.space();
55+
emit_operator(e, &n.name);
56+
e.space();
57+
super::emit_node(rexpr, e);
58+
}
59+
(None, Some(rexpr)) => {
60+
if let Some(op) = extract_simple_operator(&n.name) {
61+
if op.eq_ignore_ascii_case("not") {
62+
e.token(TokenKind::NOT_KW);
63+
e.space();
64+
super::emit_node(rexpr, e);
65+
} else {
66+
emit_simple_operator(e, op);
67+
if operator_needs_space(op) {
68+
e.space();
69+
}
70+
super::emit_node(rexpr, e);
71+
}
72+
} else {
73+
emit_operator(e, &n.name);
74+
e.space();
75+
super::emit_node(rexpr, e);
76+
}
77+
}
78+
(Some(lexpr), None) => {
79+
super::emit_node(lexpr, e);
80+
if let Some(op) = extract_simple_operator(&n.name) {
81+
if operator_needs_space(op) {
82+
e.space();
83+
}
84+
emit_simple_operator(e, op);
85+
} else {
86+
e.space();
87+
emit_operator(e, &n.name);
88+
}
89+
}
90+
(None, None) => {}
4891
}
4992
}
5093

@@ -56,8 +99,10 @@ fn emit_aexpr_op_any(e: &mut EventEmitter, n: &AExpr) {
5699
}
57100

58101
if !n.name.is_empty() {
59-
for name in &n.name {
60-
super::emit_node(name, e);
102+
if let Some(op) = extract_simple_operator(&n.name) {
103+
emit_simple_operator(e, op);
104+
} else {
105+
emit_operator(e, &n.name);
61106
}
62107
e.space();
63108
}
@@ -78,8 +123,10 @@ fn emit_aexpr_op_all(e: &mut EventEmitter, n: &AExpr) {
78123
}
79124

80125
if !n.name.is_empty() {
81-
for name in &n.name {
82-
super::emit_node(name, e);
126+
if let Some(op) = extract_simple_operator(&n.name) {
127+
emit_simple_operator(e, op);
128+
} else {
129+
emit_operator(e, &n.name);
83130
}
84131
e.space();
85132
}
@@ -158,16 +205,31 @@ fn emit_aexpr_in(e: &mut EventEmitter, n: &AExpr) {
158205
e.space();
159206
}
160207

208+
let is_not = extract_simple_operator(&n.name)
209+
.map(|op| op == "<>")
210+
.unwrap_or(false);
211+
212+
if is_not {
213+
e.token(TokenKind::NOT_KW);
214+
e.space();
215+
}
216+
161217
e.token(TokenKind::IN_KW);
162218
e.space();
163219

164220
// The rexpr is typically a List node, which emits comma-separated items
165221
// We need to wrap it in parentheses for IN clause
166-
e.token(TokenKind::L_PAREN);
167222
if let Some(ref rexpr) = n.rexpr {
168-
super::emit_node(rexpr, e);
223+
match rexpr.node.as_ref() {
224+
Some(NodeEnum::SubLink(_)) => super::emit_node(rexpr, e),
225+
_ => {
226+
e.token(TokenKind::L_PAREN);
227+
super::emit_node(rexpr, e);
228+
e.token(TokenKind::R_PAREN);
229+
return;
230+
}
231+
}
169232
}
170-
e.token(TokenKind::R_PAREN);
171233
}
172234

173235
// expr LIKE pattern [ESCAPE escape]
@@ -177,6 +239,15 @@ fn emit_aexpr_like(e: &mut EventEmitter, n: &AExpr) {
177239
e.space();
178240
}
179241

242+
let is_not = extract_simple_operator(&n.name)
243+
.map(|op| op == "!~~")
244+
.unwrap_or(false);
245+
246+
if is_not {
247+
e.token(TokenKind::NOT_KW);
248+
e.space();
249+
}
250+
180251
e.token(TokenKind::LIKE_KW);
181252
e.space();
182253

@@ -192,6 +263,15 @@ fn emit_aexpr_ilike(e: &mut EventEmitter, n: &AExpr) {
192263
e.space();
193264
}
194265

266+
let is_not = extract_simple_operator(&n.name)
267+
.map(|op| op == "!~~*")
268+
.unwrap_or(false);
269+
270+
if is_not {
271+
e.token(TokenKind::NOT_KW);
272+
e.space();
273+
}
274+
195275
e.token(TokenKind::ILIKE_KW);
196276
e.space();
197277

@@ -207,6 +287,15 @@ fn emit_aexpr_similar(e: &mut EventEmitter, n: &AExpr) {
207287
e.space();
208288
}
209289

290+
let is_not = extract_simple_operator(&n.name)
291+
.map(|op| op == "!~")
292+
.unwrap_or(false);
293+
294+
if is_not {
295+
e.token(TokenKind::NOT_KW);
296+
e.space();
297+
}
298+
210299
e.token(TokenKind::SIMILAR_KW);
211300
e.space();
212301
e.token(TokenKind::TO_KW);
@@ -336,3 +425,51 @@ fn emit_aexpr_not_between_sym(e: &mut EventEmitter, n: &AExpr) {
336425
}
337426
}
338427
}
428+
429+
fn emit_operator(e: &mut EventEmitter, name: &[Node]) {
430+
if name.len() > 1 {
431+
emit_qualified_operator(e, name);
432+
} else if let Some(first) = name.first() {
433+
emit_operator_part(e, first);
434+
}
435+
}
436+
437+
fn emit_qualified_operator(e: &mut EventEmitter, name: &[Node]) {
438+
e.token(TokenKind::OPERATOR_KW);
439+
e.token(TokenKind::L_PAREN);
440+
441+
for (idx, part) in name.iter().enumerate() {
442+
if idx > 0 {
443+
e.token(TokenKind::DOT);
444+
}
445+
emit_operator_part(e, part);
446+
}
447+
448+
e.token(TokenKind::R_PAREN);
449+
}
450+
451+
fn emit_operator_part(e: &mut EventEmitter, node: &Node) {
452+
match node.node.as_ref() {
453+
Some(NodeEnum::String(s)) => e.token(TokenKind::IDENT(s.sval.clone())),
454+
_ => super::emit_node(node, e),
455+
}
456+
}
457+
458+
fn emit_simple_operator(e: &mut EventEmitter, op: &str) {
459+
e.token(TokenKind::IDENT(op.to_string()));
460+
}
461+
462+
fn extract_simple_operator<'a>(name: &'a [Node]) -> Option<&'a str> {
463+
if name.len() != 1 {
464+
return None;
465+
}
466+
467+
match name[0].node.as_ref() {
468+
Some(NodeEnum::String(s)) => Some(&s.sval),
469+
_ => None,
470+
}
471+
}
472+
473+
fn operator_needs_space(op: &str) -> bool {
474+
op.chars().any(|c| c.is_alphabetic())
475+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
use pgt_query::NodeEnum;
2+
use pgt_query::protobuf::{AlterOperatorStmt, DefElem, List, ObjectWithArgs};
3+
4+
use crate::{
5+
TokenKind,
6+
emitter::{EventEmitter, GroupKind},
7+
nodes::node_list::emit_comma_separated_list,
8+
};
9+
10+
use super::string::emit_identifier_maybe_quoted;
11+
12+
pub(super) fn emit_alter_operator_stmt(e: &mut EventEmitter, n: &AlterOperatorStmt) {
13+
e.group_start(GroupKind::AlterOperatorStmt);
14+
15+
e.token(TokenKind::ALTER_KW);
16+
e.space();
17+
e.token(TokenKind::OPERATOR_KW);
18+
e.space();
19+
20+
if let Some(ref oper) = n.opername {
21+
emit_operator_fqn(e, oper);
22+
23+
if !oper.objargs.is_empty() || !oper.args_unspecified {
24+
e.space();
25+
e.token(TokenKind::L_PAREN);
26+
emit_comma_separated_list(e, &oper.objargs, super::emit_node);
27+
e.token(TokenKind::R_PAREN);
28+
}
29+
}
30+
31+
if !n.options.is_empty() {
32+
e.space();
33+
e.token(TokenKind::SET_KW);
34+
e.space();
35+
e.token(TokenKind::L_PAREN);
36+
emit_comma_separated_list(e, &n.options, |node, emitter| {
37+
if let Some(NodeEnum::DefElem(def)) = node.node.as_ref() {
38+
emit_operator_option(emitter, def);
39+
}
40+
});
41+
e.token(TokenKind::R_PAREN);
42+
}
43+
44+
e.token(TokenKind::SEMICOLON);
45+
e.group_end();
46+
}
47+
48+
fn emit_operator_fqn(e: &mut EventEmitter, oper: &ObjectWithArgs) {
49+
for (idx, node) in oper.objname.iter().enumerate() {
50+
if idx > 0 {
51+
e.token(TokenKind::DOT);
52+
}
53+
54+
match node.node.as_ref() {
55+
Some(NodeEnum::String(s)) => {
56+
if idx == oper.objname.len() - 1 {
57+
e.token(TokenKind::IDENT(s.sval.clone()));
58+
} else {
59+
emit_identifier_maybe_quoted(e, &s.sval);
60+
}
61+
}
62+
_ => super::emit_node(node, e),
63+
}
64+
}
65+
}
66+
67+
fn emit_operator_option(e: &mut EventEmitter, def: &DefElem) {
68+
emit_identifier_maybe_quoted(e, &def.defname);
69+
70+
match def.defname.to_ascii_lowercase().as_str() {
71+
"hashes" | "merges" => {
72+
if let Some(ref arg) = def.arg {
73+
e.space();
74+
e.token(TokenKind::IDENT("=".to_string()));
75+
e.space();
76+
emit_operator_option_arg(e, arg);
77+
}
78+
}
79+
"restrict" | "join" => {
80+
e.space();
81+
e.token(TokenKind::IDENT("=".to_string()));
82+
e.space();
83+
if let Some(ref arg) = def.arg {
84+
emit_operator_option_arg(e, arg);
85+
} else {
86+
e.token(TokenKind::IDENT("NONE".to_string()));
87+
}
88+
}
89+
_ => {
90+
if let Some(ref arg) = def.arg {
91+
e.space();
92+
e.token(TokenKind::IDENT("=".to_string()));
93+
e.space();
94+
emit_operator_option_arg(e, arg);
95+
}
96+
}
97+
}
98+
}
99+
100+
fn emit_operator_option_arg(e: &mut EventEmitter, arg: &pgt_query::protobuf::Node) {
101+
match arg.node.as_ref() {
102+
Some(NodeEnum::Boolean(b)) => {
103+
e.token(TokenKind::IDENT(if b.boolval {
104+
"TRUE".to_string()
105+
} else {
106+
"FALSE".to_string()
107+
}));
108+
}
109+
Some(NodeEnum::List(list)) => emit_operator_list(e, list),
110+
Some(NodeEnum::String(s)) => super::emit_string_literal(e, s),
111+
_ => super::emit_node(arg, e),
112+
}
113+
}
114+
115+
fn emit_operator_list(e: &mut EventEmitter, list: &List) {
116+
for (idx, item) in list.items.iter().enumerate() {
117+
if idx > 0 {
118+
e.token(TokenKind::DOT);
119+
}
120+
121+
match item.node.as_ref() {
122+
Some(NodeEnum::String(s)) => e.token(TokenKind::IDENT(s.sval.clone())),
123+
_ => super::emit_node(item, e),
124+
}
125+
}
126+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use pgt_query::protobuf::ArrayCoerceExpr;
2+
3+
use crate::emitter::EventEmitter;
4+
5+
pub(super) fn emit_array_coerce_expr(e: &mut EventEmitter, n: &ArrayCoerceExpr) {
6+
if let Some(ref arg) = n.arg {
7+
super::emit_node(arg, e);
8+
}
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use pgt_query::protobuf::CoerceToDomain;
2+
3+
use crate::emitter::EventEmitter;
4+
5+
pub(super) fn emit_coerce_to_domain(e: &mut EventEmitter, n: &CoerceToDomain) {
6+
if let Some(ref arg) = n.arg {
7+
super::emit_node(arg, e);
8+
}
9+
}

0 commit comments

Comments
 (0)