source: src/Parser/StatementNode.cpp @ 5786403

Last change on this file since 5786403 was 738a9b4, checked in by Peter A. Buhr <pabuhr@…>, 3 months ago

fformatting, make names consistent

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