-
-
Save jahan-addison/f72417f6c1789caa434219351b665193 to your computer and use it in GitHub Desktop.
DSL in Javascript (Typescript)
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
ƛ({ | |
server: 'irc.freenode.org', | |
nick: 'dslbot', | |
channel: '##dslbottesting' | |
}, | |
Connect(6667)( | |
line => on(/PING (\S+)/)(_ => { | |
server`PONG ${_}` | |
}), | |
line => on(/hello/)(_ => { | |
reply`Howdy` | |
}), | |
line => on(/dslbot\?/)(_ => { | |
reply`I was written in a DSL-ish!` | |
}) | |
), | |
error => '💥 Something went wrong.' | |
); |
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
export type Maybe<T> = Some<T> | None; | |
export type None = () => undefined; | |
export type Some<T> = () => T; | |
export const None: None = () => void(0); | |
export function Some<T>(_: T): Some<T> { | |
return () => _; | |
} | |
export function match<T>(_: Maybe<T>, some: (_: any) => any, none: Function) { | |
if (Guard<T>(_)) { | |
return some(_()); | |
} else { | |
return none(); | |
} | |
} | |
export function Guard<T>(_: Maybe<T>): _ is Maybe<T> { | |
return (<Maybe<T>>_)() !== None(); | |
} | |
export interface Authentication { | |
server: string, | |
nick: string, | |
channel: string | |
} |
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
import { None, Some, match, Maybe, Authentication } from './types'; | |
import { | |
createConnection, | |
Socket | |
} from 'net'; | |
// State Reification | |
export interface Machine { | |
auth: Maybe<Authentication>, | |
port: Maybe<number>, | |
line: Maybe<string>, | |
connection: Maybe<Socket>, | |
error: Maybe<Function>, | |
connected: boolean, | |
} | |
export async function connect(state: Machine): Promise<Machine> { | |
const auth = state.auth; | |
const port = state.port; | |
return match<Authentication>(auth, | |
credentials => { | |
return state.connection = Some<Socket>(createConnection({ | |
host: credentials.server, port: match<number>(port, | |
_ => _, | |
_ => 6667) | |
}, () => { | |
state.connected = true; | |
authenticate(state); | |
})), state; | |
}, | |
_ => { | |
throw new ReferenceError('No credentials provided'); | |
}); | |
} | |
export function authenticate(state: Machine): void { | |
match<Authentication>(state.auth, | |
credentials => match<Socket>(state.connection, | |
connection => { | |
connection.write(`USER ${credentials.nick} * * :bot\r\n`); | |
connection.write(`NICK ${credentials.nick}\r\n`); | |
setTimeout(() => { | |
connection.write(`JOIN ${credentials.channel}\r\n`); | |
}, 1000); | |
}, | |
_ => { | |
throw new ReferenceError('Connection not established'); | |
}), | |
_ => { | |
throw new ReferenceError('Not authenticated'); | |
}); | |
} |
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
import { None, Some, match, Authentication } from './types'; | |
import * as Irc from './irc'; | |
import { Socket } from 'net'; | |
let State: Irc.Machine = { | |
connection: None, | |
auth: Some({ | |
server: 'irc.freenode.org', | |
nick: 'dslbot', | |
channel: '##dslbottesting' | |
}), | |
port: None, | |
line: None, | |
error: None, | |
connected: false, | |
} | |
function applyMany(context: Irc.Machine, applied: Function[]): void { | |
applied.forEach((callback: Function) => { | |
match<string>(context.line, | |
line => { | |
callback.apply(line); | |
}, | |
_ => { | |
console.warn('context was insufficient'); | |
} | |
); | |
}); | |
} | |
export function ƛ(details: Authentication, _, Error: Function) { | |
State.auth = Some(details); | |
State.error = Some(Error); | |
} | |
export function Connect(port: number): Function { | |
State.port = Some(port); | |
return (...execution) => { | |
try { | |
Irc.connect(State).then(state => { | |
match<Socket>(State.connection, | |
client => { | |
client.on('data', data => { | |
State.line = Some(data.toString().split(/\r\n/)[0]); | |
applyMany(State, execution); | |
}); | |
}, | |
_ => { | |
throw new ReferenceError('Failed to connect'); | |
} | |
); | |
}); | |
} catch(e) { | |
match<Function>(State.error, | |
call => call(), | |
_ => { | |
console.error('Uh oh ⚡️', e); | |
}); | |
} | |
}; | |
} | |
export function server(prefix, response): boolean { | |
return match<Socket>(State.connection, | |
connection => connection.write(`${prefix[0]} :${response}\r\n`) | |
_ => false); | |
} | |
export function reply(response): boolean { | |
return match<Socket>(State.connection, | |
connection => { | |
const where = match<Authentication>(State.auth, | |
credentials => credentials.channel, | |
() => '##dslbottesting'); | |
return connection.write(`PRIVMSG ${where} :${response}\r\n`); | |
}, | |
_ => false); | |
} | |
export function on(what: RegExp): Function { | |
return (callback) => { | |
match<string>(State.line, | |
line => { | |
if (what.test(line)) { | |
const capture = what.exec(line); | |
callback(capture ? capture[1] : line.split(/:/)[2]); | |
} | |
}, | |
_ => { | |
throw new ReferenceError('Connection not established'); | |
}) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment