Skip to content

Commit

Permalink
feat: escape characters in string conversion of string constants (#908)
Browse files Browse the repository at this point in the history
Closes #904

### Summary of Changes

Newlines, tabs, etc. now get escaped when converting a string constant
to a string. This affects inlay hints for literal types, for example.
  • Loading branch information
lars-reimann authored Feb 22, 2024
1 parent 1f6502e commit 72a9c3c
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,31 @@ const handleEscapeSequence = (input: string, index: number, endIndex: number): [

return [current, index + 1];
};

const replacements = new Map([
['\b', '\\b'],
['\f', '\\f'],
['\n', '\\n'],
['\r', '\\r'],
['\t', '\\t'],
['\v', '\\v'],
['\0', '\\0'],
['"', '\\"'],
['{', '\\{'],
['\\', '\\\\'],
]);

/**
* Escape a string.
*/
export const escapeString = (input: string): string => {
let result = '';

for (let i = 0; i < input.length; i++) {
const current = input.charAt(i);
const replacement = replacements.get(current);
result += replacement ? replacement : current;
}

return result;
};
5 changes: 3 additions & 2 deletions packages/safe-ds-lang/src/language/partialEvaluation/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
type SdsParameter,
} from '../generated/ast.js';
import { getParameters, streamBlockLambdaResults } from '../helpers/nodeProperties.js';
import { escapeString } from '../grammar/safe-ds-value-converter.js';

export type ParameterSubstitutions = Map<SdsParameter, EvaluatedNode>;
export type ResultSubstitutions = Map<SdsAbstractResult, EvaluatedNode>;
Expand Down Expand Up @@ -131,11 +132,11 @@ export class StringConstant extends Constant {
}

override toString(): string {
return `"${this.value}"`;
return `"${escapeString(this.value)}"`;
}

override toInterpolationString(): string {
return this.value;
return escapeString(this.value);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
isSdsTemplateStringInner,
isSdsTemplateStringStart,
} from '../../../src/language/generated/ast.js';
import { escapeString } from '../../../src/language/grammar/safe-ds-value-converter.js';

const services = createSafeDsServices(EmptyFileSystem).SafeDs;

Expand Down Expand Up @@ -215,3 +216,22 @@ describe('runConverter', () => {
});
});
});

describe('escapeString', () => {
const tests = [
{ unescaped: '\b', escaped: '\\b' },
{ unescaped: '\f', escaped: '\\f' },
{ unescaped: '\n', escaped: '\\n' },
{ unescaped: '\r', escaped: '\\r' },
{ unescaped: '\t', escaped: '\\t' },
{ unescaped: '\v', escaped: '\\v' },
{ unescaped: '\0', escaped: '\\0' },
{ unescaped: '"', escaped: '\\"' },
{ unescaped: '{', escaped: '\\{' },
{ unescaped: '\\', escaped: '\\\\' },
];

it.each(tests)('should escape $unescaped', ({ escaped, unescaped }) => {
expect(escapeString(unescaped)).toBe(escaped);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ pipeline test {
// $TEST$ serialization "test"
»"test"«;

// $TEST$ serialization "test "
// $TEST$ serialization "test\t"
»"test\t"«;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pipeline test {
// $TEST$ serialization "start 1 inner1 true inner2 test end"
»"start {{ 1 }} inner1 {{ true }} inner2 {{ "test" }} end"«;

// $TEST$ serialization "start 1 inner1 true inner2 test end "
// $TEST$ serialization "start\\t1 inner1\\ttrue inner2\\ttest end\\t"
»"start\t{{ 1 }} inner1\t{{ true }} inner2\t{{ "test" }} end\t"«;

// $TEST$ serialization ?
Expand Down

0 comments on commit 72a9c3c

Please sign in to comment.