Skip to content

Commit

Permalink
Support nested if statements
Browse files Browse the repository at this point in the history
  • Loading branch information
mbloch committed Feb 13, 2024
1 parent fe9cdc2 commit ea1ede5
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 39 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mapshaper",
"version": "0.6.62",
"version": "0.6.63",
"description": "A tool for editing vector datasets for mapping and GIS.",
"keywords": [
"shapefile",
Expand Down
1 change: 0 additions & 1 deletion src/cli/mapshaper-run-commands.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { error, UserError, message, print, loggingEnabled, printError } from '..
import { Job } from '../mapshaper-job';
import { runningInBrowser } from '../mapshaper-env';
import utils from '../utils/mapshaper-utils';
import { resetControlFlow } from '../mapshaper-control-flow';
import require from '../mapshaper-require';
import { commandTakesFileInput } from '../cli/mapshaper-command-info';
import { version } from '../../package.json';
Expand Down
27 changes: 9 additions & 18 deletions src/commands/mapshaper-if-elif-else-endif.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,26 @@ import cmd from '../mapshaper-cmd';
import { layerIsEmpty } from '../dataset/mapshaper-layer-utils';
import { stop } from '../utils/mapshaper-logging';
import {
resetControlFlow,
inControlBlock,
enterActiveBranch,
enterInactiveBranch,
inActiveBranch,
blockWasActive,
jobIsStopped
blockIsComplete,
jobIsStopped,
enterBlock,
leaveBlock
} from '../mapshaper-control-flow';
import { compileIfCommandExpression } from '../expressions/mapshaper-layer-expressions';

export function skipCommand(cmdName, job) {
// allow all control commands to run
if (jobIsStopped(job)) return true;
if (isControlFlowCommand(cmdName)) return false;
return inControlBlock(job) && !inActiveBranch(job);
return !inActiveBranch(job);
}

cmd.if = function(job, opts) {
if (inControlBlock(job)) {
stop('Nested -if commands are not supported.');
}
enterBlock(job);
evaluateIf(job, opts);
};

Expand All @@ -38,7 +37,7 @@ cmd.else = function(job) {
if (!inControlBlock(job)) {
stop('-else command must be preceded by an -if command.');
}
if (blockWasActive(job)) {
if (blockIsComplete(job)) {
enterInactiveBranch(job);
} else {
enterActiveBranch(job);
Expand All @@ -49,32 +48,24 @@ cmd.endif = function(job) {
if (!inControlBlock(job)) {
stop('-endif command must be preceded by an -if command.');
}
resetControlFlow(job);
leaveBlock(job);
};

function isControlFlowCommand(cmd) {
return ['if','elif','else','endif'].includes(cmd);
}

function test(catalog, opts) {
// var targ = getTargetLayer(catalog, opts);
if (opts.expression) {
return compileIfCommandExpression(opts.expression, catalog, opts)();
}
// if (opts.empty) {
// return layerIsEmpty(targ.layer);
// }
// if (opts.not_empty) {
// return !layerIsEmpty(targ.layer);
// }
return true;
}

function evaluateIf(job, opts) {
if (!blockWasActive(job) && test(job.catalog, opts)) {
if (!blockIsComplete(job) && test(job.catalog, opts)) {
enterActiveBranch(job);
} else {
enterInactiveBranch(job);
}
}

56 changes: 37 additions & 19 deletions src/mapshaper-control-flow.mjs
Original file line number Diff line number Diff line change
@@ -1,41 +1,59 @@

export function resetControlFlow(job) {
job.control = null;
}

export function stopJob(job) {
getState(job).stopped = true;
job.stopped = true;
}

export function jobIsStopped(job) {
return getState(job).stopped === true;
return job.stopped === true;
}

export function inControlBlock(job) {
return !!getState(job).inControlBlock;
return getStack(job).length > 0;
}

export function enterBlock(job) {
var stack = getStack(job);
// skip over a block if it is inside an inactive branch
stack.push({
active: false,
complete: !inActiveBranch(job)
});
}

export function leaveBlock(job) {
var stack = getStack(job);
stack.pop();
}

export function enterActiveBranch(job) {
var state = getState(job);
state.inControlBlock = true;
state.active = true;
state.complete = true;
var block = getCurrentBlock(job);
block.active = true;
block.complete = true;
}

export function enterInactiveBranch(job) {
var state = getState(job);
state.inControlBlock = true;
state.active = false;
var block = getCurrentBlock(job);
block.active = false;
}

export function blockIsComplete(job) {
var block = getCurrentBlock(job);
return block.complete;
}

export function blockWasActive(job) {
return !!getState(job).complete;
function getCurrentBlock(job) {
var stack = getStack(job);
return stack[stack.length-1];
}

// A branch is considered to be active if it and all its parents are active
// (Main branch is considered to be active)
export function inActiveBranch(job) {
return !!getState(job).active;
var stack = getStack(job);
return stack.length === 0 || stack.every(block => block.active);
}

function getState(job) {
return job.control || (job.control = {});
function getStack(job) {
job.control = job.control || {stack: []};
return job.control.stack;
}
33 changes: 33 additions & 0 deletions test/if-elif-else-test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ describe('mapshaper-if-elif-else-endif.js', function () {
});
});


it ('test expression', function(done) {
var data = {
type: 'GeometryCollection',
Expand Down Expand Up @@ -188,4 +189,36 @@ describe('mapshaper-if-elif-else-endif.js', function () {
done();
});
});

it ('nested if statement 1', async function() {
var data = 'name\na';
var cmd = `-i data.csv
-if true -each 'name="b"'
-if false -each 'name="c"'
-elif true -each 'name="d"'
-else -each 'name="e"'
-endif
-else -each name="f"'
-endif
-o`;
var out = await api.applyCommands(cmd, {'data.csv': data});
assert.equal(out['data.csv'], 'name\nd');
})

it ('nested if statement 2', async function() {
var data = 'name\na';
var cmd = `-i data.csv
-if false -each 'name="b"'
-if true -each 'name="c"'
-elif true -each 'name="d"'
-else -each 'name="e"'
-endif
-else -each name="f"'
-endif
-o`;
var out = await api.applyCommands(cmd, {'data.csv': data});
assert.equal(out['data.csv'], 'name\nf');
})


})

0 comments on commit ea1ede5

Please sign in to comment.