Skip to content

Commit

Permalink
Merge branch 'feat/promises' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
adam-coster committed Sep 26, 2023
2 parents ef0cc56 + dfaeabe commit c016001
Show file tree
Hide file tree
Showing 14 changed files with 322 additions and 9 deletions.
Empty file added modules/futures/.gitignore
Empty file.
36 changes: 36 additions & 0 deletions modules/futures/Futures.yyp

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 40 additions & 0 deletions modules/futures/objects/o_test/Create_0.gml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
show_debug_message("Distributed callback test");

step = 0;

/// @type {Function.distributed_callback_signature}
var to_distribute = function (results){
show_debug_message("distributed callback."+
" Callback Idx: " + string(results.current_callback_idx) +
" Last value: " + string(results.last_value) +
" Step: " + string(o_test.step) +
" Done: " + string(results.done) +
" Aborted: " + string(results.aborted));
if(results.current_callback_idx != o_test.step){
throw("Not being distributed across steps!");
}
if(o_test.step > 0 && results.last_value != o_test.step-1){
throw("Values not being properly passed!");
}
return o_test.step;
}

distribute_over_steps([
to_distribute,
to_distribute,
to_distribute,
to_distribute,
to_distribute,
to_distribute,
to_distribute,
to_distribute
], {
/// @param {Struct.DistributedCallbacks} results
on_done: function(results){
show_debug_message("Distributed callback test done.");
if(!results.done){
throw("Distributed callback test not done!");
}
game_end();
},
});
Empty file.
1 change: 1 addition & 0 deletions modules/futures/objects/o_test/Step_1.gml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
step ++;
34 changes: 34 additions & 0 deletions modules/futures/objects/o_test/o_test.yy

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 55 additions & 0 deletions modules/futures/rooms/Room1/Room1.yy

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

130 changes: 130 additions & 0 deletions modules/futures/scripts/DistributedCallbacks/DistributedCallbacks.gml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
function DistributedCallbacks () constructor {
done = false;
aborted = false;

current_callback_idx = -1;
/// @type {Array<Function.distributed_callback_signature>}
callbacks = [];
/// @type {Struct.DistributeOverStepsOptions|Undefined}
options = undefined;

/// The values returned by all prior callbacks, in order
/// @type {Array<Any>}
values = [];
/// The value returned by the prior callback
/// @type {Any}
last_value = undefined;

static __done = function(){
if(self.done){
return;
}
self.done = true;
if( is_struct(self.options) &&
is_callable(self.options[$ "on_done"])
){
self.options.on_done(self);
}
}

static next = function(){
var this = self;

self.current_callback_idx++;
if (self.current_callback_idx >= array_length(self.callbacks)){
return self.__done();
}
var next_callback = self.callbacks[self.current_callback_idx];
// Unset to prevent memory leaks
self.callbacks[self.current_callback_idx] = undefined;

// If we're already aborted, we're done!
if(self.aborted){
self.callbacks = []; // Unset to remove refs to functions
return;
}

// If this callback doesn't exist, go ahead to the next one.
if(is_undefined(next_callback)){
return self.next();
}

// Call the callback
try{
// Add an entry to the values prior to calling, in case it fails (so we don't get wonky indexes)
array_push(self.values, undefined);
self.last_value = next_callback(this);
self.values[array_length(self.values)] = self.last_value;
}
catch(err){
self.last_value = undefined;
if(is_struct(self.options) && self.options[$ "bail"]){
self.abort();
}
if(is_struct(self.options) && is_callable(self.options[$ "on_error"])){
self.options.on_error(err);
}
else{
throw err;
}
}

var was_last = self.current_callback_idx >= array_length(self.callbacks) - 1;

// If we haven't aborted, punt the next callback to the next step
if(was_last){
self.__done();
}
else if(!self.aborted){
var _next = method(this, self.next);
call_later(1, time_source_units_frames, _next);
}
}

static abort = function(){
self.aborted = true;
self.callbacks = []; // Unset to remove function refs for GC

}
}

/// Signature for functions that can be distributed over steps.
///
/// @param {Struct.DistributedCallbacks} results The cumulative results of all prior callbacks
function distributed_callback_signature(results) {}

/// Distrubute a sequence of callbacks across steps. Callbacks
/// are executed one at a time, in order, with a single-step wait
/// in between. Callbacks are passed a `results` object, which
/// includes cumalative info about the process along with an `abort`
/// function to terminate the distribution.
///
/// @param {Array<Function.distributed_callback_signature>} callbacks
/// @param {Struct.DistributeOverStepsOptions} [options]
/// @returns {Struct.DistributedCallbacks}
function distribute_over_steps(callbacks, options){
var results = new DistributedCallbacks();
results.callbacks = callbacks;
results.options = options;
results.next();
return results;
}

function DistributeOverStepsOptions () constructor {
/// An optional function to call when all callbacks have been called.
/// @type {Function.distributed_callback_signature|Undefined}
on_done = undefined;

/// If true, the distribution will abort if any callback throws
/// an error. Else the distribution will continue to the
/// next callback on error, assuming the error was handled with on_error
/// @type {Boolean|Undefined}
bail = undefined;

/// If provided, this function will be called with the error
/// thrown by any callback. If not provided, errors will be
/// thrown. If `bail` is true AND this function is provided,
/// the distribution will abort on error.
/// @type {Function|Undefined}
on_error = undefined;
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions packages/parser/src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import type { GmlVisitor } from '../gml-cst.js';
import type { JsdocSummary } from './jsdoc.js';
import { GmlLexer } from './lexer.js';
import type { GmlParseError } from './project.diagnostics.js';
import { Reference, ReferenceableType } from './project.location.js';
import { Signifier } from './signifiers.js';
import type { Reference, ReferenceableType } from './project.location.js';
import type { Signifier } from './signifiers.js';
import { c, categories, t, tokens } from './tokens.js';
import { Type, TypeStore } from './types.js';
import type { Type, TypeStore } from './types.js';

export interface GmlParsed {
lexed: ILexingResult;
Expand Down Expand Up @@ -708,6 +708,8 @@ export interface VisitorContext {
isStatic?: boolean;
/** While processing a function expression or struct literal, the signifier may come from an assignment operation. */
signifier?: Signifier;
/** While processing a function expression, we may have expected type information for the value */
type?: TypeStore;
/** While processing `method()` calls, we may find the self-context
* of the function in the second argument.
*/
Expand Down
1 change: 1 addition & 0 deletions packages/parser/src/visitor.functionExpression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export function visitFunctionExpression(
signifier?.describe(docs?.jsdoc.description);
const functionType =
signifier?.getTypeByKind('Function') ||
getTypeOfKind(ctx.type, 'Function')?.derive() ||
new Type('Function').named(functionName);
signifier?.setType(functionType);
if (signifier && docs?.jsdoc.deprecated) {
Expand Down
3 changes: 3 additions & 0 deletions packages/parser/src/visitor.identifierAccessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,9 @@ function processFunctionArguments(
functionCtx.self = methodSelf;
}
const expectedType = functionType?.getParameter(argIdx);
if (expectedType) {
functionCtx.type = expectedType.type;
}
if (token.children.jsdoc) {
visitor.jsdoc(token.children.jsdoc[0].children, functionCtx);
}
Expand Down
8 changes: 6 additions & 2 deletions packages/parser/src/visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ export class GmlSignifierVisitor extends GmlVisitorBase {
const structFromDocs =
ctx.docs?.type[0]?.kind === 'Struct'
? (ctx.docs?.type[0] as StructType)
: undefined;
: getTypeOfKind(ctx.type, 'Struct')?.derive();
const struct =
ctx.signifier?.getTypeByKind('Struct') ||
structFromDocs ||
Expand Down Expand Up @@ -620,7 +620,11 @@ export class GmlSignifierVisitor extends GmlVisitorBase {
this,
{ name, range, container: struct },
parts.assignmentRightHandSide,
{ docs, ctx, instance: true },
{
docs,
ctx: { ...ctx, type: struct.getMember(name)?.type },
instance: true,
},
);
} else {
// Then we're in short-hand mode, where the RHS has the same
Expand Down
4 changes: 0 additions & 4 deletions packages/vscode/src/config.mts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import type { Channel } from '@bscotch/gamemaker-releases';
import vscode from 'vscode';

interface SpriteSourcesConfig {
[projectPath: string]: string[];
}

class StitchConfig {
public context!: vscode.ExtensionContext;

Expand Down

0 comments on commit c016001

Please sign in to comment.