simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

custom_scope_objects.js (2754B)


      1 const { create, all } = require('../..')
      2 
      3 const math = create(all)
      4 
      5 // The expression evaluator accepts an optional scope object.
      6 // This is the symbol table for variable defintions and function declations.
      7 
      8 // Scope can be a bare object.
      9 function withObjectScope () {
     10   const scope = { x: 3 }
     11 
     12   math.evaluate('x', scope) // 1
     13   math.evaluate('y = 2 x', scope)
     14   math.evaluate('scalar = 1', scope)
     15   math.evaluate('area(length, width) = length * width * scalar', scope)
     16   math.evaluate('A = area(x, y)', scope)
     17 
     18   console.log('Object scope:', scope)
     19 }
     20 
     21 // Where flexibility is important, scope can duck type appear to be a Map.
     22 function withMapScope (scope, name) {
     23   scope.set('x', 3)
     24 
     25   math.evaluate('x', scope) // 1
     26   math.evaluate('y = 2 x', scope)
     27   math.evaluate('scalar = 1', scope)
     28   math.evaluate('area(length, width) = length * width * scalar', scope)
     29   math.evaluate('A = area(x, y)', scope)
     30 
     31   console.log(`Map-like scope (${name}):`, scope.localScope)
     32 }
     33 
     34 // This is a minimal set of functions to look like a Map.
     35 class MapScope {
     36   constructor () {
     37     this.localScope = new Map()
     38   }
     39 
     40   get (key) {
     41     // Remember to sanitize your inputs, or use
     42     // a datastructure that isn't a footgun.
     43     return this.localScope.get(key)
     44   }
     45 
     46   set (key, value) {
     47     return this.localScope.set(key, value)
     48   }
     49 
     50   has (key) {
     51     return this.localScope.has(key)
     52   }
     53 
     54   keys () {
     55     return this.localScope.keys()
     56   }
     57 }
     58 
     59 /*
     60  * This is a more fully featured example, with all methods
     61  * used in mathjs.
     62  *
     63  */
     64 class AdvancedMapScope extends MapScope {
     65   constructor (parent) {
     66     super()
     67     this.parentScope = parent
     68   }
     69 
     70   get (key) {
     71     return this.localScope.get(key) ?? this.parentScope?.get(key)
     72   }
     73 
     74   has (key) {
     75     return this.localScope.has(key) ?? this.parentScope?.get(key)
     76   }
     77 
     78   keys () {
     79     if (this.parentScope) {
     80       return new Set([...this.localScope.keys(), ...this.parentScope.keys()])
     81     } else {
     82       return this.localScope.keys()
     83     }
     84   }
     85 
     86   delete () {
     87     return this.localScope.delete()
     88   }
     89 
     90   clear () {
     91     return this.localScope.clear()
     92   }
     93 
     94   /**
     95    * Creates a child scope from this one. This is used in function calls.
     96    *
     97    * @returns a new Map scope that has access to the symbols in the parent, but
     98    * cannot overwrite them.
     99    */
    100   createSubScope () {
    101     return new AdvancedMapScope(this)
    102   }
    103 
    104   toString () {
    105     return this.localScope.toString()
    106   }
    107 }
    108 
    109 withObjectScope()
    110 // Where safety is important, scope can also be a Map
    111 withMapScope(new Map(), 'simple Map')
    112 // Where flexibility is important, scope can duck type appear to be a Map.
    113 withMapScope(new MapScope(), 'MapScope example')
    114 // Extra methods allow even finer grain control.
    115 withMapScope(new AdvancedMapScope(), 'AdvancedScope example')