Skip to content

Instantly share code, notes, and snippets.

@OverlappingElvis
Last active July 1, 2019 14:58
Show Gist options
  • Save OverlappingElvis/fa66977abe65742c3286130ba1f50b40 to your computer and use it in GitHub Desktop.
Save OverlappingElvis/fa66977abe65742c3286130ba1f50b40 to your computer and use it in GitHub Desktop.
const chevrotain = require(`chevrotain`)
const { Lexer, Parser } = chevrotain
const allTokens = []
const createToken = (options) => {
const token = chevrotain.createToken(options)
allTokens.push(token)
return token
}
const WhiteSpace = createToken({
name: `WhiteSpace`,
pattern: /\s+/,
group: Lexer.SKIPPED
})
const On = createToken({
name: `On`,
pattern: /on/
})
const Off = createToken({
name: `Off`,
pattern: /off/
})
const Brightness = createToken({
name: `Brightness`,
pattern: /brightness/
})
const Color = createToken({
name: `Color`,
pattern: /color/
})
const Wait = createToken({
name: `Wait`,
pattern: /wait/
})
const Alias = createToken({
name: `Alias`,
pattern: /alias/
})
const NUMBER = createToken({
name: `NUMBER`,
pattern: /\d+/
})
const HEX_COLOR = createToken({
name: `HEX_COLOR`,
pattern: /#[a-fA-F0-9]{6}/
})
const IDENTIFIER = createToken({
name: `IDENTIFIER`,
pattern: /[a-zA-Z]+/
})
const LightLexer = new Lexer(allTokens)
class LightParser extends Parser {
constructor () {
super(allTokens)
const $ = this
$.RULE(`program`, () => {
$.MANY(() => {
$.SUBRULE($.statement)
})
})
$.RULE(`statement`, () => {
$.OR([
{
ALT: () => $.SUBRULE($.onStatement)
},
{
ALT: () => $.SUBRULE($.offStatement)
},
{
ALT: () => $.SUBRULE($.colorStatement)
},
{
ALT: () => $.SUBRULE($.brightnessStatement)
},
{
ALT: () => $.SUBRULE($.waitStatement)
},
{
ALT: () => $.SUBRULE($.aliasStatement)
}
])
})
$.RULE(`onStatement`, () => {
$.CONSUME(On)
})
$.RULE(`offStatement`, () => {
$.CONSUME(Off)
})
$.RULE(`colorStatement`, () => {
$.CONSUME(Color)
$.OR([
{
ALT: () => $.CONSUME(HEX_COLOR)
},
{
ALT: () => $.CONSUME(IDENTIFIER)
}
])
})
$.RULE(`brightnessStatement`, () => {
$.CONSUME(Brightness)
$.OR([
{
ALT: () => $.CONSUME(NUMBER)
},
{
ALT: () => $.CONSUME(IDENTIFIER)
}
])
})
$.RULE(`waitStatement`, () => {
$.CONSUME(Wait)
$.OR([
{
ALT: () => $.CONSUME(NUMBER)
},
{
ALT: () => $.CONSUME(IDENTIFIER)
}
])
})
$.RULE(`aliasStatement`, () => {
$.CONSUME(Alias)
$.CONSUME(IDENTIFIER)
$.OR([
{
ALT: () => $.CONSUME(HEX_COLOR)
},
{
ALT: () => $.CONSUME(NUMBER)
}
])
})
$.performSelfAnalysis()
}
}
const parser = new LightParser()
const lexed = LightLexer.tokenize(`alias green #00ff00 alias blue #0000ff alias delay 600 brightness 75 on wait delay color green wait delay color blue wait delay off`)
if (lexed.errors.length) {
console.log(`Lexer error!`)
console.log(lexed.errors)
throw new Error()
}
parser.input = lexed.tokens
const cst = parser.program()
if (parser.errors.length) {
console.log(`Parser error!`)
console.log(parser.errors)
throw new Error()
}
const BaseCstVisitor = parser.getBaseCstVisitorConstructor()
class LightInterpreter extends BaseCstVisitor {
constructor () {
super()
this.light = {
on: false,
brightness: 100,
color: `#ffffff`
}
this.scope = {}
this.validateVisitor()
}
program (context) {
for (const statement of context.statement) {
this.visit(statement)
}
}
statement (context) {
if (context.onStatement) {
this.visit(context.onStatement)
} else if (context.offStatement) {
this.visit(context.offStatement)
} else if (context.colorStatement) {
this.visit(context.colorStatement)
} else if (context.brightnessStatement) {
this.visit(context.brightnessStatement)
} else if (context.waitStatement) {
this.visit(context.waitStatement)
} else if (context.aliasStatement) {
this.visit(context.aliasStatement)
}
}
onStatement (context) {
console.log(`turning light on (light was ${this.light.on ? `on` : `off`})`)
this.light.on = true
}
offStatement (context) {
console.log(`turning light off (light was ${this.light.on ? `on` : `off`})`)
this.light.on = false
}
colorStatement (context) {
const color = context.IDENTIFIER ? this.scope[context.IDENTIFIER[0].image] : context.HEX_COLOR[0].image
console.log(`setting color to ${color} (color was ${this.light.color})`)
this.light.color = color
}
brightnessStatement (context) {
const value = context.IDENTIFIER ? this.scope[context.IDENTIFIER[0].image] : parseInt(context.NUMBER[0].image, 10)
console.log(`brightness was ${this.light.brightness}`)
console.log(`setting brightness to ${value}`)
this.light.brightness = value
}
waitStatement (context) {
const value = context.IDENTIFIER ? this.scope[context.IDENTIFIER[0].image] : parseInt(context.NUMBER[0].image, 10)
console.log(`waiting ${value}`)
}
aliasStatement (context) {
const identifier = context.IDENTIFIER[0].image
console.log(`setting ${identifier} to ${context.NUMBER ? context.NUMBER[0].image : context.HEX_COLOR[0].image}`)
if (context.NUMBER) {
this.scope[identifier] = parseInt(context.NUMBER[0].image, 10)
} else {
this.scope[identifier] = context.HEX_COLOR[0].image
}
}
}
const interpreter = new LightInterpreter()
interpreter.visit(cst)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment