Skip to content

Instantly share code, notes, and snippets.

@PaulMaynard
Last active May 23, 2016 19:55
Show Gist options
  • Save PaulMaynard/1e92a3f55844fadf60ce5c2957e44912 to your computer and use it in GitHub Desktop.
Save PaulMaynard/1e92a3f55844fadf60ce5c2957e44912 to your computer and use it in GitHub Desktop.
Norwest
class Scope {
constructor(vars, parents) {
this.vars = vars || new Map()
this.parents = parents || [Scope.global]
}
get(name) {
if(this.vars.has(name)) {
return this.vars.get(name)
} else if(this.parents.length > 0) {
for(let p of this.parents) {
if(p.has(name)) {
return p.get(name)
}
}
return nil
} else {
return nil
}
}
set(name, val) {
if(this.vars.has(name)) {
this.vars.set(name, val)
} else if(this.parents.length > 0) {
for(let p of this.parents) {
if(p.has(name)) {
p.set(name)
return
}
}
}
}
has(name) {
if(this.vars.has(name)) {
return true
} else if(this.parents.length > 0) {
for(let p of this.parents) {
if(p.has(name)) {
return true
} else {
return false
}
}
} else {
return false
}
}
def(name, val = nil) {
this.vars.set(name, val)
}
debug() {
let o = {}
for(let [k, v] of this.vars) {
o[k.value] = v.value
}
//o.Parents = this.parents.map(p => p.debug())
return o
}
}
class Value {
constructor(val, type) {
this.value = val
this.type = type
}
}
let nil = new Value(null, 'Nil')
function Name(name) {
if(Name.symbols.has(name)) {
return Name.symbols.get(name)
} else {
let val = new Value(name, 'Name')
Name.symbols.set(name, val)
return val
}
}
Name.symbols = new Map()
class Block extends Value {
constructor(exprs, lexScope) {
super(exprs, 'Block')
this.run = (runScope = new Scope()) => {
let s = new Scope(new Map(), [runScope, lexScope])
let r = nil
for(let e of this.value) {
r = e.exec(s)
}
return r
}
}
}
class JSFunc extends Value {
constructor(func) {
super(func, 'Function')
}
call(args, scope) {
return this.value(...args)
}
}
class ScopeFunc extends JSFunc {
constructor(func) {
super(func)
}
call(args, scope) {
return this.value(args, scope)
}
}
class Expression {
}
class Literal extends Expression {
constructor(val) {
super()
this.value = val
}
exec() {
return this.value
}
}
class Ref extends Expression {
constructor(name) {
super()
this.name = name
}
exec(scope) {
return scope.get(this.name)
}
}
class CallExpr extends Expression {
constructor(func, args) {
super()
this.func = func
this.args = args
}
exec(scope) {
return this.func.exec(scope).call(this.args.map(a => a.exec(scope)), scope)
}
}
class BlockExpr extends Expression {
constructor(exprs) {
super()
this.exprs = exprs
}
exec(scope) {
return new Block(this.exprs, scope)
}
}
function build(node) {
return build.types[node.Type](node)
}
build.types = {
ref({name}) {
return new Ref(Name(name))
},
literal({Class, value}) {
return new Literal(build.classes[Class](value))
},
block({exprs}) {
return new BlockExpr(exprs.map(build))
},
call({func, args}) {
return new CallExpr(build(func), args.map(build))
}
}
build.classes = {
number(num) {
return new Value(num, 'Number')
},
string(str) {
return new Value(str, 'String')
},
name(n) {
return Name(n)
},
}
Scope.global = new Scope(new Map([
[Name('nil'), nil],
[new Name('run'), new JSFunc((block, scope) => {
return block.run(scope || new Scope())
})],
[new Name('list'), new JSFunc((...elems) => new Value(elems, 'List'))],
// Scopes
[new Name('scope'), new ScopeFunc(([vars, parents], scope) => {
if(vars) {
return new Value(new Scope(new Map(vars.value.map(v => v.value)), []), 'Scope')
}
return new Value(scope, "Scope")
})],
[new Name('def'), new ScopeFunc(([name, val, scope], defscope) =>
((scope && scope.value) || defscope).def(name, val))],
[new Name('get'), new ScopeFunc(([name, scope], defscope) =>
((scope && scope.value) || defscope).get(name))],
[new Name('set'), new ScopeFunc(([name, val, scope], defscope) =>
((scope && scope.value) || defscope).set(name, val))],
// Math
[Name('+'), new JSFunc((...args) =>
new Value(args.reduce((a, b) => a + b.value, 0), 'Number'))],
[Name('-'), new JSFunc((a, b) => new Value(a.value - b.value, 'Number'))],
[Name('*'), new JSFunc((a, b) =>
new Value(args.reduce((a, b) => a * b.value, 1), 'Number'))],
[Name('/'), new JSFunc((a, b) => new Value(a.value / b.value, 'Number'))],
// Boolean
[Name('true'), new Value(true, 'Boolean')],
[Name('false'), new Value(true, 'Boolean')],
[Name('|'), new JSFunc((a, b) => new Value(a.value || b.value, 'Boolean'))],
[Name('&'), new JSFunc((a, b) => new Value(a.value && b.value, 'Boolean'))],
[Name('!'), new JSFunc(a => new Value(!a.value, 'Boolean'))],
]), [])
let scope = new Scope(new Map([
]), [Scope.global])
scope.def(Name('foo'), new Value(123, 'Number'))
console.log(build(
{
"Type": "call",
"func": {
"Type": "ref",
"name": "run"
},
"args": [
{
"Type": "block",
"exprs": [
{
"Type": "call",
"func": {
"Type": "ref",
"name": "def"
},
"args": [
{
"Type": "literal",
"Class": "name",
"value": "bar"
},
{
"Type": "literal",
"Class": "string",
"value": "baz"
}
]
}
]
}
]
}
).exec(scope))
console.log(scope.debug())
console.log('\n\n\n')
Program
= _ exprs:(e:Expression _ {return e})* {
return {
Type: 'call',
func: {Type: 'ref', name: 'run'},
args: [
{Type: 'block', exprs}
]
}
}
Expression
= Call
/ Block
/ List
/ Get
/ value:Value {
return {Type: 'literal', Class: value.Class, value: value.value}
}
/ Ref
Call
= lparen _ func:Expression args:(space e:Expression {return e})* _ rparen {
return {Type: 'call', func, args}
}
Value
= Number
/ String
/ Name
Number
= num:$([+-]?[0-9]+('.'[0-9]+)?) {
return {Class: 'number', value: Number(num, 10)}
}
String
= quote chars:$[^"]+ quote {
return {Class: 'string', value: chars}
}
Name
= colon name:Identifier {
return {Class: 'name', value: name}
}
Block
= lbrace _ exprs:(e:Expression _ {return e})* rbrace {
return {Type: 'block', exprs}
}
List
= lbracket _ first: Expression? rest:(space e:Expression {return e})* _ rbracket {
return {
Type: 'call',
func: {Type: 'ref', name: 'list'},
args: [first].filter(x => !!x).concat(rest)
}
}
Get
= scope:Identifier colon name:Identifier {
return {
Type: 'call',
func: {Type: 'ref', name: 'get'},
args: [
{
Type: 'literal',
Class: 'name',
value: name
},
{Type: 'ref', name: scope}
]
}
}
Ref
= name:Identifier {
return {Type: 'ref', name}
}
Identifier
= $[^ \t\n\r:\(\)\{\}\[\]"]+
lparen = '('
rparen = ')'
lbrace = '{'
rbrace = '}'
lbracket = '['
rbracket = ']'
quote = '"'
colon = ':'
space = [ \t\n\r]+
_ "whitespace" = space?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment