Skip to content

Commit

Permalink
Add an experimental feature to define user-specific hook
Browse files Browse the repository at this point in the history
See also: #33
  • Loading branch information
wildlarva committed May 31, 2020
1 parent 264851c commit 2b9972f
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 4 deletions.
49 changes: 46 additions & 3 deletions src/mcdecoder/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,23 @@
from dataclasses import dataclass
import glob
import importlib.resources
import importlib.util
import itertools
import json
import os.path
from types import ModuleType
from typing import (
Any, Callable, Dict, Iterable, List, Literal, Optional, Tuple, TypedDict,
cast)
Any,
Callable,
Dict,
Iterable,
List,
Literal,
Optional,
Tuple,
TypedDict,
cast,
)

import deprecation
import jsonschema
Expand Down Expand Up @@ -51,6 +62,8 @@ class McDecoderDescription(TypedDict):
"""Decoder information that isn't related to a machine, an instruction and a field"""
namespace: Optional[str]
"""Namespace for the symbols of a generated decoder"""
process_instruction_hook: Optional[str]
"""Hook function to process model for an instruction decoder"""


class McDescription(TypedDict):
Expand Down Expand Up @@ -603,6 +616,12 @@ def create_mcdecoder_model(mcfile: str) -> McDecoder:
# Load MC description
mc_desc_model = load_mc_description_model(mcfile)

# Load config
config_file = os.path.join(os.path.dirname(mcfile), 'config.py')
config_module: Optional[ModuleType] = None
if os.path.isfile(config_file):
config_module = _load_config_module(config_file)

# Create machine and instruction decoders
machine_decoder = _create_machine_decoder_model(mc_desc_model['machine'])
instruction_decoders = [_create_instruction_decoder_model(
Expand All @@ -613,13 +632,14 @@ def create_mcdecoder_model(mcfile: str) -> McDecoder:

# Create MC decoder
namespace: Optional[str] = None
decoder_desc_model: Optional[McDecoderDescription] = None
if 'decoder' in mc_desc_model:
decoder_desc_model = mc_desc_model['decoder']
if 'namespace' in decoder_desc_model:
namespace = decoder_desc_model['namespace']

extras = mc_desc_model['extras'] if 'extras' in mc_desc_model else None
return McDecoder(
mcd = McDecoder(
namespace=namespace,
namespace_prefix=_make_namespace_prefix(namespace),
machine=machine_decoder,
Expand All @@ -628,6 +648,21 @@ def create_mcdecoder_model(mcfile: str) -> McDecoder:
extras=extras,
)

# Process model
process_instruction_hook: Optional[Callable[[
InstructionDecoder], None]] = None
if config_module is not None:
if decoder_desc_model is not None:
if 'process_instruction_hook' in decoder_desc_model:
process_instruction_hook = getattr(config_module, cast(
str, decoder_desc_model['process_instruction_hook']))

if process_instruction_hook is not None:
for instruction_decoder in instruction_decoders:
process_instruction_hook(instruction_decoder)

return mcd


def load_mc_description_model(mcfile: str) -> McDescription:
"""
Expand All @@ -647,6 +682,14 @@ def load_mc_description_model(mcfile: str) -> McDescription:
return cast(McDescription, mc_desc_model)


def _load_config_module(config_file: str) -> ModuleType:
spec = importlib.util.spec_from_file_location(
'mcdecoder.config', config_file)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module


def parse_instruction_encoding(instruction_encoding: str) -> InstructionEncodingDescription:
"""
Parse a string of the encoding format of an instruction
Expand Down
4 changes: 4 additions & 0 deletions src/mcdecoder/schemas/mc_desc_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@
"namespace": {
"type": "string",
"pattern": "^[A-Za-z][A-Za-z0-9_]*$"
},
"process_instruction_hook": {
"type": "string",
"pattern": "^[A-Za-z][A-Za-z0-9_]*$"
}
},
"additionalProperties": false
Expand Down
8 changes: 8 additions & 0 deletions src/mcdecoder/test/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ def test_create_mcdecoder_model_namespace() -> None:
mcdecoder_model.namespace_prefix == 'arm_'


def test_create_mcdecoder_model_with_config() -> None:
mcdecoder_model = create_mcdecoder_model(
'test/with_config.yaml')

instruction = mcdecoder_model.instructions[0]
assert instruction.extras['extra_attribute'] == 'extra_content'


def test_create_mcdecoder_model_extras() -> None:
mcdecoder_model = create_mcdecoder_model(
'test/arm.yaml')
Expand Down
2 changes: 1 addition & 1 deletion src_docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'm2r',
# 'm2r',
'sphinx.ext.autodoc',
'sphinx.ext.graphviz',
'sphinx-jsonschema',
Expand Down
25 changes: 25 additions & 0 deletions src_docs/spec_mc_desc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,31 @@ decoder.namespace

:code:`namespace` defines the namespace for the symbols of a generated decoder.

decoder.process_instruction_hook
========================================================================================================

.. warning::

This is an experimental feature. The name and forms of this attribute might be changed in the future release.

:code:`process_instruction_hook` defines the name of the hook function to to process user-specific information into a different form.

Here's an example of defining a hook function to process user-specific information.

.. code-block:: yaml
decoder:
process_instruction_hook: process_instruction
.. code-block:: python
:caption: config.py
from mcdecoder.core import InstructionDecoder
def process_instruction(instruction: InstructionDecoder) -> None:
extra_value = instruction.extras['extra_attribute']
instruction.extras['extra_attribute'] = 'processed ' + extra_value
******************************************************************************************************
extras
******************************************************************************************************
Expand Down
2 changes: 2 additions & 0 deletions src_docs/spec_mc_desc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ instructions: # Describes instructions

decoder: # [optional] Decoder information of the global scope
namespace: ns # [optional] Namespace for the symbols of a generated decoder
# [optional] Name of the hook function to process user-specific information for an instruction
process_instruction_hook: process_instruction

extras: # [optional] User-defined data for the global scope
compiler: gcc
5 changes: 5 additions & 0 deletions test/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from mcdecoder.core import InstructionDecoder


def process_instruction(instruction: InstructionDecoder) -> None:
instruction.extras = {'extra_attribute': 'extra_content'}
7 changes: 7 additions & 0 deletions test/with_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
machine:
byteorder: little
instructions:
- name: add_1
format: xxxx:cond|00|1|0100|x:S|xxxx:Rn|xxxx:Rd|xxxx xxxx xxxx:imm12
decoder:
process_instruction_hook: process_instruction

0 comments on commit 2b9972f

Please sign in to comment.