From f722967abe036476c8aaca5e01f787308a881f13 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 5 Dec 2016 13:24:42 +0100 Subject: [PATCH] - added automatic type deduction capabilities for local variables. If you type 'let variable = value;' the type of 'variable' will be deduced from the given value. This is mostly of interest for type casting pointers, because 'let p = Classtype(objectvar);' does not require writing the class type name twice. --- src/dobjtype.cpp | 2 ++ src/dobjtype.h | 3 ++- src/namedef.h | 1 + src/sc_man_scanner.re | 1 + src/sc_man_tokens.h | 1 + src/scripting/codegeneration/codegen.cpp | 30 ++++++++++++++++++++++-- src/scripting/zscript/zcc-parse.lemon | 1 + src/scripting/zscript/zcc_compile.cpp | 4 ++++ src/scripting/zscript/zcc_parser.cpp | 1 + src/scripting/zscript/zcc_parser.h | 1 + 10 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 5e1316d71..ae26bc37d 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -74,6 +74,7 @@ TArray PClass::AllClasses; bool PClass::bShutdown; PErrorType *TypeError; +PErrorType *TypeAuto; PVoidType *TypeVoid; PInt *TypeSInt8, *TypeUInt8; PInt *TypeSInt16, *TypeUInt16; @@ -570,6 +571,7 @@ void PType::StaticInit() // Create types and add them type the type table. TypeTable.AddType(TypeError = new PErrorType); + TypeTable.AddType(TypeAuto = new PErrorType(2)); TypeTable.AddType(TypeVoid = new PVoidType); TypeTable.AddType(TypeSInt8 = new PInt(1, false)); TypeTable.AddType(TypeUInt8 = new PInt(1, true)); diff --git a/src/dobjtype.h b/src/dobjtype.h index ad626ee8b..c310f9863 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -370,7 +370,7 @@ class PErrorType : public PType { DECLARE_CLASS(PErrorType, PType); public: - PErrorType() : PType(0, 1) {} + PErrorType(int which = 1) : PType(0, which) {} }; class PVoidType : public PType @@ -930,6 +930,7 @@ PPrototype *NewPrototype(const TArray &rettypes, const TArray // Built-in types ----------------------------------------------------------- extern PErrorType *TypeError; +extern PErrorType *TypeAuto; extern PVoidType *TypeVoid; extern PInt *TypeSInt8, *TypeUInt8; extern PInt *TypeSInt16, *TypeUInt16; diff --git a/src/namedef.h b/src/namedef.h index c3866a0be..62d513437 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -721,6 +721,7 @@ xx(State) xx(Fixed) xx(Vector2) xx(Vector3) +xx(let) xx(Min) xx(Max) diff --git a/src/sc_man_scanner.re b/src/sc_man_scanner.re index a57cc34ad..8e4b42f69 100644 --- a/src/sc_man_scanner.re +++ b/src/sc_man_scanner.re @@ -183,6 +183,7 @@ std2: 'deprecated' { RET(TK_Deprecated); } 'action' { RET(TK_Action); } 'readonly' { RET(TK_ReadOnly); } + 'let' { RET(TK_Let); } /* Actor state options */ 'bright' { RET(StateOptions ? TK_Bright : TK_Identifier); } diff --git a/src/sc_man_tokens.h b/src/sc_man_tokens.h index c1ba10044..efa479cf3 100644 --- a/src/sc_man_tokens.h +++ b/src/sc_man_tokens.h @@ -142,4 +142,5 @@ xx(TK_NoDelay, "'nodelay'") xx(TK_Offset, "'offset'") xx(TK_Slow, "'slow'") xx(TK_Bright, "'bright'") +xx(TK_Let, "'let'") #undef xx diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index a0491f69d..0402ed9ab 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -9964,17 +9964,43 @@ FxExpression *FxLocalVariableDeclaration::Resolve(FCompileContext &ctx) delete this; return nullptr; } - if (ValueType->RegType == REGT_NIL) + if (ValueType->RegType == REGT_NIL && ValueType != TypeAuto) { auto sfunc = static_cast(ctx.Function->Variants[0].Implementation); StackOffset = sfunc->AllocExtraStack(ValueType); // Todo: Process the compound initializer once implemented. + if (Init != nullptr) + { + ScriptPosition.Message(MSG_ERROR, "Cannot initialize non-scalar variable %s here", Name.GetChars()); + delete this; + return nullptr; + } } - else + else if (ValueType !=TypeAuto) { if (Init) Init = new FxTypeCast(Init, ValueType, false); SAFE_RESOLVE_OPT(Init, ctx); } + else + { + if (Init == nullptr) + { + ScriptPosition.Message(MSG_ERROR, "Automatic type deduction requires an initializer for variable %s", Name.GetChars()); + delete this; + return nullptr; + } + SAFE_RESOLVE_OPT(Init, ctx); + if (Init->ValueType->RegType == REGT_NIL) + { + ScriptPosition.Message(MSG_ERROR, "Cannot initialize non-scalar variable %s here", Name.GetChars()); + delete this; + return nullptr; + } + ValueType = Init->ValueType; + // check for undersized ints and floats. These are not allowed as local variables. + if (IsInteger() && ValueType->Align < sizeof(int)) ValueType = TypeSInt32; + else if (IsFloat() && ValueType->Align < sizeof(double)) ValueType = TypeFloat64; + } if (Name != NAME_None) { for (auto l : ctx.Block->LocalVars) diff --git a/src/scripting/zscript/zcc-parse.lemon b/src/scripting/zscript/zcc-parse.lemon index 5ba5a204e..9305b9f71 100644 --- a/src/scripting/zscript/zcc-parse.lemon +++ b/src/scripting/zscript/zcc-parse.lemon @@ -704,6 +704,7 @@ type_name1(X) ::= NAME(T). { X.Int = ZCC_Name; X.SourceLoc = T.SourceLoc; } type_name1(X) ::= SOUND(T). { X.Int = ZCC_Sound; X.SourceLoc = T.SourceLoc; } type_name1(X) ::= STATE(T). { X.Int = ZCC_State; X.SourceLoc = T.SourceLoc; } type_name1(X) ::= COLOR(T). { X.Int = ZCC_Color; X.SourceLoc = T.SourceLoc; } +type_name1(X) ::= LET(T). { X.Int = ZCC_Let; X.SourceLoc = T.SourceLoc; } type_name(X) ::= type_name1(A). { diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 98a1a5ff3..856802a97 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -1449,6 +1449,10 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n retval = TypeSound; break; + case ZCC_Let: + retval = TypeAuto; + break; + case ZCC_UserType: // statelabel et.al. are not tokens - there really is no need to, it works just as well as an identifier. Maybe the same should be done for some other types, too? switch (btype->UserType->Id) diff --git a/src/scripting/zscript/zcc_parser.cpp b/src/scripting/zscript/zcc_parser.cpp index 8a1618127..44f72da28 100644 --- a/src/scripting/zscript/zcc_parser.cpp +++ b/src/scripting/zscript/zcc_parser.cpp @@ -201,6 +201,7 @@ static void InitTokenMap() TOKENDEF2(TK_State, ZCC_STATE, NAME_State); TOKENDEF2(TK_Color, ZCC_COLOR, NAME_Color); TOKENDEF2(TK_Sound, ZCC_SOUND, NAME_Sound); + TOKENDEF2(TK_Let, ZCC_LET, NAME_let); TOKENDEF (TK_Identifier, ZCC_IDENTIFIER); TOKENDEF (TK_StringConst, ZCC_STRCONST); diff --git a/src/scripting/zscript/zcc_parser.h b/src/scripting/zscript/zcc_parser.h index 7a763b2c1..d02b1a12f 100644 --- a/src/scripting/zscript/zcc_parser.h +++ b/src/scripting/zscript/zcc_parser.h @@ -131,6 +131,7 @@ enum EZCCBuiltinType ZCC_Sound, ZCC_UserType, + ZCC_Let, ZCC_NUM_BUILT_IN_TYPES };