Skip to content

Commit 1b06c63

Browse files
committed
feat(zscript): 'auto' variable declarations
1 parent d8cbf92 commit 1b06c63

File tree

12 files changed

+237
-93
lines changed

12 files changed

+237
-93
lines changed

resources/docs/ZScript_Additions.txt

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1344,8 +1344,21 @@ cases where you are not using the base function in an assign.
13441344
In this example, if the GetClosestNPC() function finds an enemy (i.e. does not return NULL), then
13451345
the 'then' block will run, doing something with the enemy's position.
13461346
If no enemy is found (the function returned NULL), then the else block will run instead.
1347-
1348-
1347+
1348+
///////////////////////
1349+
// Auto Declarations //
1350+
///////////////////////
1351+
1352+
By declaring a single variable using the type 'auto', it will inherit its
1353+
type from its initializer.
1354+
1355+
Example:
1356+
auto x = 5; //x is int
1357+
auto n = Screen->LoadNPC(1); //n is npc
1358+
auto l = Screen->CreateLWeapon(LW_FIRE); //l is lweapon
1359+
//auto a = 1, b = 2; //ERROR: cannot use 'auto' to declare multiple variables at once
1360+
//auto c; //ERROR: must use an initializer when using 'auto'
1361+
13491362
////////////
13501363
// Repeat //
13511364
////////////

src/parser/AST.cpp

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,6 +1253,18 @@ DataType const& ASTDataDecl::resolveType(ZScript::Scope* scope, CompileErrorHand
12531253
return *type;
12541254
}
12551255

1256+
DataType const* ASTDataDecl::resolve_ornull(ZScript::Scope* scope, CompileErrorHandler* errorHandler)
1257+
{
1258+
DataType const& ty = resolveType(scope, errorHandler);
1259+
return ty.isResolved() ? &ty : nullptr;
1260+
}
1261+
1262+
void ASTDataDecl::replaceType(DataType const& newty)
1263+
{
1264+
ASTDataType* baseTypeNode = list ? list->baseType.get() : baseType.get();
1265+
baseTypeNode->replace(newty);
1266+
}
1267+
12561268
bool ZScript::hasSize(ASTDataDecl const& decl)
12571269
{
12581270
for (auto it = decl.extraArrays.cbegin(); it != decl.extraArrays.cend(); ++it)
@@ -2635,16 +2647,16 @@ DataType const& ASTDataType::resolve(ZScript::Scope& scope, CompileErrorHandler*
26352647
{
26362648
if(!wasResolved_)
26372649
{
2638-
DataType* resolved = type->resolve(scope, errorHandler);
2650+
DataType const* resolved = type->resolve(scope, errorHandler);
26392651
if(resolved && constant_)
26402652
{
26412653
string name = resolved->getName();
2642-
resolved = resolved->getConstType();
2643-
if(constant_>1 || !resolved)
2654+
if(constant_>1 || resolved->isConstant())
26442655
{
26452656
errorHandler->handleError(CompileError::ConstAlreadyConstant(this, name));
26462657
return DataType::ZVOID;
26472658
}
2659+
resolved = resolved->getConstType();
26482660
}
26492661
if(resolved)
26502662
{
@@ -2666,4 +2678,11 @@ DataType const* ASTDataType::resolve_ornull(ZScript::Scope& scope, CompileErrorH
26662678
DataType const& ty = resolve(scope, errorHandler);
26672679
return ty.isResolved() ? &ty : nullptr;
26682680
}
2681+
void ASTDataType::replace(DataType const& newty)
2682+
{
2683+
wasResolved_ = false;
2684+
constant_ = false;
2685+
becomeArray = false;
2686+
type.reset(newty.clone());
2687+
}
26692688

src/parser/AST.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -951,7 +951,10 @@ namespace ZScript
951951
// Resolves the type, using either the list's or this node's own base type
952952
// as appropriate.
953953
DataType const& resolveType(Scope* scope, CompileErrorHandler* errorHandler);
954+
DataType const* resolve_ornull(Scope* scope, CompileErrorHandler* errorHandler);
954955

956+
void replaceType(DataType const& newty);
957+
955958
// The list containing this declaration. Should be set by that list when
956959
// this is added.
957960
ASTDataDeclList* list;
@@ -2142,6 +2145,7 @@ namespace ZScript
21422145
void execute(ASTVisitor& visitor, void* param = NULL);
21432146
DataType const& resolve(Scope& scope, CompileErrorHandler* errorHandler);
21442147
DataType const* resolve_ornull(Scope& scope, CompileErrorHandler* errorHandler);
2148+
void replace(DataType const& newty);
21452149
inline bool wasResolved() const { return wasResolved_; }
21462150

21472151
owning_ptr<DataType> type;

src/parser/BuildVisitors.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1337,7 +1337,7 @@ void BuildOpcodes::caseExprCall(ASTExprCall& host, void* param)
13371337
else
13381338
{
13391339
DataType const& retType = *func.returnType;
1340-
if(retType != DataType::ZVOID)
1340+
if(!retType.isVoid())
13411341
{
13421342
int32_t retval = 0;
13431343
if (std::optional<int32_t> val = func.defaultReturn->getCompileTimeValue(NULL, scope))

src/parser/CompileError.xtable

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ X( CantParseImport, P, A, E, STR, VOID, "Included file '%s' encountered an err
2929
X( ImportRecursion, P, A, E, INT, VOID, "Recursion limit of %d hit while preprocessing. Perhaps you have circular imports?" )
3030
X( ImportBadScope, P, D )
3131
X( FunctionRedef, S, A, E, STR, VOID, "Function %s was already declared with that type signature." )
32-
X( FunctionVoidParam, S, A, E, STR, VOID, "Function parameter %s cannot have void type." )
32+
X( FunctionBadParamType, S, A, E, STR, STR, "Function parameter %s cannot have %s type." )
3333
X( ScriptRedef, S, A, E, STR, VOID, "Duplicate script with name %s already exists." )
34-
X( VoidVar, S, A, E, STR, VOID, "Variable %s can't have type void." )
34+
X( BadVarType, S, A, E, STR, STR, "Variable %s can't have type %s." )
3535
X( VarRedef, S, A, E, STR, VOID, "There is already a variable with name %s in this scope." )
3636
X( VarUndeclared, S, A, E, STR, VOID, "Variable %s has not been declared." )
3737
X( FuncUndeclared, S, D )
@@ -63,7 +63,7 @@ X( ConstRedef, S, D )
6363
X( LValConst, C, A, E, STR, VOID, "Variable %s is constant and cannot be changed." )
6464
X( BadGlobalInit, T, D )
6565
X( DeprecatedGlobal, S, D )
66-
X( VoidArr, S, A, E, STR, VOID, "Array %s cannot have base type void." )
66+
X( BadArrType, S, A, E, STR, STR, "Array %s cannot have base type %s." )
6767
X( RefArr, S, A, E, STR, VOID, "Arrays of type %s are not allowed to be global." )
6868
X( ArrRedef, S, D )
6969
X( ArrayTooSmall, A, A, E, VOID, VOID, "Arrays must have a size of at least 1." )
@@ -122,6 +122,9 @@ X( BadDelete, T, A, E, VOID, VOID, "'delete' may only be used on objects of cl
122122
X( DeprecatedWarn, S, A, W, STR, STR, "%s '%s' is deprecated, and should not be used." )
123123
X( DeprecatedError, S, A, E, STR, STR, "%s '%s' is deprecated, and cannot be used due to current compiler settings." )
124124
X( BadAnnotation, P, A, E, STR, STR, "Annotation @%s invalid: %s" )
125+
X( BadAutoType, S, A, E, VOID, VOID, "'auto' must have an initializer with valid type to mimic." )
126+
X( GroupAuto, S, A, E, VOID, VOID, "'auto' cannot apply to a group declaration." )
127+
X( BadReturnType, S, A, E, STR, VOID, "Cannot return type '%s'." )
125128

126129

127130
#undef STR

src/parser/RegistrationVisitor.cpp

Lines changed: 65 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -442,22 +442,28 @@ void RegistrationVisitor::caseScriptTypeDef(ASTScriptTypeDef& host, void* param)
442442
void RegistrationVisitor::caseDataDeclList(ASTDataDeclList& host, void* param)
443443
{
444444
// Resolve the base type.
445-
DataType const& baseType = host.baseType->resolve(*scope, this);
445+
DataType const* baseType = host.baseType->resolve_ornull(*scope, this);
446446
if (breakRecursion(*host.baseType.get())) return;
447-
if (!host.baseType->wasResolved()) return;
447+
if (!host.baseType->wasResolved() || !baseType) return;
448448

449449
// Don't allow void type.
450-
if (baseType == DataType::ZVOID)
450+
if (baseType->isVoid())
451451
{
452-
handleError(CompileError::VoidVar(&host, host.asString()));
452+
handleError(CompileError::BadVarType(&host, host.asString(), baseType->getName()));
453453
doRegister(host);
454454
return;
455455
}
456-
456+
457+
if (baseType->isAuto() && host.getDeclarations().size() > 1)
458+
{
459+
handleError(CompileError::GroupAuto(&host));
460+
return;
461+
}
462+
457463
// Check for disallowed global types.
458-
if (scope->isGlobal() && !baseType.canBeGlobal())
464+
if (scope->isGlobal() && !baseType->canBeGlobal())
459465
{
460-
handleError(CompileError::RefVar(&host, baseType.getName()));
466+
handleError(CompileError::RefVar(&host, baseType->getName()));
461467
doRegister(host);
462468
return;
463469
}
@@ -471,22 +477,22 @@ void RegistrationVisitor::caseDataDeclList(ASTDataDeclList& host, void* param)
471477
void RegistrationVisitor::caseDataEnum(ASTDataEnum& host, void* param)
472478
{
473479
// Resolve the base type.
474-
DataType const& baseType = host.baseType->resolve(*scope, this);
480+
DataType const* baseType = host.baseType->resolve_ornull(*scope, this);
475481
if (breakRecursion(*host.baseType.get())) return;
476-
if (!baseType.isResolved()) return;
482+
if (!baseType) return;
477483

478-
// Don't allow void type.
479-
if (baseType == DataType::ZVOID)
484+
// Don't allow void/auto types.
485+
if (baseType->isVoid() || baseType->isAuto())
480486
{
481-
handleError(CompileError::VoidVar(&host, host.asString()));
487+
handleError(CompileError::BadVarType(&host, host.asString(), baseType->getName()));
482488
doRegister(host);
483489
return;
484490
}
485491

486492
// Check for disallowed global types.
487-
if (scope->isGlobal() && !baseType.canBeGlobal())
493+
if (scope->isGlobal() && !baseType->canBeGlobal())
488494
{
489-
handleError(CompileError::RefVar(&host, baseType.getName()));
495+
handleError(CompileError::RefVar(&host, baseType->getName()));
490496
doRegister(host);
491497
return;
492498
}
@@ -532,29 +538,51 @@ void RegistrationVisitor::caseDataDecl(ASTDataDecl& host, void* param)
532538
if (breakRecursion(host)) return;
533539
if(!(registered(host, host.extraArrays) && (!host.getInitializer() || registered(host.getInitializer())))) return;
534540
// Then resolve the type.
535-
DataType const& type = host.resolveType(scope, this);
541+
DataType const* type = host.resolve_ornull(scope, this);
536542
if (breakRecursion(host)) return;
537-
if (!type.isResolved()) return;
543+
if (!type) return;
538544

539545
doRegister(host);
540546

541547
// Don't allow void type.
542-
if (type == DataType::ZVOID)
548+
if (type->isVoid())
543549
{
544-
handleError(CompileError::VoidVar(&host, host.name));
550+
handleError(CompileError::BadVarType(&host, host.name, type->getName()));
545551
return;
546552
}
553+
554+
if (type->isAuto())
555+
{
556+
bool good = false;
557+
auto init = host.getInitializer();
558+
if(init)
559+
{
560+
auto readty = init->getReadType(scope, this);
561+
if(readty && readty->isResolved() && !readty->isVoid() && !readty->isAuto())
562+
{
563+
auto newty = type->isConstant() ? readty->getConstType() : readty->getMutType();
564+
host.replaceType(*newty);
565+
type = host.resolve_ornull(scope, this);
566+
good = true;
567+
}
568+
}
569+
if(!good)
570+
{
571+
handleError(CompileError::BadAutoType(&host));
572+
return;
573+
}
574+
}
547575

548576
// Check for disallowed global types.
549-
if (scope->isGlobal() && !type.canBeGlobal())
577+
if (scope->isGlobal() && !type->canBeGlobal())
550578
{
551579
handleError(CompileError::RefVar(
552-
&host, type.getName() + " " + host.name));
580+
&host, type->getName() + " " + host.name));
553581
return;
554582
}
555583

556584
// Currently disabled syntaxes:
557-
if (getArrayDepth(type) > 1)
585+
if (getArrayDepth(*type) > 1)
558586
{
559587
handleError(CompileError::UnimplementedFeature(
560588
&host, "Nested Array Declarations"));
@@ -563,7 +591,7 @@ void RegistrationVisitor::caseDataDecl(ASTDataDecl& host, void* param)
563591

564592
// Is it a constant?
565593
bool isConstant = false;
566-
if (type.isConstant())
594+
if (type->isConstant())
567595
{
568596
// A constant without an initializer doesn't make sense.
569597
if (!host.getInitializer())
@@ -599,13 +627,13 @@ void RegistrationVisitor::caseDataDecl(ASTDataDecl& host, void* param)
599627
}
600628

601629
int32_t value = *host.getInitializer()->getCompileTimeValue(this, scope);
602-
Constant::create(*scope, host, type, value, this);
630+
Constant::create(*scope, host, *type, value, this);
603631
}
604632
else
605633
{
606634
if(parsing_user_class == puc_vars)
607635
{
608-
UserClassVar::create(*scope, host, type, this);
636+
UserClassVar::create(*scope, host, *type, this);
609637
}
610638
else
611639
{
@@ -615,7 +643,7 @@ void RegistrationVisitor::caseDataDecl(ASTDataDecl& host, void* param)
615643
return;
616644
}
617645

618-
Variable::create(*scope, host, type, this);
646+
Variable::create(*scope, host, *type, this);
619647
}
620648
}
621649
}
@@ -701,6 +729,12 @@ void RegistrationVisitor::caseFuncDecl(ASTFuncDecl& host, void* param)
701729
DataType const& returnType = host.returnType->resolve(*scope, this);
702730
if (breakRecursion(*host.returnType.get())) {scope = oldScope; return;}
703731
if (!returnType.isResolved()) {scope = oldScope; return;}
732+
if(returnType.isAuto())
733+
{
734+
handleError(CompileError::BadReturnType(&host, returnType.getName()));
735+
scope = oldScope;
736+
return;
737+
}
704738

705739
// Gather the parameter types.
706740
vector<DataType const*> paramTypes;
@@ -712,20 +746,20 @@ void RegistrationVisitor::caseFuncDecl(ASTFuncDecl& host, void* param)
712746
ASTDataDecl& decl = **it;
713747

714748
// Resolve the parameter type under current scope.
715-
DataType const& type = decl.resolveType(scope, this);
749+
DataType const* type = decl.resolve_ornull(scope, this);
716750
if (breakRecursion(decl)) {scope = oldScope; return;}
717-
if (!type.isResolved()) {scope = oldScope; return;}
751+
if (!type) {scope = oldScope; return;}
718752

719-
// Don't allow void params.
720-
if (type == DataType::ZVOID)
753+
// Don't allow void/auto params.
754+
if (type->isVoid() || type->isAuto())
721755
{
722-
handleError(CompileError::FunctionVoidParam(&decl, decl.name));
756+
handleError(CompileError::FunctionBadParamType(&decl, decl.name, type->getName()));
723757
doRegister(host);
724758
scope = oldScope;
725759
return;
726760
}
727761
paramNames.push_back(new string(decl.name));
728-
paramTypes.push_back(&type);
762+
paramTypes.push_back(type);
729763
}
730764
if(host.prototype)
731765
{

0 commit comments

Comments
 (0)