def ignore(tag: Optional[str] = None) -> p.Parser: def process(element: Element) -> ast.NullStatement: return ast.NullStatement(source=source_from_element(element)) if tag: return p.tag(tag) ^ process return p.any() ^ process
def phase() -> p.Parser: phase_block = block( value(str, 'mission') | value(cycles, 'cycles') | value(repeat, 'repeat') | value(ref_pass, 'ref_pass', var='reference_pass') | value(time, 'start_time') | value(time, 'end_time') | subcycles()) process = named_block_processor('phase', phase_block, ast.Phase) return p.tag('phase') ^ process
def if_statement(internal: p.Parser) -> p.Parser: def process(statements: Tuple[Element, ast.Statement]) -> ast.Statement: if_element, false_statement = statements condition = parse_condition(if_element.attributes) true_statement = internal(if_element.down())[0] source = source_from_element(if_element) return ast.If(condition=condition, true_statement=true_statement, false_statement=false_statement, source=source) return p.tag('if') + p.opt( elseif_statement(internal) | else_statement(internal)) ^ process
def alias() -> p.Parser: def process(element: Element) -> ast.Alias: try: alias = element.attributes['name'] except KeyError: raise error_at(element)("'name' attribute missing from <alias>") variables = element.text.split() if element.text else [] if not variables: raise error_at(element)('<alias> cannot be empty') condition = parse_condition(element.attributes) action = parse_action(element) source = source_from_element(element) return ast.Alias(alias, variables, condition, action, source=source) return p.tag('alias') ^ process
def variable() -> p.Parser: variable_block = block( value(str, 'long_name', var='name') | value(str, 'standard_name') | value(str, 'source') | value(str, 'comment') | value(unit, 'units') | value(list_of(str), 'flag_values') | value(list_of(str), 'flag_masks') | value(range_of(float), 'limits') | value(range_of(float), 'plot_range') | # used by rads for database generation, has no effect on end users ignore('parameters') | ignore('data') | # TODO: Complex field. value(list_of(str), 'quality_flag') | # not currently used value(int, 'dimensions') | ignore('format') | ignore('compress') | ignore('default')) process = named_block_processor('var', variable_block, ast.Variable) return p.tag('var') ^ process
def elseif_statement(internal: p.Parser) -> p.Parser: def process(statements: Iterable[Any]) -> ast.Statement: elseif_element, false_statement = statements condition = parse_condition(elseif_element.attributes) true_statement = internal(elseif_element.down())[0] source = source_from_element(elseif_element) return ast.If(condition, true_statement, false_statement, source=source) return p.Apply( p.tag('elseif') + p.opt( p.lazy(lambda: elseif_statement(internal)) | else_statement(internal)), process)
def value(parser: Callable[[str], Any] = nop, tag: Optional[str] = None, var: Optional[str] = None) -> p.Parser: def process(element: Element) -> ast.Assignment: var_ = var if var else element.tag condition = parse_condition(element.attributes) action = parse_action(element) text = element.text if element.text else '' source = source_from_element(element) try: return ast.Assignment(name=var_, value=parser(text), condition=condition, action=action, source=source) except (ValueError, TypeError) as err: raise error_at(element)(str(err)) if tag: return p.tag(tag) ^ process return p.any() ^ process
def subcycles() -> p.Parser: def process(element: Element) -> ast.Statement: start: Optional[int] condition = parse_condition(element.attributes) action = parse_action(element) try: start = int(element.attributes['start']) except KeyError: start = None except ValueError as err: raise error_at(element)(str(err)) text = element.text if element.text else '' lengths = [int(s) for s in text.split()] source = source_from_element(element) return ast.Assignment(name='subcycles', value=SubCycles(lengths, start=start), condition=condition, action=action, source=source) return p.tag('subcycles') ^ process
def satellites() -> p.Parser: def process(element: Element) -> ast.Satellites: source = source_from_element(element) if not element.text: return ast.Satellites(source=source) satellites_ = [] for num, line in enumerate(element.text.strip().splitlines()): line = line.strip() if line: id_source = ast.Source(line=element.opening_line + num + 1, file=element.file) try: id_, id3, *names = line.split() except ValueError: raise p.GlobalParseFailure( id_source.file, id_source.line, f"missing 3 character ID for satellite '{id_}'") satellites_.append( ast.SatelliteID(id_, id3, set(names), source=id_source)) return ast.Satellites(*satellites_, source=source) return p.tag('satellites') ^ process
def preparse(root: Element) -> ast.Statement: def process(elements: Sequence[Element]) -> Element: return elements[-1] parser = p.until(p.tag('satellites')) + satellites() ^ process return cast(ast.Statement, parser(root.down())[0])
def else_statement(internal: p.Parser) -> p.Parser: def process(element: Element) -> Any: return internal(element.down())[0] return p.tag('else') ^ process