symbol_table 2.0.0

  • Readme
  • Changelog
  • Example
  • Installing
  • 68

symbol_table #

Pub build status

A generic symbol table implementation in Dart, with support for scopes and constants. The symbol tables produced by this package are hierarchical (in this case, tree-shaped), and utilize basic memoization to speed up repeated lookups.

Variables #

To represent a symbol, use Variable. I opted for the name Variable to avoid conflict with the Dart primitive Symbol.

var foo = new Variable<String>('foo');
var bar = new Variable<String>('bar', value: 'baz');

// Call `lock` to mark a symbol as immutable.
var shelley = new Variable<String>('foo', value: 'bar')..lock();

foo.value = 'bar';
shelley.value = 'Mary'; // Throws a StateError - constants cannot be overwritten.

foo.value = 'baz'; // Also throws a StateError - Once a variable is locked, it cannot be overwritten.

Visibility #

Variables are public by default, but can also be marked as private or protected. This can be helpful if you are trying to determine which symbols should be exported from a library or class.

myVariable.visibility = Visibility.protected;
myVariable.visibility = Visibility.private;

Symbol Tables #

It's easy to create a basic symbol table:

var mySymbolTable = new SymbolTable<int>();
var doubles = new SymbolTable<double>(values: {
  'hydrogen': 1.0,
  'avogadro': 6.022e23

// Create a new variable within the scope.
doubles.create('one', value: 1.0);
doubles.create('one', value: 1.0, constant: true);

// Set a variable within an ancestor, OR create a new variable if none exists.
doubles.assign('two', value: 2.0);

// Completely remove a variable.

// Find a symbol, either in this symbol table or an ancestor.
var symbol = doubles.resolve('one');

// Find OR create a symbol.
var symbol = doubles.resolveOrCreate('one');
var symbol = doubles.resolveOrCreate('one', value: 1.0);
var symbol = doubles.resolveOrCreate('one', value: 1.0, constant: true);

Exporting Symbols #

Due to the tree structure of symbol tables, it is extremely easy to extract a linear list of distinct variables, with variables lower in the hierarchy superseding their parents (effectively accomplishing variable shadowing).

var allSymbols = mySymbolTable.allVariables;

We can also extract symbols which are not private. This helps us export symbols from libraries or classes.

var exportedSymbols = mySymbolTable.allPublicVariables;

It's easy to extract symbols of a given visibility:

var exportedSymbols = mySymbolTable.allVariablesWithVisibility(Visibility.protected);

Child Scopes #

There are three ways to create a new symbol table:

Regular Children #

This is what most interpreters need; it simply creates a symbol table with the current symbol table as its parent. The new scope can define its own symbols, which will only shadow the ancestors within the correct scope.

var child = mySymbolTable.createChild();
var child = mySymbolTable.createChild(values: {...});

Depth #

Every symbol table has an associated depth attached to it, with the depth at the root being 0. When createChild is called, the resulting child has an incremented depth.

Clones #

This creates a scope at the same level as the current one, with all the same variables.

var clone = mySymbolTable.clone();

Forked Scopes #

If you are implementing a language with closure functions, you might consider looking into this. A forked scope is a scope identical to the current one, but instead of merely copying references to variables, the values of variables are copied into new ones.

The new scope is essentially a "frozen" version of the current one.

It is also effectively orphaned - though it is aware of its parent, the parent scope is unaware that the forked scope is a child. Thus, calls to resolve may return old variables, if a parent has called remove on a symbol.

var forked = mySymbolTable.fork();
var forked = mySymbolTable.fork(values: {...});

Creating Names #

In languages with block scope, oftentimes, identifiers will collide within a global scope. To avoid this, symbol tables expose a uniqueName() method that simply attaches a numerical suffix to an input name. The name is guaranteed to never be repeated within a specific scope.

var name0 = mySymbolTable.uniqueName('foo'); // foo0
var name1 = mySymbolTable.uniqueName('foo'); // foo1
var name2 = mySymbolTable.uniqueName('foo'); // foo2

this Context #

Many languages handle a sort of this context that values within a scope may optionally be resolved against. Symbol tables can easily set their context as follows:

void foo() {
  mySymbolTable.context = thisContext;

Resolution of the context getter functions just like a symbol; if none is set locally, then it will refer to the parent.

void bar() {
  mySymbolTable.context = thisContext;
  expect(mySymbolTable.createChild().createChild().context, thisContext);

2.0.0 #

  • Dart 2 updates.

1.0.4 #

  • Added context to SymbolTable.

1.0.3 #

  • Converted Visibility into a Comparable class.
  • Renamed add -> create, put -> assign, and allVariablesOfVisibility -> allVariablesWithVisibility.
  • Added tests for Visibility comparing, and depth.
  • Added uniqueName() to SymbolTable.
  • Fixed a typo in remove that would have prevented it from working correctly.

1.0.2 #

  • Added depth to SymbolTable.
  • Added symbolTable to Variable.
  • Deprecated the redundant Constant class.
  • Deprecated Variable.markAsPrivate().
  • Added the Visibility enumerator.
  • Added the field visibility to Variable.


import 'package:symbol_table/symbol_table.dart';

void main() {
  var scope = new SymbolTable<int>();
  var symbol = scope.assign('three', 3);
  print(symbol.value); // 3
  symbol.visibility = Visibility.private;

  var child = scope.createChild();
  child.create('three', value: 4);

  print(child.resolve('three').value); // 4

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:

  symbol_table: ^2.0.0

2. Install it

You can install packages from the command line:

with pub:

$ pub get

with Flutter:

$ flutter pub get

Alternatively, your editor might support pub get or flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:

import 'package:symbol_table/symbol_table.dart';
Describes how popular the package is relative to other packages. [more]
Code health derived from static analysis. [more]
Reflects how tidy and up-to-date the package is. [more]
Weighted score of the above. [more]
Learn more about scoring.

We analyzed this package on Jul 3, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.4
  • pana: 0.13.13

Health suggestions

Fix lib/src/symbol_table.dart. (-9.54 points)

Analysis of lib/src/symbol_table.dart reported 20 hints, including:

line 19 col 37: Use = to separate a named parameter from its default value.

line 22 col 24: Unnecessary new keyword.

line 42 col 3: Avoid return types on setters.

line 63 col 33: DO use curly braces for all flow control structures.

line 90 col 12: Unnecessary new keyword.

Fix lib/src/variable.dart. (-1.49 points)

Analysis of lib/src/variable.dart reported 3 hints:

line 34 col 3: Avoid return types on setters.

line 36 col 7: DO use curly braces for all flow control structures.

line 36 col 13: Unnecessary new keyword.

Fix lib/src/visibility.dart. (-1.49 points)

Analysis of lib/src/visibility.dart reported 3 hints:

line 16 col 37: Avoid const keyword.

line 17 col 39: Avoid const keyword.

line 18 col 36: Avoid const keyword.

Format lib/symbol_table.dart.

Run dartfmt to format lib/symbol_table.dart.

Maintenance suggestions

Package is getting outdated. (-73.97 points)

The package was last published 90 weeks ago.


Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.0.0-dev <3.0.0
Dev dependencies
test ^1.0.0