Skip to content
This repository was archived by the owner on Dec 25, 2019. It is now read-only.

Commit a5b547a

Browse files
Robert Grimmbenesch
Robert Grimm
authored andcommitted
Turn type Ident into struct Ident
The Ident type was previously an alias for a String. Turn it into a full fledged struct, so that the parser can preserve the distinction between identifier value and quote style already made by the tokenizer's Word structure.
1 parent 3a077b0 commit a5b547a

File tree

6 files changed

+160
-88
lines changed

6 files changed

+160
-88
lines changed

src/ast/mod.rs

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,61 @@ where
9090
DisplaySeparated { slice, sep: ", " }
9191
}
9292

93-
/// Identifier name, in the originally quoted form (e.g. `"id"`)
94-
pub type Ident = String;
93+
/// An identifier, decomposed into its value or character data and the quote style.
94+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
95+
pub struct Ident {
96+
/// The value of the identifier without quotes.
97+
pub value: String,
98+
/// The starting quote if any. Valid quote characters are the single quote,
99+
/// double quote, backtick, and opening square bracket.
100+
pub quote_style: Option<char>,
101+
}
102+
103+
impl Ident {
104+
/// Create a new identifier with the given value and no quotes.
105+
pub fn new<S>(value: S) -> Self
106+
where
107+
S: Into<String>,
108+
{
109+
Ident {
110+
value: value.into(),
111+
quote_style: None,
112+
}
113+
}
114+
115+
/// Create a new quoted identifier with the given quote and value. This function
116+
/// panics if the given quote is not a valid quote character.
117+
pub fn with_quote<S>(quote: char, value: S) -> Self
118+
where
119+
S: Into<String>,
120+
{
121+
assert!(quote == '\'' || quote == '"' || quote == '`' || quote == '[');
122+
Ident {
123+
value: value.into(),
124+
quote_style: Some(quote),
125+
}
126+
}
127+
}
128+
129+
impl From<&str> for Ident {
130+
fn from(value: &str) -> Self {
131+
Ident {
132+
value: value.to_string(),
133+
quote_style: None,
134+
}
135+
}
136+
}
137+
138+
impl fmt::Display for Ident {
139+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
140+
match self.quote_style {
141+
Some(q) if q == '"' || q == '\'' || q == '`' => write!(f, "{}{}{}", q, self.value, q),
142+
Some(q) if q == '[' => write!(f, "[{}]", self.value),
143+
None => f.write_str(&self.value),
144+
_ => panic!("unexpected quote style"),
145+
}
146+
}
147+
}
95148

96149
/// A name of a table, view, custom type, etc., possibly multi-part, i.e. db.schema.obj
97150
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -210,7 +263,7 @@ pub enum Expr {
210263
impl fmt::Display for Expr {
211264
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
212265
match self {
213-
Expr::Identifier(s) => f.write_str(s),
266+
Expr::Identifier(s) => write!(f, "{}", s),
214267
Expr::Wildcard => f.write_str("*"),
215268
Expr::QualifiedWildcard(q) => write!(f, "{}.*", display_separated(q, ".")),
216269
Expr::CompoundIdentifier(s) => write!(f, "{}", display_separated(s, ".")),
@@ -1092,7 +1145,7 @@ impl fmt::Display for SetVariableValue {
10921145
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10931146
use SetVariableValue::*;
10941147
match self {
1095-
Ident(ident) => f.write_str(ident),
1148+
Ident(ident) => write!(f, "{}", ident),
10961149
Literal(literal) => write!(f, "{}", literal),
10971150
}
10981151
}

src/parser.rs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -216,11 +216,11 @@ impl Parser {
216216
// identifier, a function call, or a simple identifier:
217217
_ => match self.peek_token() {
218218
Some(Token::LParen) | Some(Token::Period) => {
219-
let mut id_parts: Vec<Ident> = vec![w.as_ident()];
219+
let mut id_parts: Vec<Ident> = vec![w.to_ident()];
220220
let mut ends_with_wildcard = false;
221221
while self.consume_token(&Token::Period) {
222222
match self.next_token() {
223-
Some(Token::Word(w)) => id_parts.push(w.as_ident()),
223+
Some(Token::Word(w)) => id_parts.push(w.to_ident()),
224224
Some(Token::Mult) => {
225225
ends_with_wildcard = true;
226226
break;
@@ -240,7 +240,7 @@ impl Parser {
240240
Ok(Expr::CompoundIdentifier(id_parts))
241241
}
242242
}
243-
_ => Ok(Expr::Identifier(w.as_ident())),
243+
_ => Ok(Expr::Identifier(w.to_ident())),
244244
},
245245
}, // End of Token::Word
246246
Token::Mult => Ok(Expr::Wildcard),
@@ -1129,7 +1129,7 @@ impl Parser {
11291129
let table_name = self.parse_object_name()?;
11301130
let (columns, constraints) = self.parse_columns()?;
11311131
self.expect_keywords(&["STORED", "AS"])?;
1132-
let file_format = self.parse_identifier()?.parse::<FileFormat>()?;
1132+
let file_format = self.parse_identifier()?.value.parse::<FileFormat>()?;
11331133

11341134
self.expect_keyword("LOCATION")?;
11351135
let location = self.parse_literal_string()?;
@@ -1257,7 +1257,7 @@ impl Parser {
12571257
}
12581258

12591259
columns.push(ColumnDef {
1260-
name: column_name.as_ident(),
1260+
name: column_name.to_ident(),
12611261
data_type,
12621262
collation,
12631263
options,
@@ -1599,11 +1599,11 @@ impl Parser {
15991599
Some(Token::Word(ref w))
16001600
if after_as || !reserved_kwds.contains(&w.keyword.as_str()) =>
16011601
{
1602-
Ok(Some(w.as_ident()))
1602+
Ok(Some(w.to_ident()))
16031603
}
16041604
// MSSQL supports single-quoted strings as aliases for columns
16051605
// We accept them as table aliases too, although MSSQL does not.
1606-
Some(Token::SingleQuotedString(ref s)) => Ok(Some(format!("'{}'", s))),
1606+
Some(Token::SingleQuotedString(ref s)) => Ok(Some(Ident::with_quote('\'', s.clone()))),
16071607
not_an_ident => {
16081608
if after_as {
16091609
return self.expected("an identifier after AS", not_an_ident);
@@ -1647,7 +1647,7 @@ impl Parser {
16471647
/// Parse a simple one-word identifier (possibly quoted, possibly a keyword)
16481648
pub fn parse_identifier(&mut self) -> Result<Ident, ParserError> {
16491649
match self.next_token() {
1650-
Some(Token::Word(w)) => Ok(w.as_ident()),
1650+
Some(Token::Word(w)) => Ok(w.to_ident()),
16511651
unexpected => self.expected("identifier", unexpected),
16521652
}
16531653
}
@@ -1890,15 +1890,15 @@ impl Parser {
18901890
let token = self.peek_token();
18911891
let value = match (self.parse_value(), token) {
18921892
(Ok(value), _) => SetVariableValue::Literal(value),
1893-
(Err(_), Some(Token::Word(ident))) => SetVariableValue::Ident(ident.as_ident()),
1893+
(Err(_), Some(Token::Word(ident))) => SetVariableValue::Ident(ident.to_ident()),
18941894
(Err(_), other) => self.expected("variable value", other)?,
18951895
};
18961896
Ok(Statement::SetVariable {
18971897
local: modifier == Some("LOCAL"),
18981898
variable,
18991899
value,
19001900
})
1901-
} else if variable == "TRANSACTION" && modifier.is_none() {
1901+
} else if variable.value == "TRANSACTION" && modifier.is_none() {
19021902
Ok(Statement::SetTransaction {
19031903
modes: self.parse_transaction_modes()?,
19041904
})
@@ -2386,8 +2386,11 @@ impl Parser {
23862386
}
23872387

23882388
impl Word {
2389-
pub fn as_ident(&self) -> Ident {
2390-
self.to_string()
2389+
pub fn to_ident(&self) -> Ident {
2390+
Ident {
2391+
value: self.value.clone(),
2392+
quote_style: self.quote_style,
2393+
}
23912394
}
23922395
}
23932396

0 commit comments

Comments
 (0)