Class: AnbtSql::Parser
- Inherits:
-
Object
- Object
- AnbtSql::Parser
- Includes:
- StringUtil
- Defined in:
- lib/anbt-sql-formatter/parser.rb
Instance Method Summary collapse
-
#concat_multiwords_keyword(tokens) ⇒ Object
2つ以上並んだキーワードは1つのキーワードとみなします。 [“a”, “ ”, “group”, “ ”, “by”, “ ”, “b”] => [“a”, “ ”, “group by”, “ ”, “b”].
- #digit?(c) ⇒ Boolean
-
#initialize(rule) ⇒ Parser
constructor
A new instance of Parser.
-
#letter?(c) ⇒ Boolean
文字として認識して妥当かどうかを判定します。 全角文字なども文字として認識を許容するものと判断します。.
-
#next_sql_token ⇒ Object
トークンを次に進めます。 1.
- #next_token ⇒ Object
-
#parse(sql_str) ⇒ Object
SQL文字列をトークンの配列に変換し返します。.
- #prepare_tokens(coarse_tokens) ⇒ Object
-
#space?(c) ⇒ Boolean
- 2005.07.26
- Tosiki Iga r も処理範囲に含める必要があります。 2005.08.12
-
Tosiki Iga 65535(もとは-1)はホワイトスペースとして扱うよう変更します。.
-
#symbol?(c) ⇒ Boolean
“#” は文字列の一部とします アンダースコアは記号とは扱いません これ以降の文字の扱いは保留.
Methods included from StringUtil
Constructor Details
#initialize(rule) ⇒ Parser
Returns a new instance of Parser.
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/anbt-sql-formatter/parser.rb', line 13 def initialize(rule) @rule = rule # 解析前の文字列 @before = nil # 解析中の位置 @pos = nil # 解析中の文字。 @char = nil @token_pos = nil # 2文字からなる記号。 # なお、|| は文字列結合にあたります。 @two_character_symbol = [ "<>", "<=", ">=", "||", "!=" ] end |
Instance Method Details
#concat_multiwords_keyword(tokens) ⇒ Object
2つ以上並んだキーワードは1つのキーワードとみなします。
["a", " ", "group", " ", "by", " ", "b"]
=> ["a", " ", "group by", " ", "b"]
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/anbt-sql-formatter/parser.rb', line 245 def concat_multiwords_keyword(tokens) temp_kw_list = @rule.kw_multi_words.map{|kw| kw.split(" ") } # ワード数が多い順から temp_kw_list.sort{ |a, b| b.size <=> a.size }.each{|kw| index = 0 target_tokens_size = kw.size * 2 - 1 while index <= tokens.size - target_tokens_size temp_tokens = tokens[index, target_tokens_size].map {|x| x.string.sub(/\s+/, " ") } if equals_ignore_case(kw.join(" "), temp_tokens.join) tokens[index].string = temp_tokens.join (target_tokens_size-1).downto(1).each{|c| tokens.delete_at(index + c) } end index += 1 end } end |
#digit?(c) ⇒ Boolean
57 58 59 |
# File 'lib/anbt-sql-formatter/parser.rb', line 57 def digit?(c) return "0" <= c && c <= '9' end |
#letter?(c) ⇒ Boolean
文字として認識して妥当かどうかを判定します。全角文字なども文字として認識を許容するものと判断します。
48 49 50 51 52 53 54 |
# File 'lib/anbt-sql-formatter/parser.rb', line 48 def letter?(c) return false if space?(c) return false if digit?(c) return false if symbol?(c) true end |
#next_sql_token ⇒ Object
トークンを次に進めます。
-
posを進める。
-
sに結果を返す。
-
typeにその種類を設定する。
不正なSQLの場合、例外が発生します。ここでは、文法チェックは行っていない点に注目してください。
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/anbt-sql-formatter/parser.rb', line 79 def next_sql_token $stderr.puts "next_token #{@pos} <#{@before}> #{@before.length}" if $DEBUG start_pos = @pos if @pos >= @before.length @pos += 1 return nil end @char = char_at(@before, @pos) if space?(@char) work_string = "" loop { work_string += @char is_next_char_space = false if @pos + 1 < @before.size && space?(char_at(@before, @pos+1)) is_next_char_space = true end if not is_next_char_space @pos += 1 return AnbtSql::Token.new(AnbtSql::TokenConstants::SPACE, work_string, start_pos) else @pos += 1 next end } elsif @char == ";" @pos += 1 # 2005.07.26 Tosiki Iga セミコロンは終了扱いではないようにする。 return AnbtSql::Token.new(AnbtSql::TokenConstants::SYMBOL, ";", start_pos) elsif digit?(@char) if /^(0x[0-9a-fA-F]+)/ =~ @before[@pos..-1] || # hex /^(\d+(\.\d+(e-?\d+)?)?)/ =~ @before[@pos..-1] # integer, float or scientific num = $1 @pos += num.length return AnbtSql::Token.new(AnbtSql::TokenConstants::VALUE, num, start_pos) else raise "must not happen" end elsif letter?(@char) s = "" # 文字列中のドットについては、文字列と一体として考える。 while (letter?(@char) || digit?(@char) || @char == '.') s += @char @pos += 1 if (@pos >= @before.length()) break end @char = char_at(@before, @pos) end if AnbtSql::Constants::SQL_RESERVED_WORDS.map{|w| w.upcase }.include?(s.upcase) return AnbtSql::Token.new(AnbtSql::TokenConstants::KEYWORD, s, start_pos) end return AnbtSql::Token.new(AnbtSql::TokenConstants::NAME, s, start_pos) elsif symbol?(@char) s = "" + @char @pos += 1 if (@pos >= @before.length()) return AnbtSql::Token.new(AnbtSql::TokenConstants::SYMBOL, s, start_pos) end # 2文字の記号かどうか調べる ch2 = char_at(@before, @pos) # for (int i = 0; i < two_character_symbol.length; i++) { for i in 0...@two_character_symbol.length if (char_at(@two_character_symbol[i], 0) == @char && char_at(@two_character_symbol[i], 1) == ch2) @pos += 1 s += ch2 break end end if @char == "-" && /^(\d+(\.\d+(e-?\d+)?)?)/ =~ @before[@pos..-1] # float or scientific num = $1 @pos += num.length return AnbtSql::Token.new(AnbtSql::TokenConstants::VALUE, s + num, start_pos) end return AnbtSql::Token.new(AnbtSql::TokenConstants::SYMBOL, s, start_pos) else @pos += 1 return AnbtSql::Token.new( AnbtSql::TokenConstants::UNKNOWN, "" + @char, start_pos ) end end |
#next_token ⇒ Object
273 274 275 |
# File 'lib/anbt-sql-formatter/parser.rb', line 273 def next_token @tokens[@token_pos] end |
#parse(sql_str) ⇒ Object
SQL文字列をトークンの配列に変換し返します。
- sql_str
-
変換前のSQL文
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
# File 'lib/anbt-sql-formatter/parser.rb', line 282 def parse(sql_str) coarse_tokens = CoarseTokenizer.new.tokenize(sql_str) prepare_tokens(coarse_tokens) tokens = [] count = 0 @token_pos = 0 loop { token = next_token() if $DEBUG pp "=" * 64, count, token, token.class end if token._type == AnbtSql::TokenConstants::END_OF_SQL break else ; end tokens.push token count += 1 @token_pos += 1 } concat_multiwords_keyword(tokens) tokens end |
#prepare_tokens(coarse_tokens) ⇒ Object
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
# File 'lib/anbt-sql-formatter/parser.rb', line 192 def prepare_tokens(coarse_tokens) @tokens = [] pos = 0 while pos < coarse_tokens.size coarse_token = coarse_tokens[pos] case coarse_token._type when :quote_single @tokens << AnbtSql::Token.new(AnbtSql::TokenConstants::VALUE, coarse_token.string) when :quote_double @tokens << AnbtSql::Token.new(AnbtSql::TokenConstants::NAME, coarse_token.string) when :comment_single @tokens << AnbtSql::Token.new(AnbtSql::TokenConstants::COMMENT, coarse_token.string.chomp) when :comment_multi @tokens << AnbtSql::Token.new(AnbtSql::TokenConstants::COMMENT, coarse_token.string) when :plain @before = coarse_token.string @pos = 0 count = 0 loop { token = next_sql_token() if $DEBUG pp "@" * 64, count, token, token.class end # if token._type == AnbtSql::TokenConstants::END_OF_SQL if token == nil break end @tokens.push token count += 1 } end pos += 1 end @tokens << AnbtSql::Token.new(AnbtSql::TokenConstants::END_OF_SQL, "") end |
#space?(c) ⇒ Boolean
- 2005.07.26
-
Tosiki Iga r も処理範囲に含める必要があります。
- 2005.08.12
-
Tosiki Iga 65535(もとは-1)はホワイトスペースとして扱うよう変更します。
36 37 38 39 40 41 42 |
# File 'lib/anbt-sql-formatter/parser.rb', line 36 def space?(c) return c == ' ' || c == "\t" || c == "\n" || c == "\r" || c == 65535 end |
#symbol?(c) ⇒ Boolean
“#” は文字列の一部としますアンダースコアは記号とは扱いませんこれ以降の文字の扱いは保留
66 67 68 69 |
# File 'lib/anbt-sql-formatter/parser.rb', line 66 def symbol?(c) %w(" ? % & ' \( \) | * + , - . / : ; < = > !).include? c # " end |