Fix position bug
[hiphop-php.git] / hphp / parser / parser.cpp
blobff3a24390bb48bd152ad89371810867ac1e8dfe4
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "parser.h"
18 #include <folly/Conv.h>
19 #include <folly/Format.h>
21 #include "hphp/util/hash.h"
22 #include "hphp/util/bstring.h"
24 namespace HPHP {
25 ///////////////////////////////////////////////////////////////////////////////
27 bool ParserBase::IsClosureName(const std::string &name) {
28 return name.size() >= 8 && bstrcaseeq(name.c_str(), "closure$", 8);
31 bool ParserBase::IsAnonymousClassName(folly::StringPiece name) {
32 return name.find('$') != std::string::npos;
35 const char* ParserBase::labelScopeName(LabelScopeKind kind) {
36 switch (kind) {
37 case LabelScopeKind::Invalid: break;
38 case LabelScopeKind::LoopSwitch: return "into loop or switch";
39 case LabelScopeKind::Finally: return "into finally";
40 case LabelScopeKind::Using: return "into or across using";
43 always_assert(false && "Invalid LabelScope");
46 std::string ParserBase::newClosureName(
47 const std::string &namespaceName,
48 const std::string &className,
49 const std::string &funcName) {
50 return newAnonClassName("Closure", namespaceName, className, funcName);
53 std::string ParserBase::newAnonClassName(
54 const std::string &prefix,
55 const std::string &namespaceName,
56 const std::string &className,
57 const std::string &funcName) {
59 auto name = prefix + "$";
60 if (!className.empty()) {
61 name += className + "::";
62 } else if (!namespaceName.empty() &&
63 funcName.find('\\') == std::string::npos) {
64 // If className is present, it already includes the namespace. Or
65 // else we may be passed a function name already qualified by
66 // namespace, and in either case we don't want to include the
67 // namespace twice in the mangled class name.
68 name += namespaceName + "\\";
70 name += funcName;
72 auto const id = ++m_seenAnonClasses[name];
73 if (id > 1) {
74 // we've seen the same name before, uniquify
75 folly::format(&name, "#{}", id);
78 return name;
81 ///////////////////////////////////////////////////////////////////////////////
83 ParserBase::ParserBase(Scanner &scanner, const char *fileName)
84 : m_scanner(scanner), m_fileName(fileName) {
85 if (m_fileName == nullptr) m_fileName = "";
87 // global scope
88 m_labelInfos.reserve(3);
89 pushLabelInfo();
92 ParserBase::~ParserBase() {
95 std::string ParserBase::getMessage(bool filename /* = false */,
96 bool rawPosWhenNoError /* = false */
97 ) const {
98 std::string ret = m_scanner.getError();
100 if (!ret.empty()) {
101 ret += " ";
103 if (!ret.empty() || rawPosWhenNoError) {
104 ret += getMessage(m_scanner.getLocation()->r, filename);
107 return ret;
110 std::string ParserBase::getMessage(const Location::Range& loc,
111 bool filename /* = false */) const {
112 int line = loc.line1;
113 int column = loc.char1;
114 std::string ret = "(";
115 if (filename) {
116 ret += std::string("File: ") + file() + ", ";
118 ret += std::string("Line: ") + folly::to<std::string>(line);
119 ret += ", Char: " + folly::to<std::string>(column) + ")";
120 return ret;
123 const Location::Range& ParserBase::getRange() const {
124 return m_loc.r;
127 ///////////////////////////////////////////////////////////////////////////////
128 // T_FUNCTION related functions
130 void ParserBase::pushFuncLocation() {
131 m_funcLocs.push_back(getRange());
134 Location::Range ParserBase::popFuncLocation() {
135 assert(!m_funcLocs.empty());
136 auto loc = m_funcLocs.back();
137 m_funcLocs.pop_back();
138 return loc;
141 void ParserBase::pushClass(bool isXhpClass) {
142 m_classes.push_back(isXhpClass);
145 bool ParserBase::peekClass() {
146 assert(!m_classes.empty());
147 return m_classes.back();
150 void ParserBase::popClass() {
151 m_classes.pop_back();
154 ///////////////////////////////////////////////////////////////////////////////
155 // typevar scopes
157 void ParserBase::pushTypeScope() {
158 m_typeScopes.push_back(m_typeVars);
159 m_typeVars.clear();
162 void ParserBase::popTypeScope() {
163 m_typeScopes.pop_back();
166 void ParserBase::addTypeVar(const std::string &name) {
167 m_typeVars.insert(name);
170 bool ParserBase::isTypeVar(const std::string &name) {
171 for (TypevarScopeStack::iterator iter = m_typeScopes.begin();
172 iter != m_typeScopes.end();
173 iter++) {
174 if (iter->find(name) != iter->end()) {
175 return true;
178 return false;
181 bool ParserBase::isTypeVarInImmediateScope(const std::string &name) {
182 int currentScope = m_typeScopes.size() - 1;
183 return m_typeScopes[currentScope].find(name)
184 != m_typeScopes[currentScope].end();
187 ///////////////////////////////////////////////////////////////////////////////
188 // checks GOTO label syntax
190 void ParserBase::pushLabelInfo() {
191 m_labelInfos.emplace_back();
192 pushLabelScope(LabelScopeKind::Invalid);
195 void ParserBase::pushLabelScope(LabelScopeKind kind) {
196 assert(!m_labelInfos.empty());
197 LabelInfo &info = m_labelInfos.back();
198 info.scopes.emplace_back(kind, ++info.scopeId);
201 void ParserBase::popLabelScope() {
202 assertx(!m_labelInfos.empty());
203 assertx(!m_labelInfos.back().scopes.empty());
204 m_labelInfos.back().scopes.pop_back();
207 void ParserBase::addLabel(const std::string &label,
208 const Location::Range& loc,
209 ScannerToken *stmt) {
210 assert(!m_labelInfos.empty());
211 LabelInfo &info = m_labelInfos.back();
212 if (info.labels.find(label) != info.labels.end()) {
213 error("Label '%s' already defined: %s", label.c_str(),
214 getMessage().c_str());
215 return;
217 assert(!info.scopes.empty());
218 auto const stmtInfo = LabelStmtInfo{
219 extractStatement(stmt),
220 info.scopes.back(),
221 loc,
222 false
224 info.labels.emplace(label, stmtInfo);
227 void ParserBase::addGoto(const std::string &label,
228 const Location::Range& loc,
229 ScannerToken *stmt) {
230 assert(!m_labelInfos.empty());
231 LabelInfo &info = m_labelInfos.back();
232 GotoInfo gotoInfo;
233 gotoInfo.label = label;
234 gotoInfo.scopes = info.scopes;
235 gotoInfo.loc = loc;
236 gotoInfo.stmt = extractStatement(stmt);
237 info.gotos.push_back(gotoInfo);
240 void ParserBase::popLabelInfo() {
241 assert(!m_labelInfos.empty());
242 LabelInfo &info = m_labelInfos.back();
243 assertx(info.scopes.size() == 1);
244 assertx(info.scopes.back().kind == LabelScopeKind::Invalid);
246 LabelMap labels = info.labels; // shallow copy
248 for (unsigned int i = 0; i < info.gotos.size(); i++) {
249 const GotoInfo &gotoInfo = info.gotos[i];
250 LabelMap::const_iterator iter = info.labels.find(gotoInfo.label);
251 if (iter == info.labels.end()) {
252 error("'goto' to undefined label '%s': %s",
253 gotoInfo.label.c_str(), getMessage(gotoInfo.loc).c_str());
254 continue;
256 const LabelStmtInfo &labelInfo = iter->second;
257 int labelScopeId = labelInfo.scopeInfo.id;
258 bool found = false;
259 for (int j = gotoInfo.scopes.size() - 1; j >= 0; j--) {
260 if (labelScopeId == gotoInfo.scopes[j].id) {
261 found = true;
262 break;
265 if (!found) {
266 error("'goto' %s statement is disallowed: %s",
267 labelScopeName(labelInfo.scopeInfo.kind),
268 getMessage(gotoInfo.loc).c_str());
269 continue;
270 } else {
271 labels.erase(gotoInfo.label);
275 m_labelInfos.pop_back();
278 ///////////////////////////////////////////////////////////////////////////////