• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

polserver / polserver / 13378462461

17 Feb 2025 08:59PM UTC coverage: 58.754% (+0.3%) from 58.475%
13378462461

push

github

web-flow
Add support for sequence and index bindings (#760)

* update grammar

* add ast nodes; ast building

* Rename to unpacking

* semantic analysis

* executor work part 1, unpacking indices

* renamings; implement index binding

* small cleanup

* use multi_index; more tests

* use multi_index only for rest, otherwise list

* initial formatting

* add missing token decoding

* address self-review comments

* formatting tweaks

* add test for var binding in classes

* add StringIterator

* fix spread tests

* add cfgfile iterator; add cfgelem opersubscript; tests

* add iterator for SQLResultSet and SQLRow; tests

* Copy value in take global/local

* Allow any iterable can index rest unpacking
Always use dictionary as rest object in index unpacking

* add docs, core-changes, doc tests

* formatting changes...

* address self-review comments

* update formatter, format all binding test srcs

* address review comments
- unset var scope

* add cfgfile/cfgelem docs

* reformat objref svg

501 of 550 new or added lines in 19 files covered. (91.09%)

14 existing lines in 3 files now uncovered.

42235 of 71885 relevant lines covered (58.75%)

377989.47 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.97
/pol-core/bscript/compiler/astbuilder/SimpleStatementBuilder.cpp
1
#include "SimpleStatementBuilder.h"
2

3
#include "bscript/compiler/Report.h"
4
#include "bscript/compiler/ast/BinaryOperator.h"
5
#include "bscript/compiler/ast/BindingStatement.h"
6
#include "bscript/compiler/ast/ConstDeclaration.h"
7
#include "bscript/compiler/ast/DebugStatementMarker.h"
8
#include "bscript/compiler/ast/ElementIndexes.h"
9
#include "bscript/compiler/ast/EnumDeclaration.h"
10
#include "bscript/compiler/ast/Expression.h"
11
#include "bscript/compiler/ast/Identifier.h"
12
#include "bscript/compiler/ast/IndexBinding.h"
13
#include "bscript/compiler/ast/IntegerValue.h"
14
#include "bscript/compiler/ast/JumpStatement.h"
15
#include "bscript/compiler/ast/ReturnStatement.h"
16
#include "bscript/compiler/ast/SequenceBinding.h"
17
#include "bscript/compiler/ast/StringValue.h"
18
#include "bscript/compiler/ast/VarStatement.h"
19
#include "bscript/compiler/ast/VariableBinding.h"
20
#include "bscript/compiler/astbuilder/BuilderWorkspace.h"
21
#include "bscript/compiler/model/CompilerWorkspace.h"
22

23
using EscriptGrammar::EscriptParser;
24

25
namespace Pol::Bscript::Compiler
26
{
27
SimpleStatementBuilder::SimpleStatementBuilder( const SourceFileIdentifier& source_file_identifier,
8,468✔
28
                                                BuilderWorkspace& workspace )
8,468✔
29
    : ExpressionBuilder( source_file_identifier, workspace )
8,468✔
30
{
31
}
8,468✔
32

33
void SimpleStatementBuilder::add_intrusive_debug_marker(
×
34
    antlr4::ParserRuleContext* ctx, std::vector<std::unique_ptr<Statement>>& statements )
35
{
36
  statements.push_back( std::make_unique<DebugStatementMarker>(
×
37
      location_for( *ctx ), ctx->getText(),
×
38
      static_cast<unsigned>( ctx->start->getStartIndex() ) ) );
×
39
}
×
40

41
void SimpleStatementBuilder::add_var_statements(
3,957✔
42
    EscriptParser::VarStatementContext* ctx, const std::string& class_name,
43
    std::vector<std::unique_ptr<Statement>>& statements )
44
{
45
  if ( auto variable_declaration_list = ctx->variableDeclarationList() )
3,957✔
46
  {
47
    for ( auto decl : variable_declaration_list->variableDeclaration() )
8,167✔
48
    {
49
      if ( auto identifier = decl->IDENTIFIER() )
4,212✔
50
      {
51
        auto loc = location_for( *decl );
4,116✔
52
        std::string name = text( identifier );
4,116✔
53
        std::unique_ptr<VarStatement> var_ast;
4,116✔
54

55
        if ( auto initializer_context = decl->variableDeclarationInitializer() )
4,116✔
56
        {
57
          if ( initializer_context->ARRAY() )
3,452✔
58
          {
59
            var_ast = std::make_unique<VarStatement>( loc, class_name, std::move( name ), true );
46✔
60
          }
61
          else
62
          {
63
            auto initializer = variable_initializer( initializer_context );
3,406✔
64
            var_ast = std::make_unique<VarStatement>( loc, class_name, std::move( name ),
6,808✔
65
                                                      std::move( initializer ) );
6,808✔
66
          }
3,404✔
67
        }
68
        else
69
        {
70
          var_ast = std::make_unique<VarStatement>( loc, class_name, std::move( name ) );
664✔
71
        }
72
        statements.push_back( std::move( var_ast ) );
4,114✔
73
      }
4,118✔
74
      else if ( auto binding_decl = decl->bindingDeclaration() )
96✔
75
      {
76
        auto bindings = binding( class_name, binding_decl );
96✔
77
        auto initializer = binding_initializer( decl->bindingDeclarationInitializer() );
96✔
78
        auto binding_ast = std::make_unique<BindingStatement>(
79
            location_for( *decl ), std::move( bindings ), std::move( initializer ) );
96✔
80

81
        statements.push_back( std::move( binding_ast ) );
96✔
82
      }
96✔
83
    }
3,957✔
84
  }
85
}
3,955✔
86

87
std::unique_ptr<Expression> SimpleStatementBuilder::binding_initializer(
96✔
88
    EscriptGrammar::EscriptParser::BindingDeclarationInitializerContext* ctx )
89
{
90
  return expression( ctx->expression() );
96✔
91
}
92

93
std::unique_ptr<Node> SimpleStatementBuilder::binding(
124✔
94
    const std::string& class_name, EscriptGrammar::EscriptParser::BindingDeclarationContext* ctx )
95
{
96
  if ( auto sequence_binding_list = ctx->sequenceBindingList() )
124✔
97
  {
98
    std::vector<std::unique_ptr<Node>> bindings;
70✔
99
    for ( auto* sequence_binding : sequence_binding_list->sequenceBinding() )
284✔
100
    {
101
      if ( auto identifier = sequence_binding->IDENTIFIER() )
214✔
102
      {
103
        bool rest = sequence_binding->ELLIPSIS();
204✔
104
        bindings.push_back( std::make_unique<VariableBinding>(
204✔
105
            location_for( *sequence_binding ), class_name, text( identifier ), rest ) );
408✔
106
      }
107
      else if ( auto binding_decl = sequence_binding->bindingDeclaration() )
10✔
108
      {
109
        bindings.push_back( binding( class_name, binding_decl ) );
10✔
110
      }
111
      else
112
      {
NEW
113
        report.error( location_for( *sequence_binding ), "Unsupported indexed binding" );
×
NEW
114
        break;
×
115
      }
116
    }
70✔
117
    return std::make_unique<SequenceBinding>( location_for( *ctx ), std::move( bindings ) );
70✔
118
  }
70✔
119
  else if ( auto index_binding_list = ctx->indexBindingList() )
54✔
120
  {
121
    std::vector<std::unique_ptr<Expression>> indices;
54✔
122
    std::vector<std::unique_ptr<Node>> bindings;
54✔
123
    for ( auto* index_binding : index_binding_list->indexBinding() )
438✔
124
    {
125
      auto index_identifier = index_binding->IDENTIFIER();
384✔
126
      bool rest = index_binding->ELLIPSIS();
384✔
127

128
      if ( auto expr = index_binding->expression() )
384✔
129
      {
130
        indices.push_back( expression( expr ) );
10✔
131
      }
132
      else if ( index_identifier )
374✔
133
      {
134
        // Only add the index string value if it's not a rest binding.
135
        if ( !rest )
374✔
136
        {
137
          indices.push_back( std::make_unique<StringValue>( location_for( *index_identifier ),
354✔
138
                                                            text( index_identifier ) ) );
708✔
139
        }
140
      }
141
      else
142
      {
NEW
143
        report.error( location_for( *index_binding ), "Unsupported binding" );
×
NEW
144
        break;
×
145
      }
146

147
      if ( auto binding_ctx = index_binding->binding() )
384✔
148
      {
149
        if ( auto binding_identifier = binding_ctx->IDENTIFIER() )
50✔
150
        {
151
          bindings.push_back( std::make_unique<VariableBinding>(
32✔
152
              location_for( *binding_identifier ), class_name, text( binding_identifier ), rest ) );
64✔
153
        }
154
        else if ( auto binding_decl = binding_ctx->bindingDeclaration() )
18✔
155
        {
156
          bindings.push_back( binding( class_name, binding_decl ) );
18✔
157
        }
158
        else
159
        {
NEW
160
          report.error( location_for( *index_binding ), "Unsupported binding" );
×
NEW
161
          break;
×
162
        }
163
      }
164
      else
165
      {
166
        bindings.push_back( std::make_unique<VariableBinding>(
334✔
167
            location_for( *index_identifier ), class_name, text( index_identifier ), rest ) );
668✔
168
      }
169
    }
54✔
170

171
    auto element_indexes =
172
        std::make_unique<ElementIndexes>( location_for( *ctx ), std::move( indices ) );
54✔
173

174
    return std::make_unique<IndexBinding>( location_for( *ctx ), std::move( element_indexes ),
108✔
175
                                           std::move( bindings ) );
108✔
176
  }
54✔
177
  else
178
  {
179
    // Should never happen, as the context check is exhaustive.
NEW
180
    report.error( location_for( *ctx ), "Unsupported binding list" );
×
NEW
181
    return {};
×
182
  }
183
}
184

185
std::unique_ptr<JumpStatement> SimpleStatementBuilder::break_statement(
382✔
186
    EscriptParser::BreakStatementContext* ctx )
187
{
188
  auto source_location = location_for( *ctx );
382✔
189
  std::string label = ctx->IDENTIFIER() ? text( ctx->IDENTIFIER() ) : "";
382✔
190

191
  return std::make_unique<JumpStatement>( source_location, JumpStatement::Break,
×
192
                                          std::move( label ) );
764✔
193
}
382✔
194

195
std::unique_ptr<ConstDeclaration> SimpleStatementBuilder::const_declaration(
121,521✔
196
    EscriptParser::ConstStatementContext* ctx )
197
{
198
  auto variable_declaration = ctx->constantDeclaration();
121,521✔
199
  auto identifier = text( variable_declaration->IDENTIFIER() );
121,521✔
200
  auto expression_context = variable_declaration->variableDeclarationInitializer()->expression();
121,521✔
201
  auto value = expression( expression_context );
121,521✔
202

203
  return std::make_unique<ConstDeclaration>( location_for( *ctx ), std::move( identifier ),
121,521✔
204
                                             std::move( value ) );
364,563✔
205
}
121,521✔
206

207
std::unique_ptr<JumpStatement> SimpleStatementBuilder::continue_statement(
148✔
208
    EscriptParser::ContinueStatementContext* ctx )
209
{
210
  auto source_location = location_for( *ctx );
148✔
211
  std::string label = ctx->IDENTIFIER() ? text( ctx->IDENTIFIER() ) : "";
148✔
212

213
  return std::make_unique<JumpStatement>( source_location, JumpStatement::Continue,
×
214
                                          std::move( label ) );
296✔
215
}
148✔
216

217
std::unique_ptr<EnumDeclaration> SimpleStatementBuilder::enum_declaration(
18✔
218
    EscriptParser::EnumStatementContext* ctx )
219
{
220
  std::vector<std::string> names;
18✔
221
  std::vector<std::unique_ptr<Expression>> expressions;
18✔
222
  if ( auto enum_list = ctx->enumList() )
18✔
223
  {
224
    std::string last_identifier;
18✔
225
    for ( auto entry : enum_list->enumListEntry() )
70✔
226
    {
227
      auto source_location = location_for( *entry );
52✔
228
      std::string identifier = text( entry->IDENTIFIER() );
52✔
229
      std::unique_ptr<Expression> value;
52✔
230
      if ( auto expr_ctx = entry->expression() )
52✔
231
      {
232
        value = expression( expr_ctx );
20✔
233
      }
234
      else if ( !last_identifier.empty() )
32✔
235
      {
236
        // The optimizer runs later, so we don't necessarily know the value of
237
        // the previous enum value.  The optimizer will sort it out.
238
        auto lhs = std::make_unique<Identifier>( source_location, last_identifier );
24✔
239
        auto one = std::make_unique<IntegerValue>( source_location, 1 );
24✔
240
        value = std::make_unique<BinaryOperator>( source_location, std::move( lhs ), "+", TOK_ADD,
48✔
241
                                                  std::move( one ) );
48✔
242
      }
24✔
243
      else
244
      {
245
        value = std::make_unique<IntegerValue>( source_location, 0 );
8✔
246
      }
247
      bool allow_overwrite = true;
52✔
248
      auto constant = std::make_unique<ConstDeclaration>( location_for( *entry ), identifier,
52✔
249
                                                          std::move( value ), allow_overwrite );
104✔
250
      workspace.compiler_workspace.const_declarations.push_back( std::move( constant ) );
52✔
251

252
      last_identifier = identifier;
52✔
253
    }
70✔
254
  }
18✔
255

256
  auto source_location = location_for( *ctx );
18✔
257
  std::string identifier = text( ctx->IDENTIFIER() );
18✔
258
  return std::make_unique<EnumDeclaration>( source_location, std::move( identifier ),
18✔
259
                                            std::move( names ), std::move( expressions ) );
54✔
260
}
18✔
261

262
std::unique_ptr<Expression> SimpleStatementBuilder::variable_initializer(
3,406✔
263
    EscriptParser::VariableDeclarationInitializerContext* ctx )
264
{
265
  if ( auto expr = ctx->expression() )
3,406✔
266
    return expression( expr );
3,406✔
267
  else
268
    return std::unique_ptr<Expression>( new StringValue( location_for( *ctx ), "" ) );
×
269
}
270

271
std::unique_ptr<ReturnStatement> SimpleStatementBuilder::return_statement(
3,448✔
272
    EscriptParser::ReturnStatementContext* ctx )
273
{
274
  auto source_location = location_for( *ctx );
3,448✔
275

276
  std::unique_ptr<Expression> result;
3,448✔
277

278
  // Always emit the expression if it exists, so we can get semantic analysis
279
  // errors.
280
  if ( auto expression_ctx = ctx->expression() )
3,448✔
281
  {
282
    result = expression( expression_ctx );
3,428✔
283
  }
284
  else
285
  {
286
    // Only emit the empty string if we're not in a constructor function.
287
    if ( !in_constructor_function.top() )
20✔
288
      result = std::unique_ptr<Expression>( new StringValue( source_location, "" ) );
18✔
289
  }
290

291
  return std::make_unique<ReturnStatement>( source_location, std::move( result ) );
6,896✔
292
}
3,448✔
293

294
}  // namespace Pol::Bscript::Compiler
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc