Files
triton/lib/lang/parser.cc
Philippe Tillet 269ebc12e5 [PYTHON][TESTS][DOC] Various improvement of the API and code quality:
* Simplified `triton.kernel` API to achieve lower latency:
  > .data_ptr() must now be passed as kernel argument. No more implicit
conversion from torch.tensor
  > compilation options are now constant attributes, i.e., opt.d('VAR')
becomes opt.VAR
  > torch.device must now be passed explicitly to triton.kernel (no
longer inferred from torch.tensor arguments)
* C++ tests moved to `python/tests/`
* C++ tutorial created in `tutorials/`
* Python tutorial created in python/tutorials/
* Version changed to 1.0alpha
* No longer copying C++ headers into the Python package
* added python/triton/ops/ package for pre-written Triton ops
2021-07-27 12:38:48 -07:00

2797 lines
72 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "triton/lang/parser.h"
#include "triton/lang/cpp.h"
#include "triton/lang/encoding.h"
#include "triton/lang/error.h"
#include "triton/lang/evaluator.h"
#include "triton/lang/scope.h"
#include "triton/lang/type.h"
#include <iostream>
#include <set>
#include <string>
#include <climits>
FuncType* Parser::vaStartType_ {nullptr};
FuncType* Parser::vaArgType_ {nullptr};
FuncDef* Parser::EnterFunc(Identifier* ident) {
curFunc_ = FuncDef::New(ident, LabelStmt::New());
return curFunc_;
}
void Parser::ExitFunc() {
// Resolve 那些待定的jump
// 如果有jump无法resolve也就是有未定义的label报错
for (auto iter = unresolvedJumps_.begin();
iter != unresolvedJumps_.end(); ++iter) {
auto label = iter->first;
auto labelStmt = FindLabel(label->str_);
if (labelStmt == nullptr) {
Error(label, "label '%s' used but not defined",
label->str_.c_str());
}
iter->second->SetLabel(labelStmt);
}
unresolvedJumps_.clear(); //清空未定的 jump 动作
curLabels_.clear(); //清空 label map
curFunc_ = nullptr;
}
void Parser::EnterBlock(FuncType* funcType) {
curScope_ = new Scope(curScope_, S_BLOCK);
if (funcType) {
// Merge elements in param scope into current block scope
for (auto param: funcType->Params())
curScope_->Insert(param);
}
}
void Parser::Parse() {
DefineBuiltins();
ParseTranslationUnit();
}
void Parser::ParseTranslationUnit() {
while (!ts_.Peek()->IsEOF()) {
if (ts_.Try(Token::STATIC_ASSERT)) {
ParseStaticAssert();
continue;
} else if (ts_.Try(';')) {
continue;
}
int storageSpec, funcSpec, align;
auto declType = ParseDeclSpec(&storageSpec, &funcSpec, &align);
auto declInfo = ParseDeclarator(declType);
auto tok = declInfo.tok;
auto type = declInfo.type;
auto attrs = declInfo.attrs;
if (tok == nullptr) {
ts_.Expect(';');
continue;
}
auto ident = ProcessDeclarator(tok, type, attrs, storageSpec, funcSpec, align);
type = ident->Type();
if (tok && type->ToFunc() && ts_.Try('{')) { // Function definition
unit_->Add(ParseFuncDef(ident));
} else { // Declaration
auto decl = ParseInitDeclarator(ident);
if (decl) unit_->Add(decl);
while (ts_.Try(',')) {
auto ident = ParseDirectDeclarator(declType, storageSpec,
funcSpec, align);
decl = ParseInitDeclarator(ident);
if (decl) unit_->Add(decl);
}
// GNU extension: function/type/variable attributes
TryAttributeSpecList();
ts_.Expect(';');
}
}
}
FuncDef* Parser::ParseFuncDef(Identifier* ident) {
auto funcDef = EnterFunc(ident);
if (funcDef->FuncType()->Complete()) {
Error(ident, "redefinition of '%s'", funcDef->Name().c_str());
}
// TODO(wgtdkp): param checking
auto funcType = ident->Type()->ToFunc();
funcType->SetComplete(true);
for (auto param: funcType->Params()) {
if (param->Anonymous())
Error(param, "param name omitted");
}
funcDef->SetBody(ParseCompoundStmt(funcType));
ExitFunc();
return funcDef;
}
Expr* Parser::ParseExpr() {
return ParseCommaExpr();
}
Expr* Parser::ParseCommaExpr() {
auto lhs = ParseAssignExpr();
auto tok = ts_.Peek();
while (ts_.Try(',')) {
auto rhs = ParseAssignExpr();
lhs = BinaryOp::New(tok, lhs, rhs);
tok = ts_.Peek();
}
return lhs;
}
Expr* Parser::ParsePrimaryExpr() {
if (ts_.Empty()) {
Error(ts_.Peek(), "premature end of input");
}
auto tok = ts_.Next();
if (tok->tag_ == '(') {
auto expr = ParseExpr();
ts_.Expect(')');
return expr;
}
if (tok->IsIdentifier()) {
auto ident = curScope_->Find(tok);
if (ident) return ident;
if (IsBuiltin(tok->str_)) return GetBuiltin(tok);
Error(tok, "undefined symbol '%s'", tok->str_.c_str());
} else if (tok->IsConstant()) {
return ParseConstant(tok);
} else if (tok->IsLiteral()) {
return ConcatLiterals(tok);
} else if (tok->tag_ == Token::GENERIC) {
return ParseGeneric();
}
Error(tok, "'%s' unexpected", tok->str_.c_str());
return nullptr; // Make compiler happy
}
static void ConvertLiteral(std::string& val, Encoding enc) {
switch (enc) {
case Encoding::NONE:
case Encoding::UTF8: break;
case Encoding::CHAR16: ConvertToUTF16(val); break;
case Encoding::CHAR32:
case Encoding::WCHAR: ConvertToUTF32(val); break;
}
}
Constant* Parser::ConcatLiterals(const Token* tok) {
auto val = new std::string;
auto enc = Scanner(tok).ScanLiteral(*val);
ConvertLiteral(*val, enc);
while (ts_.Test(Token::LITERAL)) {
auto nextTok = ts_.Next();
std::string nextVal;
auto nextEnc = Scanner(nextTok).ScanLiteral(nextVal);
ConvertLiteral(nextVal, nextEnc);
if (enc == Encoding::NONE) {
ConvertLiteral(*val, nextEnc);
enc = nextEnc;
}
if (nextEnc != Encoding::NONE && nextEnc != enc)
Error(nextTok, "cannot concat lietrals with different encodings");
*val += nextVal;
}
int tag = T_CHAR;
switch (enc) {
case Encoding::NONE:
case Encoding::UTF8:
tag = T_CHAR; val->append(1, '\0'); break;
case Encoding::CHAR16:
tag = T_UNSIGNED | T_SHORT; val->append(2, '\0'); break;
case Encoding::CHAR32:
case Encoding::WCHAR:
tag = T_UNSIGNED | T_INT; val->append(4, '\0'); break;
}
return Constant::New(tok, tag, val);
}
Encoding Parser::ParseLiteral(std::string& str, const Token* tok) {
return Scanner(tok).ScanLiteral(str);
}
Constant* Parser::ParseConstant(const Token* tok) {
assert(tok->IsConstant());
if (tok->tag_ == Token::I_CONSTANT) {
return ParseInteger(tok);
} else if (tok->tag_ == Token::C_CONSTANT) {
return ParseCharacter(tok);
} else {
return ParseFloat(tok);
}
}
Constant* Parser::ParseFloat(const Token* tok) {
const auto& str = tok->str_;
size_t end = 0;
double val = 0.0;
try {
val = stod(str, &end);
} catch (const std::out_of_range& oor) {
Error(tok, "float out of range");
}
int tag = T_DOUBLE;
if (str[end] == 'f' || str[end] == 'F') {
tag = T_FLOAT;
++end;
} else if (str[end] == 'l' || str[end] == 'L') {
tag = T_LONG | T_DOUBLE;
++end;
}
if (str[end] != 0)
Error(tok, "invalid suffix");
return Constant::New(tok, tag, val);
}
Constant* Parser::ParseCharacter(const Token* tok) {
int val;
auto enc = Scanner(tok).ScanCharacter(val);
int tag;
switch (enc) {
case Encoding::NONE:
val = (char)val;
tag = T_INT; break;
case Encoding::CHAR16:
val = (char16_t)val;
tag = T_UNSIGNED | T_SHORT; break;
case Encoding::WCHAR:
case Encoding::CHAR32: tag = T_UNSIGNED | T_INT; break;
default: assert(false);
}
return Constant::New(tok, tag, static_cast<long>(val));
}
Constant* Parser::ParseInteger(const Token* tok) {
const auto& str = tok->str_;
size_t end = 0;
long val = 0;
try {
val = stoull(str, &end, 0);
} catch (const std::out_of_range& oor) {
Error(tok, "integer out of range");
}
int tag = 0;
for (; str[end]; ++end) {
if (str[end] == 'u' || str[end] == 'U') {
if (tag & T_UNSIGNED)
Error(tok, "invalid suffix");
tag |= T_UNSIGNED;
} else {
if ((tag & T_LONG) || (tag & T_LLONG))
Error(tok, "invalid suffix");
if (str[end + 1] == 'l' || str[end + 1] =='L') {
tag |= T_LLONG;
++end;
} else {
tag |= T_LONG;
}
}
}
bool decimal = ('1' <= str[0] && str[0] <= '9');
if (decimal) {
switch (tag) {
case 0:
tag |= !(val & ~(long)INT_MAX) ? T_INT: T_LONG; break;
case T_UNSIGNED:
tag |= !(val & ~(long)UINT_MAX) ? T_INT: T_LONG; break;
case T_LONG: break;
case T_UNSIGNED | T_LONG: break;
}
} else {
switch (tag) {
case 0:
tag |= !(val & ~(long)INT_MAX) ? T_INT
: !(val & ~(long)UINT_MAX) ? T_UNSIGNED
: !(val & ~(long)LONG_MAX) ? T_LONG
: T_UNSIGNED | T_LONG; break;
case T_UNSIGNED:
tag |= !(val & ~(long)UINT_MAX) ? T_INT: T_LONG; break;
case T_LONG:
tag |= !(val & ~(long)LONG_MAX) ? 0: T_UNSIGNED; break;
case T_UNSIGNED | T_LONG:
break;
}
}
return Constant::New(tok, tag, val);
}
Expr* Parser::ParseGeneric() {
ts_.Expect('(');
auto controlExpr = ParseAssignExpr();
ts_.Expect(',');
Expr* selectedExpr = nullptr;
bool isDefault = false;
while (true) {
if (ts_.Try(Token::DEFAULT)) {
ts_.Expect(':');
auto defaultExpr = ParseAssignExpr();
if (!selectedExpr) {
selectedExpr = defaultExpr;
isDefault = true;
}
} else {
auto tok = ts_.Peek();
auto type = ParseTypeName();
ts_.Expect(':');
auto expr = ParseAssignExpr();
if (type->Compatible(*controlExpr->Type())) {
if (selectedExpr && !isDefault) {
Error(tok, "more than one generic association"
" are compatible with control expression");
}
selectedExpr = expr;
isDefault = false;
}
}
if (!ts_.Try(',')) {
ts_.Expect(')');
break;
}
}
if (!selectedExpr)
Error(ts_.Peek(), "no compatible generic association");
return selectedExpr;
}
QualType Parser::TryCompoundLiteral() {
auto mark = ts_.Mark();
if (ts_.Try('(') && IsTypeName(ts_.Peek())) {
auto type = ParseTypeName();
if (ts_.Try(')') && ts_.Test('{'))
return type;
}
ts_.ResetTo(mark);
return nullptr;
}
Expr* Parser::ParsePostfixExpr() {
if (ts_.Peek()->IsEOF()) {
Error(ts_.Peek(), "premature end of input");
}
auto type = TryCompoundLiteral();
if (type) {
auto anony = ParseCompoundLiteral(type);
return ParsePostfixExprTail(anony);
}
auto primExpr = ParsePrimaryExpr();
return ParsePostfixExprTail(primExpr);
}
Object* Parser::ParseCompoundLiteral(QualType type) {
auto linkage = curScope_->Type() == S_FILE ? L_INTERNAL: L_NONE;
auto anony = Object::NewAnony(ts_.Peek(), type, 0, linkage);
auto decl = ParseInitDeclaratorSub(anony);
// Just for generator to find the compound literal
if (curScope_->Type() == S_FILE) {
unit_->Add(decl);
} else {
curScope_->Insert(anony->Repr(), anony);
}
return anony;
}
// Return the constructed postfix expression
Expr* Parser::ParsePostfixExprTail(Expr* lhs) {
while (true) {
auto tok = ts_.Next();
switch (tok->tag_) {
case '[': lhs = ParseSubScripting(lhs); break;
case '(': lhs = ParseFuncCall(lhs); break;
case Token::PTR: lhs = UnaryOp::New(Token::DEREF, lhs);
// Fall through
case '.': lhs = ParseMemberRef(tok, '.', lhs); break;
case Token::INC:
case Token::DEC: lhs = ParsePostfixIncDec(tok, lhs); break;
default: ts_.PutBack(); return lhs;
}
}
}
Expr* Parser::ParseSubScripting(Expr* lhs) {
auto lhsTile = lhs->Type()->ToTile();
if(lhsTile == nullptr)
Error(lhs, "tile expected");
TileType::ShapeInt lhsShape = lhsTile->Shape();
QualType lhsQual = lhsTile->Derived();
// create ret shape
TileType::ShapeInt shape;
TileType::ShapeInt axVec;
size_t i = 0;
const Token* tok;
std::vector<std::pair<int, int>> redInfo;
do {
tok = ts_.Next();
switch(tok->tag_) {
case ':':
shape.push_back(lhsShape[i++]);
break;
case Token::NEWAXIS:
shape.push_back(1);
break;
case Token::ADD:
case Token::SUB:
case Token::MAX:
case Token::MIN:{
int info = UnaryOp::encodeRed(i, tok->tag_);
redInfo.push_back({i, info});
shape.push_back(lhsShape[i++]);
break;
}
case '^':{
Expr* expr = ParseConditionalExpr();
EnsureInteger(expr);
int ax = Evaluator<long>().Eval(expr);
axVec.push_back(ax);
if(ax < 0 || ax >= lhsShape.size())
Error(tok, "unknown axis %d in transposition", ax);
shape.push_back(lhsShape[ax]);
i++;
break;
}
default:
Error(tok, "Unexpected subscript symbol encountered at dimension %d", i);
break;
}
}while(ts_.Try(','));
ts_.Expect(']');
// transposition mode
std::set<int> axSet(axVec.begin(), axVec.end());
if(!axSet.empty()){
if(axSet.size()!=lhsShape.size())
Error(tok, "transposition must address all axes of input array");
return TransOp::New(axVec, lhs);
}
// broadcasting mode
if(lhsShape.size() > i)
Error(tok, "broadcasting not using all operand axes");
// create ret tile
Expr* res = lhs;
for(auto r: redInfo){
shape.erase(shape.begin() + r.first);
Type *retType;
if(shape.empty())
retType = lhsQual.GetPtr();
else
retType = TileType::New(shape, lhsQual);
res = UnaryOp::New(Token::REDUCE, res, retType, r.second);
}
if(!shape.empty()){
TileType *retType = TileType::New(shape, lhsQual);
res = UnaryOp::New(Token::CAST, res, retType);
}
return res;
}
BinaryOp* Parser::ParseMemberRef(const Token* tok, int op, Expr* lhs) {
auto memberName = ts_.Peek()->str_;
ts_.Expect(Token::IDENTIFIER);
auto structUnionType = lhs->Type()->ToStruct();
if (structUnionType == nullptr) {
Error(tok, "an struct/union expected");
}
auto rhs = structUnionType->GetMember(memberName);
if (rhs == nullptr) {
Error(tok, "'%s' is not a member of '%s'",
memberName.c_str(), "[obj]");
}
return BinaryOp::New(tok, op, lhs, rhs);
}
UnaryOp* Parser::ParsePostfixIncDec(const Token* tok, Expr* operand) {
auto op = tok->tag_ == Token::INC ?
Token::POSTFIX_INC: Token::POSTFIX_DEC;
return UnaryOp::New(op, operand);
}
FuncCall* Parser::ParseFuncCall(Expr* designator) {
FuncCall::ArgList args;
while (!ts_.Try(')')) {
args.push_back(Expr::MayCast(ParseAssignExpr()));
if (!ts_.Test(')'))
ts_.Expect(',');
}
return FuncCall::New(designator, args);
}
Expr* Parser::ParseUnaryExpr() {
auto tok = ts_.Next();
switch (tok->tag_) {
case Token::ALIGNOF: return ParseAlignof();
case Token::SIZEOF: return ParseSizeof();
case Token::INC: return ParsePrefixIncDec(tok);
case Token::DEC: return ParsePrefixIncDec(tok);
case Token::EXP: return ParseUnaryIntrinsicOp(tok, Token::EXP); //FIXME: merge into generic array functions
case Token::SQRTF: return ParseUnaryIntrinsicOp(tok, Token::SQRTF);
case Token::LOG: return ParseUnaryIntrinsicOp(tok, Token::LOG); //FIXME: merge into generic array functions
case '&': return ParseUnaryOp(tok, Token::ADDR);
case '*': return ParseDerefOp(tok);
case '+': return ParseUnaryOp(tok, Token::PLUS);
case '-': return ParseUnaryOp(tok, Token::MINUS);
case '~': return ParseUnaryOp(tok, '~');
case '!': return ParseUnaryOp(tok, '!');
case '^': {
auto operand = ParseCastExpr();
TileType::ShapeInt shape = operand->Type()->ToTile()->Shape();
TransOp::PermInt perm(shape.size());
for(int d = 0; d < shape.size(); d++)
perm[d] = d;
std::rotate(perm.begin(), perm.begin() + 1, perm.end());
return TransOp::New(perm, operand);
}
default:
ts_.PutBack();
return ParsePostfixExpr();
}
}
Constant* Parser::ParseSizeof() {
QualType type(nullptr);
auto tok = ts_.Next();
if (tok->tag_ == '(' && IsTypeName(ts_.Peek())) {
type = ParseTypeName();
ts_.Expect(')');
} else {
ts_.PutBack();
auto expr = ParseUnaryExpr();
type = expr->Type();
}
if (type->ToFunc() || type->ToVoid()) {
} else if (!type->Complete()) {
Error(tok, "sizeof(incomplete type)");
}
long val = type->Width();
return Constant::New(tok, T_UNSIGNED | T_LONG, val);
}
Constant* Parser::ParseAlignof() {
ts_.Expect('(');
auto tok = ts_.Peek();
auto type = ParseTypeName();
ts_.Expect(')');
long val = type->Align();
return Constant::New(tok, T_UNSIGNED| T_LONG, val);
}
UnaryOp* Parser::ParsePrefixIncDec(const Token* tok) {
assert(tok->tag_ == Token::INC || tok->tag_ == Token::DEC);
auto op = tok->tag_ == Token::INC ?
Token::PREFIX_INC: Token::PREFIX_DEC;
auto operand = ParseUnaryExpr();
return UnaryOp::New(op, operand);
}
UnaryOp* Parser::ParseUnaryIntrinsicOp(const Token* tok, int op) {
ts_.Expect('(');
auto operand = ParseExpr();
ts_.Expect(')');
return UnaryOp::New(op, operand);
}
UnaryOp* Parser::ParseUnaryOp(const Token* tok, int op) {
auto operand = ParseCastExpr();
return UnaryOp::New(op, operand);
}
Expr* Parser::ParseDerefOp(const Token* tok) {
Expr* pred = nullptr;
if(ts_.Try('?')){
ts_.Expect('(');
pred = ParseExpr();
ts_.Expect(')');
}
Expr* addr = ParseCastExpr();
if(pred)
return BinaryOp::New(tok, Token::MASKED_DEREF, pred, addr);
else
return UnaryOp::New(Token::DEREF, addr);
}
QualType Parser::ParseTypeName() {
auto type = ParseSpecQual();
if (ts_.Test('*') || ts_.Test('(') || ts_.Test('[')) // abstract-declarator FIRST set
return ParseAbstractDeclarator(type);
return type;
}
Expr* Parser::ParseCastExpr() {
auto tok = ts_.Next();
// bitcast
if (tok->tag_ == Token::BITCAST) {
ts_.Expect('<');
auto type = ParseTypeName();
ts_.Expect('>');
ts_.Expect('(');
auto operand = ParseExpr();
ts_.Expect(')');
return UnaryOp::New(Token::BITCAST, operand, type);
}
// semantic cast
if (tok->tag_ == '(' && IsTypeName(ts_.Peek())) {
auto type = ParseTypeName();
ts_.Expect(')');
if (ts_.Test('{')) {
auto anony = ParseCompoundLiteral(type);
return ParsePostfixExprTail(anony);
}
auto operand = ParseCastExpr();
return UnaryOp::New(Token::CAST, operand, type);
}
ts_.PutBack();
return ParseUnaryExpr();
}
Expr* Parser::ParseRangeExpr() {
auto lhs = ParseCastExpr();
auto tok = ts_.Next();
while (tok->tag_ == Token::ELLIPSIS) {
auto rhs = ParseCastExpr();
lhs = BinaryOp::New(tok, lhs, rhs);
tok = ts_.Next();
}
ts_.PutBack();
return lhs;
}
Expr* Parser::ParseMatmulExpr() {
auto lhs = ParseRangeExpr();
auto tok = ts_.Next();
while (tok->tag_ == Token::MATMUL) {
auto rhs = ParseRangeExpr();
lhs = BinaryOp::New(tok, lhs, rhs);
tok = ts_.Next();
}
ts_.PutBack();
return lhs;
}
Expr* Parser::ParseMultiplicativeExpr() {
auto lhs = ParseMatmulExpr();
auto tok = ts_.Next();
while (tok->tag_ == '*' || tok->tag_ == '/' || tok->tag_ == '%') {
auto rhs = ParseMatmulExpr();
lhs = BinaryOp::New(tok, lhs, rhs);
tok = ts_.Next();
}
ts_.PutBack();
return lhs;
}
Expr* Parser::ParseAdditiveExpr() {
auto lhs = ParseMultiplicativeExpr();
auto tok = ts_.Next();
while (tok->tag_ == '+' || tok->tag_ == '-') {
auto rhs = ParseMultiplicativeExpr();
lhs = BinaryOp::New(tok, lhs, rhs);
tok = ts_.Next();
}
ts_.PutBack();
return lhs;
}
Expr* Parser::ParseShiftExpr() {
auto lhs = ParseAdditiveExpr();
auto tok = ts_.Next();
while (tok->tag_ == Token::LEFT || tok->tag_ == Token::RIGHT) {
auto rhs = ParseAdditiveExpr();
lhs = BinaryOp::New(tok, lhs, rhs);
tok = ts_.Next();
}
ts_.PutBack();
return lhs;
}
Expr* Parser::ParseRelationalExpr() {
auto lhs = ParseShiftExpr();
auto tok = ts_.Next();
while (tok->tag_ == Token::LE || tok->tag_ == Token::GE
|| tok->tag_ == '<' || tok->tag_ == '>') {
auto rhs = ParseShiftExpr();
lhs = BinaryOp::New(tok, lhs, rhs);
tok = ts_.Next();
}
ts_.PutBack();
return lhs;
}
Expr* Parser::ParseEqualityExpr() {
auto lhs = ParseRelationalExpr();
auto tok = ts_.Next();
while (tok->tag_ == Token::EQ || tok->tag_ == Token::NE) {
auto rhs = ParseRelationalExpr();
lhs = BinaryOp::New(tok, lhs, rhs);
tok = ts_.Next();
}
ts_.PutBack();
return lhs;
}
Expr* Parser::ParseBitiwiseAndExpr() {
auto lhs = ParseEqualityExpr();
auto tok = ts_.Peek();
while (ts_.Try('&')) {
auto rhs = ParseEqualityExpr();
lhs = BinaryOp::New(tok, lhs, rhs);
tok = ts_.Peek();
}
return lhs;
}
Expr* Parser::ParseBitwiseXorExpr() {
auto lhs = ParseBitiwiseAndExpr();
auto tok = ts_.Peek();
while (ts_.Try('^')) {
auto rhs = ParseBitiwiseAndExpr();
lhs = BinaryOp::New(tok, lhs, rhs);
tok = ts_.Peek();
}
return lhs;
}
Expr* Parser::ParseBitwiseOrExpr() {
auto lhs = ParseBitwiseXorExpr();
auto tok = ts_.Peek();
while (ts_.Try('|')) {
auto rhs = ParseBitwiseXorExpr();
lhs = BinaryOp::New(tok, lhs, rhs);
tok = ts_.Peek();
}
return lhs;
}
Expr* Parser::ParseLogicalAndExpr() {
auto lhs = ParseBitwiseOrExpr();
auto tok = ts_.Peek();
while (ts_.Try(Token::LOGICAL_AND)) {
auto rhs = ParseBitwiseOrExpr();
lhs = BinaryOp::New(tok, lhs, rhs);
tok = ts_.Peek();
}
return lhs;
}
Expr* Parser::ParseLogicalOrExpr() {
auto lhs = ParseLogicalAndExpr();
auto tok = ts_.Peek();
while (ts_.Try(Token::LOGICAL_OR)) {
auto rhs = ParseLogicalAndExpr();
lhs = BinaryOp::New(tok, lhs, rhs);
tok = ts_.Peek();
}
return lhs;
}
Expr* Parser::ParseConditionalExpr() {
auto cond = ParseLogicalOrExpr();
auto tok = ts_.Peek();
if (ts_.Try('?')) {
// Non-standard GNU extension
// a ?: b equals a ? a: c
auto exprTrue = ts_.Test(':') ? cond: ParseExpr();
ts_.Expect(':');
auto exprFalse = ParseConditionalExpr();
return ConditionalOp::New(tok, cond, exprTrue, exprFalse);
}
return cond;
}
Expr* Parser::ParseAssignExpr() {
// Yes, I know the lhs should be unary expression,
// let it handled by type checking
Expr* lhs = ParseConditionalExpr();
Expr* rhs;
auto tok = ts_.Next();
switch (tok->tag_) {
case Token::MUL_ASSIGN:
rhs = ParseAssignExpr();
rhs = BinaryOp::New(tok, '*', lhs, rhs);
break;
case Token::DIV_ASSIGN:
rhs = ParseAssignExpr();
rhs = BinaryOp::New(tok, '/', lhs, rhs);
break;
case Token::MOD_ASSIGN:
rhs = ParseAssignExpr();
rhs = BinaryOp::New(tok, '%', lhs, rhs);
break;
case Token::ADD_ASSIGN:
rhs = ParseAssignExpr();
rhs = BinaryOp::New(tok, '+', lhs, rhs);
break;
case Token::SUB_ASSIGN:
rhs = ParseAssignExpr();
rhs = BinaryOp::New(tok, '-', lhs, rhs);
break;
case Token::LEFT_ASSIGN:
rhs = ParseAssignExpr();
rhs = BinaryOp::New(tok, Token::LEFT, lhs, rhs);
break;
case Token::RIGHT_ASSIGN:
rhs = ParseAssignExpr();
rhs = BinaryOp::New(tok, Token::RIGHT, lhs, rhs);
break;
case Token::AND_ASSIGN:
rhs = ParseAssignExpr();
rhs = BinaryOp::New(tok, '&', lhs, rhs);
break;
case Token::XOR_ASSIGN:
rhs = ParseAssignExpr();
rhs = BinaryOp::New(tok, '^', lhs, rhs);
break;
case Token::OR_ASSIGN:
rhs = ParseAssignExpr();
rhs = BinaryOp::New(tok, '|', lhs, rhs);
break;
case '=':
rhs = ParseAssignExpr();
break;
default:
ts_.PutBack();
return lhs; // Could be constant
}
return BinaryOp::New(tok, '=', lhs, rhs);
}
void Parser::ParseStaticAssert() {
ts_.Expect('(');
auto condExpr = ParseAssignExpr();
ts_.Expect(',');
auto msg = ConcatLiterals(ts_.Expect(Token::LITERAL));
ts_.Expect(')');
ts_.Expect(';');
if (!Evaluator<long>().Eval(condExpr)) {
Error(ts_.Peek(), "static assertion failed: %s\n",
msg->SVal()->c_str());
}
}
// Return: list of declarations
CompoundStmt* Parser::ParseDecl() {
StmtList stmts;
if (ts_.Try(Token::STATIC_ASSERT)) {
ParseStaticAssert();
} else {
int storageSpec, funcSpec, align;
auto type = ParseDeclSpec(&storageSpec, &funcSpec, &align);
if (!ts_.Test(';')) {
do {
auto ident = ParseDirectDeclarator(type, storageSpec, funcSpec, align);
auto init = ParseInitDeclarator(ident);
if (init) stmts.push_back(init);
} while (ts_.Try(','));
}
ts_.Expect(';');
}
return CompoundStmt::New(stmts);
}
// For state machine
enum {
// Compatibility for these key words
COMP_SIGNED = T_SHORT | T_INT | T_LONG | T_LLONG,
COMP_UNSIGNED = T_SHORT | T_INT | T_LONG | T_LLONG,
COMP_CHAR = T_SIGNED | T_UNSIGNED,
COMP_SHORT = T_SIGNED | T_UNSIGNED | T_INT,
COMP_INT = T_SIGNED | T_UNSIGNED | T_LONG | T_SHORT | T_LLONG,
COMP_LONG = T_SIGNED | T_UNSIGNED | T_LONG | T_INT,
COMP_DOUBLE = T_LONG | T_COMPLEX,
COMP_COMPLEX = T_FLOAT | T_DOUBLE | T_LONG,
COMP_THREAD = S_EXTERN | S_STATIC,
};
static inline void TypeLL(int& typeSpec) {
if (typeSpec & T_LONG) {
typeSpec &= ~T_LONG;
typeSpec |= T_LLONG;
} else {
typeSpec |= T_LONG;
}
}
QualType Parser::ParseSpecQual() {
return ParseDeclSpec(nullptr, nullptr, nullptr);
}
static void EnsureAndSetStorageSpec(const Token* tok, int* storage, int spec) {
if (!storage)
Error(tok, "unexpected storage specifier");
if (*storage != 0)
Error(tok, "duplicated storage specifier");
*storage |= spec;
}
/*
* param: storage: null, only type specifier and qualifier accepted;
*/
QualType Parser::ParseDeclSpec(int* storageSpec, int* funcSpec, int* alignSpec) {
#define ERR_FUNC_SPEC ("unexpected function specifier")
#define ERR_STOR_SPEC ("unexpected storage specifier")
#define ERR_DECL_SPEC ("two or more data types in declaration specifiers")
QualType type(nullptr);
int qualSpec = 0;
int typeSpec = 0;
if (storageSpec) *storageSpec = 0;
if (funcSpec) *funcSpec = 0;
if (alignSpec) *alignSpec = 0;
const Token* tok;
for (; ;) {
tok = ts_.Next();
switch (tok->tag_) {
// Function specifier
case Token::INLINE:
if (!funcSpec)
Error(tok, ERR_FUNC_SPEC);
*funcSpec |= F_INLINE;
break;
case Token::NORETURN:
if (!funcSpec)
Error(tok, ERR_FUNC_SPEC);
*funcSpec |= F_NORETURN;
break;
// Alignment specifier
case Token::ALIGNAS: {
if (!alignSpec)
Error(tok, "unexpected alignment specifier");
auto align = ParseAlignas();
if (align)
*alignSpec = align;
break;
}
// Storage specifier
// TODO(wgtdkp): typedef needs more constraints
case Token::TYPEDEF:
EnsureAndSetStorageSpec(tok, storageSpec, S_TYPEDEF);
break;
case Token::EXTERN:
EnsureAndSetStorageSpec(tok, storageSpec, S_EXTERN);
break;
case Token::GLOBAL:
EnsureAndSetStorageSpec(tok, storageSpec, S_GLOBAL);
break;
case Token::STATIC:
if (!storageSpec)
Error(tok, ERR_FUNC_SPEC);
if (*storageSpec & ~S_THREAD)
Error(tok, "duplicated storage specifier");
*storageSpec |= S_STATIC;
break;
case Token::THREAD:
if (!storageSpec)
Error(tok, ERR_FUNC_SPEC);
if (*storageSpec & ~COMP_THREAD)
Error(tok, "duplicated storage specifier");
*storageSpec |= S_THREAD;
break;
// Type qualifier
case Token::CONST: qualSpec |= Qualifier::CONST; break;
case Token::RESTRICT: qualSpec |= Qualifier::RESTRICT; break;
case Token::VOLATILE: qualSpec |= Qualifier::VOLATILE; break;
case Token::CMEM: qualSpec |= Qualifier::CMEM; break;
// Type specifier
case Token::SIGNED:
if (typeSpec & ~COMP_SIGNED)
Error(tok, ERR_DECL_SPEC);
typeSpec |= T_SIGNED;
break;
case Token::UNSIGNED:
if (typeSpec & ~COMP_UNSIGNED)
Error(tok, ERR_DECL_SPEC);
typeSpec |= T_UNSIGNED;
break;
case Token::VOID:
if (typeSpec & ~0)
Error(tok, ERR_DECL_SPEC);
typeSpec |= T_VOID;
break;
case Token::CHAR:
if (typeSpec & ~COMP_CHAR)
Error(tok, ERR_DECL_SPEC);
typeSpec |= T_CHAR;
break;
case Token::SHORT:
if (typeSpec & ~COMP_SHORT)
Error(tok, ERR_DECL_SPEC);
typeSpec |= T_SHORT;
break;
case Token::INT:
if (typeSpec & ~COMP_INT)
Error(tok, ERR_DECL_SPEC);
typeSpec |= T_INT;
break;
case Token::LONG:
if (typeSpec & ~COMP_LONG)
Error(tok, ERR_DECL_SPEC);
TypeLL(typeSpec);
break;
case Token::HALF:
if(typeSpec & ~T_COMPLEX)
Error(tok, ERR_DECL_SPEC);
typeSpec |= T_HALF;
break;
case Token::FLOAT:
if (typeSpec & ~T_COMPLEX)
Error(tok, ERR_DECL_SPEC);
typeSpec |= T_FLOAT;
break;
case Token::DOUBLE:
if (typeSpec & ~COMP_DOUBLE)
Error(tok, ERR_DECL_SPEC);
typeSpec |= T_DOUBLE;
break;
case Token::BOOL:
if (typeSpec != 0)
Error(tok, ERR_DECL_SPEC);
typeSpec |= T_BOOL;
break;
case Token::COMPLEX:
if (typeSpec & ~COMP_COMPLEX)
Error(tok, ERR_DECL_SPEC);
typeSpec |= T_COMPLEX;
break;
case Token::STRUCT:
case Token::UNION:
if (typeSpec & ~0)
Error(tok, ERR_DECL_SPEC);
type = ParseStructUnionSpec(Token::STRUCT == tok->tag_);
typeSpec |= T_STRUCT_UNION;
break;
case Token::ENUM:
if (typeSpec != 0)
Error(tok, ERR_DECL_SPEC);
type = ParseEnumSpec();
typeSpec |= T_ENUM;
break;
case Token::ATOMIC:
Error(tok, "atomic not supported");
break;
default:
if (typeSpec == 0 && IsTypeName(tok)) {
auto ident = curScope_->Find(tok);
type = ident->Type();
// We may change the length of a array type by initializer,
// thus, make a copy of this type.
auto arrType = type->ToArray();
if (arrType && !type->Complete())
type = ArrayType::New(arrType->Len(), arrType->Derived());
typeSpec |= T_TYPEDEF_NAME;
} else {
goto end_of_loop;
}
}
}
end_of_loop:
ts_.PutBack();
switch (typeSpec) {
case 0:
Error(tok, "expect type specifier");
break;
case T_VOID:
type = VoidType::New();
break;
case T_STRUCT_UNION:
case T_ENUM:
case T_TYPEDEF_NAME:
break;
default:
type = ArithmType::New(typeSpec);
break;
}
// GNU extension: type attributes
//if (storageSpec && (*storageSpec & S_TYPEDEF))
// TryAttributeSpecList();
return QualType(type.GetPtr(), qualSpec | type.Qual());
#undef ERR_FUNC_SPEC
#undef ERR_STOR_SPEC
#undef ERR_DECL_SPEC
}
int Parser::ParseAlignas() {
int align;
ts_.Expect('(');
auto tok = ts_.Peek();
if (IsTypeName(ts_.Peek())) {
auto type = ParseTypeName();
ts_.Expect(')');
align = type->Align();
} else {
auto expr = ParseExpr();
align = Evaluator<long>().Eval(expr);
ts_.Expect(')');
}
if (align < 0 || ((align - 1) & align))
Error(tok, "requested alignment is not a positive power of 2");
return align;
}
Type* Parser::ParseEnumSpec() {
// GNU extension: type attributes
TryAttributeSpecList();
std::string tagName;
auto tok = ts_.Peek();
if (ts_.Try(Token::IDENTIFIER)) {
tagName = tok->str_;
if (ts_.Try('{')) {
// 定义enum类型
auto tagIdent = curScope_->FindTagInCurScope(tok);
if (!tagIdent) {
auto type = ArithmType::New(T_INT);
auto ident = Identifier::New(tok, type, L_NONE);
curScope_->InsertTag(ident);
return ParseEnumerator(type); // 处理反大括号: '}'
}
if (!tagIdent->Type()->IsInteger()) // struct/union tag
Error(tok, "redefinition of enumeration tag '%s'", tagName.c_str());
return ParseEnumerator(tagIdent->Type()->ToArithm());
} else {
auto tagIdent = curScope_->FindTag(tok);
if (tagIdent) {
return tagIdent->Type();
}
auto type = ArithmType::New(T_INT);
auto ident = Identifier::New(tok, type, L_NONE);
curScope_->InsertTag(ident);
return type;
}
}
ts_.Expect('{');
auto type = ArithmType::New(T_INT);
return ParseEnumerator(type); // 处理反大括号: '}'
}
Type* Parser::ParseEnumerator(ArithmType* type) {
assert(type && type->IsInteger());
int val = 0;
do {
auto tok = ts_.Expect(Token::IDENTIFIER);
// GNU extension: enumerator attributes
TryAttributeSpecList();
const auto& enumName = tok->str_;
auto ident = curScope_->FindInCurScope(tok);
if (ident) {
Error(tok, "redefinition of enumerator '%s'", enumName.c_str());
}
if (ts_.Try('=')) {
auto expr = ParseAssignExpr();
val = Evaluator<long>().Eval(expr);
}
auto enumer = Enumerator::New(tok, val);
++val;
curScope_->Insert(enumer);
ts_.Try(',');
} while (!ts_.Try('}'));
type->SetComplete(true);
return type;
}
/*
* 四种 name space
* 1.label, 如 goto end; 它有函数作用域
* 2.struct/union/enum 的 tag
* 3.struct/union 的成员
* 4.其它的普通的变量
*/
Type* Parser::ParseStructUnionSpec(bool isStruct) {
// GNU extension: type attributes
TryAttributeSpecList();
std::string tagName;
auto tok = ts_.Peek();
if (ts_.Try(Token::IDENTIFIER)) {
tagName = tok->str_;
if (ts_.Try('{')) {
// 看见大括号表明现在将定义该struct/union类型
// 我们不用关心上层scope是否定义了此tag如果定义了那么就直接覆盖定义
auto tagIdent = curScope_->FindTagInCurScope(tok);
if (!tagIdent) {
// 现在是在当前scope第一次看到name所以现在是第一次定义连前向声明都没有
auto type = StructType::New(isStruct, tagName.size(), curScope_);
auto ident = Identifier::New(tok, type, L_NONE);
curScope_->InsertTag(ident);
return ParseStructUnionDecl(type); // 处理反大括号: '}'
}
// 在当前scope找到了类型但可能只是声明注意声明与定义只能出现在同一个scope
// 1.如果声明在定义的外层scope,那么即使在内层scope定义了完整的类型此声明仍然是无效的
// 因为如论如何编译器都不会在内部scope里面去找定义所以声明的类型仍然是不完整的
// 2.如果声明在定义的内层scope,(也就是先定义再在内部scope声明),这时,不完整的声明会覆盖掉完整的定义;
// 因为编译器总是向上查找符号,不管找到的是完整的还是不完整的,都要;
if (!tagIdent->Type()->Complete()) {
// 找到了此tag的前向声明并更新其符号表最后设置为complete type
return ParseStructUnionDecl(tagIdent->Type()->ToStruct());
} else {
// 在当前作用域找到了完整的定义,并且现在正在定义同名的类型,所以报错;
Error(tok, "redefinition of struct tag '%s'", tagName.c_str());
}
} else {
// 没有大括号表明不是定义一个struct/union;那么现在只可能是在:
// 1.声明;
// 2.声明的同时,定义指针(指针允许指向不完整类型) (struct Foo* p; 是合法的) 或者其他合法的类型;
// 如果现在索引符号表,那么:
// 1.可能找到name的完整定义也可能只找得到不完整的声明不管name指示的是不是完整类型我们都只能选择name指示的类型
// 2.如果我们在符号表里面压根找不到name,那么现在是name的第一次声明创建不完整的类型并插入符号表
auto tagIdent = curScope_->FindTag(tok);
// 如果tag已经定义或声明那么直接返回此定义或者声明
if (tagIdent) {
return tagIdent->Type();
}
// 如果tag尚没有定义或者声明那么创建此tag的声明(因为没有见到‘{’,所以不会是定义)
auto type = StructType::New(isStruct, true, curScope_);
// 因为有tag所以不是匿名的struct/union 向当前的scope插入此tag
auto ident = Identifier::New(tok, type, L_NONE);
curScope_->InsertTag(ident);
return type;
}
}
// 没见到identifier那就必须有struct/union的定义这叫做匿名struct/union;
ts_.Expect('{');
// 现在如果是有tag那它没有前向声明如果是没有tag那更加没有前向声明
// 所以现在是第一次开始定义一个完整的struct/union类型
auto type = StructType::New(isStruct, tagName.size(), curScope_);
return ParseStructUnionDecl(type); // 处理反大括号: '}'
}
StructType* Parser::ParseStructUnionDecl(StructType* type) {
#define ADD_MEMBER() { \
auto member = Object::New(tok, memberType); \
if (align > 0) \
member->SetAlign(align); \
type->AddMember(member); \
}
// 既然是定义,那输入肯定是不完整类型,不然就是重定义了
assert(type && !type->Complete());
auto scopeBackup = curScope_;
curScope_ = type->MemberMap(); // Internal symbol lookup rely on curScope_
while (!ts_.Try('}')) {
if (ts_.Empty()) {
Error(ts_.Peek(), "premature end of input");
}
if(ts_.Try(Token::STATIC_ASSERT)) {
ParseStaticAssert();
continue;
}
// 解析type specifier/qualifier, 不接受storage等
int align;
auto baseType = ParseDeclSpec(nullptr, nullptr, &align);
do {
auto declInfo = ParseDeclarator(baseType);
auto tok = declInfo.tok;
auto memberType = declInfo.type;
if (ts_.Try(':')) {
ParseBitField(type, tok, memberType);
continue;
}
if (tok == nullptr) {
auto suType = memberType->ToStruct();
if (suType && !suType->HasTag()) {
auto anony = Object::NewAnony(ts_.Peek(), suType);
type->MergeAnony(anony);
continue;
} else {
Error(ts_.Peek(), "declaration does not declare anything");
}
}
const auto& name = tok->str_;
if (type->GetMember(name)) {
Error(tok, "duplicate member '%s'", name.c_str());
} else if (!memberType->Complete()) {
// C11 6.7.2.1 [3]:
if (type->IsStruct() &&
// Struct has more than one named member
type->MemberMap()->size() > 0 &&
memberType->ToArray()) {
ts_.Expect(';'); ts_.Expect('}');
ADD_MEMBER();
goto finalize;
} else {
Error(tok, "field '%s' has incomplete type", name.c_str());
}
} else if (memberType->ToFunc()) {
Error(tok, "field '%s' declared as a function", name.c_str());
}
ADD_MEMBER();
} while (ts_.Try(','));
ts_.Expect(';');
}
finalize:
// GNU extension: type attributes
TryAttributeSpecList();
// struct/union定义结束设置其为完整类型
type->Finalize();
type->SetComplete(true);
// TODO(wgtdkp): we need to export tags defined inside struct
const auto& tags = curScope_->AllTagsInCurScope();
for (auto tag: tags) {
if (scopeBackup->FindTag(tag->Tok()))
Error(tag, "redefinition of tag '%s'\n", tag->Name().c_str());
scopeBackup->InsertTag(tag);
}
curScope_ = scopeBackup;
return type;
}
void Parser::ParseBitField(StructType* structType,
const Token* tok,
QualType type) {
if (!type->IsInteger()) {
Error(tok ? tok: ts_.Peek(), "expect integer type for bitfield");
}
auto expr = ParseAssignExpr();
auto width = Evaluator<long>().Eval(expr);
if (width < 0) {
Error(expr, "expect non negative value");
} else if (width == 0 && tok) {
Error(tok, "no declarator expected for a bitfield with width 0");
} else if (width > type->Width() * 8) {
Error(expr, "width exceeds its type");
}
auto offset = structType->Offset() - type->Width();
// C11 6.7.5 [2]: alignment attribute shall not be specified in declaration of a bit field
// so here is ok to use type->Align()
offset = Type::MakeAlign(std::max(offset, 0), type->Align());
int bitFieldOffset;
unsigned char begin;
if (!structType->IsStruct()) {
begin = 0;
bitFieldOffset = 0;
} else if (structType->Members().size() == 0) {
begin = 0;
bitFieldOffset = 0;
} else {
auto last = structType->Members().back();
auto totalBits = last->Offset() * 8;
if (last->BitFieldWidth()) {
totalBits += last->BitFieldEnd();
} else { // Is not bit field
totalBits += last->Type()->Width() * 8;
}
if (width == 0)
width = type->Width() * 8 - totalBits; // So posterior bitfield would be packed
if (width == 0) // A bitfield with zero width is never added to member list
return; // Because we use bitfield width to tell if a member is bitfield or not.
if (width + totalBits <= type->Width() * 8) {
begin = totalBits % 8;
bitFieldOffset = totalBits / 8;
} else {
begin = 0;
bitFieldOffset = Type::MakeAlign(structType->Offset(), type->Width());
}
}
Object* bitField;
if (tok) {
bitField = Object::New(tok, type, 0, L_NONE, begin, width);
} else {
bitField = Object::NewAnony(ts_.Peek(), type, 0, L_NONE, begin, width);
}
structType->AddBitField(bitField, bitFieldOffset);
}
int Parser::ParseQual() {
int qualSpec = 0;
for (; ;) {
auto tok = ts_.Next();
switch (tok->tag_) {
case Token::CONST: qualSpec |= Qualifier::CONST; break;
case Token::RESTRICT: qualSpec |= Qualifier::RESTRICT; break;
case Token::VOLATILE: qualSpec |= Qualifier::VOLATILE; break;
case Token::CMEM: qualSpec |= Qualifier::CMEM; break;
case Token::ATOMIC: Error(tok, "do not support 'atomic'"); break;
default: ts_.PutBack(); return qualSpec;
}
}
}
QualType Parser::ParsePointer(QualType typePointedTo) {
while (ts_.Try('*')) {
auto t = PointerType::New(typePointedTo);
typePointedTo = QualType(t, ParseQual());
}
return typePointedTo;
}
static QualType ModifyBase(QualType type, QualType base, QualType newBase) {
if (type == base)
return newBase;
auto ty = type->ToDerived();
ty->SetDerived(ModifyBase(ty->Derived(), base, newBase));
return ty;
}
/*
* Return: pair of token(must be identifier) and it's type
* if token is nullptr, then we are parsing abstract declarator
* else, parsing direct declarator.
*/
DeclInfo Parser::ParseDeclarator(QualType base) {
// May be pointer
auto pointerType = ParsePointer(base);
if (ts_.Try('(')) {
// 现在的 pointerType 并不是正确的 base type
auto declInfo = ParseDeclarator(pointerType);
auto tok = declInfo.tok;
auto type = declInfo.type;
ts_.Expect(')');
auto newBase = ParseArrayFuncDeclarator(tok, pointerType);
// 修正 base type
auto retType = ModifyBase(type, pointerType, newBase);
return DeclInfo(declInfo.tok, retType);
} else if (ts_.Peek()->IsIdentifier()) {
auto tok = ts_.Next();
// GNU extension: variable attributes
ASTNode::AttrList attrList = TryAttributeSpecList();
auto retType = ParseArrayFuncDeclarator(tok, pointerType);
return DeclInfo(tok, retType, attrList);
} else {
errTok_ = ts_.Peek();
auto retType = ParseArrayFuncDeclarator(nullptr, pointerType);
return DeclInfo(nullptr, retType);
}
}
Identifier* Parser::ProcessDeclarator(const Token* tok,
QualType type,
const ASTNode::AttrList& attrs,
int storageSpec,
int funcSpec,
int align) {
assert(tok);
// 检查在同一 scope 是否已经定义此变量
// 如果 storage 是 typedef那么应该往符号表里面插入 type
// 定义 void 类型变量是非法的只能是指向void类型的指针
// 如果 funcSpec != 0, 那么现在必须是在定义函数,否则出错
const auto& name = tok->str_;
Identifier* ident;
if (storageSpec & S_TYPEDEF) {
// C11 6.7.5 [2]: alignment specifier
if (align > 0)
Error(tok, "alignment specified for typedef");
ident = curScope_->FindInCurScope(tok);
if (ident) { // There is prio declaration in the same scope
// The same declaration, simply return the prio declaration
if (!type->Compatible(*ident->Type()))
Error(tok, "conflicting types for '%s'", name.c_str());
// TODO(wgtdkp): add previous declaration information
return ident;
}
if(!attrs.empty()) {
Error(tok, "typedef attributes not allowed");
}
ident = Identifier::New(tok, type, L_NONE);
curScope_->Insert(ident);
return ident;
}
if (type->ToVoid()) {
Error(tok, "variable or field '%s' declared void",
name.c_str());
}
if (type->ToFunc() && curScope_->Type() != S_FILE
&& (storageSpec & S_STATIC)) {
Error(tok, "invalid storage class for function '%s'", name.c_str());
}
Linkage linkage;
// Identifiers in function prototype have no linkage
if (curScope_->Type() == S_PROTO) {
linkage = L_NONE;
} else if (curScope_->Type() == S_FILE) {
linkage = L_EXTERNAL; // Default linkage for file scope identifiers
if (storageSpec & S_STATIC)
linkage = L_INTERNAL;
} else if (!(storageSpec & S_EXTERN)) {
linkage = L_NONE; // Default linkage for block scope identifiers
if (type->ToFunc())
linkage = L_EXTERNAL;
} else {
linkage = L_EXTERNAL;
}
ident = curScope_->FindInCurScope(tok);
if (ident) { // There is prio declaration in the same scope
if (!type->Compatible(*ident->Type())) {
Error(tok, "conflicting types for '%s'", name.c_str());
}
// The same scope prio declaration has no linkage,
// there is a redeclaration error
if (linkage == L_NONE) {
Error(tok, "redeclaration of '%s' with no linkage",
name.c_str());
} else if (linkage == L_EXTERNAL) {
if (ident->Linkage() == L_NONE) {
Error(tok, "conflicting linkage for '%s'", name.c_str());
}
} else {
if (ident->Linkage() != L_INTERNAL) {
Error(tok, "conflicting linkage for '%s'", name.c_str());
}
}
// The same declaration, simply return the prio declaration
if (!ident->Type()->Complete())
ident->Type()->SetComplete(type->Complete());
// Prio declaration of a function may omit the param name
if (type->ToFunc())
ident->Type()->ToFunc()->SetParams(type->ToFunc()->Params());
else if (ident->ToObject() && !(storageSpec & S_EXTERN))
ident->ToObject()->SetStorage(ident->ToObject()->Storage() & ~S_EXTERN);
return ident;
} else if (linkage == L_EXTERNAL) {
ident = curScope_->Find(tok);
if (ident) {
if (!type->Compatible(*ident->Type())) {
Error(tok, "conflicting types for '%s'", name.c_str());
}
if (ident->Linkage() != L_NONE) {
linkage = ident->Linkage();
}
// Don't return, override it
} else {
ident = externalSymbols_->FindInCurScope(tok);
if (ident) {
if (!type->Compatible(*ident->Type())) {
Error(tok, "conflicting types for '%s'", name.c_str());
}
// TODO(wgtdkp): ???????
// Don't return
// To stop later declaration with the same name in the same scope overriding this declaration
// Useless here, just keep it
if (!ident->Type()->Complete())
ident->Type()->SetComplete(type->Complete());
//return ident;
}
}
}
Identifier* ret;
// TODO(wgtdkp): Treat function as object ?
if (type->ToFunc()) {
// C11 6.7.5 [2]: alignment specifier
if (align > 0)
Error(tok, "alignment specified for function");
ret = Identifier::New(tok, type, linkage, attrs);
} else {
auto obj = Object::New(tok, type, storageSpec, linkage, 0, 0, attrs);
if (align > 0)
obj->SetAlign(align);
ret = obj;
}
curScope_->Insert(ret);
if (linkage == L_EXTERNAL && ident == nullptr) {
externalSymbols_->Insert(ret);
}
return ret;
}
QualType Parser::ParseArrayFuncDeclarator(const Token* ident, QualType base) {
if (ts_.Try('[')) {
if(!base->IsScalar()) {
Error(ts_.Peek(), "tiles must have scalar elements");
}
auto shape = ParseTileShape();
ts_.Expect(']');
base = ParseArrayFuncDeclarator(ident, base);
if (!base->Complete()) {
Error(ident, "'%s' has incomplete element type", ident->str_.c_str());
}
// return a pointer for tiles in constant memory:
return TileType::New(shape, base);
} else if (ts_.Try('(')) { // Function declaration
if (base->ToFunc()) {
Error(ts_.Peek(),
"the return value of function cannot be function");
} else if (nullptr != base->ToArray()) {
Error(ts_.Peek(),
"the return value of function cannot be array");
}
FuncType::ParamList params;
EnterProto();
auto variadic = ParseParamList(params);
ExitProto();
ts_.Expect(')');
base = ParseArrayFuncDeclarator(ident, base);
return FuncType::New(base, 0, variadic, params);
}
return base;
}
/*
* Return: -1, length not specified
*/
int Parser::ParseArrayLength() {
auto hasStatic = ts_.Try(Token::STATIC);
auto qual = ParseQual();
if (0 != qual)
hasStatic = ts_.Try(Token::STATIC);
// 不支持变长数组
if (!hasStatic && ts_.Test(']'))
return -1;
auto expr = ParseAssignExpr();
EnsureInteger(expr);
auto ret = Evaluator<long>().Eval(expr);
if (ret < 0) {
Error(expr, "size of array is negative");
}
return ret;
}
TileType::ShapeInt Parser::ParseTileShape() {
TileType::ShapeInt ret;
size_t i = 0;
do {
Expr* expr = ParseConditionalExpr();
EnsureInteger(expr);
int dim = Evaluator<long>().Eval(expr);
if (dim < 0)
Error(expr, "shape %d of tile is negative", i);
ret.push_back(dim);
i++;
}while(ts_.Try(','));
return ret;
}
/*
* Return: true, variadic;
*/
bool Parser::ParseParamList(FuncType::ParamList& params) {
if (ts_.Test(')'))
return false;
auto param = ParseParamDecl();
if (param->Type()->ToVoid())
return false;
params.push_back(param);
while (ts_.Try(',')) {
if (ts_.Try(Token::ELLIPSIS))
return true;
param = ParseParamDecl();
if (param->Type()->ToVoid())
Error(param, "'void' must be the only parameter");
params.push_back(param);
}
return false;
}
Object* Parser::ParseParamDecl() {
int storageSpec, funcSpec;
// C11 6.7.5 [2]: alignment specifier cannot be specified in params
auto type = ParseDeclSpec(&storageSpec, &funcSpec, nullptr);
auto tokTypePair = ParseDeclarator(type);
auto tok = tokTypePair.tok;
QualType fullType(tokTypePair.type.GetPtr(), type.Qual());
type = Type::MayCast(fullType, true);
auto attrs = tokTypePair.attrs;
if (!tok) { // Abstract declarator
return Object::NewAnony(ts_.Peek(), type, 0, Linkage::L_NONE);
}
// Align set to non positive, stands for not specified
auto ident = ProcessDeclarator(tok, type, attrs, storageSpec, funcSpec, -1);
if (!ident->ToObject())
Error(ident, "expect object in param list");
return ident->ToObject();
}
QualType Parser::ParseAbstractDeclarator(QualType type) {
auto declInfo = ParseDeclarator(type);
auto tok = declInfo.tok;
type = declInfo.type;
if (tok) { // Not a abstract declarator!
Error(tok, "unexpected identifier '%s'", tok->str_.c_str());
}
return type;
}
Identifier* Parser::ParseDirectDeclarator(QualType type,
int storageSpec,
int funcSpec,
int align) {
auto declInfo = ParseDeclarator(type);
auto tok = declInfo.tok;
type = declInfo.type;
auto attrs = declInfo.attrs;
if (tok == nullptr) {
Error(errTok_, "expect identifier or '('");
}
return ProcessDeclarator(tok, type, attrs, storageSpec, funcSpec, align);
}
Declaration* Parser::ParseInitDeclarator(Identifier* ident) {
auto obj = ident->ToObject();
if (!obj) { // Do not record function Declaration
return nullptr;
}
const auto& name = obj->Name();
if (ts_.Try('=')) {
return ParseInitDeclaratorSub(obj);
}
if (!obj->Type()->Complete()) {
if (obj->Linkage() == L_NONE) {
Error(obj, "storage size of '%s' isnt known", name.c_str());
}
// FIXME(wgtdkp):
// Discards the incomplete object declarations
// It causes linking failure of forward-declared objects with imcomplete type
return nullptr;
}
if (!obj->Decl()) {
auto decl = Declaration::New(obj);
obj->SetDecl(decl);
return decl;
}
return nullptr;
}
Declaration* Parser::ParseInitDeclaratorSub(Object* obj) {
const auto& name = obj->Name();
if ((curScope_->Type() != S_FILE) && obj->Linkage() != L_NONE) {
Error(obj, "'%s' has both 'extern' and initializer", name.c_str());
}
if (!obj->Type()->Complete() && !obj->Type()->ToArray()) {
Error(obj, "variable '%s' has initializer but incomplete type",
name.c_str());
}
if (obj->HasInit()) {
Error(obj, "redefinition of variable '%s'", name.c_str());
}
// There could be more than one declaration for
// an object in the same scope.
// But it must has external or internal linkage.
// So, for external/internal objects,
// the initialization will always go to
// the first declaration. As the initialization
// is evaluated at compile time,
// the order doesn't matter.
// For objects with no linkage, there is
// always only one declaration.
// Once again, we need not to worry about
// the order of the initialization.
if (obj->Decl()) {
ParseInitializer(obj->Decl(), obj->Type(), 0, false, true);
return nullptr;
} else {
auto decl = Declaration::New(obj);
ParseInitializer(decl, obj->Type(), 0, false, true);
obj->SetDecl(decl);
return decl;
}
}
void Parser::ParseInitializer(Declaration* decl,
QualType type,
int offset,
bool designated,
bool forceBrace,
unsigned char bitFieldBegin,
unsigned char bitFieldWidth) {
if (designated && !ts_.Test('.') && !ts_.Test('[')) {
ts_.Expect('=');
}
// std::cout << "parsing initialized " << decl->Obj()->Name() << std::endl;
Expr* expr;
auto arrType = type->ToArray();
auto structType = type->ToStruct();
// A compound literal in initializer is reduced to a initializer directly
// It means that the compound literal will never be created
//auto literalType = TryCompoundLiteral();
//if (literalType && !literalType->Compatible(*type))
// Error("incompatible type of initializer");
if (arrType) {
if (forceBrace && !ts_.Test('{') && !ts_.Test(Token::LITERAL)) {
ts_.Expect('{');
} else if (!ParseLiteralInitializer(decl, arrType, offset)) {
ParseArrayInitializer(decl, arrType, offset, designated);
arrType->SetComplete(true);
}
return;
} else if (structType) {
if (!ts_.Test('.') && !ts_.Test('{')) {
auto mark = ts_.Mark();
expr = ParseAssignExpr();
if (structType->Compatible(*expr->Type())) {
decl->AddInit({structType, offset, expr});
return;
}
ts_.ResetTo(mark);
if (forceBrace)
ts_.Expect('{');
}
return ParseStructInitializer(decl, structType, offset, designated);
}
// Scalar type
auto hasBrace = ts_.Try('{');
expr = ParseAssignExpr();
if (hasBrace) {
ts_.Try(',');
ts_.Expect('}');
}
decl->AddInit({type.GetPtr(), offset, expr, bitFieldBegin, bitFieldWidth});
}
bool Parser::ParseLiteralInitializer(Declaration* decl,
ArrayType* type,
int offset) {
if (!type->Derived()->IsInteger())
return false;
auto hasBrace = ts_.Try('{');
if (!ts_.Test(Token::LITERAL)) {
if (hasBrace) ts_.PutBack();
return false;
}
auto literal = ConcatLiterals(ts_.Next());
auto tok = literal->Tok();
if (hasBrace) {
ts_.Try(',');
ts_.Expect('}');
}
if (!type->Complete()) {
type->SetLen(literal->Type()->ToArray()->Len());
type->SetComplete(true);
}
auto width = std::min(type->Width(), literal->Type()->Width());
auto str = literal->SVal()->c_str();
for (; width >= 8; width -= 8) {
auto p = reinterpret_cast<const long*>(str);
auto type = ArithmType::New(T_LONG);
auto val = Constant::New(tok, T_LONG, static_cast<long>(*p));
decl->AddInit({type, offset, val});
offset += 8;
str += 8;
}
for (; width >= 4; width -= 4) {
auto p = reinterpret_cast<const int*>(str);
auto type = ArithmType::New(T_INT);
auto val = Constant::New(tok, T_INT, static_cast<long>(*p));
decl->AddInit({type, offset, val});
offset += 4;
str += 4;
}
for (; width >= 2; width -= 2) {
auto p = reinterpret_cast<const short*>(str);
auto type = ArithmType::New(T_SHORT);
auto val = Constant::New(tok, T_SHORT, static_cast<long>(*p));
decl->AddInit({type, offset, val});
offset += 2;
str += 2;
}
for (; width >= 1; --width) {
auto p = str;
auto type = ArithmType::New(T_CHAR);
auto val = Constant::New(tok, T_CHAR, static_cast<long>(*p));
decl->AddInit({type, offset, val});
offset++;
str++;
}
return true;
}
void Parser::ParseArrayInitializer(Declaration* decl,
ArrayType* type,
int offset,
bool designated) {
assert(type);
if (!type->Complete())
type->SetLen(0);
int idx = 0;
auto width = type->Derived()->Width();
auto hasBrace = ts_.Try('{');
while (true) {
if (ts_.Test('}')) {
if (hasBrace)
ts_.Next();
return;
}
if (!designated && !hasBrace && (ts_.Test('.') || ts_.Test('['))) {
ts_.PutBack(); // Put the read comma(',') back
return;
} else if ((designated = ts_.Try('['))) {
auto expr = ParseAssignExpr();
EnsureInteger(expr);
idx = Evaluator<long>().Eval(expr);
ts_.Expect(']');
if (idx < 0 || (type->Complete() && idx >= type->Len())) {
Error(ts_.Peek(), "excess elements in array initializer");
}
}
ParseInitializer(decl, type->Derived(), offset + idx * width, designated);
designated = false;
++idx;
if (type->Complete() && idx >= type->Len()) {
break;
} else if (!type->Complete()) {
type->SetLen(std::max(idx, type->Len()));
}
// Needless comma at the end is legal
if (!ts_.Try(',')) {
if (hasBrace)
ts_.Expect('}');
return;
}
}
if (hasBrace) {
ts_.Try(',');
if (!ts_.Try('}')) {
Error(ts_.Peek(), "excess elements in array initializer");
}
}
}
StructType::Iterator Parser::ParseStructDesignator(StructType* type,
const std::string& name) {
auto iter = type->Members().begin();
for (; iter != type->Members().end(); ++iter) {
if ((*iter)->Anonymous()) {
auto anonyType = (*iter)->Type()->ToStruct();
assert(anonyType);
if (anonyType->GetMember(name)) {
return iter; // ParseStructDesignator(anonyType);
}
} else if ((*iter)->Name() == name) {
return iter;
}
}
assert(false);
return iter;
}
void Parser::ParseStructInitializer(Declaration* decl,
StructType* type,
int offset,
bool designated) {
assert(type);
auto hasBrace = ts_.Try('{');
auto member = type->Members().begin();
while (true) {
if (ts_.Test('}')) {
if (hasBrace)
ts_.Next();
return;
}
if (!designated && !hasBrace && (ts_.Test('.') || ts_.Test('['))) {
ts_.PutBack(); // Put the read comma(',') back
return;
}
if ((designated = ts_.Try('.'))) {
auto tok = ts_.Expect(Token::IDENTIFIER);
const auto& name = tok->str_;
if (!type->GetMember(name)) {
Error(tok, "member '%s' not found", name.c_str());
}
member = ParseStructDesignator(type, name);
}
if (member == type->Members().end())
break;
if ((*member)->Anonymous()) {
if (designated) { // Put back '.' and member name.
ts_.PutBack();
ts_.PutBack();
}
// Because offsets of member of anonymous struct/union are based
// directly on external struct/union
ParseInitializer(decl, (*member)->Type(), offset, designated, false,
(*member)->BitFieldBegin(), (*member)->BitFieldWidth());
} else {
ParseInitializer(decl, (*member)->Type(),
offset + (*member)->Offset(), designated, false,
(*member)->BitFieldBegin(), (*member)->BitFieldWidth());
}
designated = false;
++member;
// Union, just init the first member
if (!type->IsStruct())
break;
if (!hasBrace && member == type->Members().end())
break;
// Needless comma at the end is allowed
if (!ts_.Try(',')) {
if (hasBrace)
ts_.Expect('}');
return;
}
}
if (hasBrace) {
ts_.Try(',');
if (!ts_.Try('}')) {
Error(ts_.Peek(), "excess members in struct initializer");
}
}
}
/*
* Statements
*/
Stmt* Parser::ParseStmt() {
auto tok = ts_.Next();
if (tok->IsEOF())
Error(tok, "premature end of input");
switch (tok->tag_) {
// GNU extension: statement attributes
case Token::ATTRIBUTE:
TryAttributeSpecList();
case ';':
return EmptyStmt::New();
case '{':
return ParseCompoundStmt();
case Token::IF:
return ParseIfStmt();
case Token::SWITCH:
return ParseSwitchStmt();
case Token::WHILE:
return ParseWhileStmt();
case Token::DO:
return ParseDoStmt();
case Token::FOR:
return ParseForStmt();
case Token::GOTO:
return ParseGotoStmt();
case Token::CONTINUE:
return ParseContinueStmt();
case Token::BREAK:
return ParseBreakStmt();
case Token::RETURN:
return ParseReturnStmt();
case Token::CASE:
return ParseCaseStmt();
case Token::DEFAULT:
return ParseDefaultStmt();
}
if (tok->IsIdentifier() && ts_.Try(':')) {
// GNU extension: label attributes
TryAttributeSpecList();
return ParseLabelStmt(tok);
}
ts_.PutBack();
auto expr = ParseExpr();
ts_.Expect(';');
return expr;
}
CompoundStmt* Parser::ParseCompoundStmt(FuncType* funcType) {
EnterBlock(funcType);
std::list<Stmt*> stmts;
while (!ts_.Try('}')) {
if (ts_.Peek()->IsEOF()) {
Error(ts_.Peek(), "premature end of input");
}
if (IsType(ts_.Peek())) {
stmts.push_back(ParseDecl());
} else {
stmts.push_back(ParseStmt());
}
}
auto scope = curScope_;
ExitBlock();
return CompoundStmt::New(stmts, scope);
}
IfStmt* Parser::ParseIfStmt() {
ts_.Expect('(');
auto tok = ts_.Peek();
auto cond = ParseExpr();
if (!cond->Type()->IsScalar()) {
Error(tok, "expect scalar");
}
ts_.Expect(')');
auto then = ParseStmt();
Stmt* els = nullptr;
if (ts_.Try(Token::ELSE))
els = ParseStmt();
return IfStmt::New(cond, then, els);
}
/*
* for 循环结构:
* for (declaration; expression1; expression2) statement
* 展开后的结构:
* declaration
* cond: if (expression1) then empty
* else goto end
* statement
* step: expression2
* goto cond
* next:
*/
#define ENTER_LOOP_BODY(breakDest, continueDest) \
{ \
LabelStmt* breakDestBackup = breakDest_; \
LabelStmt* continueDestBackup = continueDest_; \
breakDest_ = breakDest; \
continueDest_ = continueDest;
#define EXIT_LOOP_BODY() \
breakDest_ = breakDestBackup; \
continueDest_ = continueDestBackup; \
}
ForStmt* Parser::ParseForStmt() {
EnterBlock();
ts_.Expect('(');
// init
Stmt* init = nullptr;
if (IsType(ts_.Peek())) {
init = ParseDecl();
} else if (!ts_.Try(';')) {
init = ParseExpr();
ts_.Expect(';');
}
// cond
Expr* cond = nullptr;
if (!ts_.Try(';')) {
cond = ParseExpr();
ts_.Expect(';');
}
// step
Expr* step = nullptr;
if (!ts_.Try(')')) {
step = ParseExpr();
ts_.Expect(')');
}
// body
Stmt* body = ParseStmt();
ExitBlock();
return ForStmt::New(body, init, cond, step);
}
/*
* while 循环结构:
* while (expression) statement
* 展开后的结构:
* cond: if (expression) then empty
* else goto end
* statement
* goto cond
* end:
*/
CompoundStmt* Parser::ParseWhileStmt() {
std::list<Stmt*> stmts;
ts_.Expect('(');
auto tok = ts_.Peek();
auto condExpr = ParseExpr();
ts_.Expect(')');
if (!condExpr->Type()->IsScalar()) {
Error(tok, "scalar expression expected");
}
auto condLabel = LabelStmt::New();
auto endLabel = LabelStmt::New();
auto gotoEndStmt = JumpStmt::New(endLabel);
auto ifStmt = IfStmt::New(condExpr, EmptyStmt::New(), gotoEndStmt);
stmts.push_back(condLabel);
stmts.push_back(ifStmt);
Stmt* bodyStmt;
ENTER_LOOP_BODY(endLabel, condLabel)
bodyStmt = ParseStmt();
EXIT_LOOP_BODY()
stmts.push_back(bodyStmt);
stmts.push_back(JumpStmt::New(condLabel));
stmts.push_back(endLabel);
return CompoundStmt::New(stmts);
}
/*
* do-while 循环结构:
* do statement while (expression)
* 展开后的结构:
* begin: statement
* cond: if (expression) then goto begin
* else goto end
* end:
*/
CompoundStmt* Parser::ParseDoStmt() {
auto beginLabel = LabelStmt::New();
auto condLabel = LabelStmt::New();
auto endLabel = LabelStmt::New();
Stmt* bodyStmt;
ENTER_LOOP_BODY(endLabel, beginLabel)
bodyStmt = ParseStmt();
EXIT_LOOP_BODY()
ts_.Expect(Token::WHILE);
ts_.Expect('(');
auto condExpr = ParseExpr();
ts_.Expect(')');
ts_.Expect(';');
auto gotoBeginStmt = JumpStmt::New(beginLabel);
auto gotoEndStmt = JumpStmt::New(endLabel);
auto ifStmt = IfStmt::New(condExpr, gotoBeginStmt, gotoEndStmt);
std::list<Stmt*> stmts;
stmts.push_back(beginLabel);
stmts.push_back(bodyStmt);
stmts.push_back(condLabel);
stmts.push_back(ifStmt);
stmts.push_back(endLabel);
return CompoundStmt::New(stmts);
}
#undef ENTER_LOOP_BODY
#undef EXIT_LOOP_BODY
#define ENTER_SWITCH_BODY(breakDest, caseLabels) \
{ \
CaseLabelList* caseLabelsBackup = caseLabels_; \
LabelStmt* defaultLabelBackup = defaultLabel_; \
LabelStmt* breakDestBackup = breakDest_; \
breakDest_ = breakDest; \
caseLabels_ = &caseLabels; \
defaultLabel_ = nullptr;
#define EXIT_SWITCH_BODY() \
caseLabels_ = caseLabelsBackup; \
breakDest_ = breakDestBackup; \
defaultLabel_ = defaultLabelBackup; \
}
/*
* switch
* jump stmt (skip case labels)
* case labels
* jump stmts
* default jump stmt
*/
CompoundStmt* Parser::ParseSwitchStmt() {
std::list<Stmt*> stmts;
ts_.Expect('(');
auto tok = ts_.Peek();
auto expr = ParseExpr();
ts_.Expect(')');
if (!expr->Type()->IsInteger()) {
Error(tok, "switch quantity not an integer");
}
auto testLabel = LabelStmt::New();
auto endLabel = LabelStmt::New();
auto t = TempVar::New(expr->Type());
auto assign = BinaryOp::New(tok, '=', t, expr);
stmts.push_back(assign);
stmts.push_back(JumpStmt::New(testLabel));
CaseLabelList caseLabels;
ENTER_SWITCH_BODY(endLabel, caseLabels);
auto bodyStmt = ParseStmt(); // Fill caseLabels and defaultLabel
stmts.push_back(bodyStmt);
stmts.push_back(JumpStmt::New(endLabel));
stmts.push_back(testLabel);
for (auto iter = caseLabels.begin();
iter != caseLabels.end(); ++iter) {
auto cond = BinaryOp::New(tok, Token::EQ, t, iter->first);
auto then = JumpStmt::New(iter->second);
auto ifStmt = IfStmt::New(cond, then, nullptr);
stmts.push_back(ifStmt);
}
if (defaultLabel_)
stmts.push_back(JumpStmt::New(defaultLabel_));
EXIT_SWITCH_BODY();
stmts.push_back(endLabel);
return CompoundStmt::New(stmts);
}
#undef ENTER_SWITCH_BODY
#undef EXIT_SWITCH_BODY
CompoundStmt* Parser::ParseCaseStmt() {
auto tok = ts_.Peek();
// Case ranges: Non-standard GNU extension
long begin, end;
begin = Evaluator<long>().Eval(ParseAssignExpr());
if (ts_.Try(Token::ELLIPSIS))
end = Evaluator<long>().Eval(ParseAssignExpr());
else
end = begin;
ts_.Expect(':');
auto labelStmt = LabelStmt::New();
for (auto val = begin; val <= end; ++val) {
if (val > INT_MAX)
Error(tok, "case range exceed range of int");
auto cons = Constant::New(tok, T_INT, val);
caseLabels_->push_back(std::make_pair(cons, labelStmt));
}
std::list<Stmt*> stmts;
stmts.push_back(labelStmt);
stmts.push_back(ParseStmt());
return CompoundStmt::New(stmts);
}
CompoundStmt* Parser::ParseDefaultStmt() {
auto tok = ts_.Peek();
ts_.Expect(':');
if (defaultLabel_) { // There is a 'default' stmt
Error(tok, "multiple default labels in one switch");
}
auto labelStmt = LabelStmt::New();
defaultLabel_ = labelStmt;
std::list<Stmt*> stmts;
stmts.push_back(labelStmt);
stmts.push_back(ParseStmt());
return CompoundStmt::New(stmts);
}
JumpStmt* Parser::ParseContinueStmt() {
auto tok = ts_.Peek();
ts_.Expect(';');
if (continueDest_ == nullptr) {
Error(tok, "'continue' is allowed only in loop");
}
return JumpStmt::New(continueDest_);
}
JumpStmt* Parser::ParseBreakStmt() {
auto tok = ts_.Peek();
ts_.Expect(';');
if (breakDest_ == nullptr) {
Error(tok, "'break' is allowed only in switch/loop");
}
return JumpStmt::New(breakDest_);
}
ReturnStmt* Parser::ParseReturnStmt() {
Expr* expr;
if (ts_.Try(';')) {
expr = nullptr;
} else {
expr = ParseExpr();
ts_.Expect(';');
auto retType = curFunc_->FuncType()->Derived();
expr = Expr::MayCast(expr, retType);
}
return ReturnStmt::New(expr);
}
JumpStmt* Parser::ParseGotoStmt() {
auto label = ts_.Peek();
ts_.Expect(Token::IDENTIFIER);
ts_.Expect(';');
auto labelStmt = FindLabel(label->str_);
if (labelStmt) {
return JumpStmt::New(labelStmt);
}
auto unresolvedJump = JumpStmt::New(nullptr);
unresolvedJumps_.push_back(std::make_pair(label, unresolvedJump));
return unresolvedJump;
}
CompoundStmt* Parser::ParseLabelStmt(const Token* label) {
const auto& labelStr = label->str_;
auto stmt = ParseStmt();
if (nullptr != FindLabel(labelStr)) {
Error(label, "redefinition of label '%s'", labelStr.c_str());
}
auto labelStmt = LabelStmt::New();
AddLabel(labelStr, labelStmt);
std::list<Stmt*> stmts;
stmts.push_back(labelStmt);
stmts.push_back(stmt);
return CompoundStmt::New(stmts);
}
bool Parser::IsBuiltin(const std::string& name) {
return name == "__builtin_va_arg" ||
name == "__builtin_va_start";
}
bool Parser::IsBuiltin(FuncType* type) {
assert(vaStartType_ && vaArgType_);
return type == vaStartType_ || type == vaArgType_;
}
// Builtin functions will be inlined
void Parser::DefineBuiltins() {
// FIXME: potential bug: using same object for params!!!
auto voidPtr = PointerType::New(VoidType::New());
auto param = Object::New(nullptr, voidPtr);
FuncType::ParamList pl;
pl.push_back(param);
pl.push_back(param);
vaStartType_ = FuncType::New(VoidType::New(), F_INLINE, false, pl);
vaArgType_ = FuncType::New(voidPtr, F_INLINE, false, pl);
}
Identifier* Parser::GetBuiltin(const Token* tok) {
assert(vaStartType_ && vaArgType_);
static Identifier* vaStart = nullptr;
static Identifier* vaArg = nullptr;
const auto& name = tok->str_;
if (name == "__builtin_va_start") {
if (!vaStart)
vaStart = Identifier::New(tok, vaStartType_, Linkage::L_EXTERNAL);
return vaStart;
} else if (name == "__builtin_va_arg") {
if (!vaArg)
vaArg = Identifier::New(tok, vaArgType_, Linkage::L_EXTERNAL);
return vaArg;
}
assert(false);
return nullptr;
}
/*
* GNU extensions
*/
// Attribute
ASTNode::AttrList Parser::TryAttributeSpecList() {
ASTNode::AttrList attrList;
while (ts_.Try(Token::ATTRIBUTE))
ParseAttributeSpec(attrList);
return attrList;
}
void Parser::ParseAttributeSpec(ASTNode::AttrList& attrList) {
ts_.Expect('(');
ts_.Expect('(');
while (!ts_.Try(')')) {
attrList.push_back(ParseAttribute());
if (!ts_.Try(',')) {
ts_.Expect(')');
break;
}
}
ts_.Expect(')');
}
ASTNode::Attr Parser::ParseAttribute() {
ASTNode::Attr ret;
if (!ts_.Test(Token::IDENTIFIER))
return ret;
auto tok = ts_.Next();
std::string name = tok->str_;
// set kind
if(name == "aligned")
ret.kind = ASTNode::Attr::ALIGNED;
else if(name == "readonly")
ret.kind = ASTNode::Attr::READONLY;
else if(name == "writeonly")
ret.kind = ASTNode::Attr::WRITEONLY;
else if(name == "multipleof")
ret.kind = ASTNode::Attr::MULTIPLEOF;
else if(name == "noalias")
ret.kind = ASTNode::Attr::NOALIAS;
else if(name == "retune")
ret.kind = ASTNode::Attr::RETUNE;
else
Error(tok, "unknown attribute kind");
// set exprs
if (ts_.Try('(')) {
if (ts_.Try(')'))
return ret;
ret.vals.push_back(ParseExpr());
if (ts_.Test(',')) {
while (ts_.Try(',')) {}
}
ts_.Try(')');
}
return ret;
}