Skip to content

Commit 7e0aa19

Browse files
authored
Merge pull request #140 from sugarlabs/refactor-syntax-add-scope
Refactor syntax add scope
2 parents e2c8d90 + 55868b4 commit 7e0aa19

25 files changed

+908
-410
lines changed

src/@types/elements.d.ts

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { TData, TDataName } from './data';
22
import { TElementKind, TElementType } from './specification';
3+
import { IContext, ISymbolTable } from './scope';
34

45
// -------------------------------------------------------------------------------------------------
56

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

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

5864
/** Interface for the class that implements an instruction element. */
5965
export interface IElementInstruction extends IElementSyntax {
6066
/**
6167
* Executes the instruction using the supplied parameters.
62-
* @param params - An object containing key-value pairs of each argument and it's value
68+
* @param scope object containing context and symbol table instances
69+
* @param params object containing key-value pairs of each argument and it's value
6370
*/
64-
onVisit(params: { [key: string]: TData }): void;
71+
onVisit(
72+
scope: { context: IContext; symbolTable: ISymbolTable },
73+
params: { [key: string]: TData }
74+
): void;
6575
}
6676

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

7080
/** Interface for the class that implements a block element. */
7181
export interface IElementBlock extends IElementInstruction {
72-
/** Executes before each containing instruction is executed. */
73-
onInnerVisit(): void;
74-
/** Executes after each containing instruction is executed. */
75-
onInnerExit(): void;
76-
/** Executes after all containing instructions are executed. */
77-
onExit(): void;
82+
/**
83+
* Executes before each containing instruction is executed.
84+
* @param scope object containing context and symbol table instances
85+
*/
86+
onInnerVisit(
87+
scope: { context: IContext; symbolTable: ISymbolTable },
88+
params: { [key: string]: TData }
89+
): void;
90+
/**
91+
* Executes after each containing instruction is executed.
92+
* @param scope object containing context and symbol table instances
93+
*/
94+
onInnerExit(
95+
scope: { context: IContext; symbolTable: ISymbolTable },
96+
params: { [key: string]: TData }
97+
): void;
98+
/**
99+
* Executes after all containing instructions are executed.
100+
* @param scope object containing context and symbol table instances
101+
*/
102+
onExit(
103+
scope: { context: IContext; symbolTable: ISymbolTable },
104+
params: { [key: string]: TData }
105+
): void;
78106
}

src/@types/specification.d.ts

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ export interface IElementSpecificationData {
3535
}
3636

3737
/** Type for the specification entry object for data elements. */
38-
export interface IElementSpecificationEntryData extends IElementSpecificationData {
39-
classification: TElementClassification;
38+
export interface IElementSpecificationEntryData
39+
extends Omit<IElementSpecificationData, 'classification'> {
40+
category: string;
4041
label: string;
4142
type: 'Data';
4243
prototype: typeof IElementData;
@@ -51,8 +52,9 @@ export interface IElementSpecificationExpression {
5152
}
5253

5354
/** Type for the specification object for expression elements. */
54-
export interface IElementSpecificationEntryExpression extends IElementSpecificationExpression {
55-
classification: TElementClassification;
55+
export interface IElementSpecificationEntryExpression
56+
extends Omit<IElementSpecificationEntryExpression, 'classification'> {
57+
category: string;
5658
label: string;
5759
type: 'Expression';
5860
prototype: typeof IElementExpression;
@@ -78,8 +80,11 @@ export type IElementSpecificationStatement = IElementSpecificationInstruction &
7880
};
7981

8082
/** Type for the specification object for statement elements. */
81-
export type IElementSpecificationEntryStatement = IElementSpecificationInstruction & {
82-
classification: TElementClassification;
83+
export type IElementSpecificationEntryStatement = Omit<
84+
IElementSpecificationStatement,
85+
'classification'
86+
> & {
87+
category: string;
8388
label: string;
8489
type: 'Statement';
8590
prototype: typeof IElementStatement;
@@ -96,8 +101,8 @@ export type IElementSpecificationBlock = IElementSpecificationInstruction & {
96101
};
97102

98103
/** Type for the specification entry object for block elements. */
99-
export type IElementSpecificationEntryBlock = IElementSpecificationInstruction & {
100-
classification: TElementClassification;
104+
export type IElementSpecificationEntryBlock = Omit<IElementSpecificationBlock, 'classification'> & {
105+
category: string;
101106
label: string;
102107
type: 'Block';
103108
prototype: typeof IElementBlock;
@@ -131,3 +136,20 @@ export interface IElementSpecification {
131136
export interface IElementSpecificationSnapshot extends Omit<IElementSpecification, 'prototype'> {
132137
prototypeName: string;
133138
}
139+
140+
/** Type definition for a table of element specification entries. */
141+
export type IElementSpecificationEntries = {
142+
/** Element group name. */
143+
[group: string]: {
144+
/** Element specification entries by element name/identifier. */
145+
entries: {
146+
[identifier: string]:
147+
| IElementSpecificationEntryData
148+
| IElementSpecificationEntryExpression
149+
| IElementSpecificationEntryStatement
150+
| IElementSpecificationEntryBlock;
151+
};
152+
/** Element group context key-value dictionary. */
153+
context?: Record<string, unknown>;
154+
};
155+
};

src/execution/interpreter/index.spec.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { run } from '.';
22

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

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

89
// -------------------------------------------------------------------------------------------------
910

1011
registerElementSpecificationEntries(elementSpecification);
12+
const scopeStack = new ScopeStack();
1113

1214
describe('Interpreter', () => {
1315
test('run a process and verify', () => {
@@ -147,6 +149,9 @@ describe('Interpreter', () => {
147149
const snapshot = generateSnapshot();
148150

149151
const node = snapshot.process[0];
150-
run(node.nodeID);
152+
run(node.nodeID, {
153+
context: scopeStack.getContext('programming'),
154+
symbolTable: scopeStack.getSymbolTable(),
155+
});
151156
});
152157
});

src/execution/interpreter/index.ts

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
import type { TData, TDataName } from '../../@types/data';
2-
import type { IVariable, TPCOverride } from '../../@types/execution';
3-
41
import { addGlobalSymbol, getGlobalSymbol } from '../scope';
52
import { setPCOverride, clearPCOverride, setExecutionItem, getNextElement } from '../parser';
63

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

14-
// -- private functions ----------------------------------------------------------------------------
11+
// -- types ----------------------------------------------------------------------------------------
12+
13+
import type { TData, TDataName } from '../../@types/data';
14+
import type { IVariable, TPCOverride } from '../../@types/execution';
15+
import type { IContext, ISymbolTable } from '../../@types/scope';
1516

1617
// -- public functions -----------------------------------------------------------------------------
1718

@@ -63,13 +64,28 @@ export function releaseProgramCounter(): void {
6364
* Runs a process, routine, or crumb stack from start to end.
6465
* @param nodeID syntax tree node ID of the starting node
6566
*/
66-
export function run(nodeID: string): void {
67+
export function run(
68+
nodeID: string,
69+
scope: {
70+
context: IContext<Record<string, unknown>>;
71+
symbolTable: ISymbolTable;
72+
}
73+
): void {
6774
abstract class ElementDataCover extends ElementData<TData> {
68-
abstract evaluate(): void;
75+
abstract evaluate(scope: {
76+
context: IContext<Record<string, unknown>>;
77+
symbolTable: ISymbolTable;
78+
}): void;
6979
}
7080

7181
abstract class ElementExpressionCover extends ElementExpression<TData> {
72-
abstract evaluate(params: { [key: string]: TData }): void;
82+
abstract evaluate(
83+
scope: {
84+
context: IContext<Record<string, unknown>>;
85+
symbolTable: ISymbolTable;
86+
},
87+
params: { [key: string]: TData }
88+
): void;
7389
}
7490

7591
setExecutionItem(nodeID);
@@ -86,9 +102,9 @@ export function run(nodeID: string): void {
86102
const { instance, type, marker } = element;
87103
if (type === 'Argument') {
88104
if (instance instanceof ElementDataCover) {
89-
instance.evaluate();
105+
instance.evaluate(scope);
90106
} /* instance instanceof ElementExpressionCover */ else {
91-
(instance as ElementExpressionCover).evaluate(memo);
107+
(instance as ElementExpressionCover).evaluate(scope, memo);
92108
}
93109

94110
const value = (instance as unknown as ElementArgument<TData>).value;
@@ -98,12 +114,12 @@ export function run(nodeID: string): void {
98114
}
99115
} else {
100116
if (instance instanceof ElementStatement) {
101-
instance.onVisit(memo);
117+
instance.onVisit(scope, memo);
102118
} /* instance instanceof ElementBlock */ else {
103119
if (marker !== '__rollback__') {
104-
(instance as ElementBlock).onVisit(memo);
120+
(instance as ElementBlock).onVisit(scope, memo);
105121
} else {
106-
(instance as ElementBlock).onExit();
122+
(instance as ElementBlock).onExit(scope, {});
107123
}
108124
}
109125
}

src/execution/scope/index.spec.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
_getSymbolTableManager,
44
registerContext,
55
deregisterContext,
6+
hasContext,
67
ScopeStack,
78
addGlobalSymbol,
89
removeGlobalSymbol,
@@ -39,17 +40,20 @@ describe('Scope Module', () => {
3940
registerContext('painter', {
4041
color: 'red',
4142
});
43+
expect(hasContext('painter')).toBe(true);
4244
});
4345

4446
it('re-registers existing context key-maps by overwriting', () => {
4547
registerContext('painter', {
4648
color: 'red',
4749
width: 4,
4850
});
51+
expect(hasContext('painter')).toBe(true);
4952
});
5053

5154
it('deregisters context key-maps', () => {
5255
deregisterContext('painter');
56+
expect(hasContext('painter')).toBe(false);
5357
});
5458

5559
let scopeStack: IScopeStack;

src/execution/scope/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,14 @@ export function deregisterContext(name: string): boolean {
6767
return true;
6868
}
6969

70+
/**
71+
* Returns whether a context is already registered.
72+
* @param name context name
73+
*/
74+
export function hasContext(name: string): boolean {
75+
return name in _contextManagerMap;
76+
}
77+
7078
/**
7179
* Adds new global symbol.
7280
* @param name symbol name

src/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,14 @@ export {
5757

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

60+
// -- scope ----------------------------------------------------------------------------------------
61+
62+
export type { IContext, ISymbolTable } from './@types/scope';
63+
64+
export { registerContext, deregisterContext, hasContext } from './execution/scope';
65+
66+
// -- interpreter ----------------------------------------------------------------------------------
67+
6068
export {
6169
declareVariable,
6270
queryVariable,

0 commit comments

Comments
 (0)