Skip to content

Commit

Permalink
[cocas] Add docstrings for all importable functions and modules
Browse files Browse the repository at this point in the history
  • Loading branch information
cjvth committed Feb 8, 2024
1 parent d6718d0 commit d4f1b4e
Show file tree
Hide file tree
Showing 26 changed files with 78 additions and 43 deletions.
1 change: 1 addition & 0 deletions cocas/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import assembler, linker, object_file, object_module
9 changes: 4 additions & 5 deletions cocas/assembler/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""Assembler module. Converts source files into object module structures"""

from .assembler import assemble_files, assemble_module
from .ast_builder import build_ast
from .exceptions import AssemblerException
from .macro_processor import process_macros
from .object_generator import generate_object_module
from .targets import import_target, list_assembler_targets
from .exceptions import AssemblerException, AssemblerExceptionTag
from .targets import list_assembler_targets
3 changes: 2 additions & 1 deletion cocas/assembler/assembler.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,13 @@ def assemble_files(target: str,
"""
Open and assemble multiple files into object modules
:param target: name of processor target
:param target: name of processor, should be valid
:param files: list of assembler files' paths to process
:param debug: if debug information should be collected
:param relative_path: if debug paths should be relative to some path
:param absolute_path: if relative paths should be converted to absolute
:param realpath: if paths should be converted to canonical
:return: list of pairs [source file path, object module]
"""
_ = absolute_path
target_instructions = import_target(target)
Expand Down
10 changes: 5 additions & 5 deletions cocas/assembler/ast_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
UntilLoopNode,
WhileLoopNode,
)
from .exceptions import AntlrErrorListener, AsmExceptionTag, AssemblerException
from .exceptions import AntlrErrorListener, AssemblerException, AssemblerExceptionTag
from .generated import AsmLexer, AsmParser, AsmParserVisitor


Expand Down Expand Up @@ -126,7 +126,7 @@ def visitConnective_condition(self, ctx: AsmParser.Connective_conditionContext):
cond = self.visitCondition(ctx.condition())
cond.conjunction = ctx.conjunction().getText()
if cond.conjunction != 'and' and cond.conjunction != 'or':
raise AssemblerException(AsmExceptionTag.ASM, self.source_path, ctx.start.line - self.line_offset,
raise AssemblerException(AssemblerExceptionTag.ASM, self.source_path, ctx.start.line - self.line_offset,
'Expected "and" or "or" in compound condition')
return cond

Expand Down Expand Up @@ -218,7 +218,7 @@ def visitStandaloneLabel(self, ctx: AsmParser.StandaloneLabelContext) -> LabelDe
label_decl = self.visitLabel_declaration(ctx.label_declaration())
label_decl.external = ctx.Ext() is not None
if label_decl.entry and label_decl.external:
raise AssemblerException(AsmExceptionTag.ASM, self.source_path, ctx.start.line - self.line_offset,
raise AssemblerException(AssemblerExceptionTag.ASM, self.source_path, ctx.start.line - self.line_offset,
f'Label {label_decl.label.name} cannot be both external and entry')
return label_decl

Expand Down Expand Up @@ -257,12 +257,12 @@ def build_ast(input_stream: InputStream, filepath: Path):
str_path = filepath.absolute().as_posix()
lexer = AsmLexer(input_stream)
lexer.removeErrorListeners()
lexer.addErrorListener(AntlrErrorListener(AsmExceptionTag.ASM, str_path))
lexer.addErrorListener(AntlrErrorListener(AssemblerExceptionTag.ASM, str_path))
token_stream = CommonTokenStream(lexer)
token_stream.fill()
parser = AsmParser(token_stream)
parser.removeErrorListeners()
parser.addErrorListener(AntlrErrorListener(AsmExceptionTag.ASM, str_path))
parser.addErrorListener(AntlrErrorListener(AssemblerExceptionTag.ASM, str_path))
cst = parser.program()
bav = BuildAstVisitor(str_path)
result = bav.visit(cst)
Expand Down
4 changes: 2 additions & 2 deletions cocas/assembler/code_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
UntilLoopNode,
WhileLoopNode,
)
from .exceptions import AsmExceptionTag, AssemblerException, CdmTempException
from .exceptions import AssemblerException, AssemblerExceptionTag, CdmTempException
from .targets import ICodeSegment, TargetInstructionsInterface


Expand All @@ -39,7 +39,7 @@ def __init__(self, address: int, lines: list, target_instructions: Type[TargetIn
target_instructions.finish(temp_storage)
except CdmTempException as e:
# if it isn't ok, must be at least one line
raise AssemblerException(AsmExceptionTag.ASM, lines[-1].location.file,
raise AssemblerException(AssemblerExceptionTag.ASM, lines[-1].location.file,
lines[-1].location.line, e.message)

def append_label(self, label_name):
Expand Down
7 changes: 5 additions & 2 deletions cocas/assembler/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
from .generated import AsmParser


class AsmExceptionTag(Enum):
class AssemblerExceptionTag(Enum):
"""Shows if an exception caused in macros or in usual source code"""
MACRO = "Macro"
ASM = "Assembler"

Expand All @@ -20,7 +21,9 @@ def __init__(self, message: str):


class AssemblerException(Exception):
def __init__(self, tag: AsmExceptionTag, file: str, line: int, description: str):
"""Exception raised when given source code is invalid"""

def __init__(self, tag: AssemblerExceptionTag, file: str, line: int, description: str):
self.tag = tag
self.file = file
self.line = line
Expand Down
10 changes: 5 additions & 5 deletions cocas/assembler/macro_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from cocas.object_module import CodeLocation

from .exceptions import AntlrErrorListener, AsmExceptionTag, AssemblerException, CdmTempException
from .exceptions import AntlrErrorListener, AssemblerException, AssemblerExceptionTag, CdmTempException
from .generated import MacroLexer, MacroParser, MacroVisitor


Expand Down Expand Up @@ -182,7 +182,7 @@ def visitMlb(self, ctx: MacroParser.MlbContext):
try:
self.add_macro(self.visitMlb_macro(child))
except CdmTempException as e:
raise AssemblerException(AsmExceptionTag.MACRO, self.filepath, child.start.line, e.message)
raise AssemblerException(AssemblerExceptionTag.MACRO, self.filepath, child.start.line, e.message)
return self.macros

def visitProgram(self, ctx: MacroParser.ProgramContext):
Expand All @@ -205,7 +205,7 @@ def visitProgram(self, ctx: MacroParser.ProgramContext):
self.rewriter.insertBeforeToken(child.start, expanded_text)
self.rewriter.delete(self.rewriter.DEFAULT_PROGRAM_NAME, child.start, child.stop)
except CdmTempException as e:
raise AssemblerException(AsmExceptionTag.MACRO, self.filepath, child.start.line, e.message)
raise AssemblerException(AssemblerExceptionTag.MACRO, self.filepath, child.start.line, e.message)

def visitMacro(self, ctx: MacroParser.MacroContext):
header = ctx.macro_header()
Expand Down Expand Up @@ -305,12 +305,12 @@ def process_macros(input_stream: InputStream, library_macros, filepath: Path):
str_path = filepath.absolute().as_posix()
lexer = MacroLexer(input_stream)
lexer.removeErrorListeners()
lexer.addErrorListener(AntlrErrorListener(AsmExceptionTag.MACRO, str_path))
lexer.addErrorListener(AntlrErrorListener(AssemblerExceptionTag.MACRO, str_path))
token_stream = CommonTokenStream(lexer)

parser = MacroParser(token_stream)
parser.removeErrorListeners()
parser.addErrorListener(AntlrErrorListener(AsmExceptionTag.MACRO, str_path))
parser.addErrorListener(AntlrErrorListener(AssemblerExceptionTag.MACRO, str_path))
cst = parser.program()
rewriter = TokenStreamRewriter(token_stream)
emv = ExpandMacrosVisitor(rewriter, library_macros, str_path)
Expand Down
4 changes: 2 additions & 2 deletions cocas/assembler/object_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@

from .ast_nodes import InstructionNode, LabelDeclarationNode, ProgramNode, TemplateSectionNode
from .code_block import Section
from .exceptions import AsmExceptionTag
from .exceptions import AssemblerExceptionTag
from .targets import IVaryingLengthSegment, TargetInstructionsInterface

TAG = AsmExceptionTag.ASM
TAG = AssemblerExceptionTag.ASM


@dataclass
Expand Down
1 change: 1 addition & 0 deletions cocas/assembler/targets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@


def list_assembler_targets() -> set[str]:
"""Returns a set of supported assembler targets. Takes submodules of assembler/target module"""
targets_dir = Path(__file__).parent.absolute()
targets = map(lambda x: x.name, filter(lambda x: x.is_dir(), targets_dir.glob("[!_]*")))
return set(targets)
Expand Down
4 changes: 2 additions & 2 deletions cocas/assembler/targets/abstract_code_segments.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from cocas.object_module import CodeLocation, ObjectSectionRecord

from ..exceptions import AsmExceptionTag, AssemblerException
from ..exceptions import AssemblerException, AssemblerExceptionTag

if TYPE_CHECKING:
from ..code_block import Section
Expand Down Expand Up @@ -96,4 +96,4 @@ def fill(self, object_record: "ObjectSectionRecord", section: "Section", labels:


def _error(segment: ICodeSegment, message: str):
raise AssemblerException(AsmExceptionTag.ASM, segment.location.file, segment.location.line, message)
raise AssemblerException(AssemblerExceptionTag.ASM, segment.location.file, segment.location.line, message)
4 changes: 2 additions & 2 deletions cocas/assembler/targets/cdm16/code_segments.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from cocas.object_module import CodeLocation, ExternalEntry, ObjectSectionRecord

from ...ast_nodes import LabelNode, RegisterNode, RelocatableExpressionNode, TemplateFieldNode
from ...exceptions import AsmExceptionTag, AssemblerException
from ...exceptions import AssemblerException, AssemblerExceptionTag
from .. import IAlignedSegment, IAlignmentPaddingSegment, ICodeSegment, IVaryingLengthSegment

if TYPE_CHECKING:
Expand All @@ -20,7 +20,7 @@ def pack(fmt, *args):


def _error(segment: ICodeSegment, message: str):
raise AssemblerException(AsmExceptionTag.ASM, segment.location.file, segment.location.line, message)
raise AssemblerException(AssemblerExceptionTag.ASM, segment.location.file, segment.location.line, message)


class CodeSegment(ICodeSegment, ABC):
Expand Down
8 changes: 4 additions & 4 deletions cocas/assembler/targets/cdm16/target_instructions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from typing import Callable, Union, get_args, get_origin

from ...ast_nodes import InstructionNode, LabelNode, RegisterNode, RelocatableExpressionNode
from ...exceptions import AsmExceptionTag, AssemblerException, CdmTempException
from ...exceptions import AssemblerException, AssemblerExceptionTag, CdmTempException
from .. import ICodeSegment, TargetInstructionsInterface
from .code_segments import (
AlignmentPaddingSegment,
Expand Down Expand Up @@ -56,10 +56,10 @@ def assemble_instruction(line: InstructionNode, temp_storage: dict) -> list[ICod
return h.handler(line, temp_storage, h.instructions[line.mnemonic])
if line.mnemonic.startswith('b'):
return TargetInstructions.branch(line)
raise AssemblerException(AsmExceptionTag.ASM, line.location.file, line.location.line,
raise AssemblerException(AssemblerExceptionTag.ASM, line.location.file, line.location.line,
f'Unknown instruction "{line.mnemonic}"')
except CdmTempException as e:
raise AssemblerException(AsmExceptionTag.ASM, line.location.file, line.location.line, e.message)
raise AssemblerException(AssemblerExceptionTag.ASM, line.location.file, line.location.line, e.message)

@staticmethod
def finish(temp_storage: dict):
Expand Down Expand Up @@ -184,7 +184,7 @@ def branch(line: InstructionNode, inverse=False) -> list[ICodeSegment]:
branch_code = pair.inv_code if not inverse else pair.code
break
else:
raise AssemblerException(AsmExceptionTag.ASM, line.location.file, line.location.line,
raise AssemblerException(AssemblerExceptionTag.ASM, line.location.file, line.location.line,
f'Invalid branch condition: {cond}')
assert_count_args(line.arguments, RelocatableExpressionNode)
return [Branch(line.location, branch_code, line.arguments[0])]
Expand Down
4 changes: 2 additions & 2 deletions cocas/assembler/targets/cdm8/code_segments.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
from cocas.object_module import CodeLocation, ExternalEntry, ObjectSectionRecord

from ...ast_nodes import LabelNode, RelocatableExpressionNode, TemplateFieldNode
from ...exceptions import AsmExceptionTag, AssemblerException
from ...exceptions import AssemblerException, AssemblerExceptionTag
from .. import IAlignmentPaddingSegment, ICodeSegment

if TYPE_CHECKING:
from ...code_block import Section


def _error(segment: ICodeSegment, message: str):
raise AssemblerException(AsmExceptionTag.ASM, segment.location.file, segment.location.line, message)
raise AssemblerException(AssemblerExceptionTag.ASM, segment.location.file, segment.location.line, message)


# noinspection DuplicatedCode
Expand Down
8 changes: 4 additions & 4 deletions cocas/assembler/targets/cdm8/target_instructions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from bitstruct import pack

from ...ast_nodes import InstructionNode, LabelNode, RegisterNode, RelocatableExpressionNode
from ...exceptions import AsmExceptionTag, AssemblerException, CdmTempException
from ...exceptions import AssemblerException, AssemblerExceptionTag, CdmTempException
from .. import ICodeSegment, TargetInstructionsInterface
from .code_segments import AlignmentPaddingSegment, BytesSegment, ExpressionSegment

Expand Down Expand Up @@ -48,10 +48,10 @@ def assemble_instruction(line: InstructionNode, temp_storage: dict) -> list[ICod
return h.handler(line, temp_storage, h.instructions[line.mnemonic])
if line.mnemonic.startswith('b'):
return TargetInstructions.branch(line)
raise AssemblerException(AsmExceptionTag.ASM, line.location.file, line.location.line,
raise AssemblerException(AssemblerExceptionTag.ASM, line.location.file, line.location.line,
f'Unknown instruction "{line.mnemonic}"')
except CdmTempException as e:
raise AssemblerException(AsmExceptionTag.ASM, line.location.file, line.location.line, e.message)
raise AssemblerException(AssemblerExceptionTag.ASM, line.location.file, line.location.line, e.message)

@staticmethod
def finish(temp_storage: dict):
Expand Down Expand Up @@ -155,7 +155,7 @@ def branch(line: InstructionNode, inverse=False) -> list[ICodeSegment]:
branch_code = pair.inv_code if not inverse else pair.code
break
else:
raise AssemblerException(AsmExceptionTag.ASM, line.location.file, line.location.line,
raise AssemblerException(AssemblerExceptionTag.ASM, line.location.file, line.location.line,
f'Invalid branch condition: {cond}')
assert_count_args(line.arguments, RelocatableExpressionNode)
return [BytesSegment(pack('u4u4', 0xE, branch_code), line.location),
Expand Down
4 changes: 2 additions & 2 deletions cocas/assembler/targets/cdm8e/code_segments.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
from cocas.object_module import CodeLocation, ExternalEntry, ObjectSectionRecord

from ...ast_nodes import LabelNode, RelocatableExpressionNode, TemplateFieldNode
from ...exceptions import AsmExceptionTag, AssemblerException
from ...exceptions import AssemblerException, AssemblerExceptionTag
from .. import ICodeSegment, IVaryingLengthSegment
from .simple_instructions import simple_instructions

TAG = AsmExceptionTag.ASM
TAG = AssemblerExceptionTag.ASM

if TYPE_CHECKING:
from ...code_block import Section
Expand Down
4 changes: 2 additions & 2 deletions cocas/assembler/targets/cdm8e/target_instructions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import bitstruct

from ...ast_nodes import InstructionNode, LabelNode, RegisterNode, RelocatableExpressionNode
from ...exceptions import AsmExceptionTag, AssemblerException, CdmTempException
from ...exceptions import AssemblerException, AssemblerExceptionTag, CdmTempException
from .. import ICodeSegment, TargetInstructionsInterface
from .code_segments import (
BytesSegment,
Expand Down Expand Up @@ -53,7 +53,7 @@ def assemble_instruction(line: InstructionNode, temp_storage) \
segment.location = line.location
return segments
except CdmTempException as e:
raise AssemblerException(AsmExceptionTag.ASM, line.location.file, line.location.line, e.message)
raise AssemblerException(AssemblerExceptionTag.ASM, line.location.file, line.location.line, e.message)

@staticmethod
def finish(temp_storage: dict):
Expand Down
2 changes: 2 additions & 0 deletions cocas/linker/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Functions to link multiple object modules and write image and debug information to files"""

from .debug_export import debug_export, write_debug_export
from .exceptions import LinkerException
from .image import write_image
Expand Down
2 changes: 2 additions & 0 deletions cocas/linker/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
class LinkerException(Exception):
"""Exception raised when sections cannot be correctly linked"""

def __init__(self, message: str):
self.message = message
8 changes: 7 additions & 1 deletion cocas/linker/linker.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,13 @@ def find_referenced_sects(exts_by_sect: dict[str, set[str]], sect_by_ent: dict[s
return used_sects


def link(objects: list[tuple[Any, ObjectModule]]):
def link(objects: list[tuple[Any, ObjectModule]]) -> tuple[bytearray, dict[int, CodeLocation]]:
"""
Link object modules into one image
:param objects: list of pairs (file path, object module)
:return: pair [bytearray of image data, mapping from image addresses to locations in source files]
"""
asects = list(itertools.chain.from_iterable([obj.asects for _, obj in objects]))
rsects = list(itertools.chain.from_iterable([obj.rsects for _, obj in objects]))

Expand Down
2 changes: 2 additions & 0 deletions cocas/object_file/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Convert between object modules and object file format"""

from .exceptions import ObjectFileException
from .object_export import export_object, write_object_file
from .object_import import import_object, read_object_files
Expand Down
2 changes: 2 additions & 0 deletions cocas/object_file/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@


class ObjectFileException(Exception):
"""Exception raised when given object file is invalid"""

def __init__(self, file: str, line: int, description: str):
self.file = file
self.line = line
Expand Down
14 changes: 12 additions & 2 deletions cocas/object_file/object_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,15 @@ def visitMinus(self, ctx: ObjectFileParser.MinusContext):
pass


def import_object(input_stream: InputStream, filepath: Path,
target: str) -> List[ObjectModule]:
def import_object(input_stream: InputStream, filepath: Path, target: str) -> List[ObjectModule]:
"""
Open multiple object files and create object modules
:param input_stream: contents of file
:param filepath: path of the file to use in error handling
:param target: name of processor target
:return: list of object modules contained in object file
"""
str_path = filepath.absolute().as_posix()
lexer = ObjectFileLexer(input_stream)
lexer.removeErrorListeners()
Expand All @@ -240,12 +247,15 @@ def read_object_files(target: str,
absolute_path: Optional[Path],
realpath: bool) -> list[tuple[Path, ObjectModule]]:
"""
Open multiple object files and create object modules
:param target: name of processor target
:param files: list of object files' paths to process
:param debug: if debug information should be exported
:param relative_path: if debug paths should be converted to relative to some path
:param absolute_path: if relative paths should be converted to absolute
:param realpath: if paths should be converted to canonical
:return: list of pairs [object file path, object module]
"""
_ = debug
objects = []
Expand Down
Loading

0 comments on commit d4f1b4e

Please sign in to comment.