Skip to content

Instantly share code, notes, and snippets.

@routevegetable
Last active May 14, 2016 23:09
Show Gist options
  • Save routevegetable/234f2982f31c08d382955e234c55733a to your computer and use it in GitHub Desktop.
Save routevegetable/234f2982f31c08d382955e234c55733a to your computer and use it in GitHub Desktop.
Another file from my youth
function TickerEvent(time, obj) {
this.Time = time;
this.Obj = obj;
}
function TickerSpan(start, end, obj) {
this.Start = start;
this.End = end;
this.Obj = obj;
}
/*
The ticker schedules events and spans.
ScheduleEvent schedules a TickerEvent. ScheduleSpan does the same for TickerSpan.
Both return an Id.
GetNextEvent returns an object of the form: {Id: "MyScheduledId", Obj: "MyEventObj", Event: "Start|End|Discrete", Time: 624521}
*/
function Ticker() {
this.Time = 0;
this.Events = {};
this.Spans = {};
this.EvCounter = 0;
}
Ticker.prototype.Acknowledge = function(id) {
if(this.Spans.hasOwnProperty(id))
if(!this.Spans[id].Ack)
this.Spans[id].Ack = true;
else
delete this.Spans[id];
if(this.Events.hasOwnProperty(id))
delete this.Events[id];
}
// Schedule an event
// return scheduled id
Ticker.prototype.ScheduleEvent = function(time, obj) {
var id = this.EvCounter++;
this.Events[id] = {Time: time, Obj: obj, Ack: false};
return id;
}
// Schedule a span
// return scheduled id
Ticker.prototype.ScheduleSpan = function(start, end, obj) {
var id = this.EvCounter++;
this.Spans[id] = {Start: start, End: end, Obj: obj, Ack: false};
return id;
}
function Where(things, func) {
var ret = [];
for(var i in things) {
if(func(things[i])) ret.push(things[i]);
}
return ret;
}
function Select(things, func) {
var ret = [];
for(var i in things) {
ret.push(func(things[i]));
}
return ret;
}
Ticker.prototype.GetSpansInRange = function(from, to) {
return Select(
Where(this.Spans, function(s) { return s.End >= from || s.Start < to }),
function(s) { return new TickerSpan(s.Start, s.End, s.Obj); });
}
Ticker.prototype.GetEventsInRange = function(from, to) {
return Select(
Where(this.Events, function(e) { return e.Time >= from || e.Time < to }),
function(e) { return new TickerEvent(e.Time, e.Obj); });
}
Ticker.prototype.GetActiveSpans = function(start) {
var ret = [];
for(var i in this.Spans) {
var span = this.Spans[i];
if(span.Start <= this.Time && span.Ack && span.End > this.Time)
ret.push(new TickerSpan(span.Start, span.End, span.Obj))
}
return ret;
}
// Return {Id: "MyScheduledId", Obj: "MyEventObj", Event: "Start|End|Discrete", Time: 624521}
Ticker.prototype.GetNextEvent = function() {
var earliest = 100000000000;
var ret = null;
for(var i in this.Events) {
var ev = this.Events[i];
var id = i;
if(ev.Time < earliest) {
earliest = ev.Time;
ret = {
Id: i,
Time: earliest,
Obj: ev.Obj,
Event: "Discrete"
}
}
}
for(var i in this.Spans) {
var span = this.Spans[i];
var evName = null;
if(!span.Ack) {
if(span.Start < earliest) {
earliest = span.Start;
evName = "Start";
}
}
if(span.Ack) {
if(span.End < earliest) {
earliest = span.End;
evName = "End";
}
}
if(evName !== null) {
ret = {
Id: i,
Time: earliest,
Obj: span.Obj,
Event: evName
}
}
}
return ret;
}
// Advance to a new time, processing events up until that time
Ticker.prototype.Advance = function(newTime, eventFunc) {
var nextEvent = this.GetNextEvent();
while(nextEvent !== null) {
if(nextEvent.Time > newTime) break;
var results = eventFunc(nextEvent, this.GetActiveSpans());
for(var i in results) {
var res = results[i];
if(res.constructor === TickerSpan) {
this.ScheduleSpan(res.Start, res.End, res.Obj);
} else if(res.constructor === TickerEvent) {
this.ScheduleEvent(res.Time, res.Obj);
}
}
// Remove this event/span from here
this.Acknowledge(nextEvent.Id);
nextEvent = this.GetNextEvent();
}
this.Time = newTime;
}
/*
var tick = new Ticker(0);
tick.ScheduleEvent(5, "MY TICKER EVENT");
tick.ScheduleEvent(7, "MY TICKER EVENT 2");
tick.ScheduleSpan(6,10, "MY TICKER SPAN");
tick.ScheduleEvent(0, "10SEC")
tick.ScheduleEvent(3, "10SEC")
tick.Advance(50, function(ev) {
console.log(JSON.stringify(ev));
if(ev.Obj == "10SEC") {
return [new TickerEvent(ev.Time + 10, "10SEC")]
}
});
//Ticker.Test();
exports.Ticker = Ticker
*/
// An EventObject is a set of handlers associated with a span or discrete.
// Compiles a value to an event object-returning function.
// If it's already a function, return the function.
function CompileValue(val) {
if(val.constructor === String) {
return function(a) { var obj = {}; obj[val] = a; return obj; }
} else if(val.constructor === Function) {
return val;
} else {
throw new Error("value: " + val + " is not a string or a function");
}
}
function MergeEventMaps(m1, m2) {
var ret = {};
function Add(n, v) {
if(!ret.hasOwnProperty(n)) ret[n] = [];
ret[n].push(v);
}
for(var i in m1) Add(i, m1[i]);
for(var i in m2) Add(i, m2[i]);
return ret;
}
/*
An Event Object has properties "Start", "End" and "Discrete".
It is attached to a span or discrete, and triggered 1-to-many times by it.
The values of those properties are function(time), where time is the time the event was raised.
The Obj field of an event is NOT an Event Object in this sense.
The Obj field contains either an event name string, or an EventObject.
*/
// Behold, the scheduling language!
// A span which starts on a discrete, and ends "time" ticks afterward
function After (time, discrete) {
var cDiscrete = CompileValue(discrete);
return function(eventObject) {
return cDiscrete({
Discrete: function(t) {
return new TickerSpan(t, t + time, eventObject);
}
});
}
}
// A discrete which is emitted whenever something is emitted from inside it
function Any(anything) {
var c = CompileValue(anything);
return function(eventObject) {
return c({
Start: function(t) { return new TickerEvent(t, eventObject); },
End: function(t) { return new TickerEvent(t, eventObject); },
Discrete: function(t) { return new TickerEvent(t, eventObject); }
});
}
}
// A discrete emitted "time" ticks after a discrete
function Offset(time, discrete) {
return End(After(time, discrete));
}
// A discrete emitted when a discrete is emitted
function On(discrete) {
var cDiscrete = CompileValue(discrete);
return function(eventObject) {
return cDiscrete({Discrete: function(t) { return new TickerEvent(t, eventObject); }})
}
}
/*
Event object responds to an event by creating more events.
Takes the form of:
{
Start: function(time) { return newEvent(innerEventObject); },
End: function(time) { return newEvent(innerEventObject); }
}
If we say that "Cleanup" is After(5, End("Party")) ,
we mean that "Party" needs the following event object added to it:
{End: function(time) {
TickerEvent(time, {
Discrete: function(time) {
TickerSpan(time, time + 5, "Cleanup") }})}}
It's inside out.
End takes a span and an event object ev, and returns span({End: function(time) { TickerEvent(time, ev) } })
where span is a function to put its parameter's event object at the end of its own event chain.
After takes a discrete, a length and an event object ev, and returns discrete({Discrete: function(time) { TickerSpan(time, time + length, ev) } })
where discrete is a function to put its parameter's event object at the end of its own event chain.
Event object function takes an event object
and produces an event object with the given event object
at the end of its event chain(nested most deeply).
Compiled functions like Start and End, produce an event object function.
They also take event object functions as parameters.
This is so that they can compose their returned event object function from
it in such a way that its event object ends up nested inside the nested function's event object,
and the passed in eventobject is nested inside that, deeper still.
Once this is over, we have a function which takes an event object,
and produces an event object with the given object at the end of its event chain.
We can call this function with "HELLO",
and we end up with an event object with "HELLO" nested most deeply in the event chain.
*/
// A discrete emitted when a span starts
function Start (span) {
var cSpan = CompileValue(span); // span is nested inside Start()
return function(eventObject) {
return cSpan({ // cSpan has our object fed to it.
Start: function(t) { return new TickerEvent(t, eventObject); }
})
}
}
// A discrete emitted when a span ends
function End (span) {
var cSpan = CompileValue(span);
return function(eventObject) {
return cSpan({
End: function(t) {
return new TickerEvent(t, eventObject);
}
})
}
}
function WaitUntilMult(mult, discrete) {
var cDiscrete = CompileValue(discrete);
return function(eventObject) {
return cDiscrete({
Discrete: function(t) {
var m = Math.ceil(t / mult) * mult;
return new TickerEvent(m, eventObject);
}
})
}
}
/*
function While(discrete, span) {
var cDiscrete = CompileValue(discrete);
return function(eventObject) {
return cDiscrete({
End: function(t, as) {
if(as.hasOwnProperty(span))
return new TickerEvent(t, eventObject);
else
return null;
});
}
}
}*/
function EventObjectString(ev) {
var str = "EVENT OBJECT("
for(var i in ev) {
str = str + i;
}
return str + ")"
}
function Brain(ticker) {
this.NamedIndex = {}; // EventName -> Start|End|Discrete -> function(time): [newEvents]
this.ticker = ticker;
}
Brain.prototype.AddEvent = function(name, expression) {
var compiled = expression(name);
var trigger = null;
var eventObj = null;
for(var i in compiled) {
trigger = i;
eventObj = compiled[i];
}
if(!this.NamedIndex.hasOwnProperty(trigger)) this.NamedIndex[trigger] = [];
this.NamedIndex[trigger].push(eventObj); // Add the new event object to the index
}
Brain.prototype.UserEvent = function(eventName, time) {
this.ticker.ScheduleEvent(time, eventName);
}
Brain.prototype.UserSpan = function(spanName, start, end) {
this.ticker.ScheduleSpan(start, end, spanName);
}
// Processes a ticker event, returns array of new tickerevents and tickerspans
Brain.prototype.Process = function(event, activeSpans) {
var newThings = [];
if(event.Obj.constructor === String) {
console.log("[" + event.Time + "] " + event.Obj + " (" + event.Event + ")");
var name = event.Obj;
var target= this.NamedIndex[name]
for(var i in target)
if(target[i].hasOwnProperty(event.Event)) {
var result = target[i][event.Event](event.Time, activeSpans)
if(result !== null) newThings.push(result);
}
} else {
// Not named, it's an Event Object.
if(event.Obj.hasOwnProperty(event.Event))
newThings = [event.Obj[event.Event](event.Time, activeSpans)];
}
return newThings;
}
t = new Ticker();
b = new Brain(t);
// A 5 hour birthday party
b.AddEvent("Birthday Party", After(5, "Birthday"))
b.AddEvent("Hangover", After(4, WaitUntilMult(10, End("Birthday Party"))))
// PartyDone is a discrete which happens when the party ends
b.AddEvent("Party Done", End("Birthday Party"))
// This happens when the party starts/stops
b.AddEvent("Birthday Party Edge", Any("Birthday Party"))
// Spend an hour cleaning up
b.AddEvent("Post-party cleanup", After(1, "Party Done"));
// I become a new age 3 hours after the end of my birthday
b.AddEvent("New Age", Offset(3, "Party Done"))
// Birthday happens at 20
b.UserEvent("Birthday", 20)
t.Advance(t.Time + 1, function(ev) { return b.Process(ev); });
t.Advance(t.Time + 1, function(ev) { return b.Process(ev); });
t.Advance(t.Time + 1, function(ev) { return b.Process(ev); });
t.Advance(t.Time + 1, function(ev) { return b.Process(ev); });
t.Advance(t.Time + 1, function(ev) { return b.Process(ev); });
t.Advance(t.Time + 1, function(ev) { return b.Process(ev); });
t.Advance(t.Time + 1, function(ev) { return b.Process(ev); });
t.Advance(t.Time + 1, function(ev,a) { return b.Process(ev,a); });
repl = require("repl");
repl.start();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment