Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | 3189x 3189x 3189x 3189x 3189x 3189x 3189x 51026x 51026x 51026x 6378x 44648x 44647x 44647x 44647x 1x 3189x 6378x 6378x 70158x 70158x 6378x 44647x 44647x 44647x 44647x 44647x 44647x 44647x 3190x 3190x 3190x 41457x 41457x 532563x 532563x 532563x 7x 532556x 41457x 535753x 535753x 3x 535750x 535750x 2x 535748x 535748x 102045x 433703x 334842x 98861x 98859x 2x | /**
* ADR-049: reads the target catalog -- a C-Next source file -- into plain data.
*
* The catalog is parsed by the real C-Next grammar, so it stays a C-Next
* program rather than a look-alike some second reader accepts. This module is
* the only one that sees its parse tree; what leaves it is structs, constants
* and literal values. Judging those values (kinds, completeness, the schema)
* is `TargetDescriptions`' job, not this one's.
*
* A value counts only if it is a literal: an unsigned decimal integer, `true`
* or `false`, or a string with no escapes. Anything else -- `16 + 16`, `-1`,
* `(true)`, `32u8`, a reference to another constant -- is reported, because
* no reader of the catalog may have to evaluate code to learn a target.
*/
import CNextSourceParser from "./CNextSourceParser";
import type * as Parser from "./grammar/CNextParser";
import ExpressionUnwrapper from "../../utils/ExpressionUnwrapper";
import type ITargetCatalogEntry from "../../types/ITargetCatalogEntry";
import type ITargetCatalogSource from "../../types/ITargetCatalogSource";
import type TTargetFieldValue from "../../types/TTargetFieldValue";
class TargetCatalogParser {
static parse(text: string): ITargetCatalogSource {
const parsed = CNextSourceParser.parse(text);
const errors = parsed.parseErrors.map(
(error) => `line ${error.line}: ${error.message}`,
);
const structs = new Map<string, ReadonlyMap<string, string>>();
const entries: ITargetCatalogEntry[] = [];
const directives = [
...parsed.tree.includeDirective(),
...parsed.tree.preprocessorDirective(),
];
for (const directive of directives) {
errors.push(
`line ${directive.start!.line}: directives are not allowed in the catalog`,
);
}
for (const declaration of parsed.tree.declaration()) {
const struct = declaration.structDeclaration();
const variable = declaration.variableDeclaration();
if (struct) {
structs.set(
struct.IDENTIFIER().getText(),
TargetCatalogParser.members(struct),
);
} else if (variable) {
const entry = TargetCatalogParser.entry(variable, errors);
Eif (entry) {
entries.push(entry);
}
} else {
errors.push(
`line ${declaration.start!.line}: only structs and constants are allowed in the catalog`,
);
}
}
return { structs, entries, errors };
}
private static members(
struct: Parser.StructDeclarationContext,
): ReadonlyMap<string, string> {
const members = new Map<string, string>();
for (const member of struct.structMember()) {
const suffix = member.arrayDimension().length > 0 ? "[]" : "";
members.set(
member.IDENTIFIER().getText(),
member.type().getText() + suffix,
);
}
return members;
}
private static entry(
variable: Parser.VariableDeclarationContext,
errors: string[],
): ITargetCatalogEntry | null {
const line = variable.start!.line;
const name = variable.IDENTIFIER().getText();
const expression = variable.expression();
Iif (!variable.constModifier() || !expression) {
errors.push(`line ${line}: '${name}' must be a const with a value`);
return null;
}
const typeName = variable.type()!.getText();
const initializer =
ExpressionUnwrapper.getPostfixExpression(expression)
?.primaryExpression()
.structInitializer() ?? null;
if (!initializer) {
const value = TargetCatalogParser.literal(expression);
Iif (value === null) {
errors.push(`line ${line}: '${name}' is not a literal`);
}
return {
constName: name,
typeName,
line,
fields: new Map(),
...(value === null ? {} : { value }),
};
}
const fields = new Map<string, TTargetFieldValue>();
for (const field of initializer.fieldInitializerList().fieldInitializer()) {
const fieldName = field.IDENTIFIER().getText();
const value = TargetCatalogParser.literal(field.expression());
if (value === null) {
errors.push(
`line ${field.start!.line}: '${name}.${fieldName}' is not a literal`,
);
} else {
fields.set(fieldName, value);
}
}
return { constName: name, typeName, line, fields };
}
/** The expression's value when it is one literal the catalog accepts */
private static literal(
expression: Parser.ExpressionContext,
): TTargetFieldValue | null {
const postfix = ExpressionUnwrapper.getPostfixExpression(expression);
if (!postfix || postfix.postfixOp().length > 0) {
return null;
}
const literal = postfix.primaryExpression().literal();
if (!literal) {
return null;
}
const text = literal.getText();
if (literal.TRUE() || literal.FALSE()) {
return text === "true";
}
if (literal.INTEGER_LITERAL() && /^(0|[1-9]\d*)$/.test(text)) {
return Number(text);
}
if (literal.STRING_LITERAL() && !text.includes("\\")) {
return text.slice(1, -1);
}
return null;
}
}
export default TargetCatalogParser;
|