Skip to content

Commit

Permalink
fix edge case when block's first child is comment
Browse files Browse the repository at this point in the history
  • Loading branch information
joswig committed Sep 26, 2024
1 parent 4ada443 commit 17a1e70
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 89 deletions.
29 changes: 27 additions & 2 deletions src/utilities/codemirror/cdlDictionary.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,31 @@ ccode := [ 16 ] '0002' HEX
END STEM
STEM : CAT2_STEM_INLINE_ARG ( ccode : 16, data : 0 )
cmd-type : CAT2
LOOKUP ARGUMENT : arg_inline
TITLE : "Used in 1 commands"
CONVERSION : HEX
LENGTH : 8
'AAA' = '0'
'BBB' = '1'
'CCC' = '2'
'DDD' = '3'
END LOOKUP ARGUMENT
READ ARGUMENT arg_inline
ccode := [ 16 ] '0002' HEX
!@ ATTACHMENT : desc
!@ "Test Command with inline argument"
!@ END ATTACHMENT
END STEM
`;

describe('cdl parse tests', async () => {
Expand All @@ -114,7 +139,7 @@ describe('cdl parse tests', async () => {
expect(cdlDictionary.header.mission_name).toBe('Unit_test');
expect(cdlDictionary.header.spacecraft_ids).toEqual([255]);

expect(cdlDictionary.fswCommands.length).toBe(2);
expect(cdlDictionary.fswCommands.length).toBe(3);

expect(cdlDictionary.fswCommands[1].arguments.length).toBe(6);
const arg1Range = (cdlDictionary.fswCommands[1].arguments[1] as FswCommandArgumentInteger).range;
Expand All @@ -128,7 +153,7 @@ describe('cdl parse tests', async () => {

expect(cdlDictionary.fswCommands[1].description).toEqual('Test Command with 3 arguments');

console.log(JSON.stringify(cdlDictionary.fswCommands[1], null, 2));
console.log(JSON.stringify(cdlDictionary, null, 2));
});

test('round trip', () => {
Expand Down
86 changes: 65 additions & 21 deletions src/utilities/codemirror/cdlDictionary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,9 @@ export function parseCdlDictionary(contents: string, id?: string, path?: string)
};

const enums: Enum[] = [];
const enumMap: { [name: string]: Enum } = {};

const globalArguments: FswCommandArgumentMap = {};
const fswCommands: FswCommand[] = [];

// parse globals and stems
// assumes all global arguments are defined prior to stems
for (const line of lineIterator) {
Expand All @@ -82,7 +81,6 @@ export function parseCdlDictionary(contents: string, id?: string, path?: string)
// empty enums aren't allowed in ampcs dictionary format
if (lookupEnum.values.length) {
enums.push(lookupEnum);
enumMap[lookupEnum.name] = lookupEnum;
globalArguments[lookupArg.name] = lookupArg;
}
break;
Expand All @@ -103,15 +101,17 @@ export function parseCdlDictionary(contents: string, id?: string, path?: string)
for (const stemLine of lineIterator) {
numericLines.push(stemLine);
if (stemLine.match(END_STEM)) {
fswCommands.push(parseStem(numericLines, globalArguments));
const [cmd, cmdEnums] = parseStem(numericLines, globalArguments);
fswCommands.push(cmd);
enums.push(...cmdEnums);
break;
}
}
}
}

return {
enumMap,
enumMap: Object.fromEntries(enums.map(e => [e.name, e])),
enums,
fswCommandMap: Object.fromEntries(fswCommands.map(cmd => [cmd.stem, cmd])),
fswCommands,
Expand All @@ -123,7 +123,7 @@ export function parseCdlDictionary(contents: string, id?: string, path?: string)
};
}

export function parseStem(lines: string[], globalArguments: FswCommandArgumentMap): FswCommand {
export function parseStem(lines: string[], globalArguments: FswCommandArgumentMap): [FswCommand, Enum[]] {
let stem = '';
for (const line of lines) {
const m = line.match(START_STEM);
Expand All @@ -140,25 +140,61 @@ export function parseStem(lines: string[], globalArguments: FswCommandArgumentMa
description = descriptionLineMatch[1];
}

// stems may also have arguments defined inline
const localArguments: FswCommandArgumentMap = {};
const localEnums: Enum[] = [];
const lineIterator = lines.values();
for (const line of lineIterator) {
if (line.match(START_LOOKUP_ARG)) {
const lookupLines: string[] = [line];
for (const lineOfLookup of lineIterator) {
lookupLines.push(lineOfLookup);
if (lineOfLookup.match(END_LOOKUP_ARG)) {
const [lookupArg, lookupEnum] = parseLookupArgument(lookupLines, stem);
// empty enums aren't allowed in ampcs dictionary format
if (lookupEnum.values.length) {
localEnums.push(lookupEnum);
localArguments[lookupArg.name] = lookupArg;
}
break;
}
}
} else if (line.match(START_NUMERIC_ARG)) {
const numericLines: string[] = [line];
for (const lineOfNumeric of lineIterator) {
numericLines.push(lineOfNumeric);
if (lineOfNumeric.match(END_NUMERIC_ARG)) {
const numericArgument = parseNumericArgument(numericLines);
localArguments[numericArgument.name] = numericArgument;
break;
}
}
}
}

const fswArguments: FswCommandArgument[] = [];

for (const line of lines) {
const readArgMatch = line.match(/^\s*READ\s+ARGUMENT\s+(\w+)\s*/);
if (readArgMatch) {
const globalArg = globalArguments[readArgMatch[1]];
if (globalArg) {
fswArguments.push(globalArg);
const argName = readArgMatch[1];
const argDef = localArguments[argName] ?? globalArguments[argName];
if (argDef) {
fswArguments.push(argDef);
}
}
}

return {
argumentMap: Object.fromEntries(fswArguments.map(arg => [arg.name, arg])),
arguments: fswArguments,
description,
stem,
type: 'fsw_command',
};
return [
{
argumentMap: Object.fromEntries(fswArguments.map(arg => [arg.name, arg])),
arguments: fswArguments,
description,
stem,
type: 'fsw_command',
},
localEnums,
];
}

export function parseNumericArgument(lines: string[]): FswCommandArgument {
Expand Down Expand Up @@ -275,7 +311,7 @@ export function parseNumericArgument(lines: string[]): FswCommandArgument {
};
}

export function parseLookupArgument(lines: string[]): [FswCommandArgumentEnum, Enum] {
export function parseLookupArgument(lines: string[], namespace?: string): [FswCommandArgumentEnum, Enum] {
const lineIterator = lines.values();

let name = '';
Expand All @@ -287,12 +323,20 @@ export function parseLookupArgument(lines: string[]): [FswCommandArgumentEnum, E
}

let conversion = '';
let bit_length: null | number = null;
const values: EnumValue[] = [];

for (const line of lines) {
if (line.match(END_LOOKUP_ARG)) {
break;
} else if (conversion) {
}

const lengthMatch = line.match(/^\s*LENGTH\s*:\s*(\d+)/);
if (lengthMatch) {
bit_length = parseInt(lengthMatch[1], 10);
continue;
}

if (conversion) {
const lookupMatch = line.match(/^\s*'(\w+)'\s*=\s*'(\w+)'/);
if (lookupMatch) {
const symbol = lookupMatch[1];
Expand All @@ -316,11 +360,11 @@ export function parseLookupArgument(lines: string[]): [FswCommandArgumentEnum, E
}
}

const enum_name = name;
const enum_name = namespace ? `__${namespace}_${name}` : name;
return [
{
arg_type: 'enum',
bit_length: null,
bit_length,
default_value: null,
description: '',
enum_name,
Expand Down
93 changes: 47 additions & 46 deletions src/utilities/codemirror/vml-folder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,66 +86,67 @@ export function computeBlocks(state: EditorState): TreeState {
const stack: BlockStack = [];
const docString = state.sliceDoc();

statementAndCategory
// filter out ones that don't impact blocks
// .filter(stemNode => isBlockCommand(state.sliceDoc(stemNode.from, stemNode.to)))
.forEach(([node, category]) => {
// const stem = state.sliceDoc(stemNode.from, stemNode.to);
const topStem = stack.at(-1)?.stem;

if (topStem && closesBlock(category, topStem)) {
// close current block
const blockInfo: BlockStackNode | undefined = stack.pop();
if (blockInfo) {
// pair end with existing start to provide info for fold region
const commandStr = state.toText(docString).lineAt(node.from).text;
const leadingSpaces = commandStr.length - commandStr.trimStart().length;
const endPos: undefined | number = node.from - leadingSpaces - 1;
Object.assign(treeState[blockInfo.node.from], { end: node, endPos });

// works but wrong line
// Object.assign(treeState[blockInfo.node.from], { end: node, endPos: node.from });
}
} else if (blockClosingStems.has(category)) {
// unexpected close
treeState[node.from] = {
end: node,
};
return; // don't open a new block for else_if type
}

if (blockOpeningStems.has(category)) {
// open new block

treeState[node.from] = {
start: node,
startPos: node.to - 1,
};

stack.push({
node: node,
stem: category,
});
statementAndCategory.forEach(([node, category]) => {
// const stem = state.sliceDoc(stemNode.from, stemNode.to);
const topStem = stack.at(-1)?.stem;

if (topStem && closesBlock(category, topStem)) {
// close current block
const blockInfo: BlockStackNode | undefined = stack.pop();
if (blockInfo) {
// pair end with existing start to provide info for fold region
const commandStr = state.toText(docString).lineAt(node.from).text;
const leadingSpaces = commandStr.length - commandStr.trimStart().length;
const endPos: undefined | number = node.from - leadingSpaces - 1;
Object.assign(treeState[blockInfo.node.from], { end: node, endPos });
}
});
} else if (blockClosingStems.has(category)) {
// unexpected close
treeState[node.from] = {
end: node,
};
return; // don't open a new block for else_if type
}

if (blockOpeningStems.has(category)) {
// open new block

// Time_tagged_statement -> Statement
// Statement -> Statement-subtype (If/Else_if/Else....) notably exclude Endlines
// Statement-subtype -> last token
const startPos = (node.getChild('Statement')?.firstChild?.lastChild ?? node).to;
// const startPos = (node.lastChild?.firstChild?.lastChild ?? node).to;

treeState[node.from] = {
start: node,
startPos,
};

stack.push({
node: node,
stem: category,
});
}
});

blocksForState.set(state, treeState);
}
return blocksForState.get(state)!;
}

export const vmlBlockFolder = foldService.of((state: EditorState, start, end) => {
const blocks = computeBlocks(state);
for (let node: SyntaxNode | null = syntaxTree(state).resolveInner(end, -1); node; node = node.parent) {
if (node.from < start) {
break;
}

const blocks = computeBlocks(state);
const foo = blocks[node.from];
if (foo !== undefined && foo.startPos !== undefined && foo.endPos !== undefined) {
// const block = blocks[node.from];
const block = blocks[start];
if (block?.startPos !== undefined && block?.endPos !== undefined) {
return {
from: foo.startPos,
to: foo.endPos,
from: block.startPos,
to: block.endPos,
};
}
}
Expand Down
Loading

0 comments on commit 17a1e70

Please sign in to comment.