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

polserver / polserver / 25918451630

15 May 2026 12:43PM UTC coverage: 60.929% (+2.1%) from 58.859%
25918451630

push

github

turleypol
added dynamic property which returns a pointer of the object instead of
a copy like the current imp.
needed to be able to eg store a vector

43 of 61 new or added lines in 2 files covered. (70.49%)

14455 existing lines in 345 files now uncovered.

44695 of 73356 relevant lines covered (60.93%)

449621.59 hits per line

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

77.78
/pol-core/bscript/compiler/astbuilder/SourceFileProcessor.cpp
1
#include "SourceFileProcessor.h"
2

3
#include "bscript/compiler/Profile.h"
4
#include "bscript/compiler/Report.h"
5
#include "bscript/compiler/ast/ClassBody.h"
6
#include "bscript/compiler/ast/ConstDeclaration.h"
7
#include "bscript/compiler/ast/Program.h"
8
#include "bscript/compiler/ast/Statement.h"
9
#include "bscript/compiler/ast/TopLevelStatements.h"
10
#include "bscript/compiler/astbuilder/AvailableParseTree.h"
11
#include "bscript/compiler/astbuilder/BuilderWorkspace.h"
12
#include "bscript/compiler/astbuilder/FunctionResolver.h"
13
#include "bscript/compiler/astbuilder/ModuleProcessor.h"
14
#include "bscript/compiler/file/SourceFile.h"
15
#include "bscript/compiler/file/SourceFileCache.h"
16
#include "bscript/compiler/file/SourceFileIdentifier.h"
17
#include "bscript/compiler/model/CompilerWorkspace.h"
18
#include "clib/fileutil.h"
19
#include "clib/logfacility.h"
20
#include "clib/timer.h"
21
#include "compilercfg.h"
22
#include "plib/pkg.h"
23

24
using EscriptGrammar::EscriptParser;
25

26
namespace Pol::Bscript::Compiler
27
{
28
std::string getpathof( const std::string& fname );
29

30
SourceFileProcessor::SourceFileProcessor( const SourceFileIdentifier& source_file_identifier,
2,941✔
31
                                          BuilderWorkspace& workspace, bool is_src,
32
                                          UserFunctionInclusion user_function_inclusion )
2,941✔
33
    : profile( workspace.profile ),
2,941✔
34
      report( workspace.report ),
2,941✔
35
      source_file_identifier( source_file_identifier ),
2,941✔
36
      workspace( workspace ),
2,941✔
37
      tree_builder( source_file_identifier, workspace ),
2,941✔
38
      is_src( is_src ),
2,941✔
39
      user_function_inclusion( user_function_inclusion )
2,941✔
40
{
41
}
2,941✔
42

43
void SourceFileProcessor::use_module( const std::string& module_name,
6,565✔
44
                                      SourceLocation& including_location,
45
                                      long long* micros_counted )
46
{
47
  std::string pathname =
48
      Clib::FullPath( ( compilercfg.ModuleDirectory + module_name + ".em" ).c_str() );
6,565✔
49
  if ( workspace.source_files.find( pathname ) != workspace.source_files.end() )
6,565✔
50
    return;
368✔
51

52
  auto ident = std::make_unique<SourceFileIdentifier>(
53
      static_cast<unsigned>(
×
54
          workspace.compiler_workspace.referenced_source_file_identifiers.size() ),
6,197✔
55
      pathname );
6,197✔
56

57
  Pol::Tools::HighPerfTimer load_timer;
6,197✔
58
  auto sf = workspace.em_cache.load( *ident, report );
6,197✔
59
  long long load_elapsed = load_timer.ellapsed().count();
6,197✔
60
  profile.load_em_micros += load_elapsed;
6,197✔
61
  if ( !sf )
6,197✔
62
  {
63
    // This is fatal because if we keep going, we'll likely report a bunch of errors
64
    // that would just be noise, like missing module function declarations or constants.
65
    report.fatal( including_location, "Unable to use module '{}'.", module_name );
×
66
  }
67
  workspace.source_files[pathname] = sf;
6,197✔
68

69
  ModuleProcessor module_processor( *ident, workspace, module_name );
6,197✔
70
  workspace.compiler_workspace.referenced_source_file_identifiers.push_back( std::move( ident ) );
6,197✔
71

72
  Pol::Tools::HighPerfTimer get_module_unit_timer;
6,197✔
73
  auto module_unit_context = sf->get_module_unit( report, source_file_identifier );
6,197✔
74
  long long parse_elapsed = get_module_unit_timer.ellapsed().count();
6,197✔
75
  profile.parse_em_micros += parse_elapsed;
6,197✔
76
  profile.parse_em_count++;
6,197✔
77

78
  Pol::Tools::HighPerfTimer visit_module_unit_timer;
6,197✔
79
  module_unit_context->accept( &module_processor );
6,197✔
80
  long long ast_elapsed = visit_module_unit_timer.ellapsed().count();
6,197✔
81
  profile.ast_em_micros.fetch_add( ast_elapsed );
6,197✔
82

83
  if ( micros_counted )
6,197✔
84
    *micros_counted = load_elapsed + parse_elapsed + ast_elapsed;
1,095✔
85
}
6,565✔
86

87
void SourceFileProcessor::process_source( SourceFile& sf )
2,551✔
88
{
89
  Pol::Tools::HighPerfTimer parse_timer;
2,551✔
90
  auto compilation_unit = sf.get_compilation_unit( report, source_file_identifier );
2,551✔
91
  long long parse_us_counted = parse_timer.ellapsed().count();
2,551✔
92

93
  profile.parse_src_micros.fetch_add( parse_us_counted );
2,551✔
94
  profile.parse_src_count++;
2,551✔
95

96
  if ( report.error_count() == 0 )
2,551✔
97
  {
98
    Pol::Tools::HighPerfTimer ast_timer;
2,451✔
99
    compilation_unit->accept( this );
2,451✔
100
    long long ast_us_elapsed = ast_timer.ellapsed().count();
2,449✔
101

102
    profile.ast_src_micros.fetch_add( ast_us_elapsed );
2,449✔
103
  }
104
}
2,549✔
105

106
void SourceFileProcessor::process_include( SourceFile& sf, long long* micros_counted )
390✔
107
{
108
  Pol::Tools::HighPerfTimer parse_timer;
390✔
109
  auto compilation_unit = sf.get_compilation_unit( report, source_file_identifier );
390✔
110
  profile.parse_inc_count++;
390✔
111
  long long parse_micros_elapsed = parse_timer.ellapsed().count();
390✔
112
  profile.parse_inc_micros.fetch_add( parse_micros_elapsed );
390✔
113
  *micros_counted += parse_micros_elapsed;
390✔
114

115
  if ( report.error_count() == 0 )
390✔
116
  {
117
    Pol::Tools::HighPerfTimer ast_timer;
383✔
118
    compilation_unit->accept( this );
383✔
119
    long long ast_micros_elapsed = ast_timer.ellapsed().count();
383✔
120
    profile.ast_inc_micros.fetch_add( ast_micros_elapsed );
383✔
121
    *micros_counted += ast_micros_elapsed;
383✔
122
  }
123
}
390✔
124

125
void SourceFileProcessor::handle_include_declaration( EscriptParser::IncludeDeclarationContext* ctx,
397✔
126
                                                      long long* micros_counted )
127
{
128
  auto source_location = location_for( *ctx );
397✔
129

130
  std::string include_name;
397✔
131
  if ( auto string_literal = ctx->stringIdentifier()->STRING_LITERAL() )
397✔
132
    include_name = tree_builder.unquote( string_literal );
389✔
133
  else if ( auto identifier = ctx->stringIdentifier()->IDENTIFIER() )
8✔
134
    include_name = identifier->getSymbol()->getText();
8✔
135
  else
136
    source_location.internal_error(
×
137
        "Unable to include module: expected a string literal or identifier.\n" );
138

139
  std::optional<std::string> maybe_canonical_include_pathname =
140
      locate_include_file( source_location, include_name );
397✔
141

142
  if ( !maybe_canonical_include_pathname )
397✔
143
  {
144
    report.error( source_location,
7✔
145
                  "Unable to include file '" + include_name + "': failed to locate." );
14✔
146
    return;
7✔
147
  }
148

149
  std::string canonical_include_pathname = maybe_canonical_include_pathname.value();
390✔
150

151
  if ( workspace.source_files.count( canonical_include_pathname ) == 0 )
390✔
152
  {
153
    auto ident = std::make_unique<SourceFileIdentifier>(
154
        static_cast<unsigned int>(
×
155
            workspace.compiler_workspace.referenced_source_file_identifiers.size() ),
390✔
156
        canonical_include_pathname );
390✔
157
    auto sf = workspace.inc_cache.load( *ident, report );
390✔
158
    if ( !sf )
390✔
159
    {
160
      report.error( source_location, "Unable to include file '{}': failed to load.",
×
161
                    canonical_include_pathname );
162
      return;
×
163
    }
164

165
    SourceFileProcessor include_processor( *ident, workspace, false, user_function_inclusion );
390✔
166
    workspace.compiler_workspace.referenced_source_file_identifiers.push_back( std::move( ident ) );
390✔
167
    workspace.source_files[sf->pathname] = sf;
390✔
168

169
    include_processor.process_include( *sf, micros_counted );
390✔
170
  }
390✔
171
}
404✔
172

173
std::optional<std::string> SourceFileProcessor::locate_include_file(
397✔
174
    const SourceLocation& source_location, const std::string& include_name )
175
{
176
  std::string filename_part = include_name + ".inc";
397✔
177

178
  std::string current_file_path = getpathof( source_location.source_file_identifier->pathname );
397✔
179
  std::string filename_full = current_file_path + filename_part;
397✔
180

181
  if ( filename_part[0] == ':' )
397✔
182
  {
183
    const Plib::Package* pkg = nullptr;
×
184
    std::string path;
×
185
    if ( Plib::pkgdef_split( filename_part, nullptr, &pkg, &path ) )
×
186
    {
187
      if ( pkg != nullptr )
×
188
      {
189
        filename_full = pkg->dir() + path;
×
190
        std::string try_filename_full = pkg->dir() + "include/" + path;
×
191

192
        if ( compilercfg.VerbosityLevel >= 10 )
×
193
          INFO_PRINTLN( "Searching for {}", filename_full );
×
194

195
        if ( !Clib::FileExists( filename_full.c_str() ) )
×
196
        {
197
          if ( compilercfg.VerbosityLevel >= 10 )
×
198
            INFO_PRINTLN( "Searching for {}", try_filename_full );
×
199
          if ( Clib::FileExists( try_filename_full.c_str() ) )
×
200
          {
201
            if ( compilercfg.VerbosityLevel >= 10 )
×
202
              INFO_PRINTLN( "Found {}", try_filename_full );
×
203

204
            filename_full = try_filename_full;
×
205
          }
206
        }
207
        else
208
        {
209
          if ( compilercfg.VerbosityLevel >= 10 )
×
210
            INFO_PRINTLN( "Found {}", filename_full );
×
211

212
          if ( Clib::FileExists( try_filename_full.c_str() ) )
×
213
            report.warning( source_location, "Found '{}' and '{}'! Will use first file!",
×
214
                            filename_full, try_filename_full );
215
        }
216
      }
×
217
      else
218
      {
219
        filename_full = compilercfg.PolScriptRoot + path;
×
220

221
        if ( compilercfg.VerbosityLevel >= 10 )
×
222
        {
223
          INFO_PRINTLN( "Searching for {}", filename_full );
×
224
          if ( Clib::FileExists( filename_full.c_str() ) )
×
225
            INFO_PRINTLN( "Found {}", filename_full );
×
226
        }
227
      }
228
    }
229
    else
230
    {
231
      // This is fatal because if we keep going, we'll likely report a bunch of errors
232
      // that would just be noise, like missing functions or constants.
233
      report.fatal( source_location, "Unable to read include file '{}'", include_name );
×
234
    }
235
  }
×
236
  else
237
  {
238
    if ( compilercfg.VerbosityLevel >= 10 )
397✔
239
      INFO_PRINTLN( "Searching for {}", filename_full );
×
240

241
    if ( !Clib::FileExists( filename_full.c_str() ) )
397✔
242
    {
243
      std::string try_filename_full = compilercfg.IncludeDirectory + filename_part;
185✔
244
      if ( compilercfg.VerbosityLevel >= 10 )
185✔
245
        INFO_PRINTLN( "Searching for {}", try_filename_full );
×
246
      if ( Clib::FileExists( try_filename_full.c_str() ) )
185✔
247
      {
248
        if ( compilercfg.VerbosityLevel >= 10 )
178✔
249
          INFO_PRINTLN( "Found {}", try_filename_full );
×
250

251
        // cout << "Using " << try_filename << endl;
252
        filename_full = try_filename_full;
178✔
253
      }
254
    }
185✔
255
    else
256
    {
257
      if ( compilercfg.VerbosityLevel >= 10 )
212✔
258
        INFO_PRINTLN( "Found {}", filename_full );
×
259
    }
260
  }
261
  if ( SourceFile::enforced_case_sensitivity_mismatch( source_location, filename_full, report ) )
397✔
262
  {
263
    return {};
×
264
  }
265

266
  filename_full = Clib::FullPath( filename_full.c_str() );
397✔
267

268
  if ( !filename_full.empty() )
397✔
269
    return std::optional<std::string>( filename_full );
390✔
270
  return {};
7✔
271
}
397✔
272

273
void SourceFileProcessor::handle_use_declaration( EscriptParser::UseDeclarationContext* ctx,
1,463✔
274
                                                  long long* micros_used )
275
{
276
  EscriptParser::StringIdentifierContext* stringId = ctx->stringIdentifier();
1,463✔
277
  std::string modulename = stringId->STRING_LITERAL()
1,463✔
278
                               ? tree_builder.unquote( stringId->STRING_LITERAL() )
1,463✔
279
                               : tree_builder.text( stringId->IDENTIFIER() );
1,463✔
280
  auto source_location = location_for( *ctx );
1,463✔
281
  use_module( modulename, source_location, micros_used );
1,463✔
282
}
1,463✔
283

284
// Visits global user functions. Class functions are visited in UserFunctionVisitor.
285
antlrcpp::Any SourceFileProcessor::visitFunctionDeclaration(
2,659✔
286
    EscriptParser::FunctionDeclarationContext* ctx )
287
{
288
  auto loc = location_for( *ctx );
2,659✔
289
  bool force_reference = user_function_inclusion == UserFunctionInclusion::All;
2,659✔
290
  workspace.function_resolver.register_available_user_function( loc, ctx, force_reference );
2,659✔
291
  const std::string& function_name = tree_builder.text( ctx->IDENTIFIER() );
2,659✔
292
  workspace.compiler_workspace.all_function_locations.emplace( function_name, loc );
2,659✔
293
  return antlrcpp::Any();
5,318✔
294
}
2,659✔
295

296
antlrcpp::Any SourceFileProcessor::visitClassDeclaration(
882✔
297
    EscriptGrammar::EscriptParser::ClassDeclarationContext* ctx )
298
{
299
  auto loc = location_for( *ctx );
882✔
300
  auto class_name = tree_builder.text( ctx->IDENTIFIER() );
882✔
301

302
  // Add var statements to top-level, as their declaration + semantic analysis
303
  // needs to happen in-line with other statements:
304
  //
305
  //   Foo::staticVar;  // compilation error
306
  //   class Foo var staticVar := 2; endclass
307
  //   print(Foo::staticVar == 2);  // true
308
  //
309
  // We add this `ClassBody` to the top level statements now. When the
310
  // UserFunctionVisitor visits the class declaration, it will add its variable
311
  // statements to this node.
312
  auto var_statement_holder = std::make_unique<ClassBody>( loc );
882✔
313

314
  workspace.function_resolver.register_available_class_declaration( loc, class_name, ctx,
882✔
315
                                                                    var_statement_holder.get() );
882✔
316

317
  workspace.compiler_workspace.all_class_locations.emplace( class_name, loc );
882✔
318

319
  workspace.compiler_workspace.top_level_statements->children.push_back(
1,764✔
320
      std::move( var_statement_holder ) );
882✔
321

322
  if ( user_function_inclusion == UserFunctionInclusion::All )
882✔
323
  {
UNCOV
324
    for ( auto classStatement : ctx->classBody()->classStatement() )
×
325
    {
UNCOV
326
      if ( auto func_decl = classStatement->functionDeclaration() )
×
327
      {
UNCOV
328
        auto function_name = tree_builder.text( func_decl->IDENTIFIER() );
×
329
        workspace.function_resolver.force_reference( ScopableName( class_name, function_name ),
×
330
                                                     loc );
UNCOV
331
      }
×
332
    }
×
333
  }
334
  return antlrcpp::Any();
1,764✔
335
}
882✔
336

337
antlrcpp::Any SourceFileProcessor::visitIncludeDeclaration(
397✔
338
    EscriptParser::IncludeDeclarationContext* ctx )
339
{
340
  long long micros_counted = 0;
397✔
341
  handle_include_declaration( ctx, &micros_counted );
397✔
342
  if ( is_src )
397✔
343
    profile.ast_src_micros.fetch_sub( micros_counted );
397✔
344
  else
UNCOV
345
    profile.ast_inc_micros.fetch_sub( micros_counted );
×
346
  return antlrcpp::Any();
794✔
347
}
348

349
antlrcpp::Any SourceFileProcessor::visitProgramDeclaration(
461✔
350
    EscriptParser::ProgramDeclarationContext* ctx )
351
{
352
  if ( workspace.compiler_workspace.program )
461✔
353
  {
UNCOV
354
    report.error( location_for( *ctx ),
×
355
                  "Multiple program statements.\n"
356
                  "  Other declaration: {}",
UNCOV
357
                  workspace.compiler_workspace.program->source_location );
×
358
  }
359
  else
360
  {
361
    workspace.compiler_workspace.program = tree_builder.program( ctx );
461✔
362
  }
363
  return antlrcpp::Any();
461✔
364
}
365

366
antlrcpp::Any SourceFileProcessor::visitStatement( EscriptParser::StatementContext* ctx )
13,166✔
367
{
368
  if ( auto constStatement = ctx->constStatement() )
13,166✔
369
  {
370
    workspace.compiler_workspace.const_declarations.push_back(
2,071✔
371
        tree_builder.const_declaration( constStatement ) );
4,142✔
372
  }
373
  else
374
  {
375
    std::vector<std::unique_ptr<Statement>> statements;
11,095✔
376
    tree_builder.add_statements( ctx, statements );
11,095✔
377
    for ( auto& statement : statements )
22,362✔
378
    {
379
      workspace.compiler_workspace.top_level_statements->children.push_back(
22,538✔
380
          std::move( statement ) );
11,269✔
381
    }
382
  }
11,095✔
383
  return antlrcpp::Any();
13,164✔
384
}
385

386
antlrcpp::Any SourceFileProcessor::visitUseDeclaration( EscriptParser::UseDeclarationContext* ctx )
1,463✔
387
{
388
  long long elapsed_micros = 0;
1,463✔
389
  handle_use_declaration( ctx, &elapsed_micros );
1,463✔
390
  if ( is_src )
1,463✔
391
    profile.ast_src_micros.fetch_sub( elapsed_micros );
609✔
392
  else
393
    profile.ast_inc_micros.fetch_sub( elapsed_micros );
854✔
394
  return antlrcpp::Any();
2,926✔
395
}
396

397
SourceLocation SourceFileProcessor::location_for( antlr4::ParserRuleContext& ctx ) const
5,401✔
398
{
399
  return { &source_file_identifier, ctx };
5,401✔
400
}
401

402
std::string getpathof( const std::string& fname )
397✔
403
{
404
  std::string::size_type pos = fname.find_last_of( "\\/" );
397✔
405
  if ( pos == std::string::npos )
397✔
UNCOV
406
    return "./";
×
407
  return fname.substr( 0, pos + 1 );
397✔
408
}
409
}  // 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