Last active
May 23, 2016 19:55
Revisions
-
PaulMaynard revised this gist
May 23, 2016 . 2 changed files with 35 additions and 28 deletions.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -1,14 +1,5 @@ class Scope { constructor(vars, parents) { this.vars = vars || new Map() this.parents = parents || [Scope.global] } @@ -59,12 +50,19 @@ class Scope extends Value { 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) @@ -167,25 +165,26 @@ 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) => @@ -208,10 +207,8 @@ Scope.global = new Scope(new Map([ ]), []) let scope = new Scope(new Map([ ]), [Scope.global]) scope.def(Name('foo'), new Value(123, 'Number')) console.log(build( { "Type": "call", @@ -227,7 +224,7 @@ console.log(build( "Type": "call", "func": { "Type": "ref", "name": "def" }, "args": [ { @@ -236,8 +233,9 @@ console.log(build( "value": "bar" }, { "Type": "literal", "Class": "string", "value": "baz" } ] } @@ -247,4 +245,6 @@ console.log(build( } ).exec(scope)) console.log(scope.debug()) console.log('\n\n\n') This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -14,10 +14,10 @@ Expression / 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 { @@ -26,13 +26,19 @@ Call 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} @@ -53,7 +59,7 @@ List } Get = scope:Identifier colon name:Identifier { return { Type: 'call', func: {Type: 'ref', name: 'get'}, @@ -74,14 +80,15 @@ Ref } Identifier = $[^ \t\n\r:\(\)\{\}\[\]"]+ lparen = '(' rparen = ')' lbrace = '{' rbrace = '}' lbracket = '[' rbracket = ']' quote = '"' colon = ':' space = [ \t\n\r]+ _ "whitespace" = space? -
PaulMaynard created this gist
May 23, 2016 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,250 @@ class Value { constructor(val, type) { this.value = val this.type = type } } let nil = new Value(null, 'Nil') class Scope extends Value { constructor(vars, parents) { super(null, 'Scope') this.value = this 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] = v } o.Parents = this.parents.map(p => p.debug()) return o } } 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') }, name(n) { return Name(n) } } Scope.global = new Scope(new Map([ [Name('nil'), nil], // Meta [new Name('run'), new JSFunc((block, scope) => { return block.run(scope || new Scope()) })], [new Name('list'), new JSFunc((...elems) => new Value(elems, 'List'))], [new Name('scope'), new ScopeFunc(([vars], scope) => { if(vars) { console.log(vars) //return new Value(new Scope(vars.value)) } return new Value(scope, "scope") })], // Variables [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([ [Name('foo'), new Scope(new Map([ [Name('bar'), new Value(123, 'number')] ]), [])] ]), [Scope.global]) console.log(build( { "Type": "call", "func": { "Type": "ref", "name": "run" }, "args": [ { "Type": "block", "exprs": [ { "Type": "call", "func": { "Type": "ref", "name": "get" }, "args": [ { "Type": "literal", "Class": "name", "value": "bar" }, { "Type": "ref", "name": "foo" } ] } ] } ] } ).exec(scope)) console.log('\n\n\n') This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,87 @@ Program = _ exprs:(e:Expression _ {return e})* { return { Type: 'call', func: {Type: 'ref', name: 'run'}, args: [ {Type: 'block', exprs} ] } } Expression = Call / Block / List / Get / Ref / value:Value { return {Type: 'literal', Class: value.Class, value: value.value} } Call = lparen _ func:Expression args:(space e:Expression {return e})* _ rparen { return {Type: 'call', func, args} } Value = Number / Name Number = num:$([+-]?[0-9]+('.'[0-9]+)?) { return {Class: 'number', value: Number(num, 10)} } 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 ':' 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 = $(!space [^:\(\)\{\}\[\]0-9] (!space [^:\(\)\{\}\[\]])*) lparen = '(' rparen = ')' lbrace = '{' rbrace = '}' lbracket = '[' rbracket = ']' colon = ':' space = [ \t\n\r]+ _ "whitespace" = space?