diff --git a/cocas/debug_export.py b/cocas/debug_export.py new file mode 100644 index 0000000..22dfb63 --- /dev/null +++ b/cocas/debug_export.py @@ -0,0 +1,36 @@ +import bisect +import json +import re + +from cocas.location import CodeLocation + + +def default_json_(o, files): + # if some json object should be in one line, without \n, wrap it with these __no_breaks + # do not cascade __no_breaks, regex will break (e.g. {begin, {begin, ..., end}, end} + if isinstance(o, CodeLocation): + return { + "__no_breaks_begin": [], + "f": bisect.bisect_left(files, o.file), "l": o.line, "c": o.column, + "__no_breaks_end": [] + } + else: + raise TypeError(f'Object of type {o.__class__.__name__} ' + f'is not JSON serializable') + + +def default_json(files): + return lambda x: default_json_(x, files) + + +def code_location_json(files, cl: CodeLocation): + return {"f": bisect.bisect_left(files, cl.file), "l": cl.line, "c": cl.column} + + +def debug_export(code_locations: dict[int, CodeLocation]) -> str: + files = sorted(set(map(lambda x: x.file, code_locations.values()))) + dump = json.dumps({"files": files, "codeLocations": code_locations}, + default=default_json(files), indent=4, ensure_ascii=False) + pattern = re.compile(r"{\n\s+\"__no_breaks_begin\": \[],\n\s+([\S\s]+?),\n\s+\"__no_breaks_end\": \[]\s+}") + dump = re.sub(pattern, lambda m: "{" + re.sub(r"\n\s+", " ", m.group(1)) + "}", dump) + return dump diff --git a/cocas/main.py b/cocas/main.py index f02bb27..48ab156 100755 --- a/cocas/main.py +++ b/cocas/main.py @@ -1,11 +1,9 @@ import argparse import codecs import importlib -import json import os import pathlib import pkgutil -from dataclasses import asdict from typing import Union import antlr4 @@ -13,6 +11,7 @@ from cocas.assembler import assemble from cocas.ast_builder import build_ast +from cocas.debug_export import debug_export from cocas.error import CdmException, CdmExceptionTag, CdmLinkException, log_error from cocas.linker import link from cocas.macro_processor import process_macros, read_mlb @@ -67,7 +66,7 @@ def main(): parser.add_argument('-m', '--merge', action='store_true', help='merge object files into one') parser.add_argument('-o', '--output', type=str, help='specify output file name') debug_group = parser.add_argument_group('debug') - debug_group.add_argument('--debug', type=str, nargs='?', const='out.dbg.json', help='export debug information') + debug_group.add_argument('--debug', type=str, nargs='?', const=True, help='export debug information') debug_path_group = debug_group.add_mutually_exclusive_group() debug_path_group.add_argument('--relative-path', type=pathlib.Path, help='convert source files paths to relative in debug info and object files') @@ -254,11 +253,18 @@ def main(): handle_os_error(e) if args.debug: - code_locations = {key: asdict(loc) for key, loc in code_locations.items()} - json_locations = json.dumps(code_locations, indent=4, sort_keys=True) + if args.debug is True: + if args.output: + filename = pathlib.Path(args.output).with_suffix('.dbg.json') + else: + filename = 'out.dbg.json' + else: + filename = args.debug + code_locations = {key: value for (key, value) in sorted(code_locations.items())} + debug_info = debug_export(code_locations) try: - with open(args.debug, 'w') as f: - f.write(json_locations) + with open(filename, 'w') as f: + f.write(debug_info) except OSError as e: handle_os_error(e)