Skip to content

Commit

Permalink
Support quicksort directive in C
Browse files Browse the repository at this point in the history
  • Loading branch information
SebastienTainon committed May 24, 2024
1 parent c7dbeed commit a29c4b7
Show file tree
Hide file tree
Showing 19 changed files with 295 additions and 152 deletions.
6 changes: 6 additions & 0 deletions frontend/stepper/abstract_runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {ContextEnrichingTypes} from './actionTypes';
import {TaskAnswer} from '../task/task_types';
import log from 'loglevel';
import {Codecast} from '../app_types';
import AbstractVariableFetcher from './analysis/abstract_variable_fetcher';
import DefaultVariableFetcher from './analysis/default_variable_fetcher';

export default abstract class AbstractRunner {
//TODO: improve this
Expand Down Expand Up @@ -215,4 +217,8 @@ export default abstract class AbstractRunner {
const nextThreadPosition = (currentThreadPosition + 1) % threadIds.length;
this.nextThreadId = Number(threadIds[nextThreadPosition]);
}

public getVariableFetcher(): AbstractVariableFetcher {
return new DefaultVariableFetcher();
}
}
47 changes: 47 additions & 0 deletions frontend/stepper/analysis/abstract_variable_fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {LayoutDirectiveContext} from '../../task/layout/LayoutDirective';
import {CodecastAnalysisStackFrame, CodecastAnalysisVariable} from './analysis';
import {DirectiveVariableName} from './directives/utils';

export default abstract class AbstractVariableFetcher {
getNumber(expr, options) {
return expr;
}
getList(expr, defaultValue) {
return expr;
}
stringifyVariableName(name) {
return name;
}
getVariable(context: LayoutDirectiveContext, name: DirectiveVariableName, elemCount?: number): CodecastAnalysisVariable {
// Check in the last (the current) and the first (which is the global) scopes.

const variableName = Array.isArray(name) ? name[1] : name;
const analysis = context.analysis;
const nbScopes = analysis.stackFrames.length;
let variable: CodecastAnalysisVariable = null;
if (nbScopes) {
variable = this.getVariableInScope(analysis.stackFrames[nbScopes - 1], variableName);
}
if (!variable && nbScopes > 1) {
variable = this.getVariableInScope(analysis.stackFrames[0], variableName);
}

return variable;
}
getVariables(context: LayoutDirectiveContext, names: DirectiveVariableName[]): {name: DirectiveVariableName, value: CodecastAnalysisVariable}[] {
return names.map((name) => {
return {
name,
value: this.getVariable(context, name)
}
});
}
/**
* Gets a variable by name in a scope.
*/
getVariableInScope(stackFrame: CodecastAnalysisStackFrame, name: string): CodecastAnalysisVariable {
let variable = stackFrame.variables.find(variable => name === variable.name);

return variable ? variable : null;
}
}
48 changes: 48 additions & 0 deletions frontend/stepper/analysis/default_variable_fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {LayoutDirectiveContext} from '../../task/layout/LayoutDirective';
import {CodecastAnalysisStackFrame, CodecastAnalysisVariable} from './analysis';
import {DirectiveVariableName} from './directives/utils';
import AbstractVariableFetcher from './abstract_variable_fetcher';

export default class DefaultVariableFetcher extends AbstractVariableFetcher {
getNumber(expr, options) {
return expr;
}
getList(expr, defaultValue) {
return expr;
}
stringifyVariableName(name) {
return name;
}
getVariable(context: LayoutDirectiveContext, name: DirectiveVariableName, elemCount?: number): CodecastAnalysisVariable {
// Check in the last (the current) and the first (which is the global) scopes.

const variableName = Array.isArray(name) ? name[1] : name;
const analysis = context.analysis;
const nbScopes = analysis.stackFrames.length;
let variable: CodecastAnalysisVariable = null;
if (nbScopes) {
variable = this.getVariableInScope(analysis.stackFrames[nbScopes - 1], variableName);
}
if (!variable && nbScopes > 1) {
variable = this.getVariableInScope(analysis.stackFrames[0], variableName);
}

return variable;
}
getVariables(context: LayoutDirectiveContext, names: DirectiveVariableName[]): {name: DirectiveVariableName, value: CodecastAnalysisVariable}[] {
return names.map((name) => {
return {
name,
value: this.getVariable(context, name)
}
});
}
/**
* Gets a variable by name in a scope.
*/
getVariableInScope(stackFrame: CodecastAnalysisStackFrame, name: string): CodecastAnalysisVariable {
let variable = stackFrame.variables.find(variable => name === variable.name);

return variable ? variable : null;
}
}
10 changes: 6 additions & 4 deletions frontend/stepper/analysis/directives/array1d.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {DirectiveFrame} from '../../views/DirectiveFrame';
import {StepperControls} from "../../index";
import {CodecastAnalysisVariable} from "../analysis";
import {LayoutDirectiveContext} from '../../../task/layout/LayoutDirective';
import {Codecast} from '../../../app_types';
import {getList} from '../../views/c/utils';

const TEXT_LINE_HEIGHT = 18;
const TEXT_BASELINE = 5; // from bottom
Expand Down Expand Up @@ -165,14 +167,14 @@ export class Array1D extends React.PureComponent<Array1DProps> {

const cellPan = this.getPosition();
const {byName, byPos} = directive;
const cursors = (byName.cursors) ? byName.cursors : [];
const cursors = byName.cursors ? context.variableFetcher.getList(byName.cursors, []) : [];

const cursorRows = (byName.cursorRows) ? byName.cursorRows : DEFAULT_CURSOR_ROWS;
const cursorRows = byName.cursorRows ? context.variableFetcher.getNumber(byName.cursorRows, DEFAULT_CURSOR_ROWS) : DEFAULT_CURSOR_ROWS;
const cellHeight = (3 + cursorRows) * TEXT_LINE_HEIGHT + MIN_ARROW_HEIGHT;

const cellWidth = this._cellWidth = (byName.cw) ? byName.cw : DEFAULT_CELL_WIDTH;
const cellWidth = this._cellWidth = byName.cw ? context.variableFetcher.getNumber(byName.cw, DEFAULT_CELL_WIDTH) : DEFAULT_CELL_WIDTH;

const maxVisibleCells = (byName.n) ? byName.n : DEFAULT_MAX_VISIBLE_CELLS;
const maxVisibleCells = byName.n ? context.variableFetcher.getNumber(byName.n, DEFAULT_MAX_VISIBLE_CELLS) : DEFAULT_MAX_VISIBLE_CELLS;

const {dim} = byName;

Expand Down
8 changes: 4 additions & 4 deletions frontend/stepper/analysis/directives/array2d_model.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import {DirectiveVariableName, getVariable} from './utils';
import {DirectiveVariableName} from './utils';
import {getCursorMap} from './array_utils';
import {getMessage} from "../../../lang";
import {LayoutDirectiveContext} from '../../../task/layout/LayoutDirective';

export const extractView = function (context: LayoutDirectiveContext, name: DirectiveVariableName, options) {
const ref = getVariable(context, name);
const ref = context.variableFetcher.getVariable(context, name);
if (!ref) {
return {error: getMessage('ARRAY2D_REF_UNDEFINED').format({name})};
return {error: getMessage('ARRAY2D_REF_UNDEFINED').format({name: context.variableFetcher.stringifyVariableName(name)})};
}

if (!ref.variables) {
return {error: getMessage('ARRAY2D_REF_NOT_LIST').format({name})};
return {error: getMessage('ARRAY2D_REF_NOT_LIST').format({name: context.variableFetcher.stringifyVariableName(name)})};
}

const rowCount = (options.rowCount) ? options.rowCount : ref.variables.length;
Expand Down
29 changes: 18 additions & 11 deletions frontend/stepper/analysis/directives/array_utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import range from 'node-range';

import {DirectiveVariableName, getVariable} from './utils';
import {DirectiveVariableName} from './utils';
import {getMessage} from "../../../lang";
import {LayoutDirectiveContext} from '../../../task/layout/LayoutDirective';
import {stringifyExpr} from '../../views/c/utils';

/**
extractView(context, name, options) looks up `name` in `stackFrame` and
Expand Down Expand Up @@ -61,7 +62,7 @@ import {LayoutDirectiveContext} from '../../../task/layout/LayoutDirective';
export const extractView = function (context: LayoutDirectiveContext, name, options) {
// Normalize options.
const {dim} = options;
let {cursors, cursorRows, maxVisibleCells, pointsByKind} = options;
let {cursors, cursorRows, maxVisibleCells, pointsByKind, thresholds} = options;
if (cursors === undefined) {
cursors = [];
}
Expand All @@ -87,20 +88,22 @@ export const extractView = function (context: LayoutDirectiveContext, name, opti
if (/^\d/.test(dim)) {
elemCount = dim;
} else {
const dimVariable = getVariable(context, dim);
const dimVariable = context.variableFetcher.getVariable(context, dim);
if (dimVariable && dimVariable.value) {
elemCount = Number(dimVariable.value);
} else {
return {error: getMessage('ARRAY1D_DIM_INVALID').format({dim: stringifyExpr(dim)})};
}
}
}

const ref = getVariable(context, name, elemCount);
const ref = context.variableFetcher.getVariable(context, name, elemCount);
if (!ref) {
return {error: getMessage('ARRAY1D_REF_UNDEFINED').format({name})};
return {error: getMessage('ARRAY1D_REF_UNDEFINED').format({name: context.variableFetcher.stringifyVariableName(name)})};
}

if (!ref.variables) {
return {error: getMessage('ARRAY1D_REF_NOT_LIST').format({name})};
return {error: getMessage('ARRAY1D_REF_NOT_LIST').format({name: context.variableFetcher.stringifyVariableName(name)})};
}

if (elemCount === undefined) {
Expand All @@ -112,12 +115,15 @@ export const extractView = function (context: LayoutDirectiveContext, name, opti
maxIndex: elemCount
});

const thresholdsMap = thresholds ? getCursorMap(context, thresholds, {}) : [];

const selection = range(0, elemCount + 1);
finalizeCursors(selection, cursorMap, cursorRows);

return {
ref,
cursorMap,
thresholdsMap,
};
};

Expand All @@ -133,7 +139,7 @@ export const getCursorMap = function (context: LayoutDirectiveContext, cursorNam
const cursorMap = []; // spare array

cursorNames.forEach(function(name) {
const cursorVariable = getVariable(context, name);
const cursorVariable = context.variableFetcher.getVariable(context, name);
if (!cursorVariable) {
return;
}
Expand All @@ -142,18 +148,19 @@ export const getCursorMap = function (context: LayoutDirectiveContext, cursorNam
return;
}

const cursorLabel = context.variableFetcher.stringifyVariableName(name);

const index = cursorVariable.value;

if (index >= minIndex && index <= maxIndex) {
// TODO: We currently do not attempt to find the previous value of the cursor (and when it changed) ?
if (undefined === minIndex || undefined === maxIndex || (index >= minIndex && index <= maxIndex)) {
if (!(index in cursorMap)) {
cursorMap[index] = {
index,
labels: []
labels: [],
};
}

cursorMap[index].labels.push(name);
cursorMap[index].labels.push(cursorLabel);
}
});

Expand Down
27 changes: 14 additions & 13 deletions frontend/stepper/analysis/directives/sort.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import React from 'react';

import {getVariables, renderArrow, renderValue} from './utils';
import {renderArrow, renderValue} from './utils';
import {extractView} from './array_utils';
import {SvgPan} from '../../views/SvgPan';
import {DirectiveFrame} from "../../views/DirectiveFrame";
import {StepperControls} from "../../index";
import {CodecastAnalysisVariable} from "../analysis";
import {LayoutDirectiveContext} from '../../../task/layout/LayoutDirective';

const DEFAULT_MAX_VISIBLE_CELLS = 40;
Expand Down Expand Up @@ -34,7 +33,8 @@ interface SortViewParams {
nbCells?: any,
ref?: any,
maxValue?: any,
cursorMap?: any
cursorMap?: any,
thresholdsMap?: any,
}

function getValueClass(view, index) {
Expand Down Expand Up @@ -123,22 +123,22 @@ interface ThresholdProps {
}

function Threshold({view, threshold}: ThresholdProps) {
const {name, value}: {name: string, value: CodecastAnalysisVariable} = threshold;
const {index, labels}: {index: string, labels: string[]} = threshold;

if (!value || !value.value) {
if (!index) {
return null;
}

const x0 = MARGIN_LEFT - THRESHOLD_LINE_EXT;
const x1 = MARGIN_LEFT + (BAR_WIDTH + BAR_SPACING) * view.nbCells + THRESHOLD_LINE_EXT;
const x2 = MARGIN_LEFT - THRESHOLD_MARGIN_RIGHT;
const y0 = MARGIN_TOP + BAR_HEIGHT * Number(value.value) / view.maxValue;
const y0 = MARGIN_TOP + BAR_HEIGHT * Number(index) / view.maxValue;
const y1 = y0 + TEXT_BASELINE;

return (
<g className="threshold">
<line x1={x0} x2={x1} y1={y0} y2={y0}/>
<text x={x2} y={y1}>{name}</text>
<text x={x2} y={y1}>{labels.join(',')}</text>
</g>
);
}
Expand Down Expand Up @@ -183,16 +183,17 @@ export class SortView extends React.PureComponent<SortViewProps> {

const {dim} = byName;
const cellPan = this.getPosition();
const thresholds = (byName.thresholds) ? byName.thresholds : [];
const cursors = (byName.cursors) ? byName.cursors : [];
const cursorRows = (byName.cursorRows) ? byName.cursorRows : 1;
const maxVisibleCells = (byName.n) ? byName.n : DEFAULT_MAX_VISIBLE_CELLS;
const thresholds = byName.thresholds ? context.variableFetcher.getList(byName.thresholds, []) : [];
const cursors = byName.cursors ? context.variableFetcher.getList(byName.cursors, []) : [];
const cursorRows = byName.cursorRows ? context.variableFetcher.getNumber(byName.cursorRows, 1) : 1;
const maxVisibleCells = byName.n ? context.variableFetcher.getNumber(byName.n, DEFAULT_MAX_VISIBLE_CELLS) : DEFAULT_MAX_VISIBLE_CELLS;
const svgHeight = MARGIN_TOP + BAR_HEIGHT + BAR_MARGIN_BOTTOM + TEXT_LINE_HEIGHT + MIN_ARROW_HEIGHT + TEXT_LINE_HEIGHT * cursorRows + MARGIN_BOTTOM;

const view: SortViewParams = {
dim,
fullView,
cursors,
thresholds,
maxVisibleCells,
cursorRows,
};
Expand All @@ -201,7 +202,7 @@ export class SortView extends React.PureComponent<SortViewProps> {
return <DirectiveFrame {...this.props}>{view.error}</DirectiveFrame>;
}

view.thresholds = getVariables(context, thresholds);
view.thresholds = context.variableFetcher.getVariables(context, thresholds);

const list = view.ref.variables;
view.nbCells = list.length;
Expand All @@ -227,7 +228,7 @@ export class SortView extends React.PureComponent<SortViewProps> {
{list.map((element, index) => <Bar key={index} index={index} view={view}/>)}
</g>
<g className="thresholds">
{view.thresholds.map((threshold, i) => <Threshold key={i} view={view}
{view.thresholdsMap.map(threshold => <Threshold key={threshold.index} view={view}
threshold={threshold}/>)}
</g>
</g>
Expand Down
Loading

0 comments on commit a29c4b7

Please sign in to comment.