public void ProcessVarOverride(DMASTObjectVarOverride varOverride) { DMObject oldObject = _currentObject; _currentObject = DMObjectTree.GetDMObject(varOverride.ObjectPath); try { if (varOverride.VarName == "parent_type") { DMASTConstantPath parentType = varOverride.Value as DMASTConstantPath; if (parentType == null) { throw new CompileErrorException(varOverride.Location, "Expected a constant path"); } _currentObject.Parent = DMObjectTree.GetDMObject(parentType.Value.Path); } else { DMVariable variable = new DMVariable(null, varOverride.VarName, false, false); SetVariableValue(variable, varOverride.Value); _currentObject.VariableOverrides[variable.Name] = variable; } } catch (CompileErrorException e) { DMCompiler.Error(e.Error); } _currentObject = oldObject; }
public void ProcessStatementTryCatch(DMASTProcStatementTryCatch tryCatch) { string catchLabel = _proc.NewLabelName(); string endLabel = _proc.NewLabelName(); _proc.StartScope(); ProcessBlockInner(tryCatch.TryBody); _proc.EndScope(); _proc.Jump(endLabel); if (tryCatch.CatchParameter != null) { //TODO set the value to what is thrown in try var param = tryCatch.CatchParameter as DMASTProcStatementVarDeclaration; if (!_proc.TryAddLocalVariable(param.Name, param.Type)) { DMCompiler.Error(new CompilerError(param.Location, $"Duplicate var {param.Name}")); } } //TODO make catching actually work _proc.AddLabel(catchLabel); if (tryCatch.CatchBody != null) { _proc.StartScope(); ProcessBlockInner(tryCatch.CatchBody); _proc.EndScope(); } _proc.AddLabel(endLabel); }
public override void EmitPushValue(DMObject dmObject, DMProc proc) { switch (_expr) { case Field field: field.EmitPushIsSaved(proc); return; case Dereference dereference: dereference.EmitPushIsSaved(dmObject, proc); return; case Local: proc.PushFloat(0); return; case ListIndex: proc.PushFloat(0); // Silent in BYOND DMCompiler.Warning(new CompilerWarning(_expr.Location, "calling issaved() on a list index is always false")); return; default: throw new CompileErrorException(Location, $"can't get saved value of {_expr}"); } }
public void ProcessVarDefinition(DMASTObjectVarDefinition varDefinition) { DMObject oldObject = _currentObject; DMVariable variable; _currentObject = DMObjectTree.GetDMObject(varDefinition.ObjectPath); if (varDefinition.IsGlobal) { variable = _currentObject.CreateGlobalVariable(varDefinition.Type, varDefinition.Name, varDefinition.IsConst); } else { variable = new DMVariable(varDefinition.Type, varDefinition.Name, false, varDefinition.IsConst); _currentObject.Variables[variable.Name] = variable; } try { SetVariableValue(variable, varDefinition.Value, varDefinition.ValType); } catch (CompileErrorException e) { DMCompiler.Error(e.Error); } _currentObject = oldObject; }
public override (DMReference Reference, bool Conditional) EmitReference(DMObject dmObject, DMProc proc) { if (!proc.IsOverride) { DMCompiler.Warning(new CompilerWarning(Location, "Calling parents via ..() in a proc definition does nothing")); } return(DMReference.SuperProc, false); }
public override bool TryAsJsonRepresentation(out object json) { json = null; if (!DMCompiler.Settings.SuppressUnimplementedWarnings) { DMCompiler.Warning(new CompilerWarning(Location, "DMM overrides for newlist() are not implemented")); } return(true); //TODO }
public override void EmitPushInitial(DMObject dmObject, DMProc proc) { // This happens silently in BYOND // TODO Support "vars" actually pushing initial() correctly if (_expr is Dereference deref && deref.PropertyName != "vars") { DMCompiler.Warning(new CompilerWarning(Location, "calling initial() on a list index returns the current value")); } EmitPushValue(dmObject, proc); }
public void ProcessBlockInner(DMASTBlockInner blockInner) { foreach (DMASTStatement statement in blockInner.Statements) { try { ProcessStatement(statement); } catch (CompileErrorException e) { DMCompiler.Error(e.Error); } } }
public void ProcessBlockInner(DMASTProcBlockInner block) { foreach (DMASTProcStatement statement in block.Statements) { try { ProcessStatement(statement); } catch (CompileErrorException e) { //Retreat from the statement when there's an error DMCompiler.Error(e.Error); } } }
public void ProcessObjectDefinition(DMASTObjectDefinition objectDefinition) { DMObject oldObject = _currentObject; DMCompiler.VerbosePrint($"Generating {objectDefinition.Path}"); _currentObject = DMObjectTree.GetDMObject(objectDefinition.Path); if (objectDefinition.InnerBlock != null) { ProcessBlockInner(objectDefinition.InnerBlock); } _currentObject = oldObject; }
public bool IsSaved() { // Silent in BYOND // TODO Support "vars" actually pushing issaved() correctly if (_expr is Dereference deref && deref.PropertyName != "vars") { DMCompiler.Warning(new CompilerWarning(_expr.Location, "calling issaved() on a list index is always false")); return(false); } return(true); }
public void ProcessStatementVarDeclaration(DMASTProcStatementVarDeclaration varDeclaration) { if (varDeclaration.IsGlobal) { return; } //Currently handled by DMObjectBuilder DMExpression value; if (varDeclaration.Value != null) { try { value = DMExpression.Create(_dmObject, _proc, varDeclaration.Value, varDeclaration.Type); } catch (CompileErrorException e) { DMCompiler.Error(e.Error); value = new Expressions.Null(varDeclaration.Location); } } else { value = new Expressions.Null(varDeclaration.Location); } bool successful; if (varDeclaration.IsConst) { if (!value.TryAsConstant(out var constValue)) { DMCompiler.Error(new CompilerError(varDeclaration.Location, "Const var must be set to a constant")); return; } successful = _proc.TryAddLocalConstVariable(varDeclaration.Name, varDeclaration.Type, constValue); } else { successful = _proc.TryAddLocalVariable(varDeclaration.Name, varDeclaration.Type); } if (!successful) { DMCompiler.Error(new CompilerError(varDeclaration.Location, $"Duplicate var {varDeclaration.Name}")); return; } value.EmitPushValue(_dmObject, _proc); _proc.Assign(_proc.GetLocalVariableReference(varDeclaration.Name)); _proc.Pop(); }
public void VisitDereference(DMASTDereference dereference) { var expr = DMExpression.Create(_dmObject, _proc, dereference.Expression, _inferredPath); if (dereference.Type == DMASTDereference.DereferenceType.Direct && !Dereference.DirectConvertable(expr, dereference)) { if (expr.Path == null) { throw new CompileErrorException(dereference.Location, $"Invalid property \"{dereference.Property}\""); } DMObject dmObject = DMObjectTree.GetDMObject(expr.Path.Value, false); if (dmObject == null) { throw new CompileErrorException(dereference.Location, $"Type {expr.Path.Value} does not exist"); } var property = dmObject.GetVariable(dereference.Property); if (property != null) { Result = new Expressions.Dereference(dereference.Location, property.Type, expr, dereference.Conditional, dereference.Property); } else { var globalId = dmObject.GetGlobalVariableId(dereference.Property); if (globalId != null) { property = DMObjectTree.Globals[globalId.Value]; Result = new Expressions.GlobalField(dereference.Location, property.Type, globalId.Value); } } if (property == null) { throw new CompileErrorException(dereference.Location, $"Invalid property \"{dereference.Property}\" on type {dmObject.Path}"); } if ((property.Value?.ValType & DMValueType.Unimplemented) == DMValueType.Unimplemented && !DMCompiler.Settings.SuppressUnimplementedWarnings) { DMCompiler.Warning(new CompilerWarning(dereference.Location, $"{dmObject.Path}.{dereference.Property} is not implemented and will have unexpected behavior")); } } else { Result = new Expressions.Dereference(dereference.Location, null, expr, dereference.Conditional, dereference.Property); } }
public override void EmitPushValue(DMObject dmObject, DMProc proc) { bool weighted = false; foreach (PickValue pickValue in _values) { if (pickValue.Weight != null) { weighted = true; break; } } if (weighted) { if (_values.Length == 1) { DMCompiler.Warning(new CompilerWarning(Location, "Weighted pick() with one argument")); } foreach (PickValue pickValue in _values) { DMExpression weight = pickValue.Weight ?? DMExpression.Create(dmObject, proc, new DMASTConstantInteger(Location, 100)); //Default of 100 weight.EmitPushValue(dmObject, proc); pickValue.Value.EmitPushValue(dmObject, proc); } proc.PickWeighted(_values.Length); } else { foreach (PickValue pickValue in _values) { if (pickValue.Value is Arglist args) { args.EmitPushArglist(dmObject, proc); } else { pickValue.Value.EmitPushValue(dmObject, proc); } } proc.PickUnweighted(_values.Length); } }
public void ProcessStatementForList(DMASTProcStatementForList statementForList) { DMExpression.Emit(_dmObject, _proc, statementForList.List); _proc.CreateListEnumerator(); _proc.StartScope(); { if (statementForList.Initializer != null) { ProcessStatement(statementForList.Initializer); } string loopLabel = _proc.NewLabelName(); _proc.LoopStart(loopLabel); { DMExpression outputVariable = DMExpression.Create(_dmObject, _proc, statementForList.Variable); (DMReference outputRef, _) = outputVariable.EmitReference(_dmObject, _proc); _proc.Enumerate(outputRef); _proc.BreakIfFalse(); DMASTProcStatementVarDeclaration varDeclaration = statementForList.Initializer as DMASTProcStatementVarDeclaration; if (varDeclaration != null && varDeclaration.Type != null) { //This is terrible but temporary //TODO: See https://github.com/wixoaGit/OpenDream/issues/50 var obj = DMObjectTree.GetDMObject(varDeclaration.Type.Value); if (statementForList.List is DMASTIdentifier list && list.Identifier == "world" && !obj.IsSubtypeOf(DreamPath.Atom)) { var warn = new CompilerWarning(statementForList.Location, "Cannot currently loop 'in world' for non-ATOM types"); DMCompiler.Warning(warn); } DMExpression.Emit(_dmObject, _proc, statementForList.Variable); _proc.PushPath(varDeclaration.Type.Value); _proc.IsType(); _proc.ContinueIfFalse(); } ProcessBlockInner(statementForList.Body); _proc.LoopContinue(loopLabel); _proc.LoopJumpToStart(loopLabel); } _proc.LoopEnd(); } _proc.EndScope(); _proc.DestroyEnumerator(); }
public void ProcessBlockInner(DMASTProcBlockInner block) { // TODO ProcessStatementSet() needs to be before any loops but this is nasty foreach (var stmt in block.Statements) { if (stmt is DMASTProcStatementSet set) { try { ProcessStatementSet(set); } catch (CompileAbortException e) { // The statement's location info isn't passed all the way down so change the error to make it more accurate e.Error.Location = set.Location; DMCompiler.Error(e.Error); return; // Don't spam the error that will continue to exist } catch (CompileErrorException e) { //Retreat from the statement when there's an error DMCompiler.Error(e.Error); } } } foreach (DMASTProcStatement statement in block.Statements) { try { // see above if (statement is DMASTProcStatementSet) { continue; } ProcessStatement(statement); } catch (CompileAbortException e) { // The statement's location info isn't passed all the way down so change the error to make it more accurate e.Error.Location = statement.Location; DMCompiler.Error(e.Error); return; // Don't spam the error that will continue to exist } catch (CompileErrorException e) { //Retreat from the statement when there's an error DMCompiler.Error(e.Error); } } }
public void ProcessProcDefinition(DMASTProcDefinition procDefinition) { if (procDefinition.Body == null) { return; } foreach (DMASTDefinitionParameter parameter in procDefinition.Parameters) { string parameterName = parameter.Name; if (!_proc.TryAddLocalVariable(parameterName, parameter.ObjectType)) { DMCompiler.Error(new CompilerError(procDefinition.Location, $"Duplicate argument \"{parameterName}\" on {procDefinition.ObjectPath}/proc/{procDefinition.Name}()")); continue; } if (parameter.Value != null) //Parameter has a default value { string afterDefaultValueCheck = _proc.NewLabelName(); DMReference parameterRef = _proc.GetLocalVariableReference(parameterName); //Don't set parameter to default if not null _proc.PushReferenceValue(parameterRef); _proc.IsNull(); _proc.JumpIfFalse(afterDefaultValueCheck); //Set default try { DMExpression.Emit(_dmObject, _proc, parameter.Value, parameter.ObjectType); } catch (CompileErrorException e) { DMCompiler.Error(e.Error); } _proc.Assign(parameterRef); _proc.Pop(); _proc.AddLabel(afterDefaultValueCheck); } } ProcessBlockInner(procDefinition.Body); _proc.ResolveLabels(); }
public void ProcessBlockInner(DMASTProcBlockInner block) { foreach (DMASTProcStatement statement in block.Statements) { try { ProcessStatement(statement); } catch (CompileAbortException e) { // The statement's location info isn't passed all the way down so change the error to make it more accurate e.Error.Location = statement.Location; DMCompiler.Error(e.Error); return; // Don't spam the error that will continue to exist } catch (CompileErrorException e) { //Retreat from the statement when there's an error DMCompiler.Error(e.Error); } } }
public void ProcessVarOverride(DMASTObjectVarOverride varOverride) { DMObject oldObject = _currentObject; _currentObject = DMObjectTree.GetDMObject(varOverride.ObjectPath); try { switch (varOverride.VarName) { case "parent_type": { DMASTConstantPath parentType = varOverride.Value as DMASTConstantPath; if (parentType == null) { throw new CompileErrorException(varOverride.Location, "Expected a constant path"); } _currentObject.Parent = DMObjectTree.GetDMObject(parentType.Value.Path); break; } case "tag": DMCompiler.Error(new CompilerError(varOverride.Location, "tag: may not be set at compile-time")); break; default: { DMVariable variable = new DMVariable(null, varOverride.VarName, false, false); SetVariableValue(variable, varOverride.Value); _currentObject.VariableOverrides[variable.Name] = variable; break; } } } catch (CompileErrorException e) { DMCompiler.Error(e.Error); } _currentObject = oldObject; }
public override void EmitPushInitial(DMObject dmObject, DMProc proc) { // This happens silently in BYOND DMCompiler.Warning(new CompilerWarning(Location, "calling initial() on a local variable returns the current value")); EmitPushValue(dmObject, proc); }
public override bool TryAsJsonRepresentation(out object json) { json = null; DMCompiler.UnimplementedWarning(Location, "DMM overrides for newlist() are not implemented"); return(true); //TODO }
public void ProcessStatementSet(DMASTProcStatementSet statementSet) { var attribute = statementSet.Attribute.ToLower(); // TODO deal with "src" if (!DMExpression.TryConstant(_dmObject, _proc, statementSet.Value, out var constant) && attribute != "src") { throw new CompileErrorException(statementSet.Location, $"{attribute} attribute should be a constant"); } switch (statementSet.Attribute.ToLower()) { case "waitfor": { _proc.WaitFor(constant.IsTruthy()); break; } case "opendream_unimplemented": { if (constant.IsTruthy()) { _proc.Attributes |= ProcAttributes.Unimplemented; } else { _proc.Attributes &= ~ProcAttributes.Unimplemented; } break; } case "hidden": if (constant.IsTruthy()) { _proc.Attributes |= ProcAttributes.Hidden; } else { _proc.Attributes &= ~ProcAttributes.Hidden; } break; case "popup_menu": if (constant.IsTruthy()) // The default is to show it so we flag it if it's hidden { _proc.Attributes &= ~ProcAttributes.HidePopupMenu; } else { _proc.Attributes |= ProcAttributes.HidePopupMenu; } DMCompiler.UnimplementedWarning(statementSet.Location, "set popup_menu is not implemented"); break; case "instant": if (constant.IsTruthy()) { _proc.Attributes |= ProcAttributes.Instant; } else { _proc.Attributes &= ~ProcAttributes.Instant; } DMCompiler.UnimplementedWarning(statementSet.Location, "set instant is not implemented"); break; case "background": if (constant.IsTruthy()) { _proc.Attributes |= ProcAttributes.Background; } else { _proc.Attributes &= ~ProcAttributes.Background; } break; case "name": if (constant is not Expressions.String nameStr) { throw new CompileErrorException(statementSet.Location, "name attribute must be a string"); } _proc.VerbName = nameStr.Value; break; case "category": _proc.VerbCategory = constant switch { Expressions.String str => str.Value, Expressions.Null => null, _ => throw new CompileErrorException(statementSet.Location, "category attribute must be a string or null") }; break; case "desc": if (constant is not Expressions.String descStr) { throw new CompileErrorException(statementSet.Location, "desc attribute must be a string"); } _proc.VerbDesc = descStr.Value; DMCompiler.UnimplementedWarning(statementSet.Location, "set desc is not implemented"); break; case "invisibility": // The ref says 0-101 for atoms and 0-100 for verbs // BYOND doesn't clamp the actual var value but it does seem to treat out-of-range values as their extreme if (constant is not Expressions.Number invisNum) { throw new CompileErrorException(statementSet.Location, "invisibility attribute must be an int"); } _proc.Invisibility = Convert.ToSByte(Math.Clamp(Math.Floor(invisNum.Value), 0, 100)); DMCompiler.UnimplementedWarning(statementSet.Location, "set invisibility is not implemented"); break; case "src": DMCompiler.UnimplementedWarning(statementSet.Location, "set src is not implemented"); break; } }
public void ProcessProcDefinition(DMASTProcDefinition procDefinition) { string procName = procDefinition.Name; DMObject dmObject = _currentObject; try { if (procDefinition.ObjectPath.HasValue) { dmObject = DMObjectTree.GetDMObject(_currentObject.Path.Combine(procDefinition.ObjectPath.Value)); } if (!procDefinition.IsOverride && dmObject.HasProc(procName)) { throw new CompileErrorException(procDefinition.Location, $"Type {dmObject.Path} already has a proc named \"{procName}\""); } DMProc proc; if (procDefinition.ObjectPath == null) { if (DMObjectTree.TryGetGlobalProc(procDefinition.Name, out _)) { throw new CompileErrorException(new CompilerError(procDefinition.Location, $"proc {procDefinition.Name} is already defined in global scope")); } proc = DMObjectTree.CreateDMProc(dmObject, procDefinition); DMObjectTree.AddGlobalProc(proc.Name, proc.Id); } else { proc = DMObjectTree.CreateDMProc(dmObject, procDefinition); dmObject.AddProc(procName, proc); } if (procDefinition.Body != null) { foreach (var stmt in GetStatements(procDefinition.Body)) { // TODO multiple var definitions. if (stmt is DMASTProcStatementVarDeclaration varDeclaration && varDeclaration.IsGlobal) { DMVariable variable = proc.CreateGlobalVariable(varDeclaration.Type, varDeclaration.Name, varDeclaration.IsConst); variable.Value = new Expressions.Null(varDeclaration.Location); if (varDeclaration.Value != null) { DMVisitorExpression._scopeMode = "static"; DMExpression expression = DMExpression.Create(dmObject, proc, varDeclaration.Value, varDeclaration.Type); DMVisitorExpression._scopeMode = "normal"; DMObjectTree.AddGlobalInitAssign(dmObject, proc.GetGlobalVariableId(varDeclaration.Name).Value, expression); } } } } if (procDefinition.IsVerb && (dmObject.IsSubtypeOf(DreamPath.Atom) || dmObject.IsSubtypeOf(DreamPath.Client)) && !DMCompiler.Settings.NoStandard) { Expressions.Field field = new Expressions.Field(Location.Unknown, dmObject.GetVariable("verbs")); DreamPath procPath = new DreamPath(".proc/" + procName); Expressions.Append append = new Expressions.Append(Location.Unknown, field, new Expressions.Path(Location.Unknown, procPath)); dmObject.InitializationProcExpressions.Add(append); } } catch (CompileErrorException e) { DMCompiler.Error(e.Error); } }
public void ProcessStatementSet(DMASTProcStatementSet statementSet) { var attribute = statementSet.Attribute.ToLower(); if (!DMExpression.TryConstant(_dmObject, _proc, statementSet.Value, out var constant)) { throw new CompileErrorException(statementSet.Location, $"{attribute} attribute should be a constant"); } switch (statementSet.Attribute.ToLower()) { case "waitfor": { _proc.WaitFor(constant.IsTruthy()); break; } case "opendream_unimplemented": { if (constant.IsTruthy()) { _proc.Attributes |= ProcAttributes.Unimplemented; } else { _proc.Attributes &= ~ProcAttributes.Unimplemented; } break; } case "hidden": if (constant.IsTruthy()) { _proc.Attributes |= ProcAttributes.Hidden; } else { _proc.Attributes &= ~ProcAttributes.Hidden; } if (!DMCompiler.Settings.SuppressUnimplementedWarnings) { DMCompiler.Warning(new CompilerWarning(statementSet.Location, "set hidden is not implemented")); } break; case "popup_menu": if (constant.IsTruthy()) // The default is to show it so we flag it if it's hidden { _proc.Attributes &= ~ProcAttributes.HidePopupMenu; } else { _proc.Attributes |= ProcAttributes.HidePopupMenu; } if (!DMCompiler.Settings.SuppressUnimplementedWarnings) { DMCompiler.Warning(new CompilerWarning(statementSet.Location, "set popup_menu is not implemented")); } break; case "instant": if (constant.IsTruthy()) { _proc.Attributes |= ProcAttributes.Instant; } else { _proc.Attributes &= ~ProcAttributes.Instant; } if (!DMCompiler.Settings.SuppressUnimplementedWarnings) { DMCompiler.Warning(new CompilerWarning(statementSet.Location, "set instant is not implemented")); } break; case "background": if (constant.IsTruthy()) { _proc.Attributes |= ProcAttributes.Background; } else { _proc.Attributes &= ~ProcAttributes.Background; } if (!DMCompiler.Settings.SuppressUnimplementedWarnings) { DMCompiler.Warning(new CompilerWarning(statementSet.Location, "set background is not implemented")); } break; case "name": DMASTConstantString name = statementSet.Value as DMASTConstantString; if (name is null) { throw new CompileErrorException(statementSet.Location, "bad text"); } _proc.VerbName = name.Value; if (!DMCompiler.Settings.SuppressUnimplementedWarnings) { DMCompiler.Warning(new CompilerWarning(statementSet.Location, "set name is not implemented")); } break; case "category": DMASTConstantString category = statementSet.Value as DMASTConstantString; if (category is null) { throw new CompileErrorException(statementSet.Location, "bad text"); } _proc.VerbCategory = category.Value; if (!DMCompiler.Settings.SuppressUnimplementedWarnings) { DMCompiler.Warning(new CompilerWarning(statementSet.Location, "set category is not implemented")); } break; case "desc": DMASTConstantString desc = statementSet.Value as DMASTConstantString; if (desc is null) { throw new CompileErrorException(statementSet.Location, "bad text"); } _proc.VerbDesc = desc.Value; if (!DMCompiler.Settings.SuppressUnimplementedWarnings) { DMCompiler.Warning(new CompilerWarning(statementSet.Location, "set desc is not implemented")); } break; case "invisibility": // The ref says 0-101 for atoms and 0-100 for verbs // BYOND doesn't clamp the actual var value but it does seem to treat out-of-range values as their extreme DMASTConstantFloat invisFloat = statementSet.Value as DMASTConstantFloat; if (invisFloat is null) { DMASTConstantInteger invisInt = statementSet.Value as DMASTConstantInteger; if (invisInt is null) { throw new CompileErrorException(statementSet.Location, "bad num"); } _proc.Invisibility = Convert.ToSByte(Math.Clamp(invisInt.Value, 0, 100)); } else { _proc.Invisibility = Convert.ToSByte(Math.Clamp(Math.Floor(invisFloat.Value), 0, 100)); } if (!DMCompiler.Settings.SuppressUnimplementedWarnings) { DMCompiler.Warning(new CompilerWarning(statementSet.Location, "set invisibility is not implemented")); } break; case "src": if (!DMCompiler.Settings.SuppressUnimplementedWarnings) { DMCompiler.Warning(new CompilerWarning(statementSet.Location, "set src is not implemented")); } break; } }