1 /*
2  * This file is part of the Colobot: Gold Edition source code
3  * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam
4  * http://epsitec.ch; http://colobot.info; http://github.com/colobot
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  * See the GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see http://gnu.org/licenses
18  */
19 
20 #include "CBot/CBotInstr/CBotExprLitChar.h"
21 
22 #include "CBot/CBotStack.h"
23 #include "CBot/CBotCStack.h"
24 
25 #include "CBot/CBotVar/CBotVar.h"
26 
27 namespace CBot
28 {
29 
CBotExprLitChar()30 CBotExprLitChar::CBotExprLitChar()
31 {
32 }
33 
~CBotExprLitChar()34 CBotExprLitChar::~CBotExprLitChar()
35 {
36 }
37 
Compile(CBotToken * & p,CBotCStack * pStack)38 CBotInstr* CBotExprLitChar::Compile(CBotToken* &p, CBotCStack* pStack)
39 {
40     CBotCStack* pStk = pStack->TokenStack();
41 
42     const auto& s = p->GetString();
43 
44     auto it = s.cbegin();
45     if (++it != s.cend())
46     {
47         if (*it != '\'') // not empty quotes ?
48         {
49             uint32_t valchar = 0;
50             int pos = p->GetStart() + 1;
51 
52             if (*it != '\\') valchar = *(it++); // not escape sequence ?
53             else if (++it != s.cend())
54             {
55                 pStk->SetStartError(pos++);
56                 unsigned char c = *(it++);
57                 if (c == '\"' || c == '\'' || c == '\\') valchar = c;
58                 else if (c == 'a') valchar = '\a'; // alert bell
59                 else if (c == 'b') valchar = '\b'; // backspace
60                 else if (c == 'f') valchar = '\f'; // form feed
61                 else if (c == 'n') valchar = '\n'; // new line
62                 else if (c == 'r') valchar = '\r'; // carriage return
63                 else if (c == 't') valchar = '\t'; // horizontal tab
64                 else if (c == 'v') valchar = '\v'; // vertical tab
65                 else if (c == 'u' || c == 'U') // unicode escape
66                 {
67                     if (it != s.cend())
68                     {
69                         std::string hex = "";
70                         size_t maxlen = (c == 'u') ? 4 : 8;
71 
72                         for (size_t i = 0; i < maxlen; i++)
73                         {
74                             if (!CharInList(*it, "0123456789ABCDEFabcdef")) break;
75                             ++pos;
76                             hex += *it;
77                             if (++it == s.cend()) break;
78                         }
79 
80                         if (maxlen == hex.length()) // unicode character
81                         {
82                             try
83                             {
84                                 valchar = std::stoi(hex, nullptr, 16);
85                                 if (0x10FFFF < valchar || (0xD7FF < valchar && valchar < 0xE000))
86                                     pStk->SetError(CBotErrUnicodeName, pos + 1);
87                             }
88                             catch (const std::out_of_range& e)
89                             {
90                                 pStk->SetError(CBotErrHexRange, pos + 1);
91                             }
92                         }
93                         else
94                             pStk->SetError(CBotErrHexDigits, pos + 1);
95                     }
96                     else
97                         pStk->SetError(CBotErrHexDigits, pos + 1);
98                 }
99                 else
100                     pStk->SetError(CBotErrBadEscape, pos + 1);
101             }
102 
103             if (it == s.cend() || *it != '\'')
104                 pStk->SetError(CBotErrEndQuote, p);
105 
106             if (pStk->IsOk())
107             {
108                 CBotExprLitChar* inst = new CBotExprLitChar();
109                 inst->m_valchar = valchar;
110                 inst->SetToken(p);
111                 p = p->GetNext();
112 
113                 CBotVar* var = CBotVar::Create("", CBotTypChar);
114                 pStk->SetVar(var);
115 
116                 return pStack->Return(inst, pStk);
117             }
118         }
119         pStk->SetError(CBotErrCharEmpty, p);
120     }
121 
122     pStk->SetError(CBotErrEndQuote, p);
123     return pStack->Return(nullptr, pStk);
124 }
125 
Execute(CBotStack * & pj)126 bool CBotExprLitChar::Execute(CBotStack* &pj)
127 {
128     CBotStack*    pile = pj->AddStack(this);
129 
130     if (pile->IfStep()) return false;
131 
132     CBotVar* var = CBotVar::Create("", CBotTypChar);
133 
134     var->SetValChar(m_valchar);
135 
136     pile->SetVar(var);
137 
138     return pj->Return(pile);
139 }
140 
RestoreState(CBotStack * & pj,bool bMain)141 void CBotExprLitChar::RestoreState(CBotStack* &pj, bool bMain)
142 {
143     if (bMain) pj->RestoreStack(this);
144 }
145 
GetDebugData()146 std::string CBotExprLitChar::GetDebugData()
147 {
148     return m_token.GetString();
149 }
150 
151 } // namespace CBot
152