source: src/Parser/StatementNode.cpp@ 4d5c5b6a

Last change on this file since 4d5c5b6a was cd28605, checked in by Peter A. Buhr <pabuhr@…>, 8 months ago

first attempt at generalizing attributes to statements

  • Property mode set to 100644
File size: 18.9 KB
Line 
1//
2// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
7// StatementNode.cpp -- Transform from parse data-structures to AST data-structures, usually deleting the parse
8// data-structure after the transformation.
9//
10// Author : Rodolfo G. Esteves
11// Created On : Sat May 16 14:59:41 2015
12// Last Modified By : Peter A. Buhr
13// Last Modified On : Thu Feb 6 11:38:39 2025
14// Update Count : 434
15//
16
17#include "StatementNode.hpp"
18
19#include <cassert> // for assert, strict_dynamic_cast, assertf
20#include <memory> // for unique_ptr
21#include <string> // for string
22
23#include "AST/Label.hpp" // for Label
24#include "AST/Stmt.hpp" // for Stmt, AsmStmt, BranchStmt, CaseCla...
25#include "Common/SemanticError.hpp"// for SemanticError
26#include "Common/Utility.hpp" // for maybeMoveBuild, maybeBuild
27#include "DeclarationNode.hpp" // for DeclarationNode
28#include "ExpressionNode.hpp" // for ExpressionNode
29#include "ParserUtility.hpp" // for notZeroExpr
30
31class Declaration;
32
33using namespace std;
34
35// Some helpers for cases that really want a single node but check for lists.
36static const ast::Stmt * buildMoveSingle( StatementNode * node ) {
37 std::vector<ast::ptr<ast::Stmt>> list;
38 buildMoveList( node, list );
39 assertf( list.size() == 1, "CFA Internal Error: Extra/Missing Nodes" );
40 return list.front().release();
41}
42
43static const ast::Stmt * buildMoveOptional( StatementNode * node ) {
44 std::vector<ast::ptr<ast::Stmt>> list;
45 buildMoveList( node, list );
46 assertf( list.size() <= 1, "CFA Internal Error: Extra Nodes" );
47 return list.empty() ? nullptr : list.front().release();
48}
49
50StatementNode::StatementNode( DeclarationNode * decl ) {
51 assert( decl );
52 DeclarationNode * agg = decl->extractAggregate();
53 if ( agg ) {
54 StatementNode * nextStmt = new StatementNode(
55 new ast::DeclStmt( decl->location, maybeBuild( decl ) ) );
56 next = nextStmt;
57 if ( decl->next ) {
58 next->next = new StatementNode( decl->next );
59 decl->next = nullptr;
60 } // if
61 } else {
62 if ( decl->next ) {
63 next = new StatementNode( decl->next );
64 decl->next = nullptr;
65 } // if
66 agg = decl;
67 } // if
68 // Local copy to avoid accessing the pointer after it is moved from.
69 CodeLocation declLocation = agg->location;
70 stmt.reset( new ast::DeclStmt( declLocation, maybeMoveBuild( agg ) ) );
71} // StatementNode::StatementNode
72
73StatementNode * StatementNode::addQualifiers( DeclarationNode * attr ) {
74 if ( ! attr ) { return this; } // empty attribute list
75 attributes.insert( attributes.end(), attr->attributes.begin(), attr->attributes.end() );
76 return this;
77}
78
79StatementNode * StatementNode::add_label(
80 const CodeLocation & location,
81 const std::string * name,
82 DeclarationNode * attr ) {
83 stmt->labels.emplace_back( location,
84 *name,
85 attr ? std::move( attr->attributes )
86 : std::vector<ast::ptr<ast::Attribute>>{} );
87 delete attr;
88 delete name;
89 return this;
90}
91
92ClauseNode * ClauseNode::append_last_case( StatementNode * stmt ) {
93 ClauseNode * prev = this;
94 // find end of list and maintain previous pointer
95 for ( ClauseNode * curr = prev; curr != nullptr; curr = curr->next ) {
96 ClauseNode * node = curr;
97 assert( dynamic_cast<ast::CaseClause *>( node->clause.get() ) );
98 prev = curr;
99 } // for
100 ClauseNode * node = prev;
101 // convert from StatementNode list to Statement list
102 std::vector<ast::ptr<ast::Stmt>> stmts;
103 buildMoveList( stmt, stmts );
104 // splice any new Statements to end of current Statements
105 auto caseStmt = strict_dynamic_cast<ast::CaseClause *>( node->clause.get() );
106 for ( auto const & newStmt : stmts ) {
107 caseStmt->stmts.emplace_back( newStmt );
108 }
109 stmts.clear();
110 return this;
111} // ClauseNode::append_last_case
112
113ast::Stmt * build_expr( CodeLocation const & location, ExpressionNode * ctrl ) {
114 if ( ast::Expr * e = maybeMoveBuild( ctrl ) ) {
115 return new ast::ExprStmt( location, e );
116 } else {
117 return new ast::NullStmt( location );
118 }
119} // build_expr
120
121static ast::Expr * build_if_control( CondCtrl * ctrl,
122 std::vector<ast::ptr<ast::Stmt>> & inits ) {
123 assert( inits.empty() );
124 if ( nullptr != ctrl->init ) {
125 buildMoveList( ctrl->init, inits );
126 } // if
127
128 ast::Expr * cond = nullptr;
129 if ( ctrl->condition ) {
130 cond = maybeMoveBuild( ctrl->condition );
131 } else {
132 for ( ast::ptr<ast::Stmt> & stmt : inits ) {
133 // build the && of all of the declared variables compared against 0
134 auto declStmt = stmt.strict_as<ast::DeclStmt>();
135 auto dwt = declStmt->decl.strict_as<ast::DeclWithType>();
136 ast::Expr * nze = new ast::VariableExpr( dwt->location, dwt );
137 cond = cond ? new ast::LogicalExpr( dwt->location, cond, nze, ast::AndExpr ) : nze;
138 }
139 }
140 delete ctrl;
141 return cond;
142} // build_if_control
143
144ast::Stmt * build_if( const CodeLocation & location, CondCtrl * ctrl, StatementNode * then, StatementNode * else_ ) {
145 std::vector<ast::ptr<ast::Stmt>> astinit; // maybe empty
146 ast::Expr * astcond = build_if_control( ctrl, astinit ); // ctrl deleted, cond/init set
147
148 ast::Stmt const * astthen = buildMoveSingle( then );
149 ast::Stmt const * astelse = buildMoveOptional( else_ );
150
151 return new ast::IfStmt( location, astcond, astthen, astelse,
152 std::move( astinit )
153 );
154} // build_if
155
156ast::Stmt * build_switch( const CodeLocation & location, bool isSwitch, ExpressionNode * ctrl, ClauseNode * stmt ) {
157 std::vector<ast::ptr<ast::CaseClause>> aststmt;
158 buildMoveList( stmt, aststmt );
159 // If it is not a switch it is a choose statement.
160 if ( ! isSwitch ) {
161 for ( ast::ptr<ast::CaseClause> & stmt : aststmt ) {
162 // Code after "case" is the end of case list.
163 if ( !stmt->stmts.empty() ) {
164 auto mutStmt = ast::mutate( stmt.get() );
165 // I believe the stmts are actually always one block.
166 auto stmts = mutStmt->stmts.front().get_and_mutate();
167 auto block = strict_dynamic_cast<ast::CompoundStmt *>( stmts );
168 block->kids.push_back( new ast::BranchStmt( block->location,
169 ast::BranchStmt::Break,
170 ast::Label( block->location ) ) );
171 stmt = mutStmt;
172 } // if
173 } // for
174 } // if
175 // aststmt.size() == 0 for switch (...) {}, i.e., no declaration or statements
176 return new ast::SwitchStmt( location,
177 maybeMoveBuild( ctrl ), std::move( aststmt ) );
178} // build_switch
179
180ast::CaseClause * build_case( const CodeLocation & location, ExpressionNode * ctrl ) {
181 // stmt starts empty and then added to
182 auto expr = maybeMoveBuild( ctrl );
183 return new ast::CaseClause( location, expr, {} );
184} // build_case
185
186ast::CaseClause * build_default( const CodeLocation & location ) {
187 // stmt starts empty and then added to
188 return new ast::CaseClause( location, nullptr, {} );
189} // build_default
190
191ast::Stmt * build_while( const CodeLocation & location, CondCtrl * ctrl, StatementNode * stmt, StatementNode * else_ ) {
192 std::vector<ast::ptr<ast::Stmt>> astinit; // maybe empty
193 ast::Expr * astcond = build_if_control( ctrl, astinit ); // ctrl deleted, cond/init set
194
195 return new ast::WhileDoStmt( location,
196 astcond,
197 buildMoveSingle( stmt ),
198 buildMoveOptional( else_ ),
199 std::move( astinit ),
200 ast::While
201 );
202} // build_while
203
204ast::Stmt * build_do_while( const CodeLocation & location, ExpressionNode * ctrl, StatementNode * stmt, StatementNode * else_ ) {
205 // do-while cannot have declarations in the contitional, so init is always empty
206 return new ast::WhileDoStmt( location,
207 maybeMoveBuild( ctrl ),
208 buildMoveSingle( stmt ),
209 buildMoveOptional( else_ ),
210 {},
211 ast::DoWhile
212 );
213} // build_do_while
214
215ast::Stmt * build_for( const CodeLocation & location, ForCtrl * forctrl, StatementNode * stmt, StatementNode * else_ ) {
216 std::vector<ast::ptr<ast::Stmt>> astinit; // maybe empty
217 buildMoveList( forctrl->init, astinit );
218
219 if ( forctrl->range_over ) {
220 ast::Expr * range_over = maybeMoveBuild( forctrl->range_over );
221 bool isIncreasing = forctrl->kind == OperKinds::LEThan;
222 // Copy all the data needed before the delete.
223 delete forctrl;
224 return new ast::ForeachStmt( location,
225 std::move( astinit ),
226 range_over,
227 isIncreasing ? ast::IncreasingRange : ast::DecreasingRange,
228 buildMoveSingle( stmt ),
229 buildMoveOptional( else_ )
230 );
231 }
232
233 ast::Expr * astcond = nullptr; // maybe empty
234 astcond = maybeMoveBuild( forctrl->condition );
235
236 ast::Expr * astincr = nullptr; // maybe empty
237 astincr = maybeMoveBuild( forctrl->change );
238 delete forctrl;
239
240 return new ast::ForStmt( location,
241 std::move( astinit ),
242 astcond,
243 astincr,
244 buildMoveSingle( stmt ),
245 buildMoveOptional( else_ )
246 );
247} // build_for
248
249ast::Stmt * build_branch( const CodeLocation & location, ast::BranchStmt::Kind kind ) {
250 return new ast::BranchStmt( location,
251 kind,
252 ast::Label( location )
253 );
254} // build_branch
255
256ast::Stmt * build_branch( const CodeLocation & location, string * identifier, ast::BranchStmt::Kind kind ) {
257 ast::Stmt * ret = new ast::BranchStmt( location,
258 kind,
259 ast::Label( location, *identifier )
260 );
261 delete identifier; // allocated by lexer
262 return ret;
263} // build_branch
264
265ast::Stmt * build_computedgoto( ExpressionNode * ctrl ) {
266 ast::Expr * expr = maybeMoveBuild( ctrl );
267 return new ast::BranchStmt( expr->location, expr );
268} // build_computedgoto
269
270ast::Stmt * build_return( const CodeLocation & location, ExpressionNode * ctrl ) {
271 std::vector<ast::ptr<ast::Expr>> exps;
272 buildMoveList( ctrl, exps );
273 return new ast::ReturnStmt( location,
274 exps.size() > 0 ? exps.back().release() : nullptr
275 );
276} // build_return
277
278static ast::Stmt * build_throw_stmt(
279 const CodeLocation & location,
280 ExpressionNode * ctrl,
281 ast::ExceptionKind kind ) {
282 std::vector<ast::ptr<ast::Expr>> exps;
283 buildMoveList( ctrl, exps );
284 assertf( exps.size() < 2, "CFA internal error: leaking memory" );
285 return new ast::ThrowStmt( location,
286 kind,
287 !exps.empty() ? exps.back().release() : nullptr,
288 (ast::Expr *)nullptr
289 );
290}
291
292ast::Stmt * build_throw( const CodeLocation & loc, ExpressionNode * ctrl ) {
293 return build_throw_stmt( loc, ctrl, ast::Terminate );
294} // build_throw
295
296ast::Stmt * build_resume( const CodeLocation & loc, ExpressionNode * ctrl ) {
297 return build_throw_stmt( loc, ctrl, ast::Resume );
298} // build_resume
299
300ast::Stmt * build_resume_at( ExpressionNode * ctrl, ExpressionNode * target ) {
301 (void)ctrl;
302 (void)target;
303 assertf( false, "resume at (non-local throw) is not yet supported," );
304} // build_resume_at
305
306ast::Stmt * build_try( const CodeLocation & location, StatementNode * try_, ClauseNode * catch_, ClauseNode * finally_ ) {
307 std::vector<ast::ptr<ast::CatchClause>> aststmt;
308 buildMoveList( catch_, aststmt );
309 ast::CompoundStmt * tryBlock = strict_dynamic_cast<ast::CompoundStmt *>( maybeMoveBuild( try_ ) );
310 ast::FinallyClause * finallyBlock = nullptr;
311 if ( finally_ ) {
312 finallyBlock = dynamic_cast<ast::FinallyClause *>( finally_->clause.release() );
313 }
314 return new ast::TryStmt( location,
315 tryBlock,
316 std::move( aststmt ),
317 finallyBlock
318 );
319} // build_try
320
321ast::CatchClause * build_catch( const CodeLocation & location, ast::ExceptionKind kind, DeclarationNode * decl, ExpressionNode * cond, StatementNode * body ) {
322 return new ast::CatchClause( location,
323 kind,
324 maybeMoveBuild( decl ),
325 maybeMoveBuild( cond ),
326 buildMoveSingle( body )
327 );
328} // build_catch
329
330ast::FinallyClause * build_finally( const CodeLocation & location, StatementNode * stmt ) {
331 return new ast::FinallyClause( location,
332 strict_dynamic_cast<const ast::CompoundStmt *>(
333 buildMoveSingle( stmt )
334 )
335 );
336} // build_finally
337
338ast::SuspendStmt * build_suspend( const CodeLocation & location, StatementNode * then, ast::SuspendStmt::Kind kind ) {
339 return new ast::SuspendStmt( location,
340 strict_dynamic_cast<const ast::CompoundStmt *, nullptr>(
341 buildMoveOptional( then )
342 ),
343 kind
344 );
345} // build_suspend
346
347ast::WaitForStmt * build_waitfor( const CodeLocation & location, ast::WaitForStmt * existing, ExpressionNode * when, ExpressionNode * targetExpr, StatementNode * stmt ) {
348 auto clause = new ast::WaitForClause( location );
349 clause->target = maybeBuild( targetExpr );
350 clause->stmt = maybeMoveBuild( stmt );
351 clause->when_cond = maybeMoveBuild( when );
352
353 ExpressionNode * next = targetExpr->next;
354 targetExpr->next = nullptr;
355 buildMoveList( next, clause->target_args );
356
357 delete targetExpr;
358
359 existing->clauses.insert( existing->clauses.end(), clause );
360
361 return existing;
362} // build_waitfor
363
364ast::WaitForStmt * build_waitfor_else( const CodeLocation & location, ast::WaitForStmt * existing, ExpressionNode * when, StatementNode * stmt ) {
365 existing->else_stmt = maybeMoveBuild( stmt );
366 existing->else_cond = maybeMoveBuild( when );
367
368 (void)location;
369 return existing;
370} // build_waitfor_else
371
372ast::WaitForStmt * build_waitfor_timeout( const CodeLocation & location, ast::WaitForStmt * existing, ExpressionNode * when, ExpressionNode * timeout, StatementNode * stmt ) {
373 existing->timeout_time = maybeMoveBuild( timeout );
374 existing->timeout_stmt = maybeMoveBuild( stmt );
375 existing->timeout_cond = maybeMoveBuild( when );
376
377 (void)location;
378 return existing;
379} // build_waitfor_timeout
380
381ast::WaitUntilStmt::ClauseNode * build_waituntil_clause( const CodeLocation & loc, ExpressionNode * when, ExpressionNode * targetExpr, StatementNode * stmt ) {
382 ast::WhenClause * clause = new ast::WhenClause( loc );
383 clause->when_cond = maybeMoveBuild( when );
384 clause->stmt = maybeMoveBuild( stmt );
385 clause->target = maybeMoveBuild( targetExpr );
386 return new ast::WaitUntilStmt::ClauseNode( clause );
387}
388ast::WaitUntilStmt::ClauseNode * build_waituntil_else( const CodeLocation & loc, ExpressionNode * when, StatementNode * stmt ) {
389 ast::WhenClause * clause = new ast::WhenClause( loc );
390 clause->when_cond = maybeMoveBuild( when );
391 clause->stmt = maybeMoveBuild( stmt );
392 return new ast::WaitUntilStmt::ClauseNode( ast::WaitUntilStmt::ClauseNode::Op::ELSE, clause );
393}
394
395ast::WaitUntilStmt * build_waituntil_stmt( const CodeLocation & loc, ast::WaitUntilStmt::ClauseNode * root ) {
396 ast::WaitUntilStmt * retStmt = new ast::WaitUntilStmt( loc );
397 retStmt->predicateTree = root;
398
399 // iterative tree traversal
400 std::vector<ast::WaitUntilStmt::ClauseNode *> nodeStack; // stack needed for iterative traversal
401 ast::WaitUntilStmt::ClauseNode * currNode = nullptr;
402 ast::WaitUntilStmt::ClauseNode * lastInternalNode = nullptr;
403 ast::WaitUntilStmt::ClauseNode * cleanup = nullptr; // used to cleanup removed else/timeout
404 nodeStack.push_back(root);
405
406 do {
407 currNode = nodeStack.back();
408 nodeStack.pop_back(); // remove node since it will be processed
409
410 switch (currNode->op) {
411 case ast::WaitUntilStmt::ClauseNode::LEAF:
412 retStmt->clauses.push_back(currNode->leaf);
413 break;
414 case ast::WaitUntilStmt::ClauseNode::ELSE:
415 retStmt->else_stmt = currNode->leaf->stmt
416 ? ast::deepCopy( currNode->leaf->stmt )
417 : nullptr;
418 retStmt->else_cond = currNode->leaf->when_cond
419 ? ast::deepCopy( currNode->leaf->when_cond )
420 : nullptr;
421
422 delete currNode->leaf;
423 break;
424 case ast::WaitUntilStmt::ClauseNode::TIMEOUT:
425 retStmt->timeout_time = currNode->leaf->target
426 ? ast::deepCopy( currNode->leaf->target )
427 : nullptr;
428 retStmt->timeout_stmt = currNode->leaf->stmt
429 ? ast::deepCopy( currNode->leaf->stmt )
430 : nullptr;
431 retStmt->timeout_cond = currNode->leaf->when_cond
432 ? ast::deepCopy( currNode->leaf->when_cond )
433 : nullptr;
434
435 delete currNode->leaf;
436 break;
437 default:
438 nodeStack.push_back( currNode->right ); // process right after left
439 nodeStack.push_back( currNode->left );
440
441 // Cut else/timeout out of the tree
442 if ( currNode->op == ast::WaitUntilStmt::ClauseNode::LEFT_OR ) {
443 if ( lastInternalNode )
444 lastInternalNode->right = currNode->left;
445 else // if not set then root is LEFT_OR
446 retStmt->predicateTree = currNode->left;
447
448 currNode->left = nullptr;
449 cleanup = currNode;
450 }
451
452 lastInternalNode = currNode;
453 break;
454 }
455 } while ( !nodeStack.empty() );
456
457 if ( cleanup ) delete cleanup;
458
459 return retStmt;
460}
461
462ast::Stmt * build_with( const CodeLocation & location, ExpressionNode * exprs, StatementNode * stmt ) {
463 std::vector<ast::ptr<ast::Expr>> e;
464 buildMoveList( exprs, e );
465 ast::Stmt * s = maybeMoveBuild( stmt );
466 return new ast::DeclStmt( location, new ast::WithStmt( location, std::move( e ), s ) );
467} // build_with
468
469ast::Stmt * build_compound( const CodeLocation & location, StatementNode * first ) {
470 auto cs = new ast::CompoundStmt( location );
471 buildMoveList( first, cs->kids );
472 return cs;
473} // build_compound
474
475// A single statement in a control structure is always converted to a compound statement so subsequent generated code
476// can be placed within this compound statement. Otherwise, code generation has to constantly check for a single
477// statement and wrap it into a compound statement to insert additional code. Hence, all control structures have a
478// conical form for code generation.
479StatementNode * maybe_build_compound( const CodeLocation & location, StatementNode * first ) {
480 // Optimization: if the control-structure statement is a compound statement, do not wrap it.
481 // e.g., if (...) {...} do not wrap the existing compound statement.
482 if ( !dynamic_cast<ast::CompoundStmt *>( first->stmt.get() ) ) { // unique_ptr
483 return new StatementNode( build_compound( location, first ) );
484 } // if
485 return first;
486} // maybe_build_compound
487
488// Question
489ast::Stmt * build_asm( const CodeLocation & location, bool is_volatile, ExpressionNode * instruction, ExpressionNode * output, ExpressionNode * input, ExpressionNode * clobber, LabelNode * gotolabels ) {
490 std::vector<ast::ptr<ast::Expr>> out, in;
491 std::vector<ast::ptr<ast::ConstantExpr>> clob;
492
493 buildMoveList( output, out );
494 buildMoveList( input, in );
495 buildMoveList( clobber, clob );
496 return new ast::AsmStmt( location,
497 is_volatile,
498 maybeMoveBuild( instruction ),
499 std::move( out ),
500 std::move( in ),
501 std::move( clob ),
502 gotolabels ? gotolabels->labels : std::vector<ast::Label>()
503 );
504} // build_asm
505
506ast::Stmt * build_directive( const CodeLocation & location, string * directive ) {
507 auto stmt = new ast::DirectiveStmt( location, *directive );
508 delete directive;
509 return stmt;
510} // build_directive
511
512ast::Stmt * build_mutex( const CodeLocation & location, ExpressionNode * exprs, StatementNode * stmt ) {
513 std::vector<ast::ptr<ast::Expr>> expList;
514 buildMoveList( exprs, expList );
515 ast::Stmt * body = maybeMoveBuild( stmt );
516 return new ast::MutexStmt( location, body, std::move( expList ) );
517} // build_mutex
518
519ast::Stmt * build_corun( const CodeLocation & location, StatementNode * stmt ) {
520 ast::Stmt * body = maybeMoveBuild( stmt );
521 return new ast::CorunStmt( location, body );
522} // build_corun
523
524ast::Stmt * build_cofor( const CodeLocation & location, ForCtrl * forctrl, StatementNode * stmt ) {
525 std::vector<ast::ptr<ast::Stmt>> astinit; // maybe empty
526 buildMoveList( forctrl->init, astinit );
527
528 ast::Expr * astcond = nullptr; // maybe empty
529 astcond = maybeMoveBuild( forctrl->condition );
530
531 ast::Expr * astincr = nullptr; // maybe empty
532 astincr = maybeMoveBuild( forctrl->change );
533 delete forctrl;
534
535 return new ast::CoforStmt( location,
536 std::move( astinit ),
537 astcond,
538 astincr,
539 buildMoveSingle( stmt )
540 );
541} // build_cofor
542
543// Local Variables: //
544// tab-width: 4 //
545// mode: c++ //
546// compile-command: "make install" //
547// End: //
Note: See TracBrowser for help on using the repository browser.