Last active
May 14, 2016 23:09
-
-
Save routevegetable/234f2982f31c08d382955e234c55733a to your computer and use it in GitHub Desktop.
Another file from my youth
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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