Skip to content

Instantly share code, notes, and snippets.

@PaulMaynard
Last active May 23, 2016 19:55

Revisions

  1. PaulMaynard revised this gist May 23, 2016. 2 changed files with 35 additions and 28 deletions.
    50 changes: 25 additions & 25 deletions interpreter.js
    Original file line number Diff line number Diff line change
    @@ -1,14 +1,5 @@
    class Value {
    constructor(val, type) {
    this.value = val
    this.type = type
    }
    }
    let nil = new Value(null, 'Nil')
    class Scope extends Value {
    class Scope {
    constructor(vars, parents) {
    super(null, 'Scope')
    this.value = this
    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] = v
    o[k.value] = v.value
    }
    o.Parents = this.parents.map(p => p.debug())
    //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],
    // 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) => {
    // Scopes
    [new Name('scope'), new ScopeFunc(([vars, parents], scope) => {
    if(vars) {
    console.log(vars)
    //return new Value(new Scope(vars.value))
    return new Value(new Scope(new Map(vars.value.map(v => v.value)), []), 'Scope')
    }
    return new Value(scope, "scope")
    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) =>
    @@ -208,10 +207,8 @@ Scope.global = new Scope(new Map([
    ]), [])

    let scope = new Scope(new Map([
    [Name('foo'), new Scope(new Map([
    [Name('bar'), new Value(123, 'number')]
    ]), [])]
    ]), [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": "get"
    "name": "def"
    },
    "args": [
    {
    @@ -236,8 +233,9 @@ console.log(build(
    "value": "bar"
    },
    {
    "Type": "ref",
    "name": "foo"
    "Type": "literal",
    "Class": "string",
    "value": "baz"
    }
    ]
    }
    @@ -247,4 +245,6 @@ console.log(build(
    }
    ).exec(scope))

    console.log(scope.debug())

    console.log('\n\n\n')
    13 changes: 10 additions & 3 deletions parser.pegjs
    Original file line number Diff line number Diff line change
    @@ -14,10 +14,10 @@ Expression
    / Block
    / List
    / Get
    / Ref
    / 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 ':' name:Identifier {
    = scope:Identifier colon name:Identifier {
    return {
    Type: 'call',
    func: {Type: 'ref', name: 'get'},
    @@ -74,14 +80,15 @@ Ref
    }

    Identifier
    = $(!space [^:\(\)\{\}\[\]0-9] (!space [^:\(\)\{\}\[\]])*)
    = $[^ \t\n\r:\(\)\{\}\[\]"]+

    lparen = '('
    rparen = ')'
    lbrace = '{'
    rbrace = '}'
    lbracket = '['
    rbracket = ']'
    quote = '"'
    colon = ':'
    space = [ \t\n\r]+
    _ "whitespace" = space?
  2. PaulMaynard created this gist May 23, 2016.
    250 changes: 250 additions & 0 deletions interpreter.js
    Original 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')
    87 changes: 87 additions & 0 deletions parser.pegjs
    Original 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?