From 782c0089933902322ce947975ae473e8dceb4c7e Mon Sep 17 00:00:00 2001 From: "Peter G Bouillon [fernewelten]" Date: Fri, 3 Nov 2023 10:23:24 +0100 Subject: [PATCH 1/6] New compiler: Identifier renaming Consistency, especially in the parser Also, denote unsigned literals as such --- Compiler/script2/cc_compiledscript.cpp | 20 +- Compiler/script2/cc_compiledscript.h | 6 +- Compiler/script2/cc_symboltable.cpp | 28 +-- Compiler/script2/cc_symboltable.h | 16 +- Compiler/script2/cs_parser.cpp | 316 ++++++++++++------------- Compiler/script2/cs_parser.h | 16 +- Compiler/script2/cs_scanner.cpp | 28 +-- Compiler/script2/cs_scanner.h | 2 +- Compiler/test2/cc_symboltable_test.cpp | 4 +- 9 files changed, 218 insertions(+), 218 deletions(-) diff --git a/Compiler/script2/cc_compiledscript.cpp b/Compiler/script2/cc_compiledscript.cpp index 96645d9bd7a..a99b867ab15 100644 --- a/Compiler/script2/cc_compiledscript.cpp +++ b/Compiler/script2/cc_compiledscript.cpp @@ -79,7 +79,7 @@ void AGS::ccCompiledScript::ReplaceLabels() { std::vector RemainingLabels; - for (size_t idx = 0; idx < Labels.size(); idx++) + for (size_t idx = 0u; idx < Labels.size(); idx++) { CodeLoc const loc = Labels[idx]; if (loc >= codesize) @@ -182,12 +182,12 @@ int AGS::ccCompiledScript::AddFixup(CodeLoc const where, FixupType const ftype) return numfixups++; } -AGS::CodeLoc AGS::ccCompiledScript::AddNewFunction(std::string const &func_name, size_t const num_of_parameters) +AGS::CodeLoc AGS::ccCompiledScript::AddNewFunction(std::string const &func_name, size_t const params_count) { FuncProps fp; fp.Name = func_name; fp.CodeOffs = codesize; - fp.NumOfParams = num_of_parameters; + fp.ParamsCount = params_count; Functions.push_back(fp); return codesize; } @@ -215,12 +215,12 @@ int AGS::ccCompiledScript::FindOrAddImport(std::string const &import_name) return (ImportIdx[import_name] = numimports++); } -int AGS::ccCompiledScript::AddExport(std::string const &name, CodeLoc const location, size_t const num_of_arguments) +int AGS::ccCompiledScript::AddExport(std::string const &name, CodeLoc const location, size_t const arguments_count) { - bool const is_function = (INT_MAX != num_of_arguments); + bool const is_function = (INT_MAX != arguments_count); // Exported functions get the number of parameters appended std::string const export_name = - is_function ? name + "$" + std::to_string(num_of_arguments) : name; + is_function ? name + "$" + std::to_string(arguments_count) : name; if (location >= 0x00ffffff) { @@ -345,17 +345,17 @@ void AGS::Snippet::Paste(ccCompiledScript &scrip) { CodeLoc const code_start = scrip.codesize; // Don't generate additional LINUM directives, that would throw off the fixups and labels - for (size_t idx = 0; idx < Code.size(); idx++) + for (size_t idx = 0u; idx < Code.size(); idx++) scrip.WriteCode(Code[idx]); - for (size_t idx = 0; idx < Fixups.size(); idx++) + for (size_t idx = 0u; idx < Fixups.size(); idx++) scrip.AddFixup(code_start + Fixups[idx], FixupTypes[idx]); - for (size_t idx = 0; idx < Labels.size(); idx++) + for (size_t idx = 0u; idx < Labels.size(); idx++) scrip.Labels.push_back(code_start + Labels[idx]); } bool AGS::Snippet::IsEmpty() { - for (size_t idx = 0; idx < Code.size(); idx++) + for (size_t idx = 0u; idx < Code.size(); idx++) { if (SCMD_LINENUM != Code[idx]) return false; // found some content diff --git a/Compiler/script2/cc_compiledscript.h b/Compiler/script2/cc_compiledscript.h index 96183d5dcb9..d38e17bbad8 100644 --- a/Compiler/script2/cc_compiledscript.h +++ b/Compiler/script2/cc_compiledscript.h @@ -36,7 +36,7 @@ class ccCompiledScript : public ccScript { struct FuncProps { std::string Name; - size_t NumOfParams; + size_t ParamsCount; CodeLoc CodeOffs; }; std::vector Functions = {}; @@ -73,7 +73,7 @@ class ccCompiledScript : public ccScript { inline void FixupPrevious(FixupType ftype) { AddFixup(codesize - 1, ftype); }; // Add a function named 'func_name' to the functions repository - CodeLoc AddNewFunction(std::string const &func_name, size_t num_of_parameters); + CodeLoc AddNewFunction(std::string const &func_name, size_t params_count); inline bool IsImport(std::string const &name) const { return 0 < ImportIdx.count(name); } @@ -83,7 +83,7 @@ class ccCompiledScript : public ccScript { // Add an exported entity to the export repository; // it has type vartype, resides at location; if it is a function // Note: This function returns -1 on error - int AddExport(std::string const &name, CodeLoc location, size_t num_of_arguments = INT_MAX); + int AddExport(std::string const &name, CodeLoc location, size_t arguments_count = INT_MAX); // Start a new section of the code. ErrorType StartNewSection(std::string const &name); diff --git a/Compiler/script2/cc_symboltable.cpp b/Compiler/script2/cc_symboltable.cpp index c650c57c8f1..3cc3723108a 100644 --- a/Compiler/script2/cc_symboltable.cpp +++ b/Compiler/script2/cc_symboltable.cpp @@ -434,7 +434,7 @@ bool AGS::SymbolTable::IsIdentifier(Symbol s) const return false; if ('0' <= name[0] && name[0] <= '9') return false; - for (size_t idx = 0; idx < name.size(); ++idx) + for (size_t idx = 0u; idx < name.size(); ++idx) { char const &ch = name[idx]; if ('0' <= ch && ch <= '9') continue; @@ -495,7 +495,7 @@ bool AGS::SymbolTable::IsPrimitiveVartype(Symbol s) const return kKW_NoSymbol == entries.at(s).VartypeD->BaseVartype; } -size_t AGS::SymbolTable::NumArrayElements(Symbol s) const +size_t AGS::SymbolTable::ArrayElementsCount(Symbol s) const { if (IsVariable(s)) s = entries.at(s).VariableD->Vartype; @@ -507,10 +507,10 @@ size_t AGS::SymbolTable::NumArrayElements(Symbol s) const if (0u == dims_size) return 0u; - size_t num = 1; - for (size_t dims_idx = 0; dims_idx < dims_size; ++dims_idx) - num *= vdesc->Dims[dims_idx]; - return num; + size_t count = 1u; + for (size_t dims_idx = 0u; dims_idx < dims_size; ++dims_idx) + count *= vdesc->Dims[dims_idx]; + return count; } bool AGS::SymbolTable::IsManagedVartype(Symbol s) const @@ -535,7 +535,7 @@ std::string const AGS::SymbolTable::GetName(AGS::Symbol symbl) const if (IsAutoptrVartype(symbl)) { std::string name = entries[symbl].Name; - int const pos_of_last_ch = name.length() - 1; + int const pos_of_last_ch = name.length() - 1u; if (pos_of_last_ch >= 0 && ' ' == name[pos_of_last_ch]) name.resize(pos_of_last_ch); // cut off trailing ' ' return name; @@ -550,11 +550,11 @@ AGS::Vartype AGS::SymbolTable::VartypeWithArray(std::vector const &dims, return vartype; std::string conv_name = entries[vartype].Name + "["; - size_t const last_idx = dims.size() - 1; - size_t num_elements = 1; - for (size_t dims_idx = 0; dims_idx <= last_idx; ++dims_idx) + size_t const last_idx = dims.size() - 1u; + size_t elements_count = 1u; + for (size_t dims_idx = 0u; dims_idx <= last_idx; ++dims_idx) { - num_elements *= dims[dims_idx]; + elements_count *= dims[dims_idx]; conv_name += std::to_string(dims[dims_idx]); conv_name += (dims_idx == last_idx) ? "]" : ", "; } @@ -565,7 +565,7 @@ AGS::Vartype AGS::SymbolTable::VartypeWithArray(std::vector const &dims, entries[array_vartype].VartypeD = new SymbolTableEntry::VartypeDesc; entries[array_vartype].VartypeD->Type = VTT::kArray; entries[array_vartype].VartypeD->BaseVartype = vartype; - entries[array_vartype].VartypeD->Size = num_elements * GetSize(vartype); + entries[array_vartype].VartypeD->Size = elements_count * GetSize(vartype); entries[array_vartype].VartypeD->Dims = dims; return array_vartype; } @@ -717,8 +717,8 @@ AGS::Symbol AGS::SymbolTable::Add(std::string const &name) // Extend the entries in chunks instead of one-by-one: Experiments show that this saves time if (entries.size() == entries.capacity()) { - size_t const new_size1 = entries.capacity() * 2; - size_t const new_size2 = entries.capacity() + 1000; + size_t const new_size1 = entries.capacity() * 2u; + size_t const new_size2 = entries.capacity() + 1000u; entries.reserve((new_size1 < new_size2) ? new_size1 : new_size2); } int const idx_of_new_entry = entries.size(); diff --git a/Compiler/script2/cc_symboltable.h b/Compiler/script2/cc_symboltable.h index c75658165e9..e91eb7ddb15 100644 --- a/Compiler/script2/cc_symboltable.h +++ b/Compiler/script2/cc_symboltable.h @@ -218,14 +218,14 @@ struct SymbolTableEntry; class SymbolTableConstant { public: - static size_t const kParameterScope = 1; - static size_t const kFunctionScope = 2; + static size_t const kParameterScope = 1u; + static size_t const kFunctionScope = 2u; static size_t const kNoSrcLocation = INT_MAX; - static size_t const kNoPrio = -1; - static size_t const kNoOpcode = -1; - static size_t const kSpecialLogic = -2; + static int const kNoPrio = -1; + static int const kNoOpcode = -1; + static int const kSpecialLogic = -2; }; struct SymbolTableEntry : public SymbolTableConstant @@ -469,7 +469,7 @@ struct SymbolTable : public SymbolTableConstant Vartype VartypeWithout(VartypeType vtt, Vartype vartype) const; inline bool IsArrayVartype(Symbol s) const { return IsVTT(s, VTT::kArray); } - size_t NumArrayElements(Symbol s) const; + size_t ArrayElementsCount(Symbol s) const; inline bool IsDynarrayVartype(Symbol s) const { return IsVTT(s, VTT::kDynarray); } inline bool IsAnyArrayVartype(Symbol s) const { return IsArrayVartype(s) || IsDynarrayVartype(s); } @@ -480,8 +480,8 @@ struct SymbolTable : public SymbolTableConstant bool IsManagedVartype(Symbol s) const; // Functions - inline size_t NumOfFuncParams(Symbol func) const - { return IsFunction(func) ? entries.at(func).FunctionD->Parameters.size() - 1 : 0; } + inline size_t FuncParamsCount(Symbol func) const + { return IsFunction(func) ? entries.at(func).FunctionD->Parameters.size() - 1u : 0u; } inline bool IsVariadicFunc(Symbol func) const { return IsFunction(func) && entries.at(func).FunctionD->IsVariadic; } inline AGS::Vartype FuncReturnVartype(Symbol func) const diff --git a/Compiler/script2/cs_parser.cpp b/Compiler/script2/cs_parser.cpp index ed58981e260..0d5a8ef5acc 100644 --- a/Compiler/script2/cs_parser.cpp +++ b/Compiler/script2/cs_parser.cpp @@ -285,9 +285,9 @@ void AGS::Parser::Expect(SymbolList const &expected, Symbol actual, std::string for (size_t expected_idx = 0; expected_idx < expected.size(); expected_idx++) { errmsg += "'" + _sym.GetName(expected[expected_idx]) + "'"; - if (expected_idx + 2 < expected.size()) + if (expected_idx + 2u < expected.size()) errmsg += ", "; - else if (expected_idx + 2 == expected.size()) + else if (expected_idx + 2u == expected.size()) errmsg += " or "; } } @@ -514,15 +514,15 @@ bool AGS::Parser::ContainsReleasableDynpointers(Vartype vartype) // We're at the end of a block and releasing a standard array of pointers. // MAR points to the array start. Release each array element (pointer). -void AGS::Parser::FreeDynpointersOfStdArrayOfDynpointer(size_t num_of_elements) +void AGS::Parser::FreeDynpointersOfStdArrayOfDynpointer(size_t elements_count) { - if (num_of_elements == 0) + if (elements_count == 0u) return; - if (num_of_elements < 4) + if (elements_count < SIZE_OF_STACK_CELL) { WriteCmd(SCMD_MEMZEROPTR); - for (size_t loop = 1; loop < num_of_elements; ++loop) + for (size_t loop = 1u; loop < elements_count; ++loop) { WriteCmd(SCMD_ADD, SREG_MAR, SIZE_OF_DYNPOINTER); _reg_track.SetRegister(SREG_MAR); @@ -531,7 +531,7 @@ void AGS::Parser::FreeDynpointersOfStdArrayOfDynpointer(size_t num_of_elements) return; } - WriteCmd(SCMD_LITTOREG, SREG_AX, num_of_elements); + WriteCmd(SCMD_LITTOREG, SREG_AX, elements_count); _reg_track.SetRegister(SREG_AX); BackwardJumpDest loop_start(_scrip); @@ -598,11 +598,11 @@ void AGS::Parser::FreeDynpointersOfStruct(Vartype struct_vtype) // We're at the end of a block and we're releasing a standard array of struct. // MAR points to the start of the array. Release all the pointers in the array. -void AGS::Parser::FreeDynpointersOfStdArrayOfStruct(Vartype element_vtype, size_t num_of_elements) +void AGS::Parser::FreeDynpointersOfStdArrayOfStruct(Vartype element_vtype, size_t elements_count) { // AX will be the index of the current element - WriteCmd(SCMD_LITTOREG, SREG_AX, num_of_elements); + WriteCmd(SCMD_LITTOREG, SREG_AX, elements_count); BackwardJumpDest loop_start(_scrip); loop_start.Set(); @@ -625,19 +625,19 @@ void AGS::Parser::FreeDynpointersOfStdArray(Symbol the_array) { Vartype const array_vartype = _sym.IsVartype(the_array) ? the_array : _sym.GetVartype(the_array); - size_t const num_of_elements = _sym.NumArrayElements(array_vartype); - if (num_of_elements < 1) + size_t const elements_count = _sym.ArrayElementsCount(array_vartype); + if (elements_count < 1u) return; // nothing to do Vartype const element_vartype = _sym[array_vartype].VartypeD->BaseVartype; if (_sym.IsDynpointerVartype(element_vartype)) { - FreeDynpointersOfStdArrayOfDynpointer(num_of_elements); + FreeDynpointersOfStdArrayOfDynpointer(elements_count); return; } if (_sym.IsStructVartype(element_vartype)) - FreeDynpointersOfStdArrayOfStruct(element_vartype, num_of_elements); + FreeDynpointersOfStdArrayOfStruct(element_vartype, elements_count); } // Note: Currently, the structs/arrays that are pointed to cannot contain @@ -1153,7 +1153,7 @@ void AGS::Parser::ParseFuncdecl_Paramlist(Symbol funcsym, bool body_follows) if ((++param_idx) >= MAX_FUNCTION_PARAMETERS) UserError("Too many parameters defined for function (max. allowed: %u)", MAX_FUNCTION_PARAMETERS - 1u); - ParseParamlist_Param(funcsym, body_follows, tqs, ParseVartype(), _sym.NumOfFuncParams(funcsym) + 1); + ParseParamlist_Param(funcsym, body_follows, tqs, ParseVartype(), _sym.FuncParamsCount(funcsym) + 1u); tqs = {}; // type qualifiers have been used up @@ -1218,16 +1218,16 @@ void AGS::Parser::ParseFuncdecl_CheckThatKnownInfoMatches(std::string const &fun UserError(msg.c_str(), func_name.c_str(), this_tq_str.c_str(), known_tq_str.c_str()); } - size_t const known_num_parameters = known_info->Parameters.size() - 1; - size_t const this_num_parameters = this_entry->Parameters.size() - 1; - if (known_num_parameters != this_num_parameters) + size_t const known_parameters_count = known_info->Parameters.size() - 1u; + size_t const this_parameters_count = this_entry->Parameters.size() - 1u; + if (known_parameters_count != this_parameters_count) UserError( ReferenceMsgLoc( "Function '%s' is declared with %d mandatory parameters here, %d mandatory parameters elswehere", known_declared).c_str(), func_name.c_str(), - this_num_parameters, - known_num_parameters); + this_parameters_count, + known_parameters_count); if (known_info->IsVariadic != this_entry->IsVariadic) { @@ -1257,7 +1257,7 @@ void AGS::Parser::ParseFuncdecl_CheckThatKnownInfoMatches(std::string const &fun auto const &known_params = known_info->Parameters; auto const &this_params = this_entry->Parameters; - for (size_t param_idx = 1; param_idx <= this_num_parameters; param_idx++) + for (size_t param_idx = 1u; param_idx <= this_parameters_count; param_idx++) { Vartype const known_param_vartype = known_params[param_idx].Vartype; Vartype const this_param_vartype = this_params[param_idx].Vartype; @@ -1276,7 +1276,7 @@ void AGS::Parser::ParseFuncdecl_CheckThatKnownInfoMatches(std::string const &fun { // If none of the parameters have a default, we'll let this through. bool has_default = false; - for (size_t param_idx = 1; param_idx < this_params.size(); ++param_idx) + for (size_t param_idx = 1u; param_idx < this_params.size(); ++param_idx) if (kKW_NoSymbol != this_params[param_idx].Default) { has_default = true; @@ -1286,7 +1286,7 @@ void AGS::Parser::ParseFuncdecl_CheckThatKnownInfoMatches(std::string const &fun return; } - for (size_t param_idx = 1; param_idx < this_params.size(); ++param_idx) + for (size_t param_idx = 1u; param_idx < this_params.size(); ++param_idx) { auto const this_default = this_params[param_idx].Default; auto const known_default = known_params[param_idx].Default; @@ -1311,12 +1311,12 @@ void AGS::Parser::ParseFuncdecl_CheckThatKnownInfoMatches(std::string const &fun } // Enter the function in the imports[] or functions[] array; get its index -void AGS::Parser::ParseFuncdecl_EnterAsImportOrFunc(Symbol name_of_func, bool body_follows, bool func_is_import, size_t num_of_parameters, CodeLoc &function_soffs) +void AGS::Parser::ParseFuncdecl_EnterAsImportOrFunc(Symbol name_of_func, bool body_follows, bool func_is_import, size_t params_count, CodeLoc &function_soffs) { if (body_follows) { // Index of the function in the ccCompiledScript::functions[] array - function_soffs = _scrip.AddNewFunction(_sym.GetName(name_of_func), num_of_parameters); + function_soffs = _scrip.AddNewFunction(_sym.GetName(name_of_func), params_count); if (function_soffs < 0) UserError("Max. number of functions exceeded"); @@ -1378,7 +1378,7 @@ void AGS::Parser::ParseFuncdecl_Checks(TypeQualifierSet tqs, Symbol struct_of_fu { // Functions only become struct components if they are declared in a struct or as extender std::string component = _sym.GetName(name_of_func); - component.erase(0, component.rfind(':') + 1); + component.erase(0, component.rfind(':') + 1u); UserError( ReferenceMsgSym("Function '%s' has not been declared within struct '%s' as a component", struct_of_func).c_str(), component.c_str(), _sym.GetName(struct_of_func).c_str()); @@ -1390,7 +1390,7 @@ void AGS::Parser::ParseFuncdecl_HandleFunctionOrImportIndex(TypeQualifierSet tqs if (PP::kMain == _pp) { int func_startoffs; - ParseFuncdecl_EnterAsImportOrFunc(name_of_func, body_follows, tqs[TQ::kImport], _sym.NumOfFuncParams(name_of_func), func_startoffs); + ParseFuncdecl_EnterAsImportOrFunc(name_of_func, body_follows, tqs[TQ::kImport], _sym.FuncParamsCount(name_of_func), func_startoffs); _sym[name_of_func].FunctionD->Offset = func_startoffs; } @@ -1414,7 +1414,7 @@ void AGS::Parser::ParseFuncdecl_HandleFunctionOrImportIndex(TypeQualifierSet tqs { char appendage[10]; - sprintf(appendage, "^%d", _sym.NumOfFuncParams(name_of_func) + 100 * _sym[name_of_func].FunctionD->IsVariadic); + sprintf(appendage, "^%d", _sym.FuncParamsCount(name_of_func) + 100 * _sym[name_of_func].FunctionD->IsVariadic); strcat(_scrip.imports[imports_idx], appendage); } @@ -1621,10 +1621,10 @@ CodeCell AGS::Parser::GetOpcode(Symbol const op_sym, Vartype vartype1, Vartype v CodeCell opcode = _sym[op_sym].OperatorD->IntOpcode; std::string msg = "Left-hand side of '' term"; - msg.replace(msg.find(""), 4, _sym.GetName(op_sym)); + msg.replace(msg.find(""), 4u, _sym.GetName(op_sym)); CheckVartypeMismatch(vartype1, kKW_Int, true, msg); msg = "Right-hand side of '' term"; - msg.replace(msg.find(""), 4, _sym.GetName(op_sym)); + msg.replace(msg.find(""), 4u, _sym.GetName(op_sym)); CheckVartypeMismatch(vartype2, kKW_Int, true, msg); return opcode; } @@ -2037,7 +2037,7 @@ void AGS::Parser::ParseExpression_PrefixNegate(Symbol op_sym, SrcList &expressio bool const bitwise_negation = kKW_BitNeg == op_sym; std::string msg = "Argument of ''"; - msg.replace(msg.find(""), 4, _sym.GetName(op_sym)); + msg.replace(msg.find(""), 4u, _sym.GetName(op_sym)); CheckVartypeMismatch(eres.Vartype, kKW_Int, true, msg); if (eres.kTY_Literal == eres.Type) @@ -2081,7 +2081,7 @@ void AGS::Parser::ParseExpression_PrefixCrement(Symbol op_sym, AGS::SrcList &exp ParseAssignment_ReadLHSForModification(expression, eres); std::string msg = "Argument of ''"; - msg.replace(msg.find(""), 4, _sym.GetName(op_sym).c_str()); + msg.replace(msg.find(""), 4u, _sym.GetName(op_sym).c_str()); CheckVartypeMismatch(eres.Vartype, kKW_Int, true, msg); WriteCmd((op_is_inc ? SCMD_ADD : SCMD_SUB), SREG_AX, 1); @@ -2118,7 +2118,7 @@ void AGS::Parser::ParseExpression_Prefix(SrcList &expression, EvaluationResult & { Symbol const op_sym = expression[0]; - if (expression.Length() < 2) + if (expression.Length() < 2u) UserError( "Expected a term after '%s' but didn't find any", _sym.GetName(op_sym).c_str()); @@ -2138,7 +2138,7 @@ void AGS::Parser::ParseExpression_Prefix(SrcList &expression, EvaluationResult & // We treat this here in the parser because the scanner doesn't know // whether a minus symbol stands for a unary minus. if (op_sym == kKW_Minus && - expression.Length() == 1 && + expression.Length() == 1u && expression[0] == kKW_OnePastLongMax) return ParseExpression_LongMin(eres); @@ -2164,7 +2164,7 @@ void AGS::Parser::StripOutermostParens(SrcList &expression) { while (expression[0] == kKW_OpenParenthesis) { - size_t const last = expression.Length() - 1; + size_t const last = expression.Length() - 1u; if (kKW_CloseParenthesis != expression[last]) return; expression.SetCursor(1u); @@ -2186,7 +2186,7 @@ void AGS::Parser::ParseExpression_PostfixCrement(Symbol const op_sym, SrcList &e ParseAssignment_ReadLHSForModification(expression, eres); std::string msg = "Argument of ''"; - msg.replace(msg.find(""), 4, _sym.GetName(op_sym).c_str()); + msg.replace(msg.find(""), 4u, _sym.GetName(op_sym).c_str()); CheckVartypeMismatch(eres.Vartype, kKW_Int, true, msg); // Really do the assignment the long way so that all the checks and safeguards will run. @@ -2273,7 +2273,7 @@ void AGS::Parser::ParseExpression_Ternary(size_t tern_idx, SrcList &expression, SrcList term1 = SrcList(expression, 0, tern_idx); // Second term begins after the '?', we don't know how long it is yet - SrcList after_term1 = SrcList(expression, tern_idx + 1, expression.Length() - (tern_idx + 1)); + SrcList after_term1 = SrcList(expression, tern_idx + 1u, expression.Length() - (tern_idx + 1u)); // Find beginning of third term after_term1.StartRead(); @@ -2283,7 +2283,7 @@ void AGS::Parser::ParseExpression_Ternary(size_t tern_idx, SrcList &expression, expression.SetCursor(tern_idx); UserError("Didn't find the matching ':' to '?'"); } - size_t const term3_start = after_term1.GetCursor() + 1; + size_t const term3_start = after_term1.GetCursor() + 1u; SrcList term3 = SrcList(after_term1, term3_start, after_term1.Length() - term3_start); SrcList term2 = SrcList(after_term1, 0u, after_term1.GetCursor()); if (0 == term3.Length()) @@ -2440,7 +2440,7 @@ void AGS::Parser::ParseExpression_Binary(size_t const op_idx, SrcList &expressio // if AX is 0 then the AND has failed, so just jump directly to the end of the term // AX will still be 0 so that will do as the result of the calculation lazy_evaluation = true; - expression.SetCursor(op_idx + 1); + expression.SetCursor(op_idx + 1u); WriteCmd(SCMD_JZ, kDestinationPlaceholder); to_exit.AddParam(); } @@ -2449,7 +2449,7 @@ void AGS::Parser::ParseExpression_Binary(size_t const op_idx, SrcList &expressio // If AX is non-zero then the OR has succeeded, so just jump directly to the end of the term; // AX will still be non-zero so that will do as the result of the calculation lazy_evaluation = true; - expression.SetCursor(op_idx + 1); + expression.SetCursor(op_idx + 1u); WriteCmd(SCMD_JNZ, kDestinationPlaceholder); to_exit.AddParam(); } @@ -2463,11 +2463,11 @@ void AGS::Parser::ParseExpression_Binary(size_t const op_idx, SrcList &expressio PushReg(SREG_AX); } - SrcList rhs = SrcList(expression, op_idx + 1, expression.Length()); - if (0 == rhs.Length()) + SrcList rhs = SrcList(expression, op_idx + 1u, expression.Length()); + if (0u == rhs.Length()) { // there is no right hand side for the expression - expression.SetCursor(op_idx + 1); + expression.SetCursor(op_idx + 1u); UserError("Binary operator '%s' doesn't have a right hand side", _sym.GetName(operator_sym).c_str()); } @@ -2479,7 +2479,7 @@ void AGS::Parser::ParseExpression_Binary(size_t const op_idx, SrcList &expressio if (!lazy_evaluation) { - expression.SetCursor(op_idx + 1); + expression.SetCursor(op_idx + 1u); PopReg(SREG_BX); // Note, we pop to BX although we have pushed AX _reg_track.SetRegister(SREG_BX); // now the result of the left side is in BX, of the right side is in AX @@ -2555,9 +2555,9 @@ void AGS::Parser::ParseExpression_InParens(SrcList &expression, EvaluationResult // We're in the parameter list of a function call, and we have less parameters than declared. // Provide defaults for the missing values -void AGS::Parser::AccessData_FunctionCall_ProvideDefaults(int num_func_args, size_t num_supplied_args,Symbol funcSymbol, bool func_is_import) +void AGS::Parser::AccessData_FunctionCall_ProvideDefaults(int func_args_count, size_t supplied_args_count,Symbol funcSymbol, bool func_is_import) { - for (size_t arg_idx = num_func_args; arg_idx > num_supplied_args; arg_idx--) + for (size_t arg_idx = func_args_count; arg_idx > supplied_args_count; arg_idx--) { Symbol const param_default = _sym[funcSymbol].FunctionD->Parameters[arg_idx].Default; if (kKW_NoSymbol == param_default) @@ -2612,20 +2612,20 @@ std::string const AGS::Parser::ReferenceMsgSym(std::string const &msg,Symbol sym return ReferenceMsgLoc(msg, _sym.GetDeclared(symb)); } -void AGS::Parser::AccessData_FunctionCall_PushParams(SrcList ¶meters, size_t closed_paren_idx, size_t num_func_args, size_t num_supplied_args,Symbol funcSymbol, bool func_is_import) +void AGS::Parser::AccessData_FunctionCall_PushParams(SrcList ¶ms, size_t closed_paren_idx, size_t func_args_count, size_t supplied_args_count,Symbol funcSymbol, bool func_is_import) { - size_t param_num = num_supplied_args + 1; - size_t start_of_current_param = 0; + size_t param_count = supplied_args_count + 1u; + size_t start_of_current_param = 0u; int end_of_current_param = closed_paren_idx; // can become < 0, points to (last symbol of parameter + 1) // Go backwards through the parameters since they must be pushed that way do { // Find the start of the next parameter - param_num--; + param_count--; int delimiter_nesting_depth = 0; - for (size_t paramListIdx = end_of_current_param - 1; true; paramListIdx--) + for (size_t paramListIdx = end_of_current_param - 1u; true; paramListIdx--) { - Symbol const &symb = parameters[paramListIdx]; + Symbol const &symb = params[paramListIdx]; if (_sym.IsDelimeter(symb)) { bool const is_opener = _sym[symb].DelimeterD->Opening; @@ -2638,7 +2638,7 @@ void AGS::Parser::AccessData_FunctionCall_PushParams(SrcList ¶meters, size_t if ((delimiter_nesting_depth == 0 && kKW_Comma == symb) || (delimiter_nesting_depth < 0 && kKW_OpenParenthesis == symb)) { - start_of_current_param = paramListIdx + 1; + start_of_current_param = paramListIdx + 1u; break; } if (paramListIdx == 0) @@ -2651,7 +2651,7 @@ void AGS::Parser::AccessData_FunctionCall_PushParams(SrcList ¶meters, size_t // Compile the parameter EvaluationResult eres; - SrcList current_param = SrcList(parameters, start_of_current_param, end_of_current_param - start_of_current_param); + SrcList current_param = SrcList(params, start_of_current_param, end_of_current_param - start_of_current_param); // Note, don't use 'ParseExpression_Term()' here; that function doesn't check // whether its parameter is nothing but an expression. ParseExpression(current_param, eres); @@ -2659,10 +2659,10 @@ void AGS::Parser::AccessData_FunctionCall_PushParams(SrcList ¶meters, size_t Expect(SymbolList{ kKW_Comma, kKW_CloseParenthesis }, current_param.PeekNext()); EvaluationResultToAx(eres); - if (param_num <= num_func_args) // we know what type to expect + if (param_count <= func_args_count) // we know what type to expect { // If we need a string object ptr but AX contains a normal string, convert AX - Vartype const param_vartype = _sym[funcSymbol].FunctionD->Parameters[param_num].Vartype; + Vartype const param_vartype = _sym[funcSymbol].FunctionD->Parameters[param_count].Vartype; ConvertAXStringToStringObject(param_vartype, eres.Vartype); // If we need a normal string but AX contains a string object ptr, // check that this ptr isn't null @@ -2671,7 +2671,7 @@ void AGS::Parser::AccessData_FunctionCall_PushParams(SrcList ¶meters, size_t WriteCmd(SCMD_CHECKNULLREG, SREG_AX); std::string msg = "Parameter # of call to function "; - msg.replace(msg.find(""), 5, std::to_string(param_num)); + msg.replace(msg.find(""), 5, std::to_string(param_count)); msg.replace(msg.find(""), 6, _sym.GetName(funcSymbol)); CheckVartypeMismatch(eres.Vartype, param_vartype, true, msg); } @@ -2689,23 +2689,23 @@ void AGS::Parser::AccessData_FunctionCall_PushParams(SrcList ¶meters, size_t else PushReg(SREG_AX); - end_of_current_param = start_of_current_param - 1; + end_of_current_param = start_of_current_param - 1u; } while (end_of_current_param > 0); } // Count parameters, check that all the parameters are non-empty; find closing paren -void AGS::Parser::AccessData_FunctionCall_CountAndCheckParm(SrcList ¶meters,Symbol name_of_func, size_t &index_of_close_paren, size_t &num_supplied_args) +void AGS::Parser::AccessData_FunctionCall_CountAndCheckParm(SrcList ¶ms,Symbol name_of_func, size_t &index_of_close_paren, size_t &supplied_args_count) { - size_t delimeter_nesting_depth = 1; - num_supplied_args = 1; + size_t delimeter_nesting_depth = 1u; + supplied_args_count = 1u; size_t param_idx; bool found_param_symbol = false; - for (param_idx = 1; param_idx < parameters.Length(); param_idx++) + for (param_idx = 1u; param_idx < params.Length(); param_idx++) { - Symbol const &symb = parameters[param_idx]; + Symbol const &symb = params[param_idx]; if (_sym.IsDelimeter(symb)) { @@ -2724,40 +2724,40 @@ void AGS::Parser::AccessData_FunctionCall_CountAndCheckParm(SrcList ¶meters, if (1 ==delimeter_nesting_depth && kKW_Comma == symb) { - num_supplied_args++; + supplied_args_count++; if (found_param_symbol) continue; - UserError("Argument %d in function call is empty", num_supplied_args - 1); + UserError("Argument %d in function call is empty", supplied_args_count - 1u); } found_param_symbol = true; } // Special case: "()" means 0 arguments - if (num_supplied_args == 1 && - parameters.Length() > 1 && - kKW_CloseParenthesis == parameters[1]) + if (supplied_args_count == 1u && + params.Length() > 1u && + kKW_CloseParenthesis == params[1]) { - num_supplied_args = 0; + supplied_args_count = 0u; } index_of_close_paren = param_idx; - if (kKW_CloseParenthesis != parameters[index_of_close_paren]) + if (kKW_CloseParenthesis != params[index_of_close_paren]) InternalError("Missing ')' at the end of the parameter list"); - if (index_of_close_paren > 0 && kKW_Comma == parameters[index_of_close_paren - 1]) + if (index_of_close_paren > 0u && kKW_Comma == params[index_of_close_paren - 1u]) UserError("Last argument in function call is empty"); - if (delimeter_nesting_depth > 0) + if (delimeter_nesting_depth > 0u) InternalError("Parser confused near '%s'", _sym.GetName(name_of_func).c_str()); } // We are processing a function call. General the actual function call -void AGS::Parser::AccessData_GenerateFunctionCall(Symbol name_of_func, size_t num_args, bool func_is_import) +void AGS::Parser::AccessData_GenerateFunctionCall(Symbol name_of_func, size_t args_count, bool func_is_import) { if (func_is_import) { // tell it how many args for this call (nested imported functions cause stack problems otherwise) - WriteCmd(SCMD_NUMFUNCARGS, num_args); + WriteCmd(SCMD_NUMFUNCARGS, args_count); } // Load function address into AX @@ -2778,8 +2778,8 @@ void AGS::Parser::AccessData_GenerateFunctionCall(Symbol name_of_func, size_t nu WriteCmd(SCMD_CALLEXT, SREG_AX); // Do the call _reg_track.SetAllRegisters(); // At runtime, we will arrive here when the function call has returned: Restore the stack - if (num_args > 0) - WriteCmd(SCMD_SUBREALSTACK, num_args); + if (args_count > 0u) + WriteCmd(SCMD_SUBREALSTACK, args_count); return; } @@ -2796,9 +2796,9 @@ void AGS::Parser::AccessData_GenerateFunctionCall(Symbol name_of_func, size_t nu _reg_track.SetAllRegisters(); // At runtime, we will arrive here when the function call has returned: Restore the stack - if (num_args > 0) + if (args_count > 0u) { - size_t const size_of_passed_args = num_args * SIZE_OF_STACK_CELL; + size_t const size_of_passed_args = args_count * SIZE_OF_STACK_CELL; WriteCmd(SCMD_SUB, SREG_SP, size_of_passed_args); _scrip.OffsetToLocalVarBlock -= size_of_passed_args; } @@ -2840,39 +2840,39 @@ void AGS::Parser::AccessData_GenerateDynarrayLengthFuncCall(EvaluationResult &er // We are processing a function call. // Get the parameters of the call and push them onto the stack. -void AGS::Parser::AccessData_PushFunctionCallParams(Symbol name_of_func, bool func_is_import, SrcList ¶meters, size_t &actual_num_args) +void AGS::Parser::AccessData_PushFunctionCallParams(Symbol name_of_func, bool func_is_import, SrcList ¶ms, size_t &actual_args_count) { - size_t const num_func_args = _sym.NumOfFuncParams(name_of_func); + size_t const func_args_count = _sym.FuncParamsCount(name_of_func); - size_t num_supplied_args = 0; + size_t supplied_args_count = 0u; size_t closed_paren_idx; - AccessData_FunctionCall_CountAndCheckParm(parameters, name_of_func, closed_paren_idx, num_supplied_args); + AccessData_FunctionCall_CountAndCheckParm(params, name_of_func, closed_paren_idx, supplied_args_count); // Push default parameters onto the stack when applicable // This will give an error if there aren't enough default parameters - if (num_supplied_args < num_func_args) + if (supplied_args_count < func_args_count) { - AccessData_FunctionCall_ProvideDefaults(num_func_args, num_supplied_args, name_of_func, func_is_import); + AccessData_FunctionCall_ProvideDefaults(func_args_count, supplied_args_count, name_of_func, func_is_import); } - if (num_supplied_args > num_func_args && !_sym.IsVariadicFunc(name_of_func)) + if (supplied_args_count > func_args_count && !_sym.IsVariadicFunc(name_of_func)) UserError( - (1 == num_func_args) ? + (1 == func_args_count) ? "Expected just %d parameter but found %d" : "Expected just %d parameters but found %d", - num_func_args, - num_supplied_args); + func_args_count, + supplied_args_count); // ASSERT at this point, the number of parameters is okay // Push the explicit arguments of the function - if (num_supplied_args > 0) + if (supplied_args_count > 0u) { - AccessData_FunctionCall_PushParams(parameters, closed_paren_idx, num_func_args, num_supplied_args, name_of_func, func_is_import); + AccessData_FunctionCall_PushParams(params, closed_paren_idx, func_args_count, supplied_args_count, name_of_func, func_is_import); } - actual_num_args = std::max(num_supplied_args, num_func_args); - parameters.SetCursor(closed_paren_idx + 1); // Go to the end of the parameter list + actual_args_count = std::max(supplied_args_count, func_args_count); + params.SetCursor(closed_paren_idx + 1u); // Go to the end of the parameter list } void AGS::Parser::AccessData_FunctionCall(Symbol name_of_func, SrcList &expression, EvaluationResult &eres) @@ -2915,12 +2915,12 @@ void AGS::Parser::AccessData_FunctionCall(Symbol name_of_func, SrcList &expressi mar_pushed = true; } - size_t num_args = 0; - AccessData_PushFunctionCallParams(name_of_func, func_is_import, expression, num_args); + size_t args_count = 0u; + AccessData_PushFunctionCallParams(name_of_func, func_is_import, expression, args_count); if (called_func_uses_this) { - if (0 == num_args) + if (0u == args_count) { // MAR must still be current, so undo the unneeded PUSH above. _scrip.OffsetToLocalVarBlock -= SIZE_OF_STACK_CELL; _scrip.codesize -= 2; @@ -2930,14 +2930,14 @@ void AGS::Parser::AccessData_FunctionCall(Symbol name_of_func, SrcList &expressi { // Recover the value of MAR from the stack. It's in front of the parameters. WriteCmd( SCMD_LOADSPOFFS, - (1 + (func_uses_normal_stack ? num_args : 0)) * SIZE_OF_STACK_CELL); + (1 + (func_uses_normal_stack ? args_count : 0)) * SIZE_OF_STACK_CELL); WriteCmd(SCMD_MEMREAD, SREG_MAR); _reg_track.SetRegister(SREG_MAR); } WriteCmd(SCMD_CALLOBJ, SREG_MAR); } - AccessData_GenerateFunctionCall(name_of_func, num_args, func_is_import); + AccessData_GenerateFunctionCall(name_of_func, args_count, func_is_import); eres.Type = eres.kTY_RunTimeValue; eres.Location = eres.kLOC_AX; @@ -2992,7 +2992,7 @@ bool AGS::Parser::ParseExpression_CompileTime(Symbol const op_sym, EvaluationRes void AGS::Parser::ParseExpression_NoOps(SrcList &expression, EvaluationResult &eres, bool result_used) { - if (kKW_OpenParenthesis == expression[0]) + if (kKW_OpenParenthesis == expression[0u]) return ParseExpression_InParens(expression, eres, result_used); AccessData(VAC::kReading, expression, eres); @@ -3001,7 +3001,7 @@ void AGS::Parser::ParseExpression_NoOps(SrcList &expression, EvaluationResult &e void AGS::Parser::ParseExpression_Term(SrcList &expression, EvaluationResult &eres, bool result_used) { - if (expression.Length() == 0) + if (expression.Length() == 0u) InternalError("Cannot parse empty subexpression"); int const least_binding_op_idx = IndexOfLeastBondingOperator(expression); // can be < 0 @@ -3053,7 +3053,7 @@ Symbol AGS::Parser::ConstructAttributeFuncName(Symbol attribsym, bool is_setter // If "::" in the name, take the part after the last "::" size_t const m_access_position = member_str.rfind("::"); if (std::string::npos != m_access_position) - member_str = member_str.substr(m_access_position + 2); + member_str = member_str.substr(m_access_position + 2u); char const *stem_str = is_setter ? "set" : "get"; char const *indx_str = is_indexed ? "i_" : "_"; std::string func_str = stem_str + (indx_str + member_str); @@ -3105,14 +3105,14 @@ void AGS::Parser::AccessData_CallAttributeFunc(bool is_setter, SrcList &expressi if (attrib_uses_this) PushReg(SREG_OP); // is the current this ptr, must be restored after call - size_t num_of_args = 0; + size_t args_count = 0u; if (is_setter) { if (func_is_import) WriteCmd(SCMD_PUSHREAL, SREG_AX); else PushReg(SREG_AX); - ++num_of_args; + ++args_count; } if (call_is_indexed) @@ -3133,13 +3133,13 @@ void AGS::Parser::AccessData_CallAttributeFunc(bool is_setter, SrcList &expressi WriteCmd(SCMD_PUSHREAL, SREG_AX); else PushReg(SREG_AX); - ++num_of_args; + ++args_count; } if (attrib_uses_this) WriteCmd(SCMD_CALLOBJ, SREG_MAR); // make MAR the new this ptr - AccessData_GenerateFunctionCall(qualified_func_name, num_of_args, func_is_import); + AccessData_GenerateFunctionCall(qualified_func_name, args_count, func_is_import); if (attrib_uses_this) PopReg(SREG_OP); // restore old this ptr after the func call @@ -3177,7 +3177,7 @@ void AGS::Parser::AccessData_ProcessCurrentArrayIndex(size_t const idx, size_t c SkipTo(SymbolList{ kKW_Comma, kKW_CloseBracket }, expression); size_t const index_end = expression.GetCursor(); SrcList current_index = SrcList(expression, index_start, index_end - index_start); - if (0 == current_index.Length()) + if (0u == current_index.Length()) UserError("Array index #u is empty, this is not supported here", idx + 1u); // If all ops are pending on the MAR register, it hasn't been set yet at all. @@ -3205,7 +3205,7 @@ void AGS::Parser::AccessData_ProcessCurrentArrayIndex(size_t const idx, size_t c "Array index #%u is %d, thus too low (minimum is 0)", idx + 1u, index_value); - if (dim > 0 && static_cast(index_value) >= dim) + if (dim > 0u && static_cast(index_value) >= dim) UserError( "Array index #%u is %d, thus too high (maximum is %u)", idx + 1u, @@ -3236,7 +3236,7 @@ void AGS::Parser::AccessData_ProcessCurrentArrayIndex(size_t const idx, size_t c // For better error messages at runtime, don't do CHECKBOUNDS after the multiplication. if (!is_dynarray) WriteCmd(SCMD_CHECKBOUNDS, SREG_AX, dim); - if (factor != 1) + if (factor != 1u) { WriteCmd(SCMD_MUL, SREG_AX, factor); _reg_track.SetRegister(SREG_AX); @@ -3265,7 +3265,7 @@ void AGS::Parser::AccessData_ProcessArrayIndexIfThere(SrcList &expression, Evalu Vartype const element_vartype = _sym[eres.Vartype].VartypeD->BaseVartype; size_t const element_size = _sym.GetSize(element_vartype); std::vector dim_sizes; - std::vector dynarray_dims = { 0, }; + std::vector dynarray_dims = { 0u, }; std::vector &dims = is_dynarray ? dynarray_dims : _sym[eres.Vartype].VartypeD->Dims; eres.Vartype = element_vartype; @@ -3273,16 +3273,16 @@ void AGS::Parser::AccessData_ProcessArrayIndexIfThere(SrcList &expression, Evalu AccessData_Dereference(eres); // Number of dimensions and the the size of the dimension for each dimension - size_t const num_of_dims = dims.size(); - dim_sizes.resize(num_of_dims); + size_t const dims_count = dims.size(); + dim_sizes.resize(dims_count); size_t factor = element_size; - for (int dim_idx = num_of_dims - 1; dim_idx >= 0; dim_idx--) // yes, "int" + for (int dim_idx = dims_count - 1; dim_idx >= 0; dim_idx--) // yes, "int" { dim_sizes[dim_idx] = factor; factor *= dims[dim_idx]; } - for (size_t dim_idx = 0; dim_idx < num_of_dims; dim_idx++) + for (size_t dim_idx = 0u; dim_idx < dims_count; dim_idx++) { EvaluationResult eres_index; AccessData_ProcessCurrentArrayIndex(dim_idx, dims[dim_idx], dim_sizes[dim_idx], is_dynarray, expression, eres_index); @@ -3298,13 +3298,13 @@ void AGS::Parser::AccessData_ProcessArrayIndexIfThere(SrcList &expression, Evalu } if (kKW_Comma == divider || kKW_OpenBracket == divider) { - if (num_of_dims == dim_idx + 1) - UserError("Expected %d indexes, found more", num_of_dims); + if (dims_count == dim_idx + 1u) + UserError("Expected %d indexes, found more", dims_count); expression.GetNext(); // Eat ',' or '[' continue; } - if (num_of_dims != dim_idx + 1) - UserError("Expected %d indexes, but only found %d", num_of_dims, dim_idx + 1); + if (dims_count != dim_idx + 1u) + UserError("Expected %d indexes, but only found %d", dims_count, dim_idx + 1u); } } @@ -3560,7 +3560,7 @@ AGS::Symbol AGS::Parser::FindStructOfComponent(Vartype strct, Symbol unqualified while (strct > 0 && _sym.IsVartype(strct)) { auto const &components = _sym[strct].VartypeD->Components; - if (0 < components.count(unqualified_component)) + if (0u < components.count(unqualified_component)) return strct; strct = _sym[strct].VartypeD->Parent; } @@ -3572,7 +3572,7 @@ AGS::Symbol AGS::Parser::FindComponentInStruct(Vartype strct, Symbol unqualified while (strct > 0 && _sym.IsVartype(strct)) { auto const &components = _sym[strct].VartypeD->Components; - if (0 < components.count(unqualified_component)) + if (0u < components.count(unqualified_component)) return components.at(unqualified_component); strct = _sym[strct].VartypeD->Parent; } @@ -3596,7 +3596,7 @@ bool AGS::Parser::AccessData_IsClauseLast(SrcList &expression) void AGS::Parser::AccessData(VariableAccess access_type, SrcList &expression, EvaluationResult &eres) { expression.StartRead(); - if (0 == expression.Length()) + if (0u == expression.Length()) InternalError("Empty expression"); bool implied_this_dot = false; // only true when "this." is implied @@ -3865,7 +3865,7 @@ void AGS::Parser::SkipToEndOfExpression(SrcList &src) // Let a symbol through if it can be considered a component of 'this'. if (kKW_NoSymbol != vartype_of_this && - 0 < _sym[vartype_of_this].VartypeD->Components.count(peeksym)) + 0u < _sym[vartype_of_this].VartypeD->Components.count(peeksym)) { src.GetNext(); // Eat the peeked symbol continue; @@ -3885,7 +3885,7 @@ void AGS::Parser::ParseExpression(SrcList &src, EvaluationResult &eres) size_t const expr_start = src.GetCursor(); SkipToEndOfExpression(src); SrcList expression = SrcList(src, expr_start, src.GetCursor() - expr_start); - if (0 == expression.Length()) + if (0u == expression.Length()) UserError("Expected an expression, found '%s' instead", _sym.GetName(src.GetNext()).c_str()); size_t const expr_end = src.GetCursor(); @@ -3988,13 +3988,13 @@ void AGS::Parser::ParseVardecl_InitialValAssignment_IntOrFloatVartype(Vartype co default: UserError("Cannot give an initial value to a variable of type '%s' here", _sym.GetName(wanted_vartype)); return; - case 1: + case 1u: initial_val[0] = litval; return; - case 2: + case 2u: (reinterpret_cast (&initial_val[0]))[0] = litval; return; - case 4: + case 4u: (reinterpret_cast (&initial_val[0]))[0] = litval; return; } @@ -4015,8 +4015,8 @@ void AGS::Parser::ParseVardecl_InitialValAssignment_OldString(std::vector if (lit_value.length() >= STRINGBUFFER_LENGTH) UserError( - "Initializer string is too long (max. chars allowed: %d)", - STRINGBUFFER_LENGTH - 1); + "Initializer string is too long (max. chars allowed: %u)", + STRINGBUFFER_LENGTH - 1u); initial_val.assign(lit_value.begin(), lit_value.end()); initial_val.push_back('\0'); @@ -4097,7 +4097,7 @@ void AGS::Parser::ParseStruct_ConstantDefn(Symbol const name_of_struct) _sym.GetName(parent).c_str(), _sym.GetName(qualified_component).c_str()); } - if (!in_struct && _nest.TopLevel() > 1) + if (!in_struct && _nest.TopLevel() > 1u) ParseVardecl_CheckAndStashOldDefn(qualified_component); if (_src.PeekNext() == kKW_OpenBracket) @@ -4141,7 +4141,7 @@ void AGS::Parser::ParseConstantDefn() void AGS::Parser::ParseVardecl_CheckIllegalCombis(Vartype vartype, ScopeType scope_type) { - if (vartype == kKW_String && FlagIsSet(_options, SCOPT_OLDSTRINGS) == 0) + if (vartype == kKW_String && !FlagIsSet(_options, SCOPT_OLDSTRINGS)) UserError("Variables of type 'string' aren't supported any longer (use the type 'String' instead)"); if (vartype == kKW_String && ScT::kImport == scope_type) // cannot import because string is really char *, and the pointer won't resolve properly @@ -4220,7 +4220,7 @@ void AGS::Parser::ParseVardecl_Import(Symbol var_name) void AGS::Parser::ParseVardecl_Global(Symbol var_name, Vartype vartype) { size_t const vartype_size = _sym.GetSize(vartype); - std::vector initial_val(vartype_size + 1, '\0'); + std::vector initial_val(vartype_size + 1u, '\0'); if (kKW_Assign == _src.PeekNext()) ParseVardecl_InitialValAssignment(var_name, initial_val); @@ -4320,7 +4320,7 @@ void AGS::Parser::ParseVardecl0(Symbol var_name, Vartype vartype, ScopeType scop ParseArray(var_name, vartype); // Don't warn for builtins or imports, they might have been predefined - if (!_sym.IsBuiltinVartype(vartype) && ScT::kImport != scope_type && 0 == _sym.GetSize(vartype)) + if (!_sym.IsBuiltinVartype(vartype) && ScT::kImport != scope_type && 0u == _sym.GetSize(vartype)) Warning( ReferenceMsgSym("Variable '%s' has zero size", vartype).c_str(), _sym.GetName(var_name).c_str()); @@ -4420,8 +4420,8 @@ void AGS::Parser::ParseFuncBodyStart(Symbol struct_of_func,Symbol name_of_func) // If there are dynpointer parameters, then the caller has simply "pushed" them onto the stack. // We catch up here by reading each dynpointer and writing it again using MEMINITPTR // to declare that the respective cells will from now on be used for dynpointers. - size_t const num_params = _sym.NumOfFuncParams(name_of_func); - for (size_t param_idx = 1; param_idx <= num_params; param_idx++) // skip return value param_idx == 0 + size_t const params_count = _sym.FuncParamsCount(name_of_func); + for (size_t param_idx = 1u; param_idx <= params_count; param_idx++) // skip return value param_idx == 0 { Vartype const param_vartype = _sym[name_of_func].FunctionD->Parameters[param_idx].Vartype; if (!_sym.IsDynVartype(param_vartype)) @@ -4686,13 +4686,13 @@ void AGS::Parser::ParseStruct_FuncDecl(Symbol struct_of_func, Symbol name_of_fun void AGS::Parser::ParseStruct_Attribute_CheckFunc(Symbol name_of_func, bool is_setter, bool is_indexed, Vartype vartype) { SymbolTableEntry &entry = _sym[name_of_func]; - size_t const num_parameters_wanted = (is_indexed ? 1 : 0) + (is_setter ? 1 : 0); - if (num_parameters_wanted != _sym.NumOfFuncParams(name_of_func)) + size_t const params_wanted_count = (is_indexed ? 1u : 0u) + (is_setter ? 1u : 0u); + if (params_wanted_count != _sym.FuncParamsCount(name_of_func)) { std::string const msg = ReferenceMsgSym( "The attribute function '%s' should have %d parameter(s) but is declared with %d parameter(s) instead", name_of_func); - UserError(msg.c_str(), entry.Name.c_str(), num_parameters_wanted, _sym.NumOfFuncParams(name_of_func)); + UserError(msg.c_str(), entry.Name.c_str(), params_wanted_count, _sym.FuncParamsCount(name_of_func)); } Vartype const ret_vartype = is_setter ? kKW_Void : vartype; @@ -4707,7 +4707,7 @@ void AGS::Parser::ParseStruct_Attribute_CheckFunc(Symbol name_of_func, bool is_s _sym.GetName(_sym.FuncReturnVartype(name_of_func)).c_str()); } - size_t p_idx = 1; + size_t p_idx = 1u; if (is_indexed) { auto actual_vartype = entry.FunctionD->Parameters[p_idx].Vartype; @@ -4738,17 +4738,17 @@ void AGS::Parser::ParseStruct_Attribute_CheckFunc(Symbol name_of_func, bool is_s void AGS::Parser::ParseStruct_Attribute_ParamList(Symbol struct_of_func, Symbol name_of_func, bool is_setter, bool is_indexed, Vartype vartype) { - auto ¶meters = _sym[name_of_func].FunctionD->Parameters; + auto ¶ms = _sym[name_of_func].FunctionD->Parameters; FuncParameterDesc fpd = {}; if (is_indexed) { fpd.Vartype = kKW_Int; - parameters.push_back(fpd); + params.push_back(fpd); } if (is_setter) { fpd.Vartype = vartype; - parameters.push_back(fpd); + params.push_back(fpd); } } @@ -5196,7 +5196,7 @@ void AGS::Parser::ParseStruct(TypeQualifierSet tqs, Symbol &struct_of_current_fu { // round up size to nearest multiple of STRUCT_ALIGNTO size_t &struct_size = _sym[stname].VartypeD->Size; - if (0 != (struct_size % STRUCT_ALIGNTO)) + if (0u != (struct_size % STRUCT_ALIGNTO)) struct_size += STRUCT_ALIGNTO - (struct_size % STRUCT_ALIGNTO); } @@ -5488,7 +5488,7 @@ void AGS::Parser::ParseExport_Function(Symbol func) int retval = _scrip.AddExport( _sym.GetName(func).c_str(), _sym[func].FunctionD->Offset, - _sym.NumOfFuncParams(func) + 100 * _sym[func].FunctionD->IsVariadic); + _sym.FuncParamsCount(func) + 100u * _sym[func].FunctionD->IsVariadic); if (retval < 0) InternalError("Could not export function"); } @@ -5611,7 +5611,7 @@ void AGS::Parser::ParseVartype_FuncDecl(TypeQualifierSet tqs, Vartype vartype, S void AGS::Parser::ParseVartype_VarDecl_PreAnalyze(Symbol var_name, ScopeType scope_type) { - if (0 != _givm.count(var_name)) + if (0u != _givm.count(var_name)) { if (_givm[var_name]) UserError("'%s' is already defined as a global non-import variable", _sym.GetName(var_name).c_str()); @@ -5955,7 +5955,7 @@ void AGS::Parser::ParseAssignmentOrExpression() SkipToEndOfExpression(_src); SrcList expression = SrcList(_src, expr_start, _src.GetCursor() - expr_start); - if (expression.Length() == 0) + if (expression.Length() == 0u) UserError("Unexpected symbol '%s'", _sym.GetName(_src.GetNext()).c_str()); Symbol const assignment_symbol = _src.PeekNext(); @@ -6210,7 +6210,7 @@ void AGS::Parser::ParseSwitchLabel(Symbol case_or_default) { if (NestingStack::kNoDefault != _nest.SwitchDefaultIdx()) UserError("This switch block already has a 'default:' label"); - _nest.SwitchDefaultIdx() = _nest.SwitchCaseStart().size() - 1; + _nest.SwitchDefaultIdx() = _nest.SwitchCaseStart().size() - 1u; } else // "case" { @@ -6245,7 +6245,7 @@ void AGS::Parser::ParseSwitchLabel(Symbol case_or_default) void AGS::Parser::RemoveLocalsFromStack(size_t nesting_level) { size_t const size_of_local_vars = StacksizeOfLocals(nesting_level); - if (size_of_local_vars > 0) + if (size_of_local_vars > 0u) { _scrip.OffsetToLocalVarBlock -= size_of_local_vars; WriteCmd(SCMD_SUB, SREG_SP, size_of_local_vars); @@ -6292,7 +6292,7 @@ void AGS::Parser::ParseBreak() // Find the (level of the) looping construct to which the break applies // Note that this is similar, but _different_ from what happens at 'continue'. size_t nesting_level; - for (nesting_level = _nest.TopLevel(); nesting_level > 0; nesting_level--) + for (nesting_level = _nest.TopLevel(); nesting_level > 0u; nesting_level--) { NSType const ltype = _nest.Type(nesting_level); if (NSType::kDo == ltype || NSType::kSwitch == ltype || NSType::kWhile == ltype) @@ -6305,8 +6305,8 @@ void AGS::Parser::ParseBreak() _nest.JumpOutLevel() = std::min(_nest.JumpOutLevel(), nesting_level); size_t const save_offset = _scrip.OffsetToLocalVarBlock; - FreeDynpointersOfLocals(nesting_level + 1); - RemoveLocalsFromStack(nesting_level + 1); + FreeDynpointersOfLocals(nesting_level + 1u); + RemoveLocalsFromStack(nesting_level + 1u); // Jump out of the loop or switch WriteCmd(SCMD_JMP, kDestinationPlaceholder); @@ -6325,21 +6325,21 @@ void AGS::Parser::ParseContinue() // Find the level of the looping construct to which the 'continue' applies // Note that this is similar, but _different_ from what happens at 'break'. size_t nesting_level; - for (nesting_level = _nest.TopLevel(); nesting_level > 0; nesting_level--) + for (nesting_level = _nest.TopLevel(); nesting_level > 0u; nesting_level--) { NSType const ltype = _nest.Type(nesting_level); if (NSType::kDo == ltype || NSType::kWhile == ltype) break; } - if (nesting_level == 0) + if (nesting_level == 0u) UserError("Can only use 'continue' inside a loop"); _nest.JumpOutLevel() = std::min(_nest.JumpOutLevel(), nesting_level); size_t const save_offset = _scrip.OffsetToLocalVarBlock; - FreeDynpointersOfLocals(nesting_level + 1); - RemoveLocalsFromStack(nesting_level + 1); + FreeDynpointersOfLocals(nesting_level + 1u); + RemoveLocalsFromStack(nesting_level + 1u); // Jump to the start of the loop _nest.Start(nesting_level).WriteJump(SCMD_JMP, _src.GetLineno()); @@ -6525,7 +6525,7 @@ void AGS::Parser::HandleSrcSectionChangeAt(size_t pos) void AGS::Parser::ParseInput() { Parser::NestingStack nesting_stack(_scrip); - size_t nesting_level = 0; + size_t nesting_level = 0u; // We start off in the global data part - no code is allowed until a function definition is started Symbol struct_of_current_func = kKW_NoSymbol; // non-zero only when a struct member function is open @@ -6619,7 +6619,7 @@ void AGS::Parser::Parse_ReinitSymTable(size_t size_after_scanning) void AGS::Parser::Parse_BlankOutUnusedImports() { - for (size_t entries_idx = 0; entries_idx < _sym.entries.size(); entries_idx++) + for (size_t entries_idx = 0u; entries_idx < _sym.entries.size(); entries_idx++) { if (_sym[entries_idx].Accessed) continue; @@ -6743,7 +6743,7 @@ void AGS::Parser::Parse_CheckForUnresolvedStructForwardDecls() void AGS::Parser::Parse_CheckFixupSanity() { - for (size_t fixup_idx = 0; fixup_idx < static_cast(_scrip.numfixups); fixup_idx++) + for (size_t fixup_idx = 0u; fixup_idx < static_cast(_scrip.numfixups); fixup_idx++) { if (FIXUP_IMPORT != _scrip.fixuptypes[fixup_idx]) continue; @@ -6764,12 +6764,12 @@ void AGS::Parser::Parse_CheckFixupSanity() void AGS::Parser::Parse_ExportAllFunctions() { - for (size_t func_num = 0; func_num < _scrip.Functions.size(); func_num++) + for (size_t func_idx = 0; func_idx < _scrip.Functions.size(); func_idx++) { if (0 > _scrip.AddExport( - _scrip.Functions[func_num].Name, - _scrip.Functions[func_num].CodeOffs, - _scrip.Functions[func_num].NumOfParams)) + _scrip.Functions[func_idx].Name, + _scrip.Functions[func_idx].CodeOffs, + _scrip.Functions[func_idx].ParamsCount)) InternalError("Function export failed. Out of memory?"); } } diff --git a/Compiler/script2/cs_parser.h b/Compiler/script2/cs_parser.h index eea2c767546..3eb22a320b0 100644 --- a/Compiler/script2/cs_parser.h +++ b/Compiler/script2/cs_parser.h @@ -426,7 +426,7 @@ class Parser // We're at the end of a block and releasing a standard array of dynpointers. // MAR points to the array start. Release each array element (dynpointer). - void FreeDynpointersOfStdArrayOfDynpointer(size_t num_of_elements); + void FreeDynpointersOfStdArrayOfDynpointer(size_t elements_count); // We're at the end of a block and releasing all the dynpointers in a struct. // MAR already points to the start of the struct. @@ -434,7 +434,7 @@ class Parser // We're at the end of a block and we're releasing a standard array of struct. // MAR points to the start of the array. Release all the pointers in the array. - void FreeDynpointersOfStdArrayOfStruct(Vartype element_vtype, size_t num_of_elements); + void FreeDynpointersOfStdArrayOfStruct(Vartype element_vtype, size_t elements_count); // We're at the end of a block and releasing a standard array. MAR points to the start. // Release the pointers that the array contains. @@ -498,7 +498,7 @@ class Parser void ParseFuncdecl_CheckThatKnownInfoMatches(std::string const &func_name, SymbolTableEntry::FunctionDesc const *this_entry, SymbolTableEntry::FunctionDesc const *known_info, size_t declared, bool body_follows); // Enter the function in the 'imports[]' or 'functions[]' array of '_script'; get its index - void ParseFuncdecl_EnterAsImportOrFunc(Symbol name_of_func, bool body_follows, bool func_is_import, size_t num_of_parameters, CodeLoc &function_soffs); + void ParseFuncdecl_EnterAsImportOrFunc(Symbol name_of_func, bool body_follows, bool func_is_import, size_t parameters_count, CodeLoc &function_soffs); // We're at something like 'int foo(', directly before the '(' // Return in 'body_follows' whether the symbol that follows the corresponding ')' is '{' @@ -545,15 +545,15 @@ class Parser // We're in the parameter list of a function call, and we have less parameters than declared. // Provide defaults for the missing values - void AccessData_FunctionCall_ProvideDefaults(int num_func_args, size_t num_supplied_args, Symbol funcSymbol, bool func_is_import); + void AccessData_FunctionCall_ProvideDefaults(int func_args_count, size_t supplied_args_count, Symbol funcSymbol, bool func_is_import); - void AccessData_FunctionCall_PushParams(SrcList ¶meters, size_t closed_paren_idx, size_t num_func_args, size_t num_supplied_args, Symbol funcSymbol, bool func_is_import); + void AccessData_FunctionCall_PushParams(SrcList ¶meters, size_t closed_paren_idx, size_t func_args_count, size_t supplied_args_count, Symbol funcSymbol, bool func_is_import); // Count parameters, check that all the parameters are non-empty; find closing paren - void AccessData_FunctionCall_CountAndCheckParm(SrcList ¶meters, Symbol name_of_func, size_t &index_of_close_paren, size_t &num_supplied_args); + void AccessData_FunctionCall_CountAndCheckParm(SrcList ¶meters, Symbol name_of_func, size_t &index_of_close_paren, size_t &supplied_args_count); // We are processing a function call. General the actual function call - void AccessData_GenerateFunctionCall(Symbol name_of_func, size_t num_args, bool func_is_import); + void AccessData_GenerateFunctionCall(Symbol name_of_func, size_t args_count, bool func_is_import); // Generate the function call for the function that returns the number of elements // of a dynarray. @@ -562,7 +562,7 @@ class Parser // We are processing a function call. // Get the parameters of the call and push them onto the stack. // Return the number of the parameters pushed - void AccessData_PushFunctionCallParams(Symbol name_of_func, bool func_is_import, SrcList ¶meters, size_t &actual_num_args); + void AccessData_PushFunctionCallParams(Symbol name_of_func, bool func_is_import, SrcList ¶meters, size_t &actual_args_count); // Process a function call. The parameter list begins with 'expression[1u]' (!) void AccessData_FunctionCall(Symbol name_of_func, SrcList &expression, EvaluationResult &eres); diff --git a/Compiler/script2/cs_scanner.cpp b/Compiler/script2/cs_scanner.cpp index 8c3cb2bf02f..1142602da60 100644 --- a/Compiler/script2/cs_scanner.cpp +++ b/Compiler/script2/cs_scanner.cpp @@ -237,7 +237,7 @@ void AGS::Scanner::SkipWhitespace() continue; if ('\n' == ch) - NewLine(_lineno + 1); + NewLine(_lineno + 1u); } } @@ -246,7 +246,7 @@ long long AGS::Scanner::StringToLongLong(std::string const &valstring, bool &con errno = 0; char *endptr; int base = 0; - if (valstring.length() > 1 && + if (valstring.length() > 1u && valstring[0] == '0' && IsDigit(valstring[1])) { @@ -288,7 +288,7 @@ void AGS::Scanner::ReadInNumberLit(std::string &symstring, ScanType &scan_type, if (!valstring.empty() && IsDigit(valstring.back()) && IsDigit(Peek())) continue; - if (valstring.length() > 1 && + if (valstring.length() > 1u && '0' == valstring[0] && ('x' == valstring[1] || 'X' == valstring[1]) && IsHexDigit(valstring.back()) && @@ -308,8 +308,8 @@ void AGS::Scanner::ReadInNumberLit(std::string &symstring, ScanType &scan_type, if (('-' == ch || '+' == ch) && IsDigit(Peek()) && - valstring.length() > 1 && - std::string::npos != exponent_leadin.find(valstring[valstring.length() - 2])) + valstring.length() > 1u && + std::string::npos != exponent_leadin.find(valstring[valstring.length() - 2u])) continue; // Is neither an int nor a float yet but will become a number in later loop traversals // Test convert to a long long (!) so that -LONG_MIN is still within the range that we must allow. @@ -341,12 +341,12 @@ void AGS::Scanner::ReadInNumberLit(std::string &symstring, ScanType &scan_type, long long longlong_value = StringToLongLong(valstring.c_str(), can_be_an_integer); if (can_be_an_integer) { - if (valstring.length() > 1 && '0' == valstring[0] && IsDigit(valstring[1])) + if (valstring.length() > 1u && '0' == valstring[0] && IsDigit(valstring[1])) Warning("'%s' is interpreted as a number in decimal notation", symstring.c_str()); if (longlong_value > LONG_MAX) { - if (valstring.length() > 2 && + if (valstring.length() > 2u && IsDigit(valstring[0]) && IsDigit(valstring[1]) && longlong_value == -static_cast(LONG_MIN)) @@ -362,7 +362,7 @@ void AGS::Scanner::ReadInNumberLit(std::string &symstring, ScanType &scan_type, } if (longlong_value >= 0x80000000 && - valstring.length() > 1 && + valstring.length() > 1u && valstring[0] == '0' && (valstring[1] == 'x' || valstring[1] == 'X')) { @@ -370,7 +370,7 @@ void AGS::Scanner::ReadInNumberLit(std::string &symstring, ScanType &scan_type, if (longlong_value > 0xFFFFFFFF) UserError( "Too many significant hex digits in '%s' (at most 8 significant digits allowed)", - (symstring.length() <= 20? symstring : symstring.substr(0, 20) + "...").c_str()); + (symstring.length() <= 20u? symstring : symstring.substr(0u, 20u) + "...").c_str()); // 'strtoll()' has converted this hexadecimal into a value that // is too large for a long. However, this is still legal and // yields a negative long number. @@ -380,7 +380,7 @@ void AGS::Scanner::ReadInNumberLit(std::string &symstring, ScanType &scan_type, { UserError( "Literal integer '%s' is out of bounds (maximum is '%d')", - symstring.length() <= 20 ? symstring.c_str() : (symstring.substr(0, 20) + "...").c_str(), + symstring.length() <= 20u ? symstring.c_str() : (symstring.substr(0u, 20u) + "...").c_str(), LONG_MAX); } } @@ -456,7 +456,7 @@ void AGS::Scanner::ReadInCharLit(std::string &symstring, CodeCell &value) int AGS::Scanner::OctDigits2Char(int first_digit_char, std::string &symstring) { int ret = first_digit_char - '0'; - for (size_t digit_idx = 0; digit_idx < 2; ++digit_idx) + for (size_t digit_idx = 0u; digit_idx < 2u; ++digit_idx) { int const digit = Peek() - '0'; if (digit < 0 || digit >= 8) @@ -473,7 +473,7 @@ int AGS::Scanner::OctDigits2Char(int first_digit_char, std::string &symstring) int AGS::Scanner::HexDigits2Char(std::string &symstring) { int ret = 0; - for (size_t digit_idx = 0; digit_idx < 2; ++digit_idx) + for (size_t digit_idx = 0u; digit_idx < 2u; ++digit_idx) { int hexdigit = Peek(); //convert a..f to A..F @@ -586,8 +586,8 @@ void AGS::Scanner::ReadInStringLit(std::string &symstring, std::string &valstrin pos_before_skip = _inputStream.tellg(); lineno_before_skip = _lineno; Get(); // Eat leading '"' - char tbuffer[kNewSectionLitPrefixSize + 1]; - _inputStream.get(tbuffer, kNewSectionLitPrefixSize + 1, 0); + char tbuffer[kNewSectionLitPrefixSize + 1u]; + _inputStream.get(tbuffer, kNewSectionLitPrefixSize + 1u, 0); _eofReached |= _inputStream.eof(); _failed |= _inputStream.fail(); // Undo the reading diff --git a/Compiler/script2/cs_scanner.h b/Compiler/script2/cs_scanner.h index 0f037d4df63..314172a2aa9 100644 --- a/Compiler/script2/cs_scanner.h +++ b/Compiler/script2/cs_scanner.h @@ -126,7 +126,7 @@ class Scanner // Read in a character literal void ReadInCharLit(std::string &symstring, CodeCell &value); - + // Read in a string literal. valstring is the interpreted literal (no quotes, '\\' combinations resolved) void ReadInStringLit(std::string &symstring, std::string &valstring); diff --git a/Compiler/test2/cc_symboltable_test.cpp b/Compiler/test2/cc_symboltable_test.cpp index 04b62b26472..a09125ef634 100644 --- a/Compiler/test2/cc_symboltable_test.cpp +++ b/Compiler/test2/cc_symboltable_test.cpp @@ -217,10 +217,10 @@ TEST(SymbolTable, ArrayClassic) EXPECT_TRUE(symt.IsArrayVartype(array_sym)); EXPECT_TRUE(symt.IsAnyArrayVartype(array_sym)); EXPECT_FALSE(symt.IsDynarrayVartype(array_sym)); - EXPECT_EQ(2, symt.NumArrayElements(array_sym)); + EXPECT_EQ(2, symt.ArrayElementsCount(array_sym)); symt[array_sym].VartypeD->Dims = { 2, 3, 5, 7, }; - EXPECT_EQ(2 * 3 * 5 * 7, symt.NumArrayElements(array_sym)); + EXPECT_EQ(2 * 3 * 5 * 7, symt.ArrayElementsCount(array_sym)); } TEST(SymbolTable, OperatorPrio) From ddb38708171e2dcad4eeae3e719da71510c5b8c8 Mon Sep 17 00:00:00 2001 From: "Peter G Bouillon [fernewelten]" Date: Fri, 20 Oct 2023 06:37:22 +0200 Subject: [PATCH 2/6] New compiler: Move SkipTo functions to internallist.h Also, some var renamings --- Compiler/script2/cc_internallist.cpp | 36 ++++++++++- Compiler/script2/cc_internallist.h | 32 ++++++++-- Compiler/script2/cs_parser.cpp | 89 ++++++---------------------- Compiler/script2/cs_parser.h | 16 +---- 4 files changed, 80 insertions(+), 93 deletions(-) diff --git a/Compiler/script2/cc_internallist.cpp b/Compiler/script2/cc_internallist.cpp index efef0c993f6..46375cb528a 100644 --- a/Compiler/script2/cc_internallist.cpp +++ b/Compiler/script2/cc_internallist.cpp @@ -1,6 +1,4 @@ -#include #include -#include #include #include #include "cc_internallist.h" @@ -91,6 +89,40 @@ AGS::Symbol AGS::SrcList::GetNext() return p; } +void AGS::SrcList::SkipTo(SymbolList const &stoplist) +{ + int delimeter_nesting_depth = 0; + for (; !ReachedEOF(); GetNext()) + { + // Note that the scanner/tokenizer has already verified + // that all opening symbols get closed and + // that we don't have (...] or similar in the input + Symbol const next_sym = PeekNext(); + switch (next_sym) + { + case kKW_OpenBrace: + case kKW_OpenBracket: + case kKW_OpenParenthesis: + ++delimeter_nesting_depth; + continue; + + case kKW_CloseBrace: + case kKW_CloseBracket: + case kKW_CloseParenthesis: + if (--delimeter_nesting_depth < 0) + return; + continue; + + } + if (0 < delimeter_nesting_depth) + continue; + + for (auto it = stoplist.begin(); it != stoplist.end(); ++it) + if (next_sym == *it) + return; + } +} + void AGS::SrcList::EatFirstSymbol() { if (_len < 1) diff --git a/Compiler/script2/cc_internallist.h b/Compiler/script2/cc_internallist.h index e036cd8711a..dec561b31f5 100644 --- a/Compiler/script2/cc_internallist.h +++ b/Compiler/script2/cc_internallist.h @@ -4,12 +4,12 @@ #include #include #include - #include "cs_parser_common.h" +#include "cc_symboltable.h" namespace AGS { -// A helper class that contains post-parsed list of sections, +// A helper class that contains post-parsed list of sections // and a offset-to-section map, useful for finding a symbol's location. class SectionList { @@ -17,6 +17,7 @@ class SectionList std::vector _sections; // starting line offset to section index std::map _off2sec; + public: SectionList() = default; SectionList(const std::vector §ions, @@ -93,8 +94,8 @@ class SrcList std::vector &_script; LineHandler &_lineHandler; size_t _offset; - size_t _len; // note: _len has the length relative to [0], not relative to [_offset] - size_t &_cursor; // note: _cursor has the position relative to [0], not relative to [_offset] + size_t _len; // Length relative to [0], not relative to [_offset] + size_t &_cursor; // Position relative to [0], not relative to [_offset] public: SrcList(std::vector &script, LineHandler &line_handler, size_t &cursor); @@ -103,7 +104,7 @@ class SrcList // but the resulting SrcList starts at [offset] and has the length len. // No symbols are actually copied. // NOTE: If you move the cursor of the new list then the cursor of the original list - // moves by the same amount because the cursor variable is shared. This is intentional. + // moves correspondingly because the cursor variable is shared. This is intentional. SrcList(SrcList const &src_list, size_t offset, size_t len); inline size_t GetCursor() const { return _cursor - _offset; } @@ -111,6 +112,8 @@ class SrcList inline size_t Length() const { return _len; }; inline bool ReachedEOF() const { return _cursor - _offset >= _len || _cursor >= _script.size(); } + // Whether the cursor has gone _beyond_ the start when going backwards + inline bool ReachedStartOF() const { return _cursor < _offset; } // Set the cursor to the start of the list inline void StartRead() { SetCursor(0); } @@ -118,9 +121,26 @@ class SrcList Symbol GetNext(); // Look at the symbol that will be read next, but don't read it yet inline Symbol PeekNext() const { return ReachedEOF() ? kEOF : _script[_cursor]; } + // Look at the symbol that comes next when going backwards when reading + inline Symbol PeekPrev() const { return ReachedStartOF() ? kEOF : _script[GetCursor() - 1u]; } // Move the cursor back by 1 space. inline void BackUp() { size_t c = GetCursor(); SetCursor((c > 0u) ? c - 1u : 0u); } + // Skim through the list, ignoring delimited content completely. Stop in the following cases: + // . A symbol in 'stoplist' is encountered + // . A closing symbol is encountered that hasn't been opened. + // Don't consume the symbol that stops the scan. + void SkipTo(SymbolList const &stoplist); + // Skim through the list, ignoring delimited content completely. Stop in the following cases: + // . The symbol 'stopsym' is encountered. + // . A closing symbol is encountered that hasn't been opened. + // Don't consume the symbol that stops the scan. + inline void SkipTo(Symbol stopsym) { SkipTo(SymbolList{ stopsym }); } + // Skim through the list, ignoring delimited content completely. + // Stop whem a closing symbol is encountered that hasn't been opened. + // Don't consume that symbol. + inline void SkipToCloser(void) { SkipTo(SymbolList{}); } + // Get symbol at idx. Moves the cursor, too. // Note: Can't assign through this operator, this is intentional. Symbol operator[](size_t idx) { SetCursor(idx); return GetNext(); } @@ -133,7 +153,7 @@ class SrcList void EatLastSymbol(); // Note that when this is a sub-list of an original list, the line numbers and sections - // will still be relative to the original list. This is intentional. + // will still be relative to the original list. This is intentional // (When the user gets an error, they want to know the "real" line where the error is, // not a line reference that is relative to an excerpt of their program.) inline size_t GetLinenoAt(size_t pos) const { return _lineHandler.GetLinenoAt(pos + _offset); } diff --git a/Compiler/script2/cs_parser.cpp b/Compiler/script2/cs_parser.cpp index 0d5a8ef5acc..826d8f63b55 100644 --- a/Compiler/script2/cs_parser.cpp +++ b/Compiler/script2/cs_parser.cpp @@ -229,48 +229,6 @@ AGS::Symbol AGS::Parser::MangleStructAndComponent(Symbol stname, Symbol componen return _sym.FindOrAdd(fullname_str); } -void AGS::Parser::SkipTo(SymbolList const &stoplist, SrcList &source) -{ - int delimeter_nesting_depth = 0; - for (; !source.ReachedEOF(); source.GetNext()) - { - // Note that the scanner/tokenizer has already verified - // that all opening symbols get closed and - // that we don't have (...] or similar in the input - Symbol const next_sym = _src.PeekNext(); - switch (next_sym) - { - case kKW_OpenBrace: - case kKW_OpenBracket: - case kKW_OpenParenthesis: - ++delimeter_nesting_depth; - continue; - - case kKW_CloseBrace: - case kKW_CloseBracket: - case kKW_CloseParenthesis: - if (--delimeter_nesting_depth < 0) - return; - continue; - - } - if (0 < delimeter_nesting_depth) - continue; - - for (auto it = stoplist.begin(); it != stoplist.end(); ++it) - if (next_sym == *it) - return; - } -} - -void AGS::Parser::SkipToClose(Predefined closer) -{ - SkipTo(SymbolList{}, _src); - Symbol const punctuation = _src.GetNext(); - if (closer != punctuation) - InternalError("Unexpected closing symbol %s", _sym.GetName(punctuation).c_str()); -} - void AGS::Parser::Expect(SymbolList const &expected, Symbol actual, std::string const &custom_msg) { for (size_t expected_idx = 0; expected_idx < expected.size(); expected_idx++) @@ -640,16 +598,6 @@ void AGS::Parser::FreeDynpointersOfStdArray(Symbol the_array) FreeDynpointersOfStdArrayOfStruct(element_vartype, elements_count); } -// Note: Currently, the structs/arrays that are pointed to cannot contain -// pointers in their turn. -// If they do, we need a solution at runtime to chase the pointers to release; -// we cannot do it at compile time. Also, the pointers might form "rings" -// (e.g., A contains a field that points to B; B contains a field that -// points to A), so we cannot rely on reference counting for identifying -// _all_ the unreachable memory chunks. (If nothing else points to A or B, -// both are unreachable so _could_ be released, but they still point to each -// other and so have a reference count of 1; the reference count will never reach 0). - void AGS::Parser::FreeDynpointersOfLocals(size_t from_level) { for (size_t level = from_level; level <= _nest.TopLevel(); level++) @@ -1339,7 +1287,8 @@ void AGS::Parser::ParseFuncdecl_EnterAsImportOrFunc(Symbol name_of_func, bool bo bool AGS::Parser::ParseFuncdecl_DoesBodyFollow() { int const cursor = _src.GetCursor(); - SkipToClose(kKW_CloseParenthesis); + _src.SkipToCloser(); + _src.GetNext(); // Eat ')' bool body_follows = (kKW_OpenBrace == _src.PeekNext()); _src.SetCursor(cursor); return body_follows; @@ -1941,7 +1890,7 @@ void AGS::Parser::ParseExpression_New(SrcList &expression, EvaluationResult &ere { Warning("'()' after 'new' isn't implemented, is currently ignored"); expression.GetNext(); - SkipTo(SymbolList{}, expression); + expression.SkipToCloser(); expression.GetNext(); } @@ -2168,7 +2117,7 @@ void AGS::Parser::StripOutermostParens(SrcList &expression) if (kKW_CloseParenthesis != expression[last]) return; expression.SetCursor(1u); - SkipTo(SymbolList{}, expression); + expression.SkipToCloser(); if (expression.GetCursor() != last) return; expression.EatFirstSymbol(); @@ -2277,7 +2226,7 @@ void AGS::Parser::ParseExpression_Ternary(size_t tern_idx, SrcList &expression, // Find beginning of third term after_term1.StartRead(); - SkipTo(kKW_Colon, after_term1); + after_term1.SkipTo(kKW_Colon); if (after_term1.ReachedEOF() || kKW_Colon != after_term1.PeekNext()) { expression.SetCursor(tern_idx); @@ -2545,7 +2494,7 @@ void AGS::Parser::ParseExpression_InParens(SrcList &expression, EvaluationResult { // Check for spurious symbols after the closing paren. expression.SetCursor(1u); - SkipTo(SymbolList{}, expression); + expression.SkipToCloser(); expression.GetNext(); // Eat the closing parenthesis ParseExpression_CheckUsedUp(expression); @@ -2736,7 +2685,7 @@ void AGS::Parser::AccessData_FunctionCall_CountAndCheckParm(SrcList ¶ms,Symb // Special case: "()" means 0 arguments if (supplied_args_count == 1u && params.Length() > 1u && - kKW_CloseParenthesis == params[1]) + kKW_CloseParenthesis == params[1u]) { supplied_args_count = 0u; } @@ -3174,7 +3123,7 @@ void AGS::Parser::AccessData_ProcessCurrentArrayIndex(size_t const idx, size_t c { // Get the index size_t const index_start = expression.GetCursor(); - SkipTo(SymbolList{ kKW_Comma, kKW_CloseBracket }, expression); + expression.SkipTo(SymbolList{ kKW_Comma, kKW_CloseBracket }); size_t const index_end = expression.GetCursor(); SrcList current_index = SrcList(expression, index_start, index_end - index_start); if (0u == current_index.Length()) @@ -3249,8 +3198,6 @@ void AGS::Parser::AccessData_ProcessCurrentArrayIndex(size_t const idx, size_t c _reg_track.SetRegister(SREG_MAR); } -// We're processing some struct component or global or local variable. -// If an array index follows, parse it and shorten symlist accordingly void AGS::Parser::AccessData_ProcessArrayIndexIfThere(SrcList &expression, EvaluationResult &eres) { if (kKW_OpenBracket != expression.PeekNext()) @@ -3290,7 +3237,7 @@ void AGS::Parser::AccessData_ProcessArrayIndexIfThere(SrcList &expression, Evalu eres.SideEffects = true; Symbol divider = expression.PeekNext(); Expect(SymbolList{ kKW_CloseBracket, kKW_Comma }, divider); - + if (kKW_CloseBracket == divider) { expression.GetNext(); // Eat ']' @@ -3584,7 +3531,7 @@ AGS::Symbol AGS::Parser::FindComponentInStruct(Vartype strct, Symbol unqualified bool AGS::Parser::AccessData_IsClauseLast(SrcList &expression) { size_t const cursor = expression.GetCursor(); - SkipTo(kKW_Dot, expression); + expression.SkipTo(kKW_Dot); bool is_last = (kKW_Dot != expression.PeekNext()); expression.SetCursor(cursor); return is_last; @@ -4060,7 +4007,7 @@ void AGS::Parser::ParseStruct_ConstantDefn(Symbol const name_of_struct) { if (PP::kMain != _pp) { - SkipTo(kKW_Semicolon, _src); + _src.SkipTo(kKW_Semicolon); Expect(kKW_Semicolon, _src.GetNext()); return; } @@ -4901,7 +4848,7 @@ void AGS::Parser::ParseArray(Symbol vname, Vartype &vartype) // Skip the sequence of [...] while (true) { - SkipToClose(kKW_CloseBracket); + _src.SkipToCloser(); if (kKW_OpenBracket != _src.PeekNext()) return; _src.GetNext(); // Eat '[' @@ -4934,7 +4881,7 @@ void AGS::Parser::ParseArray(Symbol vname, Vartype &vartype) EvaluationResult eres; int const cursor = _src.GetCursor(); - SkipTo(kKW_Comma, _src); + _src.SkipTo(kKW_Comma); SrcList expression = SrcList(_src, cursor, _src.GetCursor() - cursor); expression.StartRead(); ParseIntegerExpression(expression, eres, msg); @@ -4967,7 +4914,7 @@ void AGS::Parser::ParseArray(Symbol vname, Vartype &vartype) void AGS::Parser::ParseStruct_VariableDefn(TypeQualifierSet tqs, Vartype vartype, Symbol name_of_struct, Symbol vname) { if (PP::kMain != _pp) - return SkipTo(SymbolList{ kKW_Comma, kKW_Semicolon }, _src); + return _src.SkipTo(SymbolList{ kKW_Comma, kKW_Semicolon }); if (_sym.IsDynarrayVartype(vartype)) // e.g., 'int [] zonk;', disallowed Expect(kKW_OpenParenthesis, _src.PeekNext()); @@ -5519,7 +5466,7 @@ void AGS::Parser::ParseExport() { if (PP::kPreAnalyze == _pp) { - SkipTo(kKW_Semicolon, _src); + _src.SkipTo(kKW_Semicolon); _src.GetNext(); // Eat ';' return; } @@ -5621,7 +5568,7 @@ void AGS::Parser::ParseVartype_VarDecl_PreAnalyze(Symbol var_name, ScopeType sco _givm[var_name] = (ScT::kGlobal == scope_type); // Apart from this, we aren't interested in var defns at this stage, so skip this defn - SkipTo(SymbolList{ kKW_Comma, kKW_Semicolon }, _src); + _src.SkipTo(SymbolList{ kKW_Comma, kKW_Semicolon }); } void AGS::Parser::ParseVartype_VariableDefn(TypeQualifierSet tqs, Vartype vartype, Symbol vname, ScopeType scope_type) @@ -6430,7 +6377,9 @@ void AGS::Parser::ParseCommand(Symbol leading_sym, Symbol &struct_of_current_fun if (PP::kPreAnalyze == _pp) { struct_of_current_func = name_of_current_func = kKW_NoSymbol; - return SkipToClose(kKW_CloseBrace); + _src.SkipToCloser(); + _src.GetNext(); // Eat ']' + return; } return ParseOpenBrace(struct_of_current_func, name_of_current_func); diff --git a/Compiler/script2/cs_parser.h b/Compiler/script2/cs_parser.h index 3eb22a320b0..e4888e65808 100644 --- a/Compiler/script2/cs_parser.h +++ b/Compiler/script2/cs_parser.h @@ -392,20 +392,6 @@ class Parser // Combine the arguments to stname::component, get the symbol for that Symbol MangleStructAndComponent(Symbol stname, Symbol component); - // Skim through 'source', ignoring delimited content completely. Stop in the following cases: - // . A symbol is encountered whose type is in 'stoplist[]' - // . A closing symbol is encountered that hasn't been opened. - // Don't consume the symbol that stops the scan. - void SkipTo(SymbolList const &stoplist, SrcList &source); - // Skim through 'source'. Stop when 'stopsym' is encountered or a closing symbol - // that hasn't been opened. Don't consume the symbol that stops the scan. - inline void SkipTo(Symbol stopsym, SrcList &source) { SkipTo(SymbolList{ stopsym }, source); } - - // Skim through source, ignoring delimited content completely. - // Stop when a closing symbol is encountered that hasn't been opened. - // Eat that symbol and if it isn't 'closer', report an _internal_ error. - void SkipToClose(Predefined closer); - // If the symbol 'actual' isn't in the list 'expected', give an error. // 'custom_msg', if given, replaces the "Expected ..." part of the message void Expect(SymbolList const &expected, Symbol actual, std::string const &custom_msg = ""); @@ -690,7 +676,7 @@ class Parser // We're processing some struct component or global or local variable. // If a sequence of array indexes follows, parse it and shorten symlist accordingly void AccessData_ProcessArrayIndexIfThere(SrcList &expression, EvaluationResult &eres); - + void AccessData_Variable(VariableAccess access_type, SrcList &expression, EvaluationResult &eres); void AGS::Parser::AccessData_This(EvaluationResult &eres); From 59e5c9e9d3cb67bd8cb63264744440ae007d4a3a Mon Sep 17 00:00:00 2001 From: "Peter G Bouillon [fernewelten]" Date: Thu, 2 Nov 2023 14:23:12 +0100 Subject: [PATCH 3/6] New compiler: Introduce 'SkipNextSymbol()' It is very common that the compiler knows what symbol should be coming and then skips over it blindly, using 'GetNext()'. Modify these places to call the new function 'SkipNextSymbol()' instead. It takes a symbol that is expected and makes sure that it actually comes next. --- Compiler/script2/cs_parser.cpp | 158 +++++++++++++++++++-------------- Compiler/script2/cs_parser.h | 2 + 2 files changed, 92 insertions(+), 68 deletions(-) diff --git a/Compiler/script2/cs_parser.cpp b/Compiler/script2/cs_parser.cpp index 826d8f63b55..de8f3d27ad2 100644 --- a/Compiler/script2/cs_parser.cpp +++ b/Compiler/script2/cs_parser.cpp @@ -229,6 +229,17 @@ AGS::Symbol AGS::Parser::MangleStructAndComponent(Symbol stname, Symbol componen return _sym.FindOrAdd(fullname_str); } +void AGS::Parser::SkipNextSymbol(SrcList src, Symbol expected) +{ + Symbol act = _src.GetNext(); + if (act != expected) + InternalError( + "Expected to skip over '%s', found '%s' instead", + _sym.GetName(expected).c_str(), + _sym.GetName(act).c_str()); +} + + void AGS::Parser::Expect(SymbolList const &expected, Symbol actual, std::string const &custom_msg) { for (size_t expected_idx = 0; expected_idx < expected.size(); expected_idx++) @@ -598,6 +609,16 @@ void AGS::Parser::FreeDynpointersOfStdArray(Symbol the_array) FreeDynpointersOfStdArrayOfStruct(element_vartype, elements_count); } +// Note: Currently, the structs/arrays that are pointed to cannot contain +// pointers in their turn. +// If they do, we need a solution at runtime to chase the pointers to release; +// we cannot do it at compile time. Also, the pointers might form "rings" +// (e.g., A contains a field that points to B; B contains a field that +// points to A), so we cannot rely on reference counting for identifying +// _all_ the unreachable memory chunks. (If nothing else points to A or B, +// both are unreachable so _could_ be released, but they still point to each +// other and so have a reference count of 1; the reference count will never reach 0). + void AGS::Parser::FreeDynpointersOfLocals(size_t from_level) { for (size_t level = from_level; level <= _nest.TopLevel(); level++) @@ -815,7 +836,7 @@ Symbol AGS::Parser::ParseParamlist_Param_DefaultValue(size_t idx, Vartype const std::string msg = "In parameter #: "; msg.replace(msg.find(""), 5u, std::to_string(idx)); - _src.GetNext(); // Eat '=' + SkipNextSymbol(_src, kKW_Assign); Symbol const default_symbol = ParseConstantExpression(_src, msg); @@ -882,7 +903,7 @@ void AGS::Parser::ParseDynArrayMarkerIfPresent(Vartype &vartype) { if (kKW_OpenBracket != _src.PeekNext()) return; - _src.GetNext(); // Eat '[' + SkipNextSymbol(_src, kKW_OpenBracket); Expect(kKW_CloseBracket, _src.GetNext()); vartype = _sym.VartypeWith(VTT::kDynarray, vartype); } @@ -909,7 +930,7 @@ void AGS::Parser::ParseFuncdecl_ExtenderPreparations(bool is_static_extender, Sy { if (is_static_extender) UserError("Unexpected '*' after 'static' in static extender function"); - _src.GetNext(); // Eat '*' + SkipNextSymbol(_src, kKW_Dynpointer); } // If a function is defined with the Extender mechanism, it needn't have a declaration @@ -924,7 +945,7 @@ void AGS::Parser::ParseFuncdecl_ExtenderPreparations(bool is_static_extender, Sy Symbol const punctuation = _src.PeekNext(); Expect(SymbolList{ kKW_Comma, kKW_CloseParenthesis }, punctuation); if (kKW_Comma == punctuation) - _src.GetNext(); // Eat ',' + SkipNextSymbol(_src, kKW_Comma); unqualified_name = qualified_name; } @@ -941,7 +962,7 @@ void AGS::Parser::ParseVarname0(bool accept_member_access, Symbol &structname, S if (kKW_ScopeRes != _src.PeekNext()) return; - _src.GetNext(); // Eat '::' + SkipNextSymbol(_src, kKW_ScopeRes); // if (!accept_member_access) UserError("May not use '::' here"); @@ -1084,17 +1105,17 @@ void AGS::Parser::ParseFuncdecl_Paramlist(Symbol funcsym, bool body_follows) Symbol const leading_sym = _src.PeekNext(); if (param_idx == 0 && kKW_Void == leading_sym) { - _src.GetNext(); // Eat 'void' + SkipNextSymbol(_src, kKW_Void); return Expect(kKW_CloseParenthesis, _src.GetNext()); } if (kKW_CloseParenthesis == leading_sym) - return (void) _src.GetNext(); // Eat ')' + return SkipNextSymbol(_src, kKW_CloseParenthesis); if (kKW_DotDotDot == leading_sym) { _sym[funcsym].FunctionD->IsVariadic = true; - _src.GetNext(); // Eat '...' + SkipNextSymbol(_src, kKW_DotDotDot); return Expect(kKW_CloseParenthesis, _src.GetNext(), "Expected ')' following the '...'"); } @@ -1288,7 +1309,7 @@ bool AGS::Parser::ParseFuncdecl_DoesBodyFollow() { int const cursor = _src.GetCursor(); _src.SkipToCloser(); - _src.GetNext(); // Eat ')' + SkipNextSymbol(_src, kKW_CloseParenthesis); bool body_follows = (kKW_OpenBrace == _src.PeekNext()); _src.SetCursor(cursor); return body_follows; @@ -1327,7 +1348,7 @@ void AGS::Parser::ParseFuncdecl_Checks(TypeQualifierSet tqs, Symbol struct_of_fu { // Functions only become struct components if they are declared in a struct or as extender std::string component = _sym.GetName(name_of_func); - component.erase(0, component.rfind(':') + 1u); + component.erase(0u, component.rfind(':') + 1u); UserError( ReferenceMsgSym("Function '%s' has not been declared within struct '%s' as a component", struct_of_func).c_str(), component.c_str(), _sym.GetName(struct_of_func).c_str()); @@ -1891,7 +1912,7 @@ void AGS::Parser::ParseExpression_New(SrcList &expression, EvaluationResult &ere Warning("'()' after 'new' isn't implemented, is currently ignored"); expression.GetNext(); expression.SkipToCloser(); - expression.GetNext(); + SkipNextSymbol(_src, kKW_CloseParenthesis); } // Only do this check for new, not for new[]. @@ -2495,7 +2516,7 @@ void AGS::Parser::ParseExpression_InParens(SrcList &expression, EvaluationResult // Check for spurious symbols after the closing paren. expression.SetCursor(1u); expression.SkipToCloser(); - expression.GetNext(); // Eat the closing parenthesis + SkipNextSymbol(expression, kKW_CloseParenthesis); ParseExpression_CheckUsedUp(expression); StripOutermostParens(expression); @@ -2620,8 +2641,8 @@ void AGS::Parser::AccessData_FunctionCall_PushParams(SrcList ¶ms, size_t clo WriteCmd(SCMD_CHECKNULLREG, SREG_AX); std::string msg = "Parameter # of call to function "; - msg.replace(msg.find(""), 5, std::to_string(param_count)); - msg.replace(msg.find(""), 6, _sym.GetName(funcSymbol)); + msg.replace(msg.find(""), 5u, std::to_string(param_count)); + msg.replace(msg.find(""), 6u, _sym.GetName(funcSymbol)); CheckVartypeMismatch(eres.Vartype, param_vartype, true, msg); } @@ -2677,7 +2698,7 @@ void AGS::Parser::AccessData_FunctionCall_CountAndCheckParm(SrcList ¶ms,Symb if (found_param_symbol) continue; - UserError("Argument %d in function call is empty", supplied_args_count - 1u); + UserError("Argument %u in function call is empty", supplied_args_count - 1u); } found_param_symbol = true; } @@ -2806,9 +2827,9 @@ void AGS::Parser::AccessData_PushFunctionCallParams(Symbol name_of_func, bool fu if (supplied_args_count > func_args_count && !_sym.IsVariadicFunc(name_of_func)) UserError( - (1 == func_args_count) ? - "Expected just %d parameter but found %d" : - "Expected just %d parameters but found %d", + (1u == func_args_count) ? + "Expected just %u parameter but found %u" : + "Expected just %u parameters but found %u", func_args_count, supplied_args_count); @@ -3198,11 +3219,13 @@ void AGS::Parser::AccessData_ProcessCurrentArrayIndex(size_t const idx, size_t c _reg_track.SetRegister(SREG_MAR); } +// We're processing some struct component or global or local variable. +// If an array index follows, parse it and shorten symlist accordingly void AGS::Parser::AccessData_ProcessArrayIndexIfThere(SrcList &expression, EvaluationResult &eres) { if (kKW_OpenBracket != expression.PeekNext()) return; - expression.GetNext(); // Eat '[' + SkipNextSymbol(expression, kKW_OpenBracket); bool const is_dynarray = _sym.IsDynarrayVartype(eres.Vartype); bool const is_array = _sym.IsArrayVartype(eres.Vartype); @@ -3240,18 +3263,18 @@ void AGS::Parser::AccessData_ProcessArrayIndexIfThere(SrcList &expression, Evalu if (kKW_CloseBracket == divider) { - expression.GetNext(); // Eat ']' + SkipNextSymbol(expression, kKW_CloseBracket); divider = expression.PeekNext(); } if (kKW_Comma == divider || kKW_OpenBracket == divider) { if (dims_count == dim_idx + 1u) - UserError("Expected %d indexes, found more", dims_count); + UserError("Expected %u indexes, found more", dims_count); expression.GetNext(); // Eat ',' or '[' continue; } if (dims_count != dim_idx + 1u) - UserError("Expected %d indexes, but only found %d", dims_count, dim_idx + 1u); + UserError("Expected %u indexes, but only found %d", dims_count, dim_idx + 1u); } } @@ -3314,9 +3337,8 @@ void AGS::Parser::AccessData_FirstClause(VariableAccess access_type, SrcList &ex if (VAC::kReading != access_type) UserError("Cannot modify '%s'", _sym.GetName(first_sym).c_str()); - expression.GetNext(); + SkipNextSymbol(expression, first_sym); Symbol lit = first_sym; - expression.GetNext(); // eat the literal while (_sym.IsConstant(lit)) lit = _sym[lit].ConstantD->ValueSym; SetCompileTimeLiteral(lit, eres); @@ -3325,7 +3347,7 @@ void AGS::Parser::AccessData_FirstClause(VariableAccess access_type, SrcList &ex if (_sym.IsFunction(first_sym)) { - expression.GetNext(); // Eat function symbol + SkipNextSymbol(expression, first_sym); if (kKW_OpenParenthesis != expression.PeekNext()) { // Return the function symbol as-is @@ -3348,13 +3370,13 @@ void AGS::Parser::AccessData_FirstClause(VariableAccess access_type, SrcList &ex if (kKW_NoSymbol == _sym.GetVartype(kKW_This)) UserError("'this' is only legal in a non-static struct function"); - expression.GetNext(); // Eat 'this' + SkipNextSymbol(expression, kKW_This); AccessData_This(eres); if (kKW_Dot == expression.PeekNext()) { - expression.GetNext(); // Eat '.' + SkipNextSymbol(expression, kKW_Dot); // Going forward, we must "imply" "this." since we've just gobbled it. implied_this_dot = true; } @@ -3369,7 +3391,7 @@ void AGS::Parser::AccessData_FirstClause(VariableAccess access_type, SrcList &ex if (_sym.IsStructVartype(first_sym)) { - expression.GetNext(); // Eat the struct vartype + SkipNextSymbol(expression, first_sym); // Return the struct itself, static access eres.Type = eres.kTY_StructName; eres.Location = eres.kLOC_SymbolTable; @@ -3453,7 +3475,7 @@ void AGS::Parser::AccessData_SubsequentClause(VariableAccess access_type, bool a if (_sym.IsConstant(qualified_component)) { - expression.GetNext(); // Eat the constant symbol + SkipNextSymbol(expression, unqualified_component); // Eat the constant symbol eres.Type = eres.kTY_Literal; eres.Location = eres.kLOC_SymbolTable; @@ -3468,7 +3490,7 @@ void AGS::Parser::AccessData_SubsequentClause(VariableAccess access_type, bool a if (static_access && !_sym[qualified_component].FunctionD->TypeQualifiers[TQ::kStatic]) UserError("Must specify a specific object for non-static function %s", _sym.GetName(qualified_component).c_str()); - expression.GetNext(); // Eat function symbol + SkipNextSymbol(expression, unqualified_component); // Eat function symbol if (kKW_OpenParenthesis != expression.PeekNext()) { // Return the function symbol as-is @@ -3561,7 +3583,7 @@ void AGS::Parser::AccessData(VariableAccess access_type, SrcList &expression, Ev while (kKW_Dot == expression.PeekNext() || implied_this_dot) { if (!implied_this_dot) - expression.GetNext(); // Eat '.' + SkipNextSymbol(expression, kKW_Dot); // Note: do not reset "implied_this_dot" here, it's still needed. // Here, if EvaluationResult::kLOC_MemoryAtMAR == eres.location then the first byte of outer is at m[MAR + mar_offset]. @@ -3763,24 +3785,24 @@ void AGS::Parser::SkipToEndOfExpression(SrcList &src) if (--tern_depth < 0) break; - src.GetNext(); // Eat ':' + SkipNextSymbol(src, kKW_Colon); continue; } if (kKW_Dot == peeksym) { - src.GetNext(); // Eat '.' + SkipNextSymbol(src, kKW_Dot); src.GetNext(); // Eat following symbol continue; } if (kKW_New == peeksym) { // Only allowed if a type follows - src.GetNext(); // Eat 'new' + SkipNextSymbol(src, kKW_New); Symbol const sym_after_new = src.PeekNext(); if (_sym.IsVartype(sym_after_new)) { - src.GetNext(); // Eat symbol after 'new' + SkipNextSymbol(src, sym_after_new); continue; } src.BackUp(); // spit out 'new' @@ -3789,20 +3811,20 @@ void AGS::Parser::SkipToEndOfExpression(SrcList &src) if (kKW_Null == peeksym) { // Allowed. - src.GetNext(); // Eat 'null' + SkipNextSymbol(src, kKW_Null); continue; } if (kKW_Tern == peeksym) { tern_depth++; - src.GetNext(); // Eat '?' + SkipNextSymbol(src, kKW_Tern); continue; } if (_sym.IsVartype(peeksym)) { // Only allowed if a dot follows - src.GetNext(); // Eat the vartype + SkipNextSymbol(src, peeksym); // Eat the vartype Symbol const nextsym = src.PeekNext(); if (kKW_Dot == nextsym) continue; // Do not eat the dot. @@ -3814,13 +3836,13 @@ void AGS::Parser::SkipToEndOfExpression(SrcList &src) if (kKW_NoSymbol != vartype_of_this && 0u < _sym[vartype_of_this].VartypeD->Components.count(peeksym)) { - src.GetNext(); // Eat the peeked symbol + SkipNextSymbol(src, peeksym); continue; } if (!_sym.CanBePartOfAnExpression(peeksym)) break; - src.GetNext(); // Eat the peeked symbol + SkipNextSymbol(src, peeksym); } if (nesting_depth > 0) @@ -3870,7 +3892,7 @@ void AGS::Parser::ParseAssignment_ReadLHSForModification(SrcList &expression, Ev // "var = expression"; lhs is the variable void AGS::Parser::ParseAssignment_Assign(SrcList &lhs) { - _src.GetNext(); // Eat '=' + SkipNextSymbol(_src, kKW_Assign); EvaluationResult eres; ParseExpression(_src, eres); // RHS of the assignment @@ -3880,7 +3902,7 @@ void AGS::Parser::ParseAssignment_Assign(SrcList &lhs) // We compile something like "var += expression" void AGS::Parser::ParseAssignment_MAssign(Symbol const ass_symbol, SrcList &lhs) { - _src.GetNext(); // Eat assignment symbol + SkipNextSymbol(_src, ass_symbol); // Parse RHS EvaluationResult rhs_eres; @@ -3971,7 +3993,7 @@ void AGS::Parser::ParseVardecl_InitialValAssignment_OldString(std::vector void AGS::Parser::ParseVardecl_InitialValAssignment(Symbol varname, std::vector &initial_val) { - _src.GetNext(); // Eat '=' + SkipNextSymbol(_src, kKW_Assign); Vartype const vartype = _sym.GetVartype(varname); if (_sym.IsManagedVartype(vartype)) @@ -4220,7 +4242,7 @@ void AGS::Parser::ParseVardecl_Local(Symbol var_name, Vartype vartype) } // "readonly" vars cannot be assigned to, so don't use standard assignment function here. - _src.GetNext(); // Eat '=' + SkipNextSymbol(_src, kKW_Assign); EvaluationResult rhs_eres; ParseExpression(_src, rhs_eres); EvaluationResultToAx(rhs_eres); @@ -4507,7 +4529,7 @@ void AGS::Parser::ParseStruct_SetTypeInSymboltable(Symbol stname, TypeQualifierS // We have accepted something like "struct foo" and are waiting for "extends" void AGS::Parser::ParseStruct_ExtendsClause(Symbol stname) { - _src.GetNext(); // Eat "extends" + SkipNextSymbol(_src, kKW_Extends); Symbol const parent = _src.GetNext(); // name of the extended struct if (PP::kPreAnalyze == _pp) @@ -4623,7 +4645,7 @@ void AGS::Parser::ParseStruct_FuncDecl(Symbol struct_of_func, Symbol name_of_fun if (tqs[TQ::kWriteprotected]) UserError("Cannot apply 'writeprotected' to this function declaration"); - _src.GetNext(); // Eat '(' + SkipNextSymbol(_src, kKW_OpenParenthesis); ParseFuncdecl(tqs, vartype, struct_of_func, name_of_func, false, false); // Can't code a body behind the function, so the next symbol must be ';' @@ -4637,7 +4659,7 @@ void AGS::Parser::ParseStruct_Attribute_CheckFunc(Symbol name_of_func, bool is_s if (params_wanted_count != _sym.FuncParamsCount(name_of_func)) { std::string const msg = ReferenceMsgSym( - "The attribute function '%s' should have %d parameter(s) but is declared with %d parameter(s) instead", + "The attribute function '%s' should have %u parameter(s) but is declared with %u parameter(s) instead", name_of_func); UserError(msg.c_str(), entry.Name.c_str(), params_wanted_count, _sym.FuncParamsCount(name_of_func)); } @@ -4806,7 +4828,7 @@ void AGS::Parser::ParseStruct_Attribute(TypeQualifierSet tqs, Symbol const name_ bool const is_const_vartype = kKW_Const == _src.PeekNext(); if (is_const_vartype) - _src.GetNext(); // Eat 'const' + SkipNextSymbol(_src, kKW_Const); Vartype vartype = _src.GetNext(); @@ -4825,7 +4847,7 @@ void AGS::Parser::ParseStruct_Attribute(TypeQualifierSet tqs, Symbol const name_ bool const is_indexed = (kKW_OpenBracket == _src.PeekNext()); if (is_indexed) { - _src.GetNext(); // Eat '[' + SkipNextSymbol(_src, kKW_OpenBracket); Expect(kKW_CloseBracket, _src.GetNext()); } @@ -4841,7 +4863,7 @@ void AGS::Parser::ParseStruct_Attribute(TypeQualifierSet tqs, Symbol const name_ // We're parsing an array var. void AGS::Parser::ParseArray(Symbol vname, Vartype &vartype) { - _src.GetNext(); // Eat '[' + SkipNextSymbol(_src, kKW_OpenBracket); if (PP::kPreAnalyze == _pp) { @@ -4851,14 +4873,14 @@ void AGS::Parser::ParseArray(Symbol vname, Vartype &vartype) _src.SkipToCloser(); if (kKW_OpenBracket != _src.PeekNext()) return; - _src.GetNext(); // Eat '[' + SkipNextSymbol(_src, kKW_OpenBracket); } } if (kKW_CloseBracket == _src.PeekNext()) { // Dynamic array - _src.GetNext(); // Eat ']' + SkipNextSymbol(_src, kKW_CloseBracket); if (vartype == kKW_String) UserError("Dynamic arrays of old-style strings are not supported"); if (!_sym.IsAnyIntegerVartype(vartype) && !_sym.IsManagedVartype(vartype) && kKW_Float != vartype) @@ -4906,7 +4928,7 @@ void AGS::Parser::ParseArray(Symbol vname, Vartype &vartype) continue; if (kKW_OpenBracket != _src.PeekNext()) break; - _src.GetNext(); // Eat '[' + SkipNextSymbol(_src, kKW_OpenBracket); } vartype = _sym.VartypeWithArray(dims, vartype); } @@ -5000,7 +5022,7 @@ void AGS::Parser::EatDynpointerSymbolIfPresent(Vartype vartype) if (PP::kPreAnalyze == _pp || _sym.IsManagedVartype(vartype)) { - _src.GetNext(); // Eat '*' + SkipNextSymbol(_src, kKW_Dynpointer); return; } @@ -5099,7 +5121,7 @@ void AGS::Parser::ParseStruct(TypeQualifierSet tqs, Symbol &struct_of_current_fu { if (!tqs[TQ::kManaged]) UserError("Forward-declared 'struct's must be 'managed'"); - _src.GetNext(); // Eat ';' + SkipNextSymbol(_src, kKW_Semicolon); return; } @@ -5147,7 +5169,7 @@ void AGS::Parser::ParseStruct(TypeQualifierSet tqs, Symbol &struct_of_current_fu struct_size += STRUCT_ALIGNTO - (struct_size % STRUCT_ALIGNTO); } - _src.GetNext(); // Eat '}' + SkipNextSymbol(_src, kKW_CloseBrace); // Struct has now been completely defined _sym[stname].VartypeD->Flags[VTF::kUndefined] = false; @@ -5164,7 +5186,7 @@ void AGS::Parser::ParseStruct(TypeQualifierSet tqs, Symbol &struct_of_current_fu _src.SetCursor(start_of_struct_decl); UserError("'readonly' can only be used in a variable declaration"); } - _src.GetNext(); // Eat ';' + SkipNextSymbol(_src, kKW_Semicolon); return; } @@ -5202,7 +5224,7 @@ void AGS::Parser::ParseStruct(TypeQualifierSet tqs, Symbol &struct_of_current_fu // We've accepted something like "enum foo { bar"; '=' follows void AGS::Parser::ParseEnum_AssignedValue(Symbol vname, CodeCell &value) { - _src.GetNext(); // eat "=" + SkipNextSymbol(_src, kKW_Assign); std::string msg = "In the assignment to : "; msg.replace(msg.find(""), 6u, _sym.GetName(vname)); @@ -5249,7 +5271,7 @@ AGS::Symbol AGS::Parser::ParseVartype(bool const with_dynpointer_handling) { bool const leading_const = (kKW_Const == _src.PeekNext()); if (leading_const) - _src.GetNext(); // Eat 'const' + SkipNextSymbol(_src, kKW_Const); Vartype vartype = _src.GetNext(); if (!_sym.IsVartype(vartype)) @@ -5335,7 +5357,7 @@ void AGS::Parser::ParseEnum(TypeQualifierSet tqs, Symbol &struct_of_current_func Symbol const nextsym = _src.PeekNext(); if (kKW_Semicolon == nextsym) { - _src.GetNext(); // Eat ';' + SkipNextSymbol(_src, kKW_Semicolon); if (tqs[TQ::kReadonly]) { // Only now do we find out that there isn't any following declaration @@ -5390,7 +5412,7 @@ void AGS::Parser::ParseAttribute(TypeQualifierSet tqs, Symbol const name_of_curr bool const is_indexed = (kKW_OpenBracket == _src.PeekNext()); if (is_indexed) { - _src.GetNext(); // Eat '[' + SkipNextSymbol(_src, kKW_OpenBracket); Expect(kKW_CloseBracket, _src.GetNext()); } @@ -5408,7 +5430,7 @@ void AGS::Parser::ParseAttribute(TypeQualifierSet tqs, Symbol const name_of_curr ReferenceMsgSym("Cannot use 'this' with the unmanaged struct '%s'", name_of_struct).c_str(), _sym.GetName(name_of_struct).c_str()); if (kKW_Dynpointer == _src.PeekNext()) - _src.GetNext(); // Eat optional '*' + SkipNextSymbol(_src, kKW_Dynpointer); // Optional here } Expect(kKW_CloseParenthesis, _src.GetNext()); @@ -5467,7 +5489,7 @@ void AGS::Parser::ParseExport() if (PP::kPreAnalyze == _pp) { _src.SkipTo(kKW_Semicolon); - _src.GetNext(); // Eat ';' + SkipNextSymbol(_src, kKW_Semicolon); return; } @@ -5520,7 +5542,7 @@ void AGS::Parser::ParseVartype_CheckIllegalCombis(bool is_function,TypeQualifier void AGS::Parser::ParseVartype_FuncDecl(TypeQualifierSet tqs, Vartype vartype, Symbol struct_name, Symbol func_name, bool no_loop_check, Symbol &struct_of_current_func, Symbol &name_of_current_func, bool &body_follows) { size_t const declaration_start = _src.GetCursor(); - _src.GetNext(); // Eat '(' + SkipNextSymbol(_src, kKW_OpenParenthesis); bool const func_is_static_extender = (kKW_Static == _src.PeekNext()); bool const func_is_extender = func_is_static_extender || (kKW_This == _src.PeekNext()); @@ -5814,7 +5836,7 @@ void AGS::Parser::HandleEndOfIf(bool &else_follows) } else_follows = true; - _src.GetNext(); // Eat "else" + SkipNextSymbol(_src, kKW_Else); _nest.BranchJumpOutLevel() = _nest.JumpOutLevel(); _nest.JumpOutLevel() = _nest.kNoJumpOut; @@ -5958,7 +5980,7 @@ void AGS::Parser::ParseFor_InitClauseVardecl() Symbol const punctuation = _src.PeekNext(); Expect(SymbolList{ kKW_Comma, kKW_Semicolon }, punctuation); if (kKW_Comma == punctuation) - _src.GetNext(); // Eat ',' + SkipNextSymbol(_src, kKW_Comma); if (kKW_Semicolon == punctuation) return; } @@ -6095,7 +6117,7 @@ void AGS::Parser::ParseSwitch() // A switch without any clauses, tantamount to a NOP // Don't throw away the code that was generated for the switch expression: // It might have side effects! - _src.GetNext(); // Eat '}' + SkipNextSymbol(_src, kKW_CloseBrace); return; } @@ -6378,7 +6400,7 @@ void AGS::Parser::ParseCommand(Symbol leading_sym, Symbol &struct_of_current_fun { struct_of_current_func = name_of_current_func = kKW_NoSymbol; _src.SkipToCloser(); - _src.GetNext(); // Eat ']' + SkipNextSymbol(_src, kKW_CloseBrace); return; } return ParseOpenBrace(struct_of_current_func, name_of_current_func); diff --git a/Compiler/script2/cs_parser.h b/Compiler/script2/cs_parser.h index e4888e65808..dc14396c92a 100644 --- a/Compiler/script2/cs_parser.h +++ b/Compiler/script2/cs_parser.h @@ -392,6 +392,8 @@ class Parser // Combine the arguments to stname::component, get the symbol for that Symbol MangleStructAndComponent(Symbol stname, Symbol component); + // Skip over the next symbol in 'src' which _must_ be 'expected', or else an internal (!) error is given. + void SkipNextSymbol(SrcList src, Symbol sym); // If the symbol 'actual' isn't in the list 'expected', give an error. // 'custom_msg', if given, replaces the "Expected ..." part of the message void Expect(SymbolList const &expected, Symbol actual, std::string const &custom_msg = ""); From b54b7b2fc259e1e823abbf01cd64a08536159d16 Mon Sep 17 00:00:00 2001 From: "Peter G Bouillon [fernewelten]" Date: Thu, 2 Nov 2023 12:35:21 +0100 Subject: [PATCH 4/6] New compiler: Modify 'VartypeWithArray()', split up and modify 'VartypeWith()' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modify 'VartypeWithArray()' to make it possible to join adjacent classic array specifiers '[2, 3][5]' → '[2, 3, 5]' Replace 'VartypeWith()' by the functions - 'VartypeWithConst()' - 'VartypeWithDynarray()' - 'VartypeWithDynpointer()' The semantics of the respected cases has become that different that it doesn't make sense to have a common ‘umbrella’ function for them all. --- Compiler/script2/cc_symboltable.cpp | 150 +++++++++++++++++-------- Compiler/script2/cc_symboltable.h | 8 +- Compiler/script2/cs_parser.cpp | 65 +++++------ Compiler/script2/cs_parser.h | 15 ++- Compiler/script2/cs_scanner.cpp | 2 +- Compiler/test2/cc_bytecode_test_0.cpp | 13 +-- Compiler/test2/cc_bytecode_test_1.cpp | 10 +- Compiler/test2/cc_scanner_test.cpp | 2 +- Compiler/test2/cc_symboltable_test.cpp | 10 +- 9 files changed, 168 insertions(+), 107 deletions(-) diff --git a/Compiler/script2/cc_symboltable.cpp b/Compiler/script2/cc_symboltable.cpp index 3cc3723108a..b95e9b7e23d 100644 --- a/Compiler/script2/cc_symboltable.cpp +++ b/Compiler/script2/cc_symboltable.cpp @@ -374,7 +374,7 @@ AGS::SymbolTable::SymbolTable() entries[float_zero_sym].LiteralD->Value = 0; entries[float_zero_sym].LiteralD->Vartype = kKW_Float; } - _lastAllocated = VartypeWith(VTT::kConst, kKW_String); + _lastAllocated = VartypeWithConst(kKW_String); } bool AGS::SymbolTable::IsVTT(Symbol s, VartypeType vtt) const @@ -406,7 +406,7 @@ bool AGS::SymbolTable::IsVTF(Symbol s, VartypeFlag flag) const void AGS::SymbolTable::SetStringStructSym(Symbol const s) { _stringStructSym = s; - _stringStructPtrSym = VartypeWith(VTT::kDynpointer, s); + _stringStructPtrSym = VartypeWithDynpointer(s); } bool AGS::SymbolTable::IsInUse(Symbol s) const @@ -545,67 +545,125 @@ std::string const AGS::SymbolTable::GetName(AGS::Symbol symbl) const AGS::Vartype AGS::SymbolTable::VartypeWithArray(std::vector const &dims, AGS::Vartype vartype) { - // Can't have classic arrays of classic arrays - if (IsVTT(vartype, VTT::kArray)) - return vartype; + bool const is_array_of_array = IsVTT(vartype, VTT::kArray); - std::string conv_name = entries[vartype].Name + "["; - size_t const last_idx = dims.size() - 1u; - size_t elements_count = 1u; - for (size_t dims_idx = 0u; dims_idx <= last_idx; ++dims_idx) + std::vector aoa_dims; + if (is_array_of_array) { - elements_count *= dims[dims_idx]; - conv_name += std::to_string(dims[dims_idx]); - conv_name += (dims_idx == last_idx) ? "]" : ", "; + // Classic array of classic array: Make the vartype of one joint array of them both + std::vector const &old_dims = entries[vartype].VartypeD->Dims; + aoa_dims.reserve(old_dims.size() + dims.size()); + aoa_dims = old_dims; + aoa_dims.insert(aoa_dims.end(), dims.begin(), dims.end()); + } + std::vector const &dims_to_use = is_array_of_array ? aoa_dims : dims; + + std::string vartype_name = entries[vartype].Name + "["; + size_t element_count = 1u; + for (auto it = dims_to_use.cbegin(); it != dims_to_use.cend(); ++it) + { + element_count *= *it; + vartype_name += std::to_string(*it); + vartype_name += (it + 1 == dims_to_use.cend()) ? "]" : ", "; + } + Vartype const array_vartype = FindOrAdd(vartype_name); + if (!IsVartype(array_vartype)) + { + entries[array_vartype].VartypeD = new SymbolTableEntry::VartypeDesc; + entries[array_vartype].VartypeD->Type = VTT::kArray; + entries[array_vartype].VartypeD->BaseVartype = vartype; + entries[array_vartype].VartypeD->Size = element_count * GetSize(vartype); + entries[array_vartype].VartypeD->Dims = dims; } - Vartype const array_vartype = FindOrAdd(conv_name); - if (IsVartype(array_vartype)) - return array_vartype; - - entries[array_vartype].VartypeD = new SymbolTableEntry::VartypeDesc; - entries[array_vartype].VartypeD->Type = VTT::kArray; - entries[array_vartype].VartypeD->BaseVartype = vartype; - entries[array_vartype].VartypeD->Size = elements_count * GetSize(vartype); - entries[array_vartype].VartypeD->Dims = dims; return array_vartype; } -AGS::Vartype AGS::SymbolTable::VartypeWith(VartypeType vtt, AGS::Vartype vartype) +AGS::Vartype AGS::SymbolTable::VartypeWithConst(AGS::Vartype vartype) { + if (IsVTT(vartype, VTT::kConst)) + return vartype; // Nothing to be done + // Return cached result if existent - std::pair const arg = { vartype, vtt }; + std::pair const arg = { vartype, VTT::kConst }; Vartype &valref(_vartypesCache[arg]); if (valref) return valref; - if (IsVTT(vartype, vtt)) - return (valref = vartype); // Nothing to be done + std::string const new_typename = "const " + entries[vartype].Name; + Vartype new_type = valref = FindOrAdd(new_typename); + if (!IsVartype(new_type)) + { + // Initialise entry for new type + SymbolTableEntry &entry = entries[new_type]; + entry.VartypeD = new SymbolTableEntry::VartypeDesc; + entry.VartypeD->Type = VTT::kConst; + entry.VartypeD->BaseVartype = vartype; + entry.VartypeD->Size = GetSize(vartype); + } + return new_type; +} - std::string pre = ""; - std::string post = ""; - switch (vtt) +AGS::Vartype AGS::SymbolTable::VartypeWithDynarray(AGS::Vartype vartype) +{ + // Return cached result if it exists + std::pair const arg = { vartype, VTT::kDynarray }; + Vartype &valref(_vartypesCache[arg]); + if (valref) + return valref; + + // For the new typename, insert '[}' in front of the first bracket of the actual typename + std::string &act_typename = entries[vartype].Name; + size_t pos_of_first_openbracket = act_typename.find_first_of('['); + if (std::string::npos == pos_of_first_openbracket) + pos_of_first_openbracket = act_typename.length(); + std::string const pre = act_typename.substr(0u, pos_of_first_openbracket); + std::string const post = act_typename.substr(pos_of_first_openbracket); + std::string const new_typename = pre + "[]" + post; + + Vartype new_type = valref = FindOrAdd(new_typename); + if (!IsVartype(new_type)) { - default: pre = "QUAL" + std::to_string(static_cast(vtt)) + " "; break; - case VTT::kConst: pre = "const "; break; - // Note: Even if we have an autoptr and suppress the '*', we still need to add _something_ to the name. - // The name for the pointered type must be different from the name of the unpointered type. - // (If this turns out to be too ugly, then we need two fields for vartypes: - // one field that is output to the user, another field that is guaranteed to have different values - // for different vartypes.) - case VTT::kDynpointer: post = (IsVartype(vartype) && entries[vartype].VartypeD->Flags[VTF::kAutoptr]) ? " " : " *"; break; - case VTT::kDynarray: post = "[]"; break; + // Initialise entry for new type + SymbolTableEntry &entry = entries[new_type]; + entry.VartypeD = new SymbolTableEntry::VartypeDesc; + entry.VartypeD->Type = VTT::kDynarray; + entry.VartypeD->BaseVartype = vartype; + entry.VartypeD->Size = SIZE_OF_DYNPOINTER; } - std::string const conv_name = (pre + entries[vartype].Name) + post; - valref = FindOrAdd(conv_name); - if (IsVartype(valref)) + return new_type; +} + +AGS::Vartype AGS::SymbolTable::VartypeWithDynpointer(Vartype vartype) +{ + if (IsVTT(vartype, VTT::kDynpointer)) + return vartype; // Nothing to be done + + // Return cached result if it exists + std::pair const arg = { vartype, VTT::kDynpointer }; + Vartype &valref(_vartypesCache[arg]); + if (valref) return valref; - SymbolTableEntry &entry = entries[valref]; - entry.VartypeD = new SymbolTableEntry::VartypeDesc; - entry.VartypeD->Type = vtt; - entry.VartypeD->BaseVartype = vartype; - entry.VartypeD->Size = (VTT::kConst == vtt) ? GetSize(vartype) : SIZE_OF_DYNPOINTER; - return valref; + std::string post = " *"; + // Note: Different types must have different names. So even if we have an + // autoptr and suppress the '*', we still need to add _something_ to the new name. + // (If this turns out to be too ugly for users to see, then we need two + // fields per vartype: one field that is output to the user, another field for + // internal use that is guaranteed to have different values for different vartypes.) + if (IsVartype(vartype) && entries[vartype].VartypeD->Flags[VTF::kAutoptr]) + post = " "; + std::string const new_typename = entries[vartype].Name + post; + Vartype new_type = valref = FindOrAdd(new_typename); + if (!IsVartype(new_type)) + { + // Initialise entry for new type + SymbolTableEntry &entry = entries[new_type]; + entry.VartypeD = new SymbolTableEntry::VartypeDesc; + entry.VartypeD->Type = VTT::kDynpointer; + entry.VartypeD->BaseVartype = vartype; + entry.VartypeD->Size = SIZE_OF_DYNPOINTER; + } + return new_type; } AGS::Vartype AGS::SymbolTable::VartypeWithout(VartypeType const vtt, AGS::Vartype vartype) const diff --git a/Compiler/script2/cc_symboltable.h b/Compiler/script2/cc_symboltable.h index e91eb7ddb15..8db4d81a212 100644 --- a/Compiler/script2/cc_symboltable.h +++ b/Compiler/script2/cc_symboltable.h @@ -463,8 +463,12 @@ struct SymbolTable : public SymbolTableConstant // Arrays and variables that are arrays // The "Array[...] of vartype" vartype Vartype VartypeWithArray(std::vector const &dims, AGS::Vartype vartype); - // The "Dynarray / Dynpointer/ Const ... of vartype" vartype - Vartype VartypeWith(VartypeType vtt, Vartype vartype); + // The "Const of vartype" vartype + Vartype VartypeWithConst(AGS::Vartype vartype); + // The "Dynarray of vartype" vartype + Vartype VartypeWithDynarray(AGS::Vartype vartype); + // The "Const ... of vartype" vartype + Vartype VartypeWithDynpointer(Vartype vartype); // The vartype without the qualifier given in vtt Vartype VartypeWithout(VartypeType vtt, Vartype vartype) const; diff --git a/Compiler/script2/cs_parser.cpp b/Compiler/script2/cs_parser.cpp index de8f3d27ad2..6839e11efee 100644 --- a/Compiler/script2/cs_parser.cpp +++ b/Compiler/script2/cs_parser.cpp @@ -437,7 +437,7 @@ AGS::Parser::Parser(SrcList &src, FlagSet options, ccCompiledScript &scrip, Symb void AGS::Parser::SetDynpointerInManagedVartype(Vartype &vartype) { if (_sym.IsManagedVartype(vartype)) - vartype = _sym.VartypeWith(VTT::kDynpointer, vartype); + vartype = _sym.VartypeWithDynpointer(vartype); } size_t AGS::Parser::StacksizeOfLocals(size_t from_level) @@ -905,7 +905,7 @@ void AGS::Parser::ParseDynArrayMarkerIfPresent(Vartype &vartype) return; SkipNextSymbol(_src, kKW_OpenBracket); Expect(kKW_CloseBracket, _src.GetNext()); - vartype = _sym.VartypeWith(VTT::kDynarray, vartype); + vartype = _sym.VartypeWithDynarray(vartype); } // extender function, eg. function GoAway(this Character *someone) @@ -1750,7 +1750,7 @@ void AGS::Parser::ConvertAXStringToStringObject(Vartype wanted_vartype, Vartype _sym.GetStringStructSym() == _sym.VartypeWithout(VTT::kDynpointer, wanted_vartype)) { WriteCmd(SCMD_CREATESTRING, SREG_AX); // convert AX - current_vartype = _sym.VartypeWith(VTT::kDynpointer, _sym.GetStringStructSym()); + current_vartype = _sym.VartypeWithDynpointer(_sym.GetStringStructSym()); } } @@ -1788,7 +1788,7 @@ void AGS::Parser::HandleStructOrArrayResult(EvaluationResult &eres) // Interpret the memory address as the result // We don't have a way of saying, "MAR _is_ the value" // so we move the value to AX, we _can_ say "AX _is_ the value". - eres.Vartype = _sym.VartypeWith(VTT::kDynpointer, vartype); + eres.Vartype = _sym.VartypeWithDynpointer(vartype); _marMgr.UpdateMAR(_src.GetLineno(), _scrip); WriteCmd(SCMD_REGTOREG, SREG_MAR, SREG_AX); _reg_track.SetRegister(SREG_AX); @@ -1897,8 +1897,8 @@ void AGS::Parser::ParseExpression_New(SrcList &expression, EvaluationResult &ere EvaluationResultToAx(bracketed_eres); Expect(kKW_CloseBracket, _src.GetNext()); - element_vartype = is_managed ? _sym.VartypeWith(VTT::kDynpointer, argument_vartype) : argument_vartype; - eres.Vartype = _sym.VartypeWith(VTT::kDynarray, element_vartype); + element_vartype = is_managed ? _sym.VartypeWithDynpointer(argument_vartype) : argument_vartype; + eres.Vartype = _sym.VartypeWithDynarray(element_vartype); } else { @@ -1924,7 +1924,7 @@ void AGS::Parser::ParseExpression_New(SrcList &expression, EvaluationResult &ere _sym.GetName(argument_vartype).c_str()); element_vartype = argument_vartype; - eres.Vartype = _sym.VartypeWith(VTT::kDynpointer, argument_vartype); + eres.Vartype = _sym.VartypeWithDynpointer(argument_vartype); } size_t const element_size = _sym.GetSize(element_vartype); @@ -2525,11 +2525,11 @@ void AGS::Parser::ParseExpression_InParens(SrcList &expression, EvaluationResult // We're in the parameter list of a function call, and we have less parameters than declared. // Provide defaults for the missing values -void AGS::Parser::AccessData_FunctionCall_ProvideDefaults(int func_args_count, size_t supplied_args_count,Symbol funcSymbol, bool func_is_import) +void AGS::Parser::AccessData_FunctionCall_ProvideDefaults(int func_args_count, size_t supplied_args_count,Symbol func_symbol, bool func_is_import) { for (size_t arg_idx = func_args_count; arg_idx > supplied_args_count; arg_idx--) { - Symbol const param_default = _sym[funcSymbol].FunctionD->Parameters[arg_idx].Default; + Symbol const param_default = _sym[func_symbol].FunctionD->Parameters[arg_idx].Default; if (kKW_NoSymbol == param_default) UserError("Function call parameter #%d isn't provided and doesn't have any default value", arg_idx); if (!_sym.IsLiteral(param_default)) @@ -2582,7 +2582,7 @@ std::string const AGS::Parser::ReferenceMsgSym(std::string const &msg,Symbol sym return ReferenceMsgLoc(msg, _sym.GetDeclared(symb)); } -void AGS::Parser::AccessData_FunctionCall_PushParams(SrcList ¶ms, size_t closed_paren_idx, size_t func_args_count, size_t supplied_args_count,Symbol funcSymbol, bool func_is_import) +void AGS::Parser::AccessData_FunctionCall_PushParams(SrcList ¶ms, size_t closed_paren_idx, size_t func_args_count, size_t supplied_args_count,Symbol func_symbol, bool func_is_import) { size_t param_count = supplied_args_count + 1u; size_t start_of_current_param = 0u; @@ -2593,9 +2593,9 @@ void AGS::Parser::AccessData_FunctionCall_PushParams(SrcList ¶ms, size_t clo // Find the start of the next parameter param_count--; int delimiter_nesting_depth = 0; - for (size_t paramListIdx = end_of_current_param - 1u; true; paramListIdx--) + for (size_t param_list_idx = end_of_current_param - 1u; true; param_list_idx--) { - Symbol const &symb = params[paramListIdx]; + Symbol const &symb = params[param_list_idx]; if (_sym.IsDelimeter(symb)) { bool const is_opener = _sym[symb].DelimeterD->Opening; @@ -2608,10 +2608,10 @@ void AGS::Parser::AccessData_FunctionCall_PushParams(SrcList ¶ms, size_t clo if ((delimiter_nesting_depth == 0 && kKW_Comma == symb) || (delimiter_nesting_depth < 0 && kKW_OpenParenthesis == symb)) { - start_of_current_param = paramListIdx + 1u; + start_of_current_param = param_list_idx + 1u; break; } - if (paramListIdx == 0) + if (param_list_idx == 0) break; // Don't put this into the for header! } @@ -2632,7 +2632,7 @@ void AGS::Parser::AccessData_FunctionCall_PushParams(SrcList ¶ms, size_t clo if (param_count <= func_args_count) // we know what type to expect { // If we need a string object ptr but AX contains a normal string, convert AX - Vartype const param_vartype = _sym[funcSymbol].FunctionD->Parameters[param_count].Vartype; + Vartype const param_vartype = _sym[func_symbol].FunctionD->Parameters[param_count].Vartype; ConvertAXStringToStringObject(param_vartype, eres.Vartype); // If we need a normal string but AX contains a string object ptr, // check that this ptr isn't null @@ -2642,7 +2642,7 @@ void AGS::Parser::AccessData_FunctionCall_PushParams(SrcList ¶ms, size_t clo std::string msg = "Parameter # of call to function "; msg.replace(msg.find(""), 5u, std::to_string(param_count)); - msg.replace(msg.find(""), 6u, _sym.GetName(funcSymbol)); + msg.replace(msg.find(""), 6u, _sym.GetName(func_symbol)); CheckVartypeMismatch(eres.Vartype, param_vartype, true, msg); } @@ -2847,7 +2847,7 @@ void AGS::Parser::AccessData_PushFunctionCallParams(Symbol name_of_func, bool fu void AGS::Parser::AccessData_FunctionCall(Symbol name_of_func, SrcList &expression, EvaluationResult &eres) { - if (kKW_OpenParenthesis != expression[1]) + if (kKW_OpenParenthesis != expression[1u]) UserError("Expected '('"); expression.EatFirstSymbol(); @@ -3021,9 +3021,9 @@ Symbol AGS::Parser::ConstructAttributeFuncName(Symbol attribsym, bool is_setter { std::string member_str = _sym.GetName(attribsym); // If "::" in the name, take the part after the last "::" - size_t const m_access_position = member_str.rfind("::"); - if (std::string::npos != m_access_position) - member_str = member_str.substr(m_access_position + 2u); + size_t const member_access_pos = member_str.rfind("::"); + if (std::string::npos != member_access_pos) + member_str = member_str.substr(member_access_pos + 2u); char const *stem_str = is_setter ? "set" : "get"; char const *indx_str = is_indexed ? "i_" : "_"; std::string func_str = stem_str + (indx_str + member_str); @@ -3040,7 +3040,7 @@ void AGS::Parser::AccessData_CallAttributeFunc(bool is_setter, SrcList &expressi if (kKW_NoSymbol == struct_of_component) UserError( ReferenceMsgSym( - "Struct '%s' does not have an attribute named '%s'", + "Struct '%s' doesn't have an attribute named '%s'", struct_of_component).c_str(), _sym.GetName(vartype).c_str(), _sym.GetName(unqualified_component).c_str()); @@ -3186,7 +3186,8 @@ void AGS::Parser::AccessData_ProcessCurrentArrayIndex(size_t const idx, size_t c { // We need to check the offset at runtime because we can't know the // array size that has been allocated. - WriteCmd(SCMD_LITTOREG, SREG_AX, index_value * factor); + // Make sure that a _whole_ array element fits, so 'index_value * factor' isn't enough + WriteCmd(SCMD_LITTOREG, SREG_AX, (index_value + 1) * factor); _reg_track.SetRegister(SREG_AX); WriteCmd(SCMD_DYNAMICBOUNDS, SREG_AX); } @@ -3976,7 +3977,7 @@ void AGS::Parser::ParseVardecl_InitialValAssignment_OldString(std::vector string_lit = _sym[string_lit].ConstantD->ValueSym; if (!_sym.IsLiteral(string_lit) || - _sym.VartypeWith(VTT::kConst, kKW_String) != _sym[string_lit].LiteralD->Vartype) + _sym.VartypeWithConst(kKW_String) != _sym[string_lit].LiteralD->Vartype) UserError("Expected a string literal after '=', found '%s' instead", _sym.GetName(_src.PeekNext()).c_str()); // The scanner has put the constant string into the strings table. That's where we must find and get it. @@ -4222,7 +4223,7 @@ void AGS::Parser::ParseVardecl_Local(Symbol var_name, Vartype vartype) return; // Initialize the variable with binary zeroes. - if (4u == var_size && !is_dyn) + if (SIZE_OF_STACK_CELL == var_size && !is_dyn) { WriteCmd(SCMD_LITTOREG, SREG_AX, 0); _reg_track.SetRegister(SREG_AX); @@ -4640,7 +4641,7 @@ void AGS::Parser::ParseQualifiers(TypeQualifierSet &tqs) }; } -void AGS::Parser::ParseStruct_FuncDecl(Symbol struct_of_func, Symbol name_of_func, TypeQualifierSet tqs, Vartype vartype) +void AGS::Parser::ParseStruct_FuncDecl(TypeQualifierSet tqs, Symbol struct_of_func, Vartype vartype, Symbol name_of_func) { if (tqs[TQ::kWriteprotected]) UserError("Cannot apply 'writeprotected' to this function declaration"); @@ -4887,7 +4888,7 @@ void AGS::Parser::ParseArray(Symbol vname, Vartype &vartype) UserError( "Can only have dynamic arrays of integer types, 'float', or managed structs. '%s' isn't any of this.", _sym.GetName(vartype).c_str()); - vartype = _sym.VartypeWith(VTT::kDynarray, vartype); + vartype = _sym.VartypeWithDynarray(vartype); return; } @@ -4944,9 +4945,9 @@ void AGS::Parser::ParseStruct_VariableDefn(TypeQualifierSet tqs, Vartype vartype UserError("Static variables are not supported"); if (tqs[TQ::kImport]) UserError("Cannot import struct component variables; import the whole struct instead"); - + if (!FlagIsSet(_options, SCOPT_RTTIOPS) && - (_sym.IsManagedVartype(vartype) && _sym.IsManagedVartype(name_of_struct))) + (_sym.IsManagedVartype(vartype) && _sym.IsManagedVartype(name_of_struct))) // Cannot allow nested managed pointers without RTTI support (will cause memory leaks at runtime) UserError("Cannot have managed variable components in managed struct (RTTI is not enabled)"); @@ -5008,7 +5009,7 @@ void AGS::Parser::ParseStruct_VariableOrFunctionDefn(Symbol name_of_struct, Type _sym[name_of_struct].VartypeD->Components[unqualified_component] = qualified_component; if (is_function) - ParseStruct_FuncDecl(name_of_struct, qualified_component, tqs, vartype); + ParseStruct_FuncDecl(tqs, name_of_struct, vartype, qualified_component); else ParseStruct_VariableDefn(tqs, vartype, name_of_struct, qualified_component); @@ -5283,7 +5284,7 @@ AGS::Symbol AGS::Parser::ParseVartype(bool const with_dynpointer_handling) { if (kKW_String != vartype) UserError("The only allowed type beginning with 'const' is 'const string' (did you want to use 'readonly'?)"); - vartype = _sym.VartypeWith(VTT::kConst, vartype); + vartype = _sym.VartypeWithConst(vartype); } if (with_dynpointer_handling) @@ -5679,7 +5680,7 @@ void AGS::Parser::ParseVartypeClause(TypeQualifierSet tqs, Symbol &struct_of_cur _sym.IsAutoptrVartype(vartype) || (ScT::kImport != scope_type && _sym.IsManagedVartype(vartype))) { - vartype = _sym.VartypeWith(VTT::kDynpointer, vartype); + vartype = _sym.VartypeWithDynpointer(vartype); } EatDynpointerSymbolIfPresent(vartype); @@ -5693,7 +5694,7 @@ void AGS::Parser::ParseVartypeClause(TypeQualifierSet tqs, Symbol &struct_of_cur _sym.IsAutoptrVartype(vartype) || (ScT::kImport != scope_type && _sym.IsManagedVartype(vartype))) { - vartype = _sym.VartypeWith(VTT::kDynpointer, vartype); + vartype = _sym.VartypeWithDynpointer(vartype); } EatDynpointerSymbolIfPresent(vartype); diff --git a/Compiler/script2/cs_parser.h b/Compiler/script2/cs_parser.h index dc14396c92a..e4447abaf16 100644 --- a/Compiler/script2/cs_parser.h +++ b/Compiler/script2/cs_parser.h @@ -486,7 +486,7 @@ class Parser void ParseFuncdecl_CheckThatKnownInfoMatches(std::string const &func_name, SymbolTableEntry::FunctionDesc const *this_entry, SymbolTableEntry::FunctionDesc const *known_info, size_t declared, bool body_follows); // Enter the function in the 'imports[]' or 'functions[]' array of '_script'; get its index - void ParseFuncdecl_EnterAsImportOrFunc(Symbol name_of_func, bool body_follows, bool func_is_import, size_t parameters_count, CodeLoc &function_soffs); + void ParseFuncdecl_EnterAsImportOrFunc(Symbol name_of_func, bool body_follows, bool func_is_import, size_t params_count, CodeLoc &function_soffs); // We're at something like 'int foo(', directly before the '(' // Return in 'body_follows' whether the symbol that follows the corresponding ')' is '{' @@ -533,9 +533,9 @@ class Parser // We're in the parameter list of a function call, and we have less parameters than declared. // Provide defaults for the missing values - void AccessData_FunctionCall_ProvideDefaults(int func_args_count, size_t supplied_args_count, Symbol funcSymbol, bool func_is_import); + void AccessData_FunctionCall_ProvideDefaults(int func_args_count, size_t supplied_args_count, Symbol func_symbol, bool func_is_import); - void AccessData_FunctionCall_PushParams(SrcList ¶meters, size_t closed_paren_idx, size_t func_args_count, size_t supplied_args_count, Symbol funcSymbol, bool func_is_import); + void AccessData_FunctionCall_PushParams(SrcList ¶ms, size_t closed_paren_idx, size_t func_args_count, size_t supplied_args_count, Symbol funcSymbol, bool func_is_import); // Count parameters, check that all the parameters are non-empty; find closing paren void AccessData_FunctionCall_CountAndCheckParm(SrcList ¶meters, Symbol name_of_func, size_t &index_of_close_paren, size_t &supplied_args_count); @@ -550,7 +550,7 @@ class Parser // We are processing a function call. // Get the parameters of the call and push them onto the stack. // Return the number of the parameters pushed - void AccessData_PushFunctionCallParams(Symbol name_of_func, bool func_is_import, SrcList ¶meters, size_t &actual_args_count); + void AccessData_PushFunctionCallParams(Symbol name_of_func, bool func_is_import, SrcList ¶ms, size_t &actual_args_count); // Process a function call. The parameter list begins with 'expression[1u]' (!) void AccessData_FunctionCall(Symbol name_of_func, SrcList &expression, EvaluationResult &eres); @@ -678,7 +678,7 @@ class Parser // We're processing some struct component or global or local variable. // If a sequence of array indexes follows, parse it and shorten symlist accordingly void AccessData_ProcessArrayIndexIfThere(SrcList &expression, EvaluationResult &eres); - + void AccessData_Variable(VariableAccess access_type, SrcList &expression, EvaluationResult &eres); void AGS::Parser::AccessData_This(EvaluationResult &eres); @@ -758,7 +758,6 @@ class Parser // we have accepted something like "int a" and we're expecting "[" void ParseArray(Symbol vname, Vartype &vartype); - void ParseVardecl_CheckIllegalCombis(Vartype vartype, ScopeType scope_type); // there was a forward declaration -- check that the real declaration matches it @@ -794,7 +793,7 @@ class Parser void ParseQualifiers(TypeQualifierSet &tqs); - void ParseStruct_FuncDecl(Symbol struct_of_func, Symbol name_of_func, TypeQualifierSet tqs, Vartype vartype); + void ParseStruct_FuncDecl(TypeQualifierSet tqs, Symbol struct_of_func, Vartype vartype, Symbol name_of_func); void ParseStruct_Attribute_ParamList(Symbol struct_of_func, Symbol name_of_func, bool is_setter, bool is_indexed, Vartype vartype); @@ -810,7 +809,7 @@ class Parser void ParseStruct_Attribute(TypeQualifierSet tqs, Symbol stname); // We're inside a struct decl, processing a member variable - void ParseStruct_VariableDefn(TypeQualifierSet tqs, Vartype curtype, Symbol stname, Symbol vname); + void ParseStruct_VariableDefn(TypeQualifierSet tqs, Vartype vartype, Symbol stname, Symbol vname); // We're inside a struct decl, processing a compile-time constant void ParseStruct_ConstantDefn(Symbol name_of_struct); diff --git a/Compiler/script2/cs_scanner.cpp b/Compiler/script2/cs_scanner.cpp index 1142602da60..5f28ac69541 100644 --- a/Compiler/script2/cs_scanner.cpp +++ b/Compiler/script2/cs_scanner.cpp @@ -700,7 +700,7 @@ void AGS::Scanner::ReadInGTCombi(std::string &symstring) void AGS::Scanner::SymstringToSym(std::string const &symstring, ScanType scan_type, CodeCell value, Symbol &symb) { - static Symbol const const_string_vartype = _sym.VartypeWith(VTT::kConst, kKW_String); + static Symbol const const_string_vartype = _sym.VartypeWithConst(kKW_String); static const char *const one_past_long_max_string = "2147483648"; symb = _sym.FindOrAdd(symstring); diff --git a/Compiler/test2/cc_bytecode_test_0.cpp b/Compiler/test2/cc_bytecode_test_0.cpp index 333462e3d37..b24072e1e3b 100644 --- a/Compiler/test2/cc_bytecode_test_0.cpp +++ b/Compiler/test2/cc_bytecode_test_0.cpp @@ -2098,7 +2098,7 @@ TEST_F(Bytecode0, Struct01) { 2, 52, 6, 3, 0, 8, 3, 36, // 23 11, 6, 3, 5, 75, 3, 3, 4, // 31 51, 4, 47, 3, 36, 12, 51, 4, // 39 - 48, 2, 52, 6, 3, 12, 71, 3, // 47 + 48, 2, 52, 6, 3, 16, 71, 3, // 47 6, 3, 77, 1, 2, 12, 8, 3, // 55 36, 13, 51, 4, 48, 3, 29, 3, // 63 51, 4, 50, 3, 51, 8, 49, 51, // 71 @@ -2110,7 +2110,7 @@ TEST_F(Bytecode0, Struct01) { 0, 23, 3, 2, 1, 4, 30, 2, // 119 51, 0, 47, 3, 1, 1, 4, 36, // 127 20, 51, 4, 48, 2, 52, 6, 3, // 135 - 12, 71, 3, 1, 2, 12, 7, 3, // 143 + 16, 71, 3, 1, 2, 12, 7, 3, // 143 29, 3, 36, 21, 51, 8, 49, 2, // 151 1, 12, 5, -999 }; @@ -2517,7 +2517,7 @@ TEST_F(Bytecode0, Struct06) { 0, 29, 3, 36, 16, 6, 3, 5, // 15 75, 3, 91, 4, 51, 4, 47, 3, // 23 36, 17, 51, 4, 48, 2, 52, 6, // 31 - 3, 12, 71, 3, 1, 2, 12, 48, // 39 + 3, 16, 71, 3, 1, 2, 12, 48, // 39 2, 52, 6, 3, 77, 8, 3, 36, // 47 18, 51, 4, 49, 2, 1, 4, 6, // 55 3, 0, 5, -999 @@ -3166,8 +3166,7 @@ TEST_F(Bytecode0, Struct12) { ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); - // WriteOutput("Struct12_NoRtti", scrip); - + // WriteOutput("Struct12", scrip); size_t const codesize = 147; EXPECT_EQ(codesize, scrip.codesize); @@ -3176,7 +3175,7 @@ TEST_F(Bytecode0, Struct12) { 0, 29, 3, 36, 10, 6, 3, 10, // 15 72, 3, 4, 0, 6, 2, 4, 47, // 23 3, 36, 14, 6, 2, 4, 48, 2, // 31 - 52, 36, 15, 6, 3, 12, 71, 3, // 39 + 52, 36, 15, 6, 3, 16, 71, 3, // 39 36, 16, 6, 3, 7, 1, 2, 12, // 47 8, 3, 36, 20, 6, 2, 4, 48, // 55 2, 52, 36, 21, 29, 2, 51, 8, // 63 @@ -3188,7 +3187,7 @@ TEST_F(Bytecode0, Struct12) { 71, 3, 11, 2, 3, 36, 30, 7, // 111 3, 36, 24, 51, 4, 8, 3, 36, // 119 36, 6, 2, 4, 48, 2, 52, 36, // 127 - 37, 6, 3, 12, 71, 3, 1, 2, // 135 + 37, 6, 3, 16, 71, 3, 1, 2, // 135 12, 36, 38, 7, 3, 36, 39, 2, // 143 1, 4, 5, -999 }; diff --git a/Compiler/test2/cc_bytecode_test_1.cpp b/Compiler/test2/cc_bytecode_test_1.cpp index 4e76d3c815a..5a137b901bf 100644 --- a/Compiler/test2/cc_bytecode_test_1.cpp +++ b/Compiler/test2/cc_bytecode_test_1.cpp @@ -2545,7 +2545,7 @@ TEST_F(Bytecode1, StructWOldstyleString2) { int compileResult = cc_compile(inpl, scrip); ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); - // WriteOutput("StructWOldstyleString2_NoRtti", scrip); + // WriteOutput("StructWOldstyleString2", scrip); size_t const codesize = 185; EXPECT_EQ(codesize, scrip.codesize); @@ -2564,7 +2564,7 @@ TEST_F(Bytecode1, StructWOldstyleString2) { 0, 8, 3, 36, 14, 51, 8, 48, // 95 2, 52, 1, 2, 2, 3, 2, 3, // 103 29, 3, 51, 8, 48, 2, 52, 6, // 111 - 3, 8, 71, 3, 1, 2, 8, 48, // 119 + 3, 12, 71, 3, 1, 2, 8, 48, // 119 2, 52, 30, 3, 1, 2, 2, 3, // 127 3, 5, 3, 2, 4, 6, 7, 199, // 135 3, 4, 2, 7, 3, 3, 5, 2, // 143 @@ -3241,12 +3241,12 @@ TEST_F(Bytecode1, DynarrayOfPrimitives) { 36, 2, 38, 0, 36, 3, 6, 3, // 7 10, 75, 3, 5, 2, 51, 0, 47, // 15 3, 1, 1, 4, 36, 4, 51, 4, // 23 - 48, 2, 52, 6, 3, 14, 71, 3, // 31 + 48, 2, 52, 6, 3, 16, 71, 3, // 31 6, 3, 0, 1, 2, 14, 27, 3, // 39 36, 5, 51, 4, 48, 2, 52, 6, // 47 - 3, 14, 71, 3, 1, 2, 14, 25, // 55 + 3, 16, 71, 3, 1, 2, 14, 25, // 55 3, 29, 3, 51, 8, 48, 2, 52, // 63 - 6, 3, 6, 71, 3, 30, 3, 1, // 71 + 6, 3, 8, 71, 3, 30, 3, 1, // 71 2, 6, 27, 3, 36, 6, 51, 4, // 79 49, 2, 1, 4, 6, 3, 0, 5, // 87 -999 diff --git a/Compiler/test2/cc_scanner_test.cpp b/Compiler/test2/cc_scanner_test.cpp index 3933c55ab38..62f3c725b5d 100644 --- a/Compiler/test2/cc_scanner_test.cpp +++ b/Compiler/test2/cc_scanner_test.cpp @@ -284,7 +284,7 @@ TEST_F(Scan, StringCollect) AGS::Symbol const s1 = sym.Find("\"Zwiebelkuchen\""); ASSERT_LT(0, s1); ASSERT_TRUE(sym.IsLiteral(s1)); - EXPECT_EQ(sym.VartypeWith(AGS::VTT::kConst, AGS::kKW_String), sym[s1].LiteralD->Vartype); + EXPECT_EQ(sym.VartypeWithConst(AGS::kKW_String), sym[s1].LiteralD->Vartype); int const pos1 = sym[s1].LiteralD->Value; ASSERT_LE(0, pos1); text_in_buffer.assign(string_collector.strings + pos1); diff --git a/Compiler/test2/cc_symboltable_test.cpp b/Compiler/test2/cc_symboltable_test.cpp index a09125ef634..667979936dd 100644 --- a/Compiler/test2/cc_symboltable_test.cpp +++ b/Compiler/test2/cc_symboltable_test.cpp @@ -42,11 +42,11 @@ TEST(SymbolTable, GetVartypeName) symt[foo_vartype].VartypeD->Type = AGS::VTT::kAtomic; EXPECT_STREQ( "foo[]", - symt.GetName(symt.VartypeWith(AGS::VTT::kDynarray, foo_vartype)).c_str()); + symt.GetName(symt.VartypeWithDynarray(foo_vartype)).c_str()); EXPECT_STREQ( "foo *", - symt.GetName(symt.VartypeWith(AGS::VTT::kDynpointer, foo_vartype)).c_str()); + symt.GetName(symt.VartypeWithDynpointer(foo_vartype)).c_str()); std::vector const dims = { 3, 5, 7 }; EXPECT_STREQ( @@ -62,10 +62,10 @@ TEST(SymbolTable, VartypeWithWithout) symt.MakeEntryVartype(foo_vartype); symt[foo_vartype].VartypeD->Type = AGS::VTT::kAtomic; - AGS::Vartype foo_converted = symt.VartypeWith(AGS::VTT::kDynarray, foo_vartype); + AGS::Vartype foo_converted = symt.VartypeWithDynarray(foo_vartype); EXPECT_EQ(foo_vartype, symt.VartypeWithout(AGS::VTT::kDynarray, foo_converted)); - foo_converted = symt.VartypeWith(AGS::VTT::kConst, foo_vartype); + foo_converted = symt.VartypeWithConst(foo_vartype); EXPECT_EQ(foo_vartype, symt.VartypeWithout(AGS::VTT::kConst, foo_converted)); EXPECT_EQ(foo_converted, symt.VartypeWithout(AGS::VTT::kDynarray, foo_converted)); } @@ -199,7 +199,7 @@ TEST(SymbolTable, IsPrimitiveAtomic) EXPECT_FALSE(symt.IsPrimitiveVartype(structy_sym)); EXPECT_TRUE(symt.IsAtomicVartype(structy_sym)); - AGS::Symbol const structy_ptr_sym = symt.VartypeWith(AGS::VTT::kDynpointer, structy_sym); + AGS::Symbol const structy_ptr_sym = symt.VartypeWithDynpointer(structy_sym); EXPECT_FALSE(symt.IsPrimitiveVartype(structy_ptr_sym)); EXPECT_FALSE(symt.IsAtomicVartype(structy_ptr_sym)); } From 39095605a1612fb131c92ddbf05acc5443434fda Mon Sep 17 00:00:00 2001 From: "Peter G Bouillon [fernewelten]" Date: Sun, 26 Nov 2023 01:21:03 +0100 Subject: [PATCH 5/6] New compiler: Arrays of arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Symboltable: – Modify 'VartypeWithArray()' to process arrays of arrays Parser: – Allow dynamic arrays of non-managed structs – Disallow dynamic arrays of classic arrays because that would make 'new' expressions ambiguous – Grok 'new[…][]' when initialising dynarrays of arrays – Make multiple indexes big-endian – In 'AccessData_ProcessCurrentArrayIndex()' → '…_ProcessArrayIndex()', grok a single index of a sequence of indexes. Clarify comments Googletests – Retracted the test that checks whether dynamic arrays of non-managed structs are disallowed (they are allowed now) --- Compiler/script2/cc_symboltable.cpp | 30 +- Compiler/script2/cs_parser.cpp | 313 ++++++++-------- Compiler/script2/cs_parser.h | 13 +- Compiler/test2/cc_bytecode_test_0.cpp | 520 +++++++++++++++++++++++++- Compiler/test2/cc_bytecode_test_1.cpp | 57 +-- Compiler/test2/cc_parser_test_0.cpp | 25 +- Compiler/test2/cc_parser_test_1.cpp | 16 + 7 files changed, 719 insertions(+), 255 deletions(-) diff --git a/Compiler/script2/cc_symboltable.cpp b/Compiler/script2/cc_symboltable.cpp index b95e9b7e23d..bfac98a7755 100644 --- a/Compiler/script2/cc_symboltable.cpp +++ b/Compiler/script2/cc_symboltable.cpp @@ -545,35 +545,51 @@ std::string const AGS::SymbolTable::GetName(AGS::Symbol symbl) const AGS::Vartype AGS::SymbolTable::VartypeWithArray(std::vector const &dims, AGS::Vartype vartype) { - bool const is_array_of_array = IsVTT(vartype, VTT::kArray); + // If 'vartype' is an array, the new array index must be spliced into the vartype name + // in front of the first '[' + std::string pre = GetName(vartype); + size_t first_bracket_pos = pre.find_first_of('['); + if (std::string::npos == first_bracket_pos) + first_bracket_pos = pre.length(); + std::string post = pre.substr(first_bracket_pos); + pre = pre.substr(0u, first_bracket_pos); + bool const is_array_of_array = IsVTT(vartype, VTT::kArray); std::vector aoa_dims; if (is_array_of_array) { // Classic array of classic array: Make the vartype of one joint array of them both std::vector const &old_dims = entries[vartype].VartypeD->Dims; aoa_dims.reserve(old_dims.size() + dims.size()); - aoa_dims = old_dims; - aoa_dims.insert(aoa_dims.end(), dims.begin(), dims.end()); + aoa_dims = dims; + aoa_dims.insert(aoa_dims.end(), old_dims.begin(), old_dims.end()); + // Cut off the first '[…]', it will be replaced by the index of the joint array + first_bracket_pos = post.find_first_of('[', 1u); + if (std::string::npos == first_bracket_pos) + first_bracket_pos = post.length(); + post = post.substr(first_bracket_pos); + vartype = entries[vartype].VartypeD->BaseVartype; } std::vector const &dims_to_use = is_array_of_array ? aoa_dims : dims; - std::string vartype_name = entries[vartype].Name + "["; + std::string insert = ""; size_t element_count = 1u; for (auto it = dims_to_use.cbegin(); it != dims_to_use.cend(); ++it) { element_count *= *it; - vartype_name += std::to_string(*it); - vartype_name += (it + 1 == dims_to_use.cend()) ? "]" : ", "; + insert += std::to_string(*it) + ", "; } + insert = "[" + insert.substr(0u, insert.length() - 2u) + "]"; + std::string const vartype_name = pre + insert + post; Vartype const array_vartype = FindOrAdd(vartype_name); if (!IsVartype(array_vartype)) { + // Initialise the new vartype entries[array_vartype].VartypeD = new SymbolTableEntry::VartypeDesc; entries[array_vartype].VartypeD->Type = VTT::kArray; entries[array_vartype].VartypeD->BaseVartype = vartype; entries[array_vartype].VartypeD->Size = element_count * GetSize(vartype); - entries[array_vartype].VartypeD->Dims = dims; + entries[array_vartype].VartypeD->Dims = dims_to_use; } return array_vartype; } diff --git a/Compiler/script2/cs_parser.cpp b/Compiler/script2/cs_parser.cpp index 6839e11efee..231103fd61d 100644 --- a/Compiler/script2/cs_parser.cpp +++ b/Compiler/script2/cs_parser.cpp @@ -242,7 +242,7 @@ void AGS::Parser::SkipNextSymbol(SrcList src, Symbol expected) void AGS::Parser::Expect(SymbolList const &expected, Symbol actual, std::string const &custom_msg) { - for (size_t expected_idx = 0; expected_idx < expected.size(); expected_idx++) + for (size_t expected_idx = 0u; expected_idx < expected.size(); expected_idx++) if (actual == expected[expected_idx]) return; @@ -251,7 +251,7 @@ void AGS::Parser::Expect(SymbolList const &expected, Symbol actual, std::string { // Provide a default message errmsg = "Expected "; - for (size_t expected_idx = 0; expected_idx < expected.size(); expected_idx++) + for (size_t expected_idx = 0u; expected_idx < expected.size(); expected_idx++) { errmsg += "'" + _sym.GetName(expected[expected_idx]) + "'"; if (expected_idx + 2u < expected.size()) @@ -343,14 +343,18 @@ void AGS::Parser::MarMgr::UpdateMAR(size_t lineno, ccCompiledScript &scrip) default: // The start offset is already reached (e.g., when a Dynpointer chain is dereferenced) // but the component offset may need to be processed. - if (_componentOffs > 0) + if (_componentOffs > 0u) + { scrip.WriteCmd(SCMD_ADD, SREG_MAR, _componentOffs); + _parser._reg_track.SetRegister(SREG_MAR); + } break; case ScT::kGlobal: scrip.RefreshLineno(lineno); scrip.WriteCmd(SCMD_LITTOREG, SREG_MAR, _startOffs + _componentOffs); scrip.FixupPrevious(Parser::kFx_GlobalData); + _parser._reg_track.SetRegister(SREG_MAR); break; case ScT::kImport: @@ -359,8 +363,9 @@ void AGS::Parser::MarMgr::UpdateMAR(size_t lineno, ccCompiledScript &scrip) scrip.RefreshLineno(lineno); scrip.WriteCmd(SCMD_LITTOREG, SREG_MAR, _startOffs); scrip.FixupPrevious(Parser::kFx_Import); - if (_componentOffs != 0) + if (_componentOffs != 0u) scrip.WriteCmd(SCMD_ADD, SREG_MAR, _componentOffs); + _parser._reg_track.SetRegister(SREG_MAR); break; case ScT::kLocal: @@ -371,6 +376,7 @@ void AGS::Parser::MarMgr::UpdateMAR(size_t lineno, ccCompiledScript &scrip) _parser.InternalError("Trying to emit the negative offset %d to the top-of-stack", (int) offset); scrip.WriteCmd(SCMD_LOADSPOFFS, offset); + _parser._reg_track.SetRegister(SREG_MAR); break; } Reset(); @@ -1854,28 +1860,16 @@ void AGS::Parser::EvaluationResultToAx(EvaluationResult &eres) } } -void AGS::Parser::ParseExpression_CheckArgOfNew(Vartype argument_vartype) -{ - if (!_sym.IsVartype(argument_vartype)) - UserError("Expected a type after 'new', found '%s' instead", _sym.GetName(argument_vartype).c_str()); - if (_sym[argument_vartype].VartypeD->Flags[VTF::kUndefined]) - UserError( - ReferenceMsgSym("The struct '%s' hasn't been completely defined yet", argument_vartype).c_str(), - _sym.GetName(argument_vartype).c_str()); - if (!_sym.IsAnyIntegerVartype(argument_vartype) && kKW_Float != argument_vartype && !_sym.IsManagedVartype(argument_vartype)) - UserError("Can only use integer types or 'float' or managed types with 'new'"); - - // Note: While it is an error to use a built-in type with new, it is - // allowed to use a built-in type with new[]. -} - void AGS::Parser::ParseExpression_New(SrcList &expression, EvaluationResult &eres) { if (expression.ReachedEOF()) UserError("Expected a type after 'new' but didn't find any"); Vartype const argument_vartype = expression.GetNext(); - ParseExpression_CheckArgOfNew(argument_vartype); + if (!_sym.IsVartype(argument_vartype)) + UserError( + "Expected a type after 'new', found '%s' instead", + _sym.GetName(argument_vartype).c_str()); bool const is_managed = _sym.IsManagedVartype(argument_vartype); bool const with_bracket_expr = kKW_OpenBracket == expression.PeekNext(); // "new FOO[BAR]" @@ -1899,13 +1893,26 @@ void AGS::Parser::ParseExpression_New(SrcList &expression, EvaluationResult &ere element_vartype = is_managed ? _sym.VartypeWithDynpointer(argument_vartype) : argument_vartype; eres.Vartype = _sym.VartypeWithDynarray(element_vartype); + + while (kKW_OpenBracket == expression.PeekNext()) + { + SkipNextSymbol(_src, kKW_OpenBracket); + Expect(kKW_CloseBracket, expression.GetNext()); + element_vartype = eres.Vartype; + eres.Vartype = _sym.VartypeWithDynarray(element_vartype); + } } else { + if (_sym[argument_vartype].VartypeD->Flags[VTF::kUndefined]) + UserError( + ReferenceMsgSym("The struct '%s' hasn't been completely defined yet", argument_vartype).c_str(), + _sym.GetName(argument_vartype).c_str()); + if (_sym.IsBuiltinVartype(argument_vartype)) UserError("Expected '[' after the built-in type '%s'", _sym.GetName(argument_vartype).c_str()); if (!is_managed) - UserError("Expected '[' after the integer type '%s'", _sym.GetName(argument_vartype).c_str()); + UserError("Expected '[' after the non-managed type '%s'", _sym.GetName(argument_vartype).c_str()); if (kKW_OpenParenthesis == expression.PeekNext()) { @@ -1916,7 +1923,7 @@ void AGS::Parser::ParseExpression_New(SrcList &expression, EvaluationResult &ere } // Only do this check for new, not for new[]. - if (0 == _sym.GetSize(argument_vartype)) + if (0u == _sym.GetSize(argument_vartype)) UserError( ReferenceMsgSym( "Struct '%s' doesn't contain any variables, cannot use 'new' with it", @@ -1928,7 +1935,7 @@ void AGS::Parser::ParseExpression_New(SrcList &expression, EvaluationResult &ere } size_t const element_size = _sym.GetSize(element_vartype); - if (0 == element_size) + if (0u == element_size) // The Engine really doesn't like that (division by zero error) InternalError("Trying to emit allocation of zero dynamic memory"); @@ -2876,7 +2883,6 @@ void AGS::Parser::AccessData_FunctionCall(Symbol name_of_func, SrcList &expressi { // MAR contains the address of "outer"; this is what will be used for "this" in the called function. _marMgr.UpdateMAR(_src.GetLineno(), _scrip); - _reg_track.SetRegister(SREG_MAR); // Parameter processing might entail calling yet other functions, e.g., in "f(...g(x)...)". // So we cannot emit SCMD_CALLOBJ here, before parameters have been processed. @@ -3140,54 +3146,54 @@ void AGS::Parser::AccessData_Dereference(EvaluationResult &eres) } } -void AGS::Parser::AccessData_ProcessCurrentArrayIndex(size_t const idx, size_t const dim, size_t const factor, bool const is_dynarray, SrcList &expression, EvaluationResult &eres) +void AGS::Parser::AccessData_ProcessArrayIndex(size_t const dim, size_t const factor, bool const is_dynarray, SrcList &index_expr, EvaluationResult &eres) { - // Get the index - size_t const index_start = expression.GetCursor(); - expression.SkipTo(SymbolList{ kKW_Comma, kKW_CloseBracket }); - size_t const index_end = expression.GetCursor(); - SrcList current_index = SrcList(expression, index_start, index_end - index_start); - if (0u == current_index.Length()) - UserError("Array index #u is empty, this is not supported here", idx + 1u); - + index_expr.StartRead(); // If all ops are pending on the MAR register, it hasn't been set yet at all. // So then we don't need to protect MAR against being clobbered, - // but we do need to keep track of those pending ops in this case. - bool const all_ops_are_pending = _marMgr.AreAllOpsPending(); - MarMgr save_mar_state(_marMgr); - RegisterGuard(all_ops_are_pending ? RegisterList{} : RegisterList{ SREG_MAR }, + bool const guard_mar_register = !_marMgr.AreAllOpsPending(); + MarMgr orig_mar_state(_marMgr); + RegisterGuard(guard_mar_register ? RegisterList{ SREG_MAR } : RegisterList{}, [&] { - std::string msg = "In array index #: "; - msg.replace(msg.find(""), 5u, std::to_string(idx + 1u)); - current_index.StartRead(); - ParseIntegerExpression(current_index, eres, msg); + ParseIntegerExpression(index_expr, eres); + // We need the result of the array index expression (i.e., the offset to + // add to the location of the base variable) in AX unless it is a literal. + // That's the only location we can use. We can't use the address that MAR + // points to as the location because MAR will be clobbered by the 'POP(MAR)' + // that will be generated at the end of 'RegisterGuard()'. if (eres.kTY_Literal != eres.Type) EvaluationResultToAx(eres); }); + // Restore the state that the MAR register has been in + // This is important because we compute the correct MAR value lazily, + // directly before the value is needed. The state contains accumulated + // instructions that haven't been emitted yet. + _marMgr = orig_mar_state; + + if (!index_expr.ReachedEOF()) + UserError( + "Unexpected '%s' after array index", + _sym.GetName(index_expr.GetNext()).c_str()); + if (eres.kTY_Literal == eres.Type) { // The arrax index is known at compile time, so check it as far as possible int const index_value = _sym[eres.Symbol].LiteralD->Value; if (index_value < 0) UserError( - "Array index #%u is %d, thus too low (minimum is 0)", - idx + 1u, - index_value); + "Array index is %d, thus too low (minimum is 0)", index_value); if (dim > 0u && static_cast(index_value) >= dim) UserError( - "Array index #%u is %d, thus too high (maximum is %u)", - idx + 1u, - index_value, - dim - 1u); + "Array index is %d, thus too high (maximum is %u)", index_value, dim - 1u); if (is_dynarray && index_value > 0) { // We need to check the offset at runtime because we can't know the // array size that has been allocated. // Make sure that a _whole_ array element fits, so 'index_value * factor' isn't enough - WriteCmd(SCMD_LITTOREG, SREG_AX, (index_value + 1) * factor); + WriteCmd(SCMD_LITTOREG, SREG_AX, (index_value + 1) * factor - 1); _reg_track.SetRegister(SREG_AX); WriteCmd(SCMD_DYNAMICBOUNDS, SREG_AX); } @@ -3195,16 +3201,13 @@ void AGS::Parser::AccessData_ProcessCurrentArrayIndex(size_t const idx, size_t c _marMgr.AddComponentOffset(index_value * factor); return; } - - if (all_ops_are_pending) - _marMgr = save_mar_state; - + // DYNAMICBOUNDS compares the offset into the memory block: // it mustn't be larger than the size of the allocated memory. // On the other hand, CHECKBOUNDS checks the index: it mustn't be // larger than the maximum given. So dynamic bounds must be checked // _after_ the multiplication; static bounds _before_ the multiplication. - // For better error messages at runtime, don't do CHECKBOUNDS after the multiplication. + // For better error messages at runtime, do CHECKBOUNDS first, then multiply. if (!is_dynarray) WriteCmd(SCMD_CHECKBOUNDS, SREG_AX, dim); if (factor != 1u) @@ -3220,62 +3223,67 @@ void AGS::Parser::AccessData_ProcessCurrentArrayIndex(size_t const idx, size_t c _reg_track.SetRegister(SREG_MAR); } -// We're processing some struct component or global or local variable. -// If an array index follows, parse it and shorten symlist accordingly -void AGS::Parser::AccessData_ProcessArrayIndexIfThere(SrcList &expression, EvaluationResult &eres) +void AGS::Parser::AccessData_ProcessArrayIndexes(SrcList &expression, EvaluationResult &eres) { - if (kKW_OpenBracket != expression.PeekNext()) - return; - SkipNextSymbol(expression, kKW_OpenBracket); - - bool const is_dynarray = _sym.IsDynarrayVartype(eres.Vartype); - bool const is_array = _sym.IsArrayVartype(eres.Vartype); - if (!is_dynarray && !is_array) - UserError("Array index is only legal after an array expression"); - - Vartype const element_vartype = _sym[eres.Vartype].VartypeD->BaseVartype; - size_t const element_size = _sym.GetSize(element_vartype); - std::vector dim_sizes; - std::vector dynarray_dims = { 0u, }; - std::vector &dims = is_dynarray ? dynarray_dims : _sym[eres.Vartype].VartypeD->Dims; - eres.Vartype = element_vartype; - - if (is_dynarray) - AccessData_Dereference(eres); - - // Number of dimensions and the the size of the dimension for each dimension - size_t const dims_count = dims.size(); - dim_sizes.resize(dims_count); - size_t factor = element_size; - for (int dim_idx = dims_count - 1; dim_idx >= 0; dim_idx--) // yes, "int" - { - dim_sizes[dim_idx] = factor; - factor *= dims[dim_idx]; - } - - for (size_t dim_idx = 0u; dim_idx < dims_count; dim_idx++) + while (kKW_OpenBracket == expression.PeekNext()) { - EvaluationResult eres_index; - AccessData_ProcessCurrentArrayIndex(dim_idx, dims[dim_idx], dim_sizes[dim_idx], is_dynarray, expression, eres_index); - if (eres_index.SideEffects) - eres.SideEffects = true; - Symbol divider = expression.PeekNext(); - Expect(SymbolList{ kKW_CloseBracket, kKW_Comma }, divider); - - if (kKW_CloseBracket == divider) + if (!_sym.IsAnyArrayVartype(eres.Vartype)) + UserError( + "An array index cannot follow an expression of type '%s'", + _sym.GetName(eres.Vartype)); + + Vartype const array_vartype = eres.Vartype; + Vartype const element_vartype = _sym[array_vartype].VartypeD->BaseVartype; + bool const is_dynarray = _sym.IsDynarrayVartype(array_vartype); + + if (is_dynarray) + AccessData_Dereference(eres); + + std::vector dynarray_dims = { 0u, }; // Dynarrays have just 1 dimension + std::vector &dims = is_dynarray ? dynarray_dims : _sym[array_vartype].VartypeD->Dims; + std::vector factors; + factors.resize(dims.size()); + size_t factor = _sym.GetSize(element_vartype); + for (int idx = dims.size() - 1; idx >= 0; idx--) // yes, 'int' { - SkipNextSymbol(expression, kKW_CloseBracket); - divider = expression.PeekNext(); + factors[idx] = factor; + factor *= dims[idx]; } - if (kKW_Comma == divider || kKW_OpenBracket == divider) + + Expect(kKW_OpenBracket, expression.GetNext()); + + for (size_t dim_idx = 0u; dim_idx < dims.size(); dim_idx++) { - if (dims_count == dim_idx + 1u) - UserError("Expected %u indexes, found more", dims_count); - expression.GetNext(); // Eat ',' or '[' - continue; + // Get the current index + size_t const index_start = expression.GetCursor(); + expression.SkipTo(SymbolList{ kKW_Comma, kKW_CloseBracket }); + size_t const index_end = expression.GetCursor(); + SrcList index_expr = SrcList(expression, index_start, index_end - index_start); + if (0u == index_expr.Length()) + UserError("Array index must not be empty"); + + EvaluationResult eres_index; + AccessData_ProcessArrayIndex(dims[dim_idx], factors[dim_idx], is_dynarray, index_expr, eres_index); + if (eres_index.SideEffects) + eres.SideEffects = true; + + Symbol const divider = expression.GetNext(); + if (dim_idx + 1u == dims.size()) + { + Expect(kKW_CloseBracket, divider); + break; + } + Expect(SymbolList{ kKW_CloseBracket, kKW_Comma }, divider); + if (kKW_CloseBracket == divider) + { + Expect( + kKW_OpenBracket, + expression.GetNext(), + "Another index should follow: Expected"); + } } - if (dims_count != dim_idx + 1u) - UserError("Expected %u indexes, but only found %d", dims_count, dim_idx + 1u); + + eres.Vartype = element_vartype; } } @@ -3295,7 +3303,7 @@ void AGS::Parser::AccessData_Variable(VariableAccess access_type, SrcList &expre _marMgr.Reset(); _marMgr.SetStart(scope_type, soffs); - _reg_track.SetRegister(SREG_MAR); + // Note, no instructions are generated, so MAR is not set here eres.Type = eres.kTY_RunTimeValue; eres.Location = eres.kLOC_MemoryAtMAR; @@ -3303,7 +3311,7 @@ void AGS::Parser::AccessData_Variable(VariableAccess access_type, SrcList &expre eres.Vartype = _sym.GetVartype(varname); eres.LocalNonParameter = (ScT::kLocal == scope_type && entry.Scope != _sym.kParameterScope); eres.Modifiable = !var_tqs[TQ::kReadonly]; - return AccessData_ProcessArrayIndexIfThere(expression, eres); + return AccessData_ProcessArrayIndexes(expression, eres); } @@ -3362,7 +3370,7 @@ void AGS::Parser::AccessData_FirstClause(VariableAccess access_type, SrcList &ex AccessData_FunctionCall(first_sym, expression, eres); if (_sym.IsDynarrayVartype(eres.Vartype)) - AccessData_ProcessArrayIndexIfThere(expression, eres); + AccessData_ProcessArrayIndexes(expression, eres); return; } @@ -3449,7 +3457,6 @@ void AGS::Parser::AccessData_SubsequentClause(VariableAccess access_type, bool a { // make MAR point to the struct of the attribute _marMgr.UpdateMAR(_src.GetLineno(), _scrip); - _reg_track.SetRegister(SREG_MAR); if (VAC::kWriting == access_type) { @@ -3507,7 +3514,7 @@ void AGS::Parser::AccessData_SubsequentClause(VariableAccess access_type, bool a SrcList start_of_funccall = SrcList(expression, expression.GetCursor(), expression.Length()); AccessData_FunctionCall(qualified_component, start_of_funccall, eres); if (_sym.IsDynarrayVartype(vartype)) - return AccessData_ProcessArrayIndexIfThere(expression, eres); + return AccessData_ProcessArrayIndexes(expression, eres); return; } @@ -3519,7 +3526,7 @@ void AGS::Parser::AccessData_SubsequentClause(VariableAccess access_type, bool a _sym.GetName(qualified_component).c_str()); AccessData_StructMember(qualified_component, access_type, access_via_this, expression, eres); - return AccessData_ProcessArrayIndexIfThere(expression, eres); + return AccessData_ProcessArrayIndexes(expression, eres); } InternalError("Unknown kind of component of '%s'", _sym.GetName(vartype).c_str()); @@ -3611,7 +3618,7 @@ void AGS::Parser::AccessData(VariableAccess access_type, SrcList &expression, Ev continue; } - if (_sym.IsArrayVartype(eres.Vartype) || _sym.IsDynarrayVartype(eres.Vartype)) + if (_sym.IsAnyArrayVartype(eres.Vartype)) UserError("Expected a struct in front of '.' but found an array instead"); else if (!_sym.IsStructVartype(eres.Vartype)) UserError( @@ -4249,7 +4256,8 @@ void AGS::Parser::ParseVardecl_Local(Symbol var_name, Vartype vartype) EvaluationResultToAx(rhs_eres); // Vartypes must match. This is true even if the lhs is readonly. - // As a special case, a string may be assigned a const string because the const string will be copied, not modified. + // As a special case, a string may be assigned a const string + // because the const string will be copied, not modified. Vartype rhsvartype = rhs_eres.Vartype; Vartype const lhsvartype = vartype; @@ -4287,7 +4295,7 @@ void AGS::Parser::ParseVardecl_Local(Symbol var_name, Vartype vartype) void AGS::Parser::ParseVardecl0(Symbol var_name, Vartype vartype, ScopeType scope_type, TypeQualifierSet tqs) { if (kKW_OpenBracket == _src.PeekNext()) - ParseArray(var_name, vartype); + ParseArrayDecl(var_name, vartype); // Don't warn for builtins or imports, they might have been predefined if (!_sym.IsBuiltinVartype(vartype) && ScT::kImport != scope_type && 0u == _sym.GetSize(vartype)) @@ -4861,77 +4869,82 @@ void AGS::Parser::ParseStruct_Attribute(TypeQualifierSet tqs, Symbol const name_ } } -// We're parsing an array var. -void AGS::Parser::ParseArray(Symbol vname, Vartype &vartype) + +void AGS::Parser::ParseArrayDecl(Symbol vname, Vartype &vartype) { + // To the end of the '[…]' clause SkipNextSymbol(_src, kKW_OpenBracket); + size_t const array_expr_start = _src.GetCursor(); + + _src.SkipToCloser(); + SkipNextSymbol(_src, kKW_CloseBracket); + + // When more '[…]' clauses follow, they need to be processed first, so call this func recursively + if (kKW_OpenBracket == _src.PeekNext()) + ParseArrayDecl(vname, vartype); if (PP::kPreAnalyze == _pp) - { - // Skip the sequence of [...] - while (true) - { - _src.SkipToCloser(); - if (kKW_OpenBracket != _src.PeekNext()) - return; - SkipNextSymbol(_src, kKW_OpenBracket); - } - } + return; // No need to analyse it further in this phase + + size_t const array_expr_end = _src.GetCursor(); + _src.SetCursor(array_expr_start); if (kKW_CloseBracket == _src.PeekNext()) { // Dynamic array SkipNextSymbol(_src, kKW_CloseBracket); if (vartype == kKW_String) - UserError("Dynamic arrays of old-style strings are not supported"); - if (!_sym.IsAnyIntegerVartype(vartype) && !_sym.IsManagedVartype(vartype) && kKW_Float != vartype) - UserError( - "Can only have dynamic arrays of integer types, 'float', or managed structs. '%s' isn't any of this.", - _sym.GetName(vartype).c_str()); + UserError("Cannot have a dynamic array of old-style strings"); + if (_sym.IsArrayVartype(vartype)) + UserError("Cannot have a dynamic array of a classic array"); vartype = _sym.VartypeWithDynarray(vartype); + _src.SetCursor(array_expr_end); return; } - std::vector dims; - // Static array + std::vector dims; while (true) { - std::string msg = "For dimension # of array '': "; - msg.replace(msg.find(""), 5u, std::to_string(dims.size())); - msg.replace(msg.find(""), 5u, _sym.GetName(vname).c_str()); - Symbol const first_sym = _src.PeekNext(); - EvaluationResult eres; - int const cursor = _src.GetCursor(); + int const dim_start = _src.GetCursor(); _src.SkipTo(kKW_Comma); - SrcList expression = SrcList(_src, cursor, _src.GetCursor() - cursor); + SrcList expression = SrcList(_src, dim_start, _src.GetCursor() - dim_start); + if (0u == expression.Length()) + UserError( + "Expected an integer expression for array dimension #%u, did not find any", + dims.size() + 1); expression.StartRead(); - ParseIntegerExpression(expression, eres, msg); + ParseIntegerExpression(expression, eres); if (eres.kTY_Literal != eres.Type) UserError( - (msg + "Cannot evaluate the expression starting with '%s' at compile time").c_str(), - _sym.GetName(first_sym).c_str()); + "Cannot evaluate the integer expression for array dimension #%u at compile time", + dims.size() + 1u); + if (!expression.ReachedEOF()) + UserError( + "Unexpected '%s' after the integer expression for array dimension #%u", + _sym.GetName(expression.GetNext()).c_str(), + dims.size() + 1u); CodeCell const dimension_as_int = _sym[eres.Symbol].LiteralD->Value; if (dimension_as_int < 1) UserError( - "Array dimension #%u of array '%s' must be at least 1 but is %d instead", - dims.size(), - _sym.GetName(vname).c_str(), + "Array dimension #%u must be at least 1 but is %d instead", + dims.size() + 1u, dimension_as_int); - dims.push_back(dimension_as_int); + dims.push_back(static_cast(dimension_as_int)); Symbol const punctuation = _src.GetNext(); Expect(SymbolList{ kKW_Comma, kKW_CloseBracket }, punctuation); if (kKW_Comma == punctuation) continue; - if (kKW_OpenBracket != _src.PeekNext()) - break; - SkipNextSymbol(_src, kKW_OpenBracket); + // Successive static arrays will be joined within 'VartypeWithArray()', below. + break; } + vartype = _sym.VartypeWithArray(dims, vartype); + _src.SetCursor(array_expr_end); } void AGS::Parser::ParseStruct_VariableDefn(TypeQualifierSet tqs, Vartype vartype, Symbol name_of_struct, Symbol vname) @@ -4967,7 +4980,7 @@ void AGS::Parser::ParseStruct_VariableDefn(TypeQualifierSet tqs, Vartype vartype if (_src.PeekNext() == kKW_OpenBracket) { Vartype vartype = _sym[vname].VariableD->Vartype; - ParseArray(vname, vartype); + ParseArrayDecl(vname, vartype); _sym[vname].VariableD->Vartype = vartype; } diff --git a/Compiler/script2/cs_parser.h b/Compiler/script2/cs_parser.h index e4447abaf16..782c9216ed3 100644 --- a/Compiler/script2/cs_parser.h +++ b/Compiler/script2/cs_parser.h @@ -559,9 +559,6 @@ class Parser // Return whether this is possible. bool ParseExpression_CompileTime(Symbol op_sym, EvaluationResult const &eres_lhs, EvaluationResult const &eres_rhs, EvaluationResult &eres); - // Check the vartype following 'new' - void ParseExpression_CheckArgOfNew(Vartype new_vartype); - // Parse the term given in 'expression'. The lowest-binding operator is unary 'new' // 'expression' is parsed from the beginning. The term must use up 'expression' completely. void ParseExpression_New(SrcList &expression, EvaluationResult &eres); @@ -673,12 +670,12 @@ class Parser void AccessData_Dereference(EvaluationResult &eres); // Process one index in a sequence of array indexes - void AccessData_ProcessCurrentArrayIndex(size_t idx, size_t dim, size_t factor, bool is_dynarray, SrcList &expression, EvaluationResult &eres); + void AccessData_ProcessArrayIndex(size_t dim, size_t factor, bool is_dynarray, SrcList &index_expr, EvaluationResult &eres); // We're processing some struct component or global or local variable. - // If a sequence of array indexes follows, parse it and shorten symlist accordingly - void AccessData_ProcessArrayIndexIfThere(SrcList &expression, EvaluationResult &eres); - + // If a sequence of array indexes follows, parse it + void AccessData_ProcessArrayIndexes(SrcList &expression, EvaluationResult &eres); + void AccessData_Variable(VariableAccess access_type, SrcList &expression, EvaluationResult &eres); void AGS::Parser::AccessData_This(EvaluationResult &eres); @@ -757,7 +754,7 @@ class Parser void ParseVardecl_Var2SymTable(Symbol var_name, Vartype vartype); // we have accepted something like "int a" and we're expecting "[" - void ParseArray(Symbol vname, Vartype &vartype); + void ParseArrayDecl(Symbol vname, Vartype &vartype); void ParseVardecl_CheckIllegalCombis(Vartype vartype, ScopeType scope_type); // there was a forward declaration -- check that the real declaration matches it diff --git a/Compiler/test2/cc_bytecode_test_0.cpp b/Compiler/test2/cc_bytecode_test_0.cpp index b24072e1e3b..6809c4bed8c 100644 --- a/Compiler/test2/cc_bytecode_test_0.cpp +++ b/Compiler/test2/cc_bytecode_test_0.cpp @@ -2088,7 +2088,7 @@ TEST_F(Bytecode0, Struct01) { int compileResult = cc_compile(inpl, scrip); ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); - // WriteOutput("Struct01_Rtti", scrip); + // WriteOutput("Struct01", scrip); size_t const codesize = 155; EXPECT_EQ(codesize, scrip.codesize); @@ -2098,7 +2098,7 @@ TEST_F(Bytecode0, Struct01) { 2, 52, 6, 3, 0, 8, 3, 36, // 23 11, 6, 3, 5, 75, 3, 3, 4, // 31 51, 4, 47, 3, 36, 12, 51, 4, // 39 - 48, 2, 52, 6, 3, 16, 71, 3, // 47 + 48, 2, 52, 6, 3, 15, 71, 3, // 47 6, 3, 77, 1, 2, 12, 8, 3, // 55 36, 13, 51, 4, 48, 3, 29, 3, // 63 51, 4, 50, 3, 51, 8, 49, 51, // 71 @@ -2110,7 +2110,7 @@ TEST_F(Bytecode0, Struct01) { 0, 23, 3, 2, 1, 4, 30, 2, // 119 51, 0, 47, 3, 1, 1, 4, 36, // 127 20, 51, 4, 48, 2, 52, 6, 3, // 135 - 16, 71, 3, 1, 2, 12, 7, 3, // 143 + 15, 71, 3, 1, 2, 12, 7, 3, // 143 29, 3, 36, 21, 51, 8, 49, 2, // 151 1, 12, 5, -999 }; @@ -2508,7 +2508,7 @@ TEST_F(Bytecode0, Struct06) { int compileResult = cc_compile(inpl, scrip); ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); - // WriteOutput("Struct06_Rtti", scrip); + // WriteOutput("Struct06", scrip); size_t const codesize = 59; EXPECT_EQ(codesize, scrip.codesize); @@ -2517,7 +2517,7 @@ TEST_F(Bytecode0, Struct06) { 0, 29, 3, 36, 16, 6, 3, 5, // 15 75, 3, 91, 4, 51, 4, 47, 3, // 23 36, 17, 51, 4, 48, 2, 52, 6, // 31 - 3, 16, 71, 3, 1, 2, 12, 48, // 39 + 3, 15, 71, 3, 1, 2, 12, 48, // 39 2, 52, 6, 3, 77, 8, 3, 36, // 47 18, 51, 4, 49, 2, 1, 4, 6, // 55 3, 0, 5, -999 @@ -3174,20 +3174,20 @@ TEST_F(Bytecode0, Struct12) { 36, 8, 38, 0, 36, 9, 6, 3, // 7 0, 29, 3, 36, 10, 6, 3, 10, // 15 72, 3, 4, 0, 6, 2, 4, 47, // 23 - 3, 36, 14, 6, 2, 4, 48, 2, // 31 - 52, 36, 15, 6, 3, 16, 71, 3, // 39 + 3, 36, 13, 6, 2, 4, 48, 2, // 31 + 52, 36, 15, 6, 3, 15, 71, 3, // 39 36, 16, 6, 3, 7, 1, 2, 12, // 47 - 8, 3, 36, 20, 6, 2, 4, 48, // 55 + 8, 3, 36, 19, 6, 2, 4, 48, // 55 2, 52, 36, 21, 29, 2, 51, 8, // 63 7, 3, 30, 2, 32, 3, 4, 71, // 71 3, 11, 2, 3, 36, 22, 6, 3, // 79 - 7, 8, 3, 36, 28, 6, 2, 4, // 87 + 7, 8, 3, 36, 27, 6, 2, 4, // 87 48, 2, 52, 36, 29, 29, 2, 51, // 95 8, 7, 3, 30, 2, 32, 3, 4, // 103 71, 3, 11, 2, 3, 36, 30, 7, // 111 3, 36, 24, 51, 4, 8, 3, 36, // 119 - 36, 6, 2, 4, 48, 2, 52, 36, // 127 - 37, 6, 3, 16, 71, 3, 1, 2, // 135 + 35, 6, 2, 4, 48, 2, 52, 36, // 127 + 37, 6, 3, 15, 71, 3, 1, 2, // 135 12, 36, 38, 7, 3, 36, 39, 2, // 143 1, 4, 5, -999 }; @@ -5203,6 +5203,501 @@ TEST_F(Bytecode0, ArrayOfPointers2_RTTI) { EXPECT_EQ(stringssize, scrip.stringssize); } +TEST_F(Bytecode0, DynarrayOfDynarray1) { + + // Accept a dynamic array of a dynamic array of a non-managed type + // RTTI ENABLED + + char *inpl = "\ + struct Foo \n\ + { \n\ + int FooI; \n\ + char FooK[20]; \n\ + }; \n\ + \n\ + int game_start() \n\ + { \n\ + int i = 2; \n\ + Foo Test[][]; \n\ + Test = new Foo[3][]; \n\ + Test[2] = new Foo[5]; \n\ + Test[2][i].FooK[11] = 'c'; \n\ + } \n\ + "; + + ccSetOption(SCOPT_RTTIOPS, true); + int compileResult = cc_compile(inpl, scrip); + ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); + + WriteOutput("DynarrayOfDynarray1", scrip); +} + +TEST_F(Bytecode0, DynarrayOfDynarray2) { + + // Accept a dynamic array of a dynamic array of a non-managed type + // RTTI ENABLED + + char *inpl = "\ + short Test[][]; \n\ + \n\ + int game_start() \n\ + { \n\ + int i = 2; \n\ + Test = new short[3][]; \n\ + Test[i] = new short[5]; \n\ + Test[2][4] = 4711; \n\ + } \n\ + "; + + ccSetOption(SCOPT_RTTIOPS, true); + int compileResult = cc_compile(inpl, scrip); + ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); + + // WriteOutput("DynarrayOfDynarray2", scrip); + size_t const codesize = 103; + EXPECT_EQ(codesize, scrip.codesize); + + int32_t code[] = { + 36, 4, 38, 0, 36, 5, 6, 3, // 7 + 2, 29, 3, 36, 6, 6, 3, 3, // 15 + 75, 3, 5, 4, 6, 2, 0, 47, // 23 + 3, 36, 7, 6, 3, 5, 75, 3, // 31 + 5, 2, 29, 3, 6, 2, 0, 48, // 39 + 2, 52, 29, 2, 51, 12, 7, 3, // 47 + 30, 2, 32, 3, 4, 71, 3, 11, // 55 + 2, 3, 30, 3, 47, 3, 36, 8, // 63 + 6, 2, 0, 48, 2, 52, 6, 3, // 71 + 11, 71, 3, 1, 2, 8, 48, 2, // 79 + 52, 6, 3, 9, 71, 3, 6, 3, // 87 + 4711, 1, 2, 8, 27, 3, 36, 9, // 95 + 2, 1, 4, 6, 3, 0, 5, -999 + }; + CompareCode(&scrip, codesize, code); + + size_t const numfixups = 3; + EXPECT_EQ(numfixups, scrip.numfixups); + + int32_t fixups[] = { + 22, 38, 66, -999 + }; + char fixuptypes[] = { + 1, 1, 1, '\0' + }; + CompareFixups(&scrip, numfixups, fixups, fixuptypes); + + int const numimports = 0; + std::string imports[] = { + "[[SENTINEL]]" + }; + CompareImports(&scrip, numimports, imports); + + size_t const numexports = 0; + EXPECT_EQ(numexports, scrip.numexports); + + size_t const stringssize = 0; + EXPECT_EQ(stringssize, scrip.stringssize); +} + +TEST_F(Bytecode0, DynarrayOfDynarray3) { + + // Accept a dynamic array of a dynamic array of a managed type + // RTTI ENABLED + + char *inpl = "\ + managed struct Foo \n\ + { \n\ + int FooI; \n\ + char FooK[20]; \n\ + }; \n\ + \n\ + int i = 2; \n\ + int game_start() \n\ + { \n\ + Foo Test[][]; \n\ + Test = new Foo[3][]; \n\ + Test[2] = new Foo[5]; \n\ + Test[2][i] = new Foo; \n\ + Test[2][i].FooK[11] = 'c'; \n\ + } \n\ + "; + + ccSetOption(SCOPT_RTTIOPS, true); + int compileResult = cc_compile(inpl, scrip); + ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); + + // WriteOutput("DynarrayOfDynarray3", scrip); + size_t const codesize = 156; + EXPECT_EQ(codesize, scrip.codesize); + + int32_t code[] = { + 36, 9, 38, 0, 36, 10, 51, 0, // 7 + 49, 1, 1, 4, 36, 11, 6, 3, // 15 + 3, 75, 3, 91, 4, 51, 4, 47, // 23 + 3, 36, 12, 6, 3, 5, 75, 3, // 31 + 91, 4, 29, 3, 51, 8, 48, 2, // 39 + 52, 6, 3, 11, 71, 3, 30, 3, // 47 + 1, 2, 8, 47, 3, 36, 13, 74, // 55 + 3, 91, 24, 29, 3, 51, 8, 48, // 63 + 2, 52, 6, 3, 11, 71, 3, 1, // 71 + 2, 8, 48, 2, 52, 29, 2, 6, // 79 + 2, 0, 7, 3, 30, 2, 32, 3, // 87 + 4, 71, 3, 11, 2, 3, 30, 3, // 95 + 47, 3, 36, 14, 51, 4, 48, 2, // 103 + 52, 6, 3, 11, 71, 3, 1, 2, // 111 + 8, 48, 2, 52, 29, 2, 6, 2, // 119 + 0, 7, 3, 30, 2, 32, 3, 4, // 127 + 71, 3, 11, 2, 3, 48, 2, 52, // 135 + 6, 3, 99, 1, 2, 15, 26, 3, // 143 + 36, 15, 51, 4, 49, 2, 1, 4, // 151 + 6, 3, 0, 5, -999 + }; + CompareCode(&scrip, codesize, code); + + size_t const numfixups = 2; + EXPECT_EQ(numfixups, scrip.numfixups); + + int32_t fixups[] = { + 81, 120, -999 + }; + char fixuptypes[] = { + 1, 1, '\0' + }; + CompareFixups(&scrip, numfixups, fixups, fixuptypes); + + int const numimports = 0; + std::string imports[] = { + "[[SENTINEL]]" + }; + CompareImports(&scrip, numimports, imports); + + size_t const numexports = 0; + EXPECT_EQ(numexports, scrip.numexports); + + size_t const stringssize = 0; + EXPECT_EQ(stringssize, scrip.stringssize); +} + + +TEST_F(Bytecode0, ArrayOfDynarray) { + + // Accept a dynamic array of a dynamic array + // RTTI ENABLED + + char *inpl = "\ + int game_start() \n\ + { \n\ + int i = 2; \n\ + int Test[2, 3][]; \n\ + Test[1, 2] = new int[3]; \n\ + Test[i / 2][i][i] = 'c'; \n\ + } \n\ + "; + + ccSetOption(SCOPT_RTTIOPS, true); + int compileResult = cc_compile(inpl, scrip); + ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); + + // WriteOutput("ArrayOfDynarray", scrip); + size_t const codesize = 115; + EXPECT_EQ(codesize, scrip.codesize); + + int32_t code[] = { + 36, 2, 38, 0, 36, 3, 6, 3, // 7 + 2, 29, 3, 36, 4, 51, 0, 63, // 15 + 24, 1, 1, 24, 36, 5, 6, 3, // 23 + 3, 75, 3, 3, 4, 51, 4, 47, // 31 + 3, 36, 6, 51, 28, 7, 3, 29, // 39 + 3, 6, 3, 2, 30, 4, 10, 4, // 47 + 3, 3, 4, 3, 46, 3, 2, 32, // 55 + 3, 12, 51, 24, 11, 2, 3, 29, // 63 + 2, 51, 32, 7, 3, 30, 2, 46, // 71 + 3, 3, 32, 3, 4, 11, 2, 3, // 79 + 48, 2, 52, 29, 2, 51, 32, 7, // 87 + 3, 30, 2, 32, 3, 4, 71, 3, // 95 + 11, 2, 3, 6, 3, 99, 8, 3, // 103 + 36, 7, 51, 24, 2, 1, 28, 6, // 111 + 3, 0, 5, -999 + }; + CompareCode(&scrip, codesize, code); + + size_t const numfixups = 0; + EXPECT_EQ(numfixups, scrip.numfixups); + + int const numimports = 0; + std::string imports[] = { + "[[SENTINEL]]" + }; + CompareImports(&scrip, numimports, imports); + + size_t const numexports = 0; + EXPECT_EQ(numexports, scrip.numexports); + + size_t const stringssize = 0; + EXPECT_EQ(stringssize, scrip.stringssize); +} + +TEST_F(Bytecode0, ArrayOfManagedStruct) { + + // Accept a classic array of a managed struct + + char *inpl = "\ + struct Foo \n\ + { \n\ + int FooI; \n\ + float FooJ; \n\ + char FooK[20]; \n\ + }; \n\ + managed struct Bar \n\ + { \n\ + int BarI; \n\ + Foo BarJ[3]; \n\ + Foo BarK; \n\ + }; \n\ + \n\ + int game_start() \n\ + { \n\ + int i = 4; \n\ + Bar Test[5, 7]; \n\ + Test[2, 3].BarJ[i].FooJ = 5.0; \n\ + Test[i, 0].BarK.FooK[11] = 'c'; \n\ + } \n\ + "; + + int compileResult = cc_compile(inpl, scrip); + ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); + + // WriteOutput("ArrayOfManaged", scrip); + size_t const codesize = 106; + EXPECT_EQ(codesize, scrip.codesize); + + int32_t code[] = { + 36, 15, 38, 0, 36, 16, 6, 3, // 7 + 4, 29, 3, 36, 17, 51, 0, 63, // 15 + 140, 1, 1, 140, 36, 18, 51, 72, // 23 + 48, 2, 52, 29, 2, 51, 148, 7, // 31 + 3, 30, 2, 46, 3, 3, 32, 3, // 39 + 28, 1, 2, 4, 11, 2, 3, 6, // 47 + 3, 1084227584, 1, 2, 4, 8, 3, 36, // 55 + 19, 51, 144, 7, 3, 46, 3, 5, // 63 + 32, 3, 28, 51, 140, 11, 2, 3, // 71 + 48, 2, 52, 6, 3, 99, 1, 2, // 79 + 107, 26, 3, 36, 20, 51, 140, 6, // 87 + 3, 35, 49, 1, 2, 4, 2, 3, // 95 + 1, 70, -9, 2, 1, 144, 6, 3, // 103 + 0, 5, -999 + }; + CompareCode(&scrip, codesize, code); + + size_t const numfixups = 0; + EXPECT_EQ(numfixups, scrip.numfixups); + + int const numimports = 0; + std::string imports[] = { + "[[SENTINEL]]" + }; + CompareImports(&scrip, numimports, imports); + + size_t const numexports = 0; + EXPECT_EQ(numexports, scrip.numexports); + + size_t const stringssize = 0; + EXPECT_EQ(stringssize, scrip.stringssize); +} + +TEST_F(Bytecode0, DynarrayOfNonManaged_NoRtti) { + + // RTTI DISABLED + // Accept a dynarray of a non-managed struct that doesn't contain managed variable components + // (Elsewhere there's a googletest checking that structs are not accepted that do + // contain managed variable components.) + + char *inpl = "\ + struct Foo \n\ + { \n\ + int FooI; \n\ + float FooJ; \n\ + char FooK[20]; \n\ + }; \n\ + \n\ + struct Bar \n\ + { \n\ + int BarI; \n\ + float BarJ; \n\ + Foo BarK; \n\ + }; \n\ + \n\ + int game_start() \n\ + { \n\ + Bar Test[] = new Bar[17]; \n\ + Test[3].BarJ = 5.0; \n\ + Test[7].BarK.FooK[11] = 'c'; \n\ + } \n\ + "; + + ccSetOption(SCOPT_RTTIOPS, false); + int compileResult = cc_compile(inpl, scrip); + ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); + + // WriteOutput("DynarrayOfNonManaged_NoRtti", scrip); + size_t const codesize = 72; + EXPECT_EQ(codesize, scrip.codesize); + + int32_t code[] = { + 36, 16, 38, 0, 36, 17, 6, 3, // 7 + 17, 72, 3, 36, 0, 51, 0, 47, // 15 + 3, 1, 1, 4, 36, 18, 51, 4, // 23 + 48, 2, 52, 6, 3, 143, 71, 3, // 31 + 6, 3, 1084227584, 1, 2, 112, 8, 3, // 39 + 36, 19, 51, 4, 48, 2, 52, 6, // 47 + 3, 287, 71, 3, 6, 3, 99, 1, // 55 + 2, 279, 26, 3, 36, 20, 51, 4, // 63 + 49, 2, 1, 4, 6, 3, 0, 5, // 71 + -999 + }; + CompareCode(&scrip, codesize, code); + + size_t const numfixups = 0; + EXPECT_EQ(numfixups, scrip.numfixups); + + int const numimports = 0; + std::string imports[] = { + "[[SENTINEL]]" + }; + CompareImports(&scrip, numimports, imports); + + size_t const numexports = 0; + EXPECT_EQ(numexports, scrip.numexports); + + size_t const stringssize = 0; + EXPECT_EQ(stringssize, scrip.stringssize); +} + +TEST_F(Bytecode0, DynarrayOfNonManaged_Rtti) { + + // RTTI ENABLED + + // Accept a dynarray of a non-managed struct. + // RTTI is enabled, and so the non-managed struct may contain dynamic components. + + char *inpl = "\ + struct Foo \n\ + { \n\ + int FooI; \n\ + float FooJ; \n\ + char FooK[]; \n\ + }; \n\ + \n\ + struct Bar \n\ + { \n\ + int BarI; \n\ + float BarJ; \n\ + Foo BarK; \n\ + }; \n\ + \n\ + int game_start() \n\ + { \n\ + Bar Test[] = new Bar[17]; \n\ + int i = 7; \n\ + Test[3].BarJ = 5.0; \n\ + Test[i].BarK.FooK[11] = 'c'; \n\ + } \n\ + "; + + ccSetOption(SCOPT_RTTIOPS, true); + int compileResult = cc_compile(inpl, scrip); + ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); + + // WriteOutput("DynarrayOfNonManaged_Rtti", scrip); + size_t const codesize = 101; + EXPECT_EQ(codesize, scrip.codesize); + + int32_t code[] = { + 36, 16, 38, 0, 36, 17, 6, 3, // 7 + 17, 75, 3, 95, 20, 51, 0, 47, // 15 + 3, 1, 1, 4, 36, 18, 6, 3, // 23 + 7, 29, 3, 36, 19, 51, 8, 48, // 31 + 2, 52, 6, 3, 79, 71, 3, 6, // 39 + 3, 1084227584, 1, 2, 64, 8, 3, 36, // 47 + 20, 51, 8, 48, 2, 52, 29, 2, // 55 + 51, 8, 7, 3, 30, 2, 32, 3, // 63 + 20, 71, 3, 11, 2, 3, 1, 2, // 71 + 16, 48, 2, 52, 6, 3, 11, 71, // 79 + 3, 6, 3, 99, 1, 2, 11, 26, // 87 + 3, 36, 21, 51, 8, 49, 2, 1, // 95 + 8, 6, 3, 0, 5, -999 + }; + CompareCode(&scrip, codesize, code); + + size_t const numfixups = 0; + EXPECT_EQ(numfixups, scrip.numfixups); + + int const numimports = 0; + std::string imports[] = { + "[[SENTINEL]]" + }; + CompareImports(&scrip, numimports, imports); + + size_t const numexports = 0; + EXPECT_EQ(numexports, scrip.numexports); + + size_t const stringssize = 0; + EXPECT_EQ(stringssize, scrip.stringssize); +} + +TEST_F(Bytecode0, DynarrayOfPrimitives) { + + // Dynamic arrays of primitives are allowed. + + char const *inpl = "\ + int main() \n\ + { \n\ + short PrmArray[] = new short[10]; \n\ + PrmArray[7] = 0; \n\ + PrmArray[3] = PrmArray[7]; \n\ + } \n\ + "; + + ccSetOption(SCOPT_RTTIOPS, true); + + int compileResult = cc_compile(inpl, scrip); + ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); + + // WriteOutput("DynarrayOfPrimitives_Rtti", scrip); + size_t const codesize = 88; + EXPECT_EQ(codesize, scrip.codesize); + + int32_t code[] = { + 36, 2, 38, 0, 36, 3, 6, 3, // 7 + 10, 75, 3, 5, 2, 51, 0, 47, // 15 + 3, 1, 1, 4, 36, 4, 51, 4, // 23 + 48, 2, 52, 6, 3, 15, 71, 3, // 31 + 6, 3, 0, 1, 2, 14, 27, 3, // 39 + 36, 5, 51, 4, 48, 2, 52, 6, // 47 + 3, 15, 71, 3, 1, 2, 14, 25, // 55 + 3, 29, 3, 51, 8, 48, 2, 52, // 63 + 6, 3, 7, 71, 3, 30, 3, 1, // 71 + 2, 6, 27, 3, 36, 6, 51, 4, // 79 + 49, 2, 1, 4, 6, 3, 0, 5, // 87 + -999 + }; + CompareCode(&scrip, codesize, code); + + size_t const numfixups = 0; + EXPECT_EQ(numfixups, scrip.numfixups); + + int const numimports = 0; + std::string imports[] = { + "[[SENTINEL]]" + }; + CompareImports(&scrip, numimports, imports); + + size_t const numexports = 0; + EXPECT_EQ(numexports, scrip.numexports); + + size_t const stringssize = 0; + EXPECT_EQ(stringssize, scrip.stringssize); +} + TEST_F(Bytecode0, Writeprotected) { // Directly taken from the doc on writeprotected, simplified. @@ -5563,4 +6058,5 @@ TEST_F(Bytecode0, Import) { EXPECT_EQ(numexports, scrip.numexports); size_t const stringssize = 0; - EXPECT_EQ(stringssize, scrip.stringssize);} + EXPECT_EQ(stringssize, scrip.stringssize); +} diff --git a/Compiler/test2/cc_bytecode_test_1.cpp b/Compiler/test2/cc_bytecode_test_1.cpp index 5a137b901bf..7dc33e66272 100644 --- a/Compiler/test2/cc_bytecode_test_1.cpp +++ b/Compiler/test2/cc_bytecode_test_1.cpp @@ -2564,7 +2564,7 @@ TEST_F(Bytecode1, StructWOldstyleString2) { 0, 8, 3, 36, 14, 51, 8, 48, // 95 2, 52, 1, 2, 2, 3, 2, 3, // 103 29, 3, 51, 8, 48, 2, 52, 6, // 111 - 3, 12, 71, 3, 1, 2, 8, 48, // 119 + 3, 11, 71, 3, 1, 2, 8, 48, // 119 2, 52, 30, 3, 1, 2, 2, 3, // 127 3, 5, 3, 2, 4, 6, 7, 199, // 135 3, 4, 2, 7, 3, 3, 5, 2, // 143 @@ -3215,60 +3215,6 @@ TEST_F(Bytecode1, DynarrayLength2_RTTI) { EXPECT_EQ(stringssize, scrip.stringssize); } -TEST_F(Bytecode1, DynarrayOfPrimitives) { - - // Dynamic arrays of primitives are allowed. - - char const *inpl = "\ - int main() \n\ - { \n\ - short PrmArray[] = new short[10]; \n\ - PrmArray[7] = 0; \n\ - PrmArray[3] = PrmArray[7]; \n\ - } \n\ - "; - - ccSetOption(SCOPT_RTTIOPS, true); - - int compileResult = cc_compile(inpl, scrip); - ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); - - // WriteOutput("DynarrayOfPrimitives_Rtti", scrip); - size_t const codesize = 88; - EXPECT_EQ(codesize, scrip.codesize); - - int32_t code[] = { - 36, 2, 38, 0, 36, 3, 6, 3, // 7 - 10, 75, 3, 5, 2, 51, 0, 47, // 15 - 3, 1, 1, 4, 36, 4, 51, 4, // 23 - 48, 2, 52, 6, 3, 16, 71, 3, // 31 - 6, 3, 0, 1, 2, 14, 27, 3, // 39 - 36, 5, 51, 4, 48, 2, 52, 6, // 47 - 3, 16, 71, 3, 1, 2, 14, 25, // 55 - 3, 29, 3, 51, 8, 48, 2, 52, // 63 - 6, 3, 8, 71, 3, 30, 3, 1, // 71 - 2, 6, 27, 3, 36, 6, 51, 4, // 79 - 49, 2, 1, 4, 6, 3, 0, 5, // 87 - -999 - }; - CompareCode(&scrip, codesize, code); - - size_t const numfixups = 0; - EXPECT_EQ(numfixups, scrip.numfixups); - - int const numimports = 0; - std::string imports[] = { - "[[SENTINEL]]" - }; - CompareImports(&scrip, numimports, imports); - - size_t const numexports = 0; - EXPECT_EQ(numexports, scrip.numexports); - - size_t const stringssize = 0; - EXPECT_EQ(stringssize, scrip.stringssize); -} - TEST_F(Bytecode1, StringLiteral2String) { char const *inpl = "\ @@ -3486,3 +3432,4 @@ TEST_F(Bytecode1, Linenum02) size_t const stringssize = 0; EXPECT_EQ(stringssize, scrip.stringssize); } + diff --git a/Compiler/test2/cc_parser_test_0.cpp b/Compiler/test2/cc_parser_test_0.cpp index d165fe4a12c..a23b55796bf 100644 --- a/Compiler/test2/cc_parser_test_0.cpp +++ b/Compiler/test2/cc_parser_test_0.cpp @@ -1143,30 +1143,9 @@ TEST_F(Compile0, ImportOverride1) { EXPECT_EQ(std::string::npos, err.find("xception")); } -TEST_F(Compile0, DynamicNonManaged1) { - - // Dynamic array of non-managed struct not allowed - - char const *inpl = "\ - struct Inner \n\ - { \n\ - short Payload; \n\ - }; \n\ - managed struct Struct \n\ - { \n\ - Inner In[]; \n\ - }; \n\ - "; - - int compileResult = cc_compile(inpl, scrip); - ASSERT_STRNE("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); - std::string res(last_seen_cc_error()); - EXPECT_NE(std::string::npos, res.find("Inner")); -} - TEST_F(Compile0, DynamicNonManaged2) { - // Dynamic array of non-managed struct not allowed + // Dynamic pointer to non-managed struct not allowed char const *inpl = "\ struct Inner \n\ @@ -1187,7 +1166,7 @@ TEST_F(Compile0, DynamicNonManaged2) { TEST_F(Compile0, DynamicNonManaged3) { - // Dynamic array of non-managed struct not allowed + // Dynamic pointer to non-managed struct not allowed char const *inpl = "\ struct Inner \n\ diff --git a/Compiler/test2/cc_parser_test_1.cpp b/Compiler/test2/cc_parser_test_1.cpp index 994fcd9c4ae..6cac4534f5b 100644 --- a/Compiler/test2/cc_parser_test_1.cpp +++ b/Compiler/test2/cc_parser_test_1.cpp @@ -2643,3 +2643,19 @@ TEST_F(Compile1, ParensAfterNew) { ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : mh.GetError().Message.c_str()); ASSERT_EQ(1u, mh.GetMessages().size()); } + +TEST_F(Compile1, DynarrayOfArray) { + + // Refuse a dynarray of an array + + char *inpl = "\ + int game_start() \n\ + { \n\ + float Test[][5, 7]; \n\ + } \n\ + "; + + ccSetOption(SCOPT_RTTIOPS, false); + int compileResult = cc_compile(inpl, scrip); + ASSERT_STRNE("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); +} From 1169261bef9cedc1201e478bef4334a9c696be0e Mon Sep 17 00:00:00 2001 From: "Peter G Bouillon [fernewelten]" Date: Sun, 3 Dec 2023 23:14:37 +0100 Subject: [PATCH 6/6] New compiler: Proper typechecking for dynamic arrays Dynamic array types must match exactly. Also: fix small bug by adding '.c_str()' to a std::string expression --- Compiler/script2/cs_parser.cpp | 27 +++++++++++++-------------- Compiler/test2/cc_parser_test_0.cpp | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/Compiler/script2/cs_parser.cpp b/Compiler/script2/cs_parser.cpp index 231103fd61d..c391db93cd1 100644 --- a/Compiler/script2/cs_parser.cpp +++ b/Compiler/script2/cs_parser.cpp @@ -1640,26 +1640,25 @@ bool AGS::Parser::IsVartypeMismatch_Oneway(Vartype vartype_is, Vartype vartype_w return false; } - // Note: CanNOT convert String * or const string to string; - // a function that has a string parameter may modify it, but a String or const string may not be modified. + // Note: CanNOT convert 'String *' or 'const string' to 'string'; + // a function that has a 'string' parameter may modify it, + // but a 'String' or 'const string' may not be modified. if (_sym.IsOldstring(vartype_is) != _sym.IsOldstring(vartype_wants_to_be)) return true; // Note: the position of this test is important. // Don't "group" string tests "together" and move this test above or below them. - // cannot convert const to non-const + // Cannot convert 'const' to non-'const' if (_sym.IsConstVartype(vartype_is) && !_sym.IsConstVartype(vartype_wants_to_be)) return true; if (_sym.IsOldstring(vartype_is)) return false; - // From here on, don't mind constness or dynarray-ness + // From here on, don't mind constness vartype_is = _sym.VartypeWithout(VTT::kConst, vartype_is); - vartype_is = _sym.VartypeWithout(VTT::kDynarray, vartype_is); vartype_wants_to_be = _sym.VartypeWithout(VTT::kConst, vartype_wants_to_be); - vartype_wants_to_be = _sym.VartypeWithout(VTT::kDynarray, vartype_wants_to_be); // floats cannot mingle with other types if ((vartype_is == kKW_Float) != (vartype_wants_to_be == kKW_Float)) @@ -1676,12 +1675,12 @@ bool AGS::Parser::IsVartypeMismatch_Oneway(Vartype vartype_is, Vartype vartype_w if (_sym.IsDynarrayVartype(vartype_is) != _sym.IsDynarrayVartype(vartype_wants_to_be)) return false; - // The underlying core vartypes must be identical: - // A dynarray contains a sequence of elements whose size are used - // to index the individual element, so no extending elements - Symbol const target_core_vartype = _sym.VartypeWithout(VTT::kDynarray, vartype_wants_to_be); - Symbol const current_core_vartype = _sym.VartypeWithout(VTT::kDynarray, vartype_is); - return current_core_vartype != target_core_vartype; + // Array types must match exactly. We can't allow a 'child*[]' to be + // assigned to 'pareent[]' without additional dynamic type checking + Symbol const core_wants_to_be = _sym.VartypeWithout(VTT::kDynarray, vartype_wants_to_be); + Symbol const core_is = _sym.VartypeWithout(VTT::kDynarray, vartype_is); + + return core_is != core_wants_to_be; } // Checks to do if at least one is dynpointer @@ -1697,7 +1696,7 @@ bool AGS::Parser::IsVartypeMismatch_Oneway(Vartype vartype_is, Vartype vartype_w while (current_core_vartype != target_core_vartype) { current_core_vartype = _sym[current_core_vartype].VartypeD->Parent; - if (current_core_vartype == 0) + if (kKW_NoSymbol == current_core_vartype) return true; } return false; @@ -3230,7 +3229,7 @@ void AGS::Parser::AccessData_ProcessArrayIndexes(SrcList &expression, Evaluation if (!_sym.IsAnyArrayVartype(eres.Vartype)) UserError( "An array index cannot follow an expression of type '%s'", - _sym.GetName(eres.Vartype)); + _sym.GetName(eres.Vartype).c_str()); Vartype const array_vartype = eres.Vartype; Vartype const element_vartype = _sym[array_vartype].VartypeD->BaseVartype; diff --git a/Compiler/test2/cc_parser_test_0.cpp b/Compiler/test2/cc_parser_test_0.cpp index a23b55796bf..d18d6fb93d7 100644 --- a/Compiler/test2/cc_parser_test_0.cpp +++ b/Compiler/test2/cc_parser_test_0.cpp @@ -106,6 +106,25 @@ TEST_F(Compile0, DynamicArrayReturnValueErrorText) { EXPECT_STREQ("Type mismatch: Cannot convert 'DynamicSprite *[]' to 'int[]'", last_seen_cc_error()); } +TEST_F(Compile0, DynarrayOfDynarrayTypecheck1) { + + // Can't convert 'int[]' to 'int[][]' + + char const *inpl = "\ + int game_start() \n\ + { \n\ + int arr1[][], arr2[][]; \n\ + arr1 = new int[10][]; \n\ + arr1[5] = new int[20]; \n\ + arr2 = arr1[5]; \n\ + } \n\ + "; + + int compileResult = cc_compile(inpl, scrip); + ASSERT_STRNE("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); + EXPECT_STREQ("Cannot assign a type 'int[]' value to a type 'int[][]' variable", last_seen_cc_error()); +} + TEST_F(Compile0, StructMemberQualifierOrder) { // The order of qualifiers shouldn't matter. @@ -1113,6 +1132,7 @@ TEST_F(Compile0, StructRecursiveComponent02) EXPECT_NE(std::string::npos, err.find("'Bar'")); } + TEST_F(Compile0, Undefined) { char const *inpl = "\