Skip to content

Commit

Permalink
Merge pull request #140 from sugarlabs/refactor-syntax-add-scope
Browse files Browse the repository at this point in the history
Refactor syntax add scope
  • Loading branch information
meganindya authored Nov 12, 2022
2 parents e2c8d90 + 55868b4 commit 7e0aa19
Show file tree
Hide file tree
Showing 25 changed files with 908 additions and 410 deletions.
50 changes: 39 additions & 11 deletions src/@types/elements.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { TData, TDataName } from './data';
import { TElementKind, TElementType } from './specification';
import { IContext, ISymbolTable } from './scope';

// -------------------------------------------------------------------------------------------------

Expand Down Expand Up @@ -42,37 +43,64 @@ export interface IElementArgument<T> extends IElementSyntax {
export interface IElementData<T> extends IElementArgument<T> {
/**
* Evalutates the logic of the data element (usually based on the label).
* @param scope An object containing context and symbol table instances
*/
evaluate(): void;
evaluate(scope: { context: IContext; symbolTable: ISymbolTable }): void;
}

/** Generic interface for the class that implements an expression element. */
export interface IElementExpression<T> extends IElementArgument<T> {
/**
* Evalutates the logic of the expression using the supplied parameters and stores the value.
* @param params - An object containing key-value pairs of each argument and it's value
* @param scope object containing context and symbol table instances
* @param params object containing key-value pairs of each argument and it's value
*/
evaluate(params: { [key: string]: TData }): void;
evaluate(
scope: { context: IContext; symbolTable: ISymbolTable },
params: { [key: string]: TData }
): void;
}

/** Interface for the class that implements an instruction element. */
export interface IElementInstruction extends IElementSyntax {
/**
* Executes the instruction using the supplied parameters.
* @param params - An object containing key-value pairs of each argument and it's value
* @param scope object containing context and symbol table instances
* @param params object containing key-value pairs of each argument and it's value
*/
onVisit(params: { [key: string]: TData }): void;
onVisit(
scope: { context: IContext; symbolTable: ISymbolTable },
params: { [key: string]: TData }
): void;
}

/** Interface for the class that implements a statement element. */
export interface IElementStatement extends IElementInstruction {}

/** Interface for the class that implements a block element. */
export interface IElementBlock extends IElementInstruction {
/** Executes before each containing instruction is executed. */
onInnerVisit(): void;
/** Executes after each containing instruction is executed. */
onInnerExit(): void;
/** Executes after all containing instructions are executed. */
onExit(): void;
/**
* Executes before each containing instruction is executed.
* @param scope object containing context and symbol table instances
*/
onInnerVisit(
scope: { context: IContext; symbolTable: ISymbolTable },
params: { [key: string]: TData }
): void;
/**
* Executes after each containing instruction is executed.
* @param scope object containing context and symbol table instances
*/
onInnerExit(
scope: { context: IContext; symbolTable: ISymbolTable },
params: { [key: string]: TData }
): void;
/**
* Executes after all containing instructions are executed.
* @param scope object containing context and symbol table instances
*/
onExit(
scope: { context: IContext; symbolTable: ISymbolTable },
params: { [key: string]: TData }
): void;
}
38 changes: 30 additions & 8 deletions src/@types/specification.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ export interface IElementSpecificationData {
}

/** Type for the specification entry object for data elements. */
export interface IElementSpecificationEntryData extends IElementSpecificationData {
classification: TElementClassification;
export interface IElementSpecificationEntryData
extends Omit<IElementSpecificationData, 'classification'> {
category: string;
label: string;
type: 'Data';
prototype: typeof IElementData;
Expand All @@ -51,8 +52,9 @@ export interface IElementSpecificationExpression {
}

/** Type for the specification object for expression elements. */
export interface IElementSpecificationEntryExpression extends IElementSpecificationExpression {
classification: TElementClassification;
export interface IElementSpecificationEntryExpression
extends Omit<IElementSpecificationEntryExpression, 'classification'> {
category: string;
label: string;
type: 'Expression';
prototype: typeof IElementExpression;
Expand All @@ -78,8 +80,11 @@ export type IElementSpecificationStatement = IElementSpecificationInstruction &
};

/** Type for the specification object for statement elements. */
export type IElementSpecificationEntryStatement = IElementSpecificationInstruction & {
classification: TElementClassification;
export type IElementSpecificationEntryStatement = Omit<
IElementSpecificationStatement,
'classification'
> & {
category: string;
label: string;
type: 'Statement';
prototype: typeof IElementStatement;
Expand All @@ -96,8 +101,8 @@ export type IElementSpecificationBlock = IElementSpecificationInstruction & {
};

/** Type for the specification entry object for block elements. */
export type IElementSpecificationEntryBlock = IElementSpecificationInstruction & {
classification: TElementClassification;
export type IElementSpecificationEntryBlock = Omit<IElementSpecificationBlock, 'classification'> & {
category: string;
label: string;
type: 'Block';
prototype: typeof IElementBlock;
Expand Down Expand Up @@ -131,3 +136,20 @@ export interface IElementSpecification {
export interface IElementSpecificationSnapshot extends Omit<IElementSpecification, 'prototype'> {
prototypeName: string;
}

/** Type definition for a table of element specification entries. */
export type IElementSpecificationEntries = {
/** Element group name. */
[group: string]: {
/** Element specification entries by element name/identifier. */
entries: {
[identifier: string]:
| IElementSpecificationEntryData
| IElementSpecificationEntryExpression
| IElementSpecificationEntryStatement
| IElementSpecificationEntryBlock;
};
/** Element group context key-value dictionary. */
context?: Record<string, unknown>;
};
};
7 changes: 6 additions & 1 deletion src/execution/interpreter/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { run } from '.';

import { generateFromSnapshot, generateSnapshot } from '../../syntax/tree';
import { ScopeStack } from '../../execution/scope';

import { registerElementSpecificationEntries } from '../../syntax/specification';
import elementSpecification from '../../library';

// -------------------------------------------------------------------------------------------------

registerElementSpecificationEntries(elementSpecification);
const scopeStack = new ScopeStack();

describe('Interpreter', () => {
test('run a process and verify', () => {
Expand Down Expand Up @@ -147,6 +149,9 @@ describe('Interpreter', () => {
const snapshot = generateSnapshot();

const node = snapshot.process[0];
run(node.nodeID);
run(node.nodeID, {
context: scopeStack.getContext('programming'),
symbolTable: scopeStack.getSymbolTable(),
});
});
});
40 changes: 28 additions & 12 deletions src/execution/interpreter/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import type { TData, TDataName } from '../../@types/data';
import type { IVariable, TPCOverride } from '../../@types/execution';

import { addGlobalSymbol, getGlobalSymbol } from '../scope';
import { setPCOverride, clearPCOverride, setExecutionItem, getNextElement } from '../parser';

Expand All @@ -11,7 +8,11 @@ import {
} from '../../syntax/elements/elementArgument';
import { ElementStatement, ElementBlock } from '../../syntax/elements/elementInstruction';

// -- private functions ----------------------------------------------------------------------------
// -- types ----------------------------------------------------------------------------------------

import type { TData, TDataName } from '../../@types/data';
import type { IVariable, TPCOverride } from '../../@types/execution';
import type { IContext, ISymbolTable } from '../../@types/scope';

// -- public functions -----------------------------------------------------------------------------

Expand Down Expand Up @@ -63,13 +64,28 @@ export function releaseProgramCounter(): void {
* Runs a process, routine, or crumb stack from start to end.
* @param nodeID syntax tree node ID of the starting node
*/
export function run(nodeID: string): void {
export function run(
nodeID: string,
scope: {
context: IContext<Record<string, unknown>>;
symbolTable: ISymbolTable;
}
): void {
abstract class ElementDataCover extends ElementData<TData> {
abstract evaluate(): void;
abstract evaluate(scope: {
context: IContext<Record<string, unknown>>;
symbolTable: ISymbolTable;
}): void;
}

abstract class ElementExpressionCover extends ElementExpression<TData> {
abstract evaluate(params: { [key: string]: TData }): void;
abstract evaluate(
scope: {
context: IContext<Record<string, unknown>>;
symbolTable: ISymbolTable;
},
params: { [key: string]: TData }
): void;
}

setExecutionItem(nodeID);
Expand All @@ -86,9 +102,9 @@ export function run(nodeID: string): void {
const { instance, type, marker } = element;
if (type === 'Argument') {
if (instance instanceof ElementDataCover) {
instance.evaluate();
instance.evaluate(scope);
} /* instance instanceof ElementExpressionCover */ else {
(instance as ElementExpressionCover).evaluate(memo);
(instance as ElementExpressionCover).evaluate(scope, memo);
}

const value = (instance as unknown as ElementArgument<TData>).value;
Expand All @@ -98,12 +114,12 @@ export function run(nodeID: string): void {
}
} else {
if (instance instanceof ElementStatement) {
instance.onVisit(memo);
instance.onVisit(scope, memo);
} /* instance instanceof ElementBlock */ else {
if (marker !== '__rollback__') {
(instance as ElementBlock).onVisit(memo);
(instance as ElementBlock).onVisit(scope, memo);
} else {
(instance as ElementBlock).onExit();
(instance as ElementBlock).onExit(scope, {});
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/execution/scope/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
_getSymbolTableManager,
registerContext,
deregisterContext,
hasContext,
ScopeStack,
addGlobalSymbol,
removeGlobalSymbol,
Expand Down Expand Up @@ -39,17 +40,20 @@ describe('Scope Module', () => {
registerContext('painter', {
color: 'red',
});
expect(hasContext('painter')).toBe(true);
});

it('re-registers existing context key-maps by overwriting', () => {
registerContext('painter', {
color: 'red',
width: 4,
});
expect(hasContext('painter')).toBe(true);
});

it('deregisters context key-maps', () => {
deregisterContext('painter');
expect(hasContext('painter')).toBe(false);
});

let scopeStack: IScopeStack;
Expand Down
8 changes: 8 additions & 0 deletions src/execution/scope/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ export function deregisterContext(name: string): boolean {
return true;
}

/**
* Returns whether a context is already registered.
* @param name context name
*/
export function hasContext(name: string): boolean {
return name in _contextManagerMap;
}

/**
* Adds new global symbol.
* @param name symbol name
Expand Down
8 changes: 8 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ export {

export type { TPCOverride } from './@types/execution';

// -- scope ----------------------------------------------------------------------------------------

export type { IContext, ISymbolTable } from './@types/scope';

export { registerContext, deregisterContext, hasContext } from './execution/scope';

// -- interpreter ----------------------------------------------------------------------------------

export {
declareVariable,
queryVariable,
Expand Down
Loading

0 comments on commit 7e0aa19

Please sign in to comment.