OSDN Git Service

SchemeLexerの実装により、字句解析部分の作成を完了。
[simplecms/utakata.git] / src / lexer / string_lexer.cpp
1 #include <utility>
2 #include "lib/smart_ptr.h"
3 #include "src/exception_macro.h"
4 #include "src/lexer/string_lexer.h"
5 #include "src/lexer/term_lexer.h"
6 #include "src/lexer/term_checker.h"
7 #include "src/lexer/inline_hex_escape_lexer.h"
8 #include "src/lexer/token.h"
9 #include "src/encoding_reader.h"
10 #include "src/unicode.h"
11
12 namespace lexer = utakata::lexer;
13 namespace term = utakata::lexer::term;
14 namespace reader = utakata::reader;
15 namespace unicode = utakata::unicode;
16
17 // 宣言のコメントを参照してください。
18 unsigned int lexer::EscapeConverter::Convert(unsigned int code) {
19   switch (code) {
20     case 'a'  : return kAlarm;
21     case 'b'  : return kBackspace;
22     case 't'  : return kTab;
23     case 'n'  : return kLinefeed;
24     case 'v'  : return kVTab;
25     case 'f'  : return kForward;
26     case 'r'  : return kCaridgeReturn;
27     case '"'  : return kDoubleQuote;
28     case '\\' : return kBackslash;
29     default: return 0;
30   }
31 }
32
33 // 宣言のコメントを参照して下さい。
34 bool lexer::EscapeLexer::Lex(reader::EncodingReader* reader,
35                              unsigned int* code) {
36   unicode::UniString escape(
37       unicode::Convert(reader->Peek(kEscapeValidLength)));
38   if (escape.IsEmpty() || escape.At(0).rawcode() != '\\') {
39     return false;
40   }
41
42   if (escape.GetSize() < static_cast<unsigned int>(kEscapeValidLength)) {
43     THROW_EXCEPTION_(lexer::LexException,
44                      unicode::Convert("invalid escape sequence"));
45   }
46
47   lexer::InlineHexEscapeLexer inline_escape;
48
49   if (inline_escape.Lex(reader, code)) {
50     return true;
51   }
52
53   // \だけを読み飛ばします。
54   reader->Read();
55
56   lexer::TermLexer<term::IntralineWhitespace> intraline;
57   lexer::TermLexer<term::LineEnding> lineend;
58
59   if (!EscapeConverter().CanConvert(escape.At(1).rawcode()) &&
60       !intraline.CheckToken(reader) && !lineend.CheckToken(reader)) {
61     unicode::UniString encoded_string(
62         unicode::Convert("invalid escape sequence : "));
63     encoded_string.Append(escape);
64     THROW_EXCEPTION_(lexer::LexException, encoded_string);
65   }
66
67   *code = ConvertEscapeToCode(escape, reader);
68   return true;
69 }
70
71 // \<intraline whitespace>に該当する場合、返却されるcodeは0と
72 // なります。
73 // Convertの直前でreader->Readとしているのは、直前で読み出されていない
74 // \<.>の<.>にあたる部分を読み飛ばすためです。
75 unsigned int lexer::EscapeLexer::ConvertEscapeToCode(
76     const unicode::UniString& escape, reader::EncodingReader* reader) {
77   unsigned int code = 0;
78   lexer::TermLexer<term::IntralineWhitespace> intraline;
79   lexer::TermLexer<term::LineEnding> lineend;
80
81   if (!intraline.CheckToken(reader) && !lineend.CheckToken(reader)) {
82     lexer::EscapeConverter converter;
83     if (!converter.CanConvert(escape.At(1).rawcode())) {
84       unicode::UniString encoded_string(
85           unicode::Convert("invalid escape sequence : "));
86       encoded_string.Append(escape);
87       THROW_EXCEPTION_(lexer::LexException, encoded_string);
88     }
89     reader->Read();
90     code = converter.Convert(escape.At(1).rawcode());
91   } else {
92     ReadIntralines(reader);
93   }
94
95   return code;
96 }
97
98 // 処理の先頭で1文字だけReadしているのは、すでに\と次の文字が
99 // 読出されているためです。
100 void lexer::EscapeLexer::ReadIntralines(reader::EncodingReader* reader) {
101   lexer::TermLexer<term::IntralineWhitespace> intraline;
102   lexer::TermLexer<term::LineEnding> lineend;
103
104   while (intraline.CheckToken(reader) && !reader->IsEof()) {
105     reader->Read(intraline.previous_read_size());
106   }
107
108   if (!lineend.CheckToken(reader)) {
109     unicode::UniString encoded_string;
110     THROW_EXCEPTION_(lexer::LexException,
111                      unicode::Convert("must line ending after whitespaces"));
112   }
113   reader->Read(lineend.previous_read_size());
114
115   while (intraline.CheckToken(reader) && !reader->IsEof()) {
116     reader->Read(intraline.previous_read_size());
117   }
118 }
119
120 // 文字列の終了地点は、同一行の`"`か、\\を含む複数行後の対応する`"`となります。
121 // 対応する`"`が存在しないままreaderの末尾に到達すると、LexExceptionが
122 // 送出されます。
123 // また、\から行末まで空白文字のみが続き、次の行の最初の文字までが、改行か
124 // 空白のみである場合、その間の空白及び改行は無視され、文字列は継続している
125 // とみなされます。
126 akebono::smart_ptr<lexer::Token> lexer::StringLexer::Lex(
127     reader::EncodingReader* reader) {
128   lexer::TermLexer<term::StringDoubleQuote> string_delimiter;
129   if (!string_delimiter.CheckToken(reader)) {
130     return akebono::smart_ptr<lexer::Token>();
131   }
132   reader->Read(string_delimiter.previous_read_size());
133
134   lexer::EscapeLexer escape;
135   unicode::UniString str;
136   bool syntax_ok = false;
137
138   while (!reader->IsEof()) {
139     if (string_delimiter.CheckToken(reader)) {
140       reader->Read(string_delimiter.previous_read_size());
141       syntax_ok = true;
142       break;
143     }
144
145     unsigned int code = 0;
146     if (escape.Lex(reader, &code)) {
147       if (code != 0) {
148         str.Append(unicode::UniChar(code));
149       }
150     } else {
151       str.Append(unicode::UniChar(reader->Read()));
152     }
153   }
154
155   if (!syntax_ok) {
156     THROW_EXCEPTION_(lexer::LexException,
157                      unicode::Convert("not found end of string."));
158   }
159
160   return akebono::smart_ptr<lexer::Token>(
161       new lexer::Token(str, lexer::Token::kString));
162 }