2020-08-18 03:07:51 -07:00
|
|
|
#include "ValueBuilder.h"
|
|
|
|
|
|
2020-08-20 01:27:35 -07:00
|
|
|
#include <cstring>
|
|
|
|
|
|
2021-02-25 16:53:22 +01:00
|
|
|
#include "bscript/compiler/Report.h"
|
2024-01-20 12:38:25 +08:00
|
|
|
#include "bscript/compiler/ast/BooleanValue.h"
|
2021-02-25 16:53:22 +01:00
|
|
|
#include "bscript/compiler/ast/FloatValue.h"
|
|
|
|
|
#include "bscript/compiler/ast/FunctionReference.h"
|
|
|
|
|
#include "bscript/compiler/ast/IntegerValue.h"
|
|
|
|
|
#include "bscript/compiler/ast/StringValue.h"
|
2024-02-01 15:21:52 +07:00
|
|
|
#include "bscript/compiler/ast/UninitializedValue.h"
|
2021-02-25 16:53:22 +01:00
|
|
|
#include "bscript/compiler/astbuilder/BuilderWorkspace.h"
|
|
|
|
|
#include "bscript/compiler/file/SourceLocation.h"
|
|
|
|
|
#include "bscript/compiler/model/FunctionLink.h"
|
2024-01-12 06:51:44 +01:00
|
|
|
#include "clib/strutil.h"
|
2020-08-18 03:07:51 -07:00
|
|
|
|
2020-08-19 10:43:08 -07:00
|
|
|
using EscriptGrammar::EscriptParser;
|
|
|
|
|
|
2020-08-18 03:07:51 -07:00
|
|
|
namespace Pol::Bscript::Compiler
|
|
|
|
|
{
|
|
|
|
|
ValueBuilder::ValueBuilder( const SourceFileIdentifier& source_file_identifier,
|
|
|
|
|
BuilderWorkspace& workspace )
|
2024-01-12 06:51:44 +01:00
|
|
|
: TreeBuilder( source_file_identifier, workspace )
|
2020-08-18 03:07:51 -07:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-20 12:38:25 +08:00
|
|
|
std::unique_ptr<BooleanValue> ValueBuilder::bool_value(
|
|
|
|
|
EscriptGrammar::EscriptParser::BoolLiteralContext* ctx )
|
|
|
|
|
{
|
|
|
|
|
bool value;
|
|
|
|
|
auto loc = location_for( *ctx );
|
|
|
|
|
|
|
|
|
|
if ( ctx->BOOL_TRUE() )
|
|
|
|
|
{
|
|
|
|
|
value = true;
|
|
|
|
|
}
|
|
|
|
|
else if ( ctx->BOOL_FALSE() )
|
|
|
|
|
{
|
|
|
|
|
value = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
location_for( *ctx ).internal_error( "unhandled boolean literal" );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return std::make_unique<BooleanValue>( loc, value );
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-19 10:43:08 -07:00
|
|
|
std::unique_ptr<FloatValue> ValueBuilder::float_value(
|
|
|
|
|
EscriptGrammar::EscriptParser::FloatLiteralContext* ctx )
|
|
|
|
|
{
|
|
|
|
|
auto loc = location_for( *ctx );
|
2021-02-27 23:54:07 +01:00
|
|
|
auto terminal = ctx->FLOAT_LITERAL();
|
|
|
|
|
if ( !terminal )
|
|
|
|
|
{
|
|
|
|
|
terminal = ctx->HEX_FLOAT_LITERAL();
|
|
|
|
|
if ( !terminal )
|
|
|
|
|
{
|
|
|
|
|
location_for( *ctx ).internal_error( "unhandled float literal" );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
double value = std::stod( text( terminal ) );
|
2020-08-19 10:43:08 -07:00
|
|
|
return std::make_unique<FloatValue>( loc, value );
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-08 23:20:32 -07:00
|
|
|
std::unique_ptr<FunctionReference> ValueBuilder::function_reference(
|
|
|
|
|
EscriptParser::FunctionReferenceContext* ctx )
|
|
|
|
|
{
|
|
|
|
|
auto source_location = location_for( *ctx );
|
|
|
|
|
auto name = ctx->IDENTIFIER()->getSymbol()->getText();
|
|
|
|
|
|
|
|
|
|
auto function_link = std::make_shared<FunctionLink>( source_location );
|
|
|
|
|
auto function_reference =
|
|
|
|
|
std::make_unique<FunctionReference>( source_location, name, function_link );
|
|
|
|
|
|
|
|
|
|
workspace.function_resolver.register_function_link( name, function_link );
|
|
|
|
|
|
|
|
|
|
return function_reference;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-25 23:27:24 -07:00
|
|
|
std::unique_ptr<IntegerValue> ValueBuilder::integer_value(
|
|
|
|
|
EscriptParser::IntegerLiteralContext* ctx )
|
|
|
|
|
{
|
|
|
|
|
auto loc = location_for( *ctx );
|
|
|
|
|
return std::make_unique<IntegerValue>( loc, to_int( ctx ) );
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-12 06:51:44 +01:00
|
|
|
std::unique_ptr<StringValue> ValueBuilder::string_value( antlr4::tree::TerminalNode* string_literal,
|
|
|
|
|
bool expect_quotes )
|
2020-08-20 01:27:35 -07:00
|
|
|
{
|
|
|
|
|
auto loc = location_for( *string_literal );
|
2021-03-11 22:48:16 +01:00
|
|
|
return std::make_unique<StringValue>( loc, unquote( string_literal, expect_quotes ) );
|
2020-08-20 01:27:35 -07:00
|
|
|
}
|
|
|
|
|
|
2021-03-11 22:48:16 +01:00
|
|
|
std::string ValueBuilder::unquote( antlr4::tree::TerminalNode* string_literal, bool expect_quotes )
|
2020-08-20 01:27:35 -07:00
|
|
|
{
|
|
|
|
|
std::string input = string_literal->getSymbol()->getText();
|
|
|
|
|
const char* s = input.c_str();
|
2021-03-11 22:48:16 +01:00
|
|
|
if ( *s != '\"' && expect_quotes )
|
2020-08-20 01:27:35 -07:00
|
|
|
location_for( *string_literal ).internal_error( "string does not begin with a quote?" );
|
|
|
|
|
|
2021-03-11 22:48:16 +01:00
|
|
|
const char* end = s + ( expect_quotes ? 1 : 0 );
|
2020-08-20 01:27:35 -07:00
|
|
|
std::string lit;
|
2021-03-11 22:48:16 +01:00
|
|
|
lit.reserve( input.length() );
|
2020-08-20 01:27:35 -07:00
|
|
|
bool escnext = false; // true when waiting for 2nd char in an escape sequence
|
|
|
|
|
u8 hexnext = 0; // tells how many more chars in a \xNN escape sequence
|
|
|
|
|
char hexstr[3]; // will contain the \x escape chars to be processed
|
|
|
|
|
memset( hexstr, 0, 3 );
|
|
|
|
|
|
|
|
|
|
for ( ;; )
|
|
|
|
|
{
|
|
|
|
|
if ( !*end )
|
|
|
|
|
{
|
2021-03-11 22:48:16 +01:00
|
|
|
if ( !expect_quotes )
|
|
|
|
|
break;
|
2020-08-20 01:27:35 -07:00
|
|
|
// parser should catch this.
|
|
|
|
|
location_for( *string_literal ).internal_error( "unterminated string" );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( escnext && hexnext )
|
|
|
|
|
location_for( *string_literal )
|
|
|
|
|
.internal_error( "Bug in the compiler. Please report this on the forums." );
|
|
|
|
|
|
|
|
|
|
if ( escnext )
|
|
|
|
|
{
|
|
|
|
|
// waiting for 2nd character after a backslash
|
|
|
|
|
escnext = false;
|
|
|
|
|
if ( *end == 'n' )
|
|
|
|
|
lit += '\n';
|
|
|
|
|
else if ( *end == 't' )
|
|
|
|
|
lit += '\t';
|
|
|
|
|
else if ( *end == 'x' )
|
|
|
|
|
hexnext = 2;
|
|
|
|
|
else
|
|
|
|
|
lit += *end;
|
|
|
|
|
}
|
|
|
|
|
else if ( hexnext )
|
|
|
|
|
{
|
|
|
|
|
// waiting for next (two) chars in hex escape sequence (eg. \xFF)
|
|
|
|
|
hexstr[2 - hexnext] = *end;
|
|
|
|
|
if ( !--hexnext )
|
|
|
|
|
{
|
|
|
|
|
char* endptr;
|
|
|
|
|
char ord = static_cast<char>( strtol( hexstr, &endptr, 16 ) );
|
|
|
|
|
if ( *endptr != '\0' )
|
|
|
|
|
{
|
2024-01-12 06:51:44 +01:00
|
|
|
report.error( location_for( *string_literal ), "Invalid hex escape sequence '{}'.",
|
|
|
|
|
hexstr );
|
2020-08-20 01:27:35 -07:00
|
|
|
return lit;
|
|
|
|
|
}
|
|
|
|
|
lit += ord;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if ( *end == '\\' )
|
|
|
|
|
escnext = true;
|
|
|
|
|
else if ( *end == '\"' )
|
|
|
|
|
break;
|
|
|
|
|
else
|
|
|
|
|
lit += *end;
|
|
|
|
|
}
|
|
|
|
|
++end;
|
|
|
|
|
}
|
|
|
|
|
if ( !Clib::isValidUnicode( lit ) )
|
|
|
|
|
{
|
|
|
|
|
report.warning( location_for( *string_literal ),
|
2024-01-12 06:51:44 +01:00
|
|
|
"Warning: invalid unicode character detected. Assuming ISO8859." );
|
2020-08-20 01:27:35 -07:00
|
|
|
|
|
|
|
|
Clib::sanitizeUnicodeWithIso( &lit );
|
|
|
|
|
}
|
|
|
|
|
return lit;
|
|
|
|
|
}
|
2020-08-19 10:43:08 -07:00
|
|
|
|
|
|
|
|
std::unique_ptr<Value> ValueBuilder::value( EscriptParser::LiteralContext* ctx )
|
|
|
|
|
{
|
2020-08-20 01:27:35 -07:00
|
|
|
if ( auto string_literal = ctx->STRING_LITERAL() )
|
|
|
|
|
{
|
|
|
|
|
return string_value( string_literal );
|
|
|
|
|
}
|
2020-08-25 23:27:24 -07:00
|
|
|
else if ( auto integer_literal = ctx->integerLiteral() )
|
|
|
|
|
{
|
|
|
|
|
return integer_value( integer_literal );
|
|
|
|
|
}
|
2020-08-20 01:27:35 -07:00
|
|
|
else if ( auto float_literal = ctx->floatLiteral() )
|
2020-08-19 10:43:08 -07:00
|
|
|
{
|
|
|
|
|
return float_value( float_literal );
|
|
|
|
|
}
|
2024-02-01 15:21:52 +07:00
|
|
|
else if ( auto bool_literal = ctx->boolLiteral() )
|
|
|
|
|
{
|
|
|
|
|
return bool_value( bool_literal );
|
|
|
|
|
}
|
|
|
|
|
else if ( ctx->UNINIT() )
|
|
|
|
|
{
|
|
|
|
|
return std::make_unique<UninitializedValue>( location_for( *ctx ) );
|
|
|
|
|
}
|
2020-08-19 10:43:08 -07:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
location_for( *ctx ).internal_error( "unhandled literal" );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-25 23:27:24 -07:00
|
|
|
int ValueBuilder::to_int( EscriptParser::IntegerLiteralContext* ctx )
|
|
|
|
|
{
|
2020-09-20 11:04:37 -07:00
|
|
|
try
|
2020-08-25 23:27:24 -07:00
|
|
|
{
|
2020-09-20 11:04:37 -07:00
|
|
|
if ( auto decimal_literal = ctx->DECIMAL_LITERAL() )
|
|
|
|
|
{
|
|
|
|
|
return std::stoi( decimal_literal->getSymbol()->getText() );
|
|
|
|
|
}
|
|
|
|
|
else if ( auto hex_literal = ctx->HEX_LITERAL() )
|
|
|
|
|
{
|
|
|
|
|
return static_cast<int>( std::stoul( hex_literal->getSymbol()->getText(), nullptr, 16 ) );
|
|
|
|
|
}
|
|
|
|
|
else if ( auto oct_literal = ctx->OCT_LITERAL() )
|
|
|
|
|
{
|
|
|
|
|
return std::stoi( oct_literal->getSymbol()->getText(), nullptr, 8 );
|
|
|
|
|
}
|
|
|
|
|
else if ( auto binary_literal = ctx->BINARY_LITERAL() )
|
|
|
|
|
{
|
|
|
|
|
return std::stoi( binary_literal->getSymbol()->getText(), nullptr, 2 );
|
|
|
|
|
}
|
2020-08-25 23:27:24 -07:00
|
|
|
}
|
2020-09-20 11:04:37 -07:00
|
|
|
catch ( std::invalid_argument& )
|
2020-08-25 23:27:24 -07:00
|
|
|
{
|
2024-01-12 06:51:44 +01:00
|
|
|
report.error( location_for( *ctx ), "unable to convert integer value '{}'.", ctx->getText() );
|
2020-09-20 11:04:37 -07:00
|
|
|
throw;
|
2020-08-25 23:27:24 -07:00
|
|
|
}
|
2020-09-20 11:04:37 -07:00
|
|
|
catch ( std::out_of_range& )
|
2020-08-25 23:27:24 -07:00
|
|
|
{
|
2024-01-12 06:51:44 +01:00
|
|
|
report.error( location_for( *ctx ), "integer value '{}' out of range.", ctx->getText() );
|
2020-09-20 11:04:37 -07:00
|
|
|
throw;
|
2020-08-25 23:27:24 -07:00
|
|
|
}
|
2020-09-20 11:04:37 -07:00
|
|
|
|
2020-08-25 23:27:24 -07:00
|
|
|
location_for( *ctx ).internal_error( "unhandled integer literal" );
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-18 03:07:51 -07:00
|
|
|
} // namespace Pol::Bscript::Compiler
|