To protect your data, the CISO officer has suggested users to enable GitLab 2FA as soon as possible.

Commit 958804d9 authored by Pavel Zakopaylo's avatar Pavel Zakopaylo
Browse files

Modified pretty-printer to correctly refer to CommInsts by name

Also added automatic method of generating Mu.AST.CommInst based
on the newest `muapi.h`.
parent 9d42314e
# Copyright 2017 The Australian National University
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from collections import namedtuple
from typing import Dict
import sys
import pprint
import muapiparser
def print_indent(value, **kwargs):
print(' ' + value, **kwargs)
def slurp(fn):
"""Read the contents of a file into memory and dump the string.
"""
with open(fn) as f:
return f.read()
def pascalToCamel(string, startLowercase=True):
"""Turn a Pascal_Cased string into a CamelCase string with optional
lowercased first letter.
"""
if not string:
return ""
result = ""
for word in string.lower().split('_'):
if word:
result += word[0].upper() + word[1:]
if startLowercase:
result = result[0].lower() + result[1:]
return result
def get_comminsts(stuff):
index = -1
for i in range(len(stuff["enums"])):
if stuff["enums"][i]["name"] == "MuCommInst":
index = i
break
return stuff["enums"][index]["defs"]
def print_def(insts):
print("data CommInst")
print_indent("= {}".format(pascalToCamel(insts[0]["name"])[2:]))
for i in range(1, len(insts)):
print_indent("| {}".format(pascalToCamel(insts[i]["name"])[2:]))
print_indent("deriving (Generic)")
def print_show(insts):
print ("instance Show CommInst where")
for inst in insts:
print_indent("show {} = \"{}\"".format(pascalToCamel(inst["name"])[2:], inst["muname"]))
def print_enum(insts):
print("instance Enum CommInst where")
for inst in insts:
print_indent("fromEnum {} = {}".format(pascalToCamel(inst["name"])[2:], inst["value"]))
for inst in insts:
print_indent("toEnum {} = {}".format(inst["value"], pascalToCamel(inst["name"])[2:]))
def print_module_header():
print("""--
-- Copyright 2017 The Australian National University
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
{-# LANGUAGE DeriveGeneric #-}
module Mu.AST.CommInst (CommInst (..)) where
import Data.Binary (Binary)
import GHC.Generics (Generic)
""")
def print_haskell_file(stuff):
print_module_header()
insts = get_comminsts(stuff)
print_def(insts)
print("")
print_enum(insts)
print("")
print("instance Binary CommInst")
print("")
print_show(insts)
if __name__ == '__main__':
stuff = muapiparser.parse_muapi(slurp(sys.argv[1]))
print_haskell_file(stuff)
# Copyright 2017 The Australian National University
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import re
from typing import List, Union, Tuple, Any, Callable, TypeVar, Mapping
from typing.re import Pattern
import tempfile, os.path
Predicate = Union[str,
Tuple[Pattern, ...],
Callable[[Any], bool]]
def _string_contains(line, string):
return string in line
def _pattern_value_match(line, tup):
pat = tup[0]
vals = tup[1:]
m = pat.search(line)
return m is not None and all(
v is None or g == v
for g,v in zip(m.groups(), vals))
def _apply_func(line, func):
return func(line)
def find_line(lines: List[str], substr: Predicate, start: int = 0) -> int:
"""Find the line that contains or matches substr since line ``start``. """
if isinstance(substr, str):
pred = _string_contains
elif isinstance(substr, tuple):
pred = _pattern_value_match
else:
pred = _apply_func
for i in range(start, len(lines)):
if pred(lines[i], substr):
return i
raise KeyError("Not found: " + str(substr) + "\n text:" + str(lines) )
def extract_lines(parent: str, begin: Predicate, end: Predicate) -> str:
"""
Extract the lines between the line containing ``begin`` and the line
containing ``end`` (excluding both lines) in ``parent``.
"""
lines = parent.splitlines()
begin_line = find_line(lines, begin)
end_line = find_line(lines, end, begin_line+1)
new_lines = lines[begin_line+1:end_line]
return "\n".join(new_lines)
def inject_lines(parent: str, begin: Predicate, end: Predicate, generated: str) -> str:
"""
Replace the lines between the line containing ``begin`` and the line
containing ``end`` (excluding both lines) in ``parent`` with ``generated``.
"""
lines = parent.splitlines()
begin_line = find_line(lines, begin)
end_line = find_line(lines, end, begin_line+1)
new_lines = lines[:begin_line+1] + generated.splitlines() + lines[end_line:]
return "\n".join(new_lines)
STANDARD_PREFIX_BEGIN = "GEN:BEGIN:"
STANDARD_PREFIX_END = "GEN:END:"
class StandardInjectableFile(object):
def __init__(self, path: str, injection_points: List[str] = None):
self.path = path
if injection_points is None:
injection_points = []
self.injection_points = injection_points
def inject_many(self, m: Mapping[str, str], force=False):
with open(self.path) as f:
orig_txt = f.read()
txt = orig_txt
for inj_point, inj_content in m.items():
if inj_point not in self.injection_points and not force:
raise Exception("Unknown injection point '{}'".format(inj_point))
inj_begin = STANDARD_PREFIX_BEGIN + inj_point
inj_end = STANDARD_PREFIX_END + inj_point
new_txt = inject_lines(txt, inj_begin, inj_end, inj_content)
txt = new_txt
if not txt.endswith("\n"):
txt += "\n"
with tempfile.NamedTemporaryFile("w", delete=False) as f:
print("Backup to temporary file: {} -> {}".format(self.path, f.name))
f.write(orig_txt)
with open(self.path, "w") as f:
print("Writing to file: {}".format(self.path))
f.write(txt)
def make_injectable_file_set(
root_path: str,
items : List[Tuple[str, str, List[str]]],
) -> Mapping[str, StandardInjectableFile]:
m = InjectableFileSet()
for name, path, inj_points in items:
full_path = os.path.join(root_path, path)
sif = StandardInjectableFile(full_path, inj_points)
m[name] = sif
return m
class InjectableFileSet(dict):
def inject_many(self, m: Mapping[str, Mapping[str, str]]):
for name, mm in m.items():
self[name].inject_many(mm)
# Copyright 2017 The Australian National University
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#################################################################
# Parse the muapi.h so that you can generate different bindings.
#
# The result will be a simple JSON object (dict of dicts).
#################################################################
import re
import injecttools
rfrag_commpragma = r'(?:///\s*MUAPIPARSER\s+(?P<pragma>.*))?'
r_comment = re.compile(r'//.*$', re.MULTILINE)
r_decl = re.compile(r'(?P<ret>\w+\s*\*?)\s*\(\s*\*\s*(?P<name>\w+)\s*\)\s*\((?P<params>[^)]*)\)\s*;\s*' + rfrag_commpragma, re.MULTILINE)
r_param = re.compile(r'\s*(?P<type>\w+\s*\*?)\s*(?P<name>\w+)')
r_define = re.compile(r'^\s*#define\s+(?P<name>\w+)\s*\(\((?P<type>\w+)\)(?P<value>\w+)\)\s*' + rfrag_commpragma + r'\s*$', re.MULTILINE)
r_typedef = re.compile(r'^\s*typedef\s+(?P<const>const\s+)?(?P<expand_to>\w+\s*\*?)\s*(?P<name>\w+)\s*;', re.MULTILINE)
r_struct_start = re.compile(r'^struct\s+(\w+)\s*\{')
r_struct_end = re.compile(r'^\};')
def filter_ret_ty(text):
return text.replace(" ","")
def extract_params(text):
params = []
for text1 in text.split(','):
ty, name = r_param.search(text1).groups()
ty = ty.replace(" ",'')
params.append({"type": ty, "name": name})
return params
def extract_pragmas(text):
text = text.strip()
if len(text) == 0:
return []
else:
return text.split(";")
def extract_method(name, params, ret_ty, pragmas):
params = extract_params(params)
ret_ty = filter_ret_ty(ret_ty)
pragmas = extract_pragmas(pragmas)
params_index = {p["name"]:p for p in params}
for pragma in pragmas:
parts = pragma.split(":")
param_name = parts[0]
if param_name not in params_index:
raise Exception("Method {}: Pragma {} is for unknown param {}".format(
name, pragma, param_name))
param = params_index[param_name]
kind = parts[1]
if kind == 'array':
sz_param_name = parts[2]
param["array_sz_param"] = sz_param_name
if sz_param_name not in params_index:
raise Exception(
"Method {}: param {}: Array length parameter {} does not exist".format(
name, pn, sz_param_name))
sz_param = params_index[sz_param_name]
sz_param["is_sz_param"] = True
elif kind == 'optional':
param["is_optional"] = True
elif kind == 'out':
param["is_out"] = True
else:
raise Exception("Method {}: param {}: Unrecognised pragma {}".format(
name, pn, pragma))
return {
"name": name,
"params": params,
"ret_ty": ret_ty,
# "pragmas": pragmas, # Don't include it any more, since we handle everything.
}
def extract_methods(body):
methods = []
for ret, name, params, pragmas in r_decl.findall(body):
method = extract_method(name, params, ret, pragmas)
methods.append(method)
return methods
def extract_struct(text, name):
return injecttools.extract_lines(text, (r_struct_start, name), (r_struct_end,))
def extract_enum(name, ty, value, pragmas):
pragmas = extract_pragmas(pragmas)
enum = {
"name": name,
"value": value,
# "pragmas": pragmas, # Don't include it any more, since we handle everything.
}
for pragma in pragmas:
parts = pragma.split(":")
pragma_name = parts[0]
if pragma_name == "muname":
muname = parts[1]
enum["muname"] = muname
return enum
def extract_enums(text, typename, pattern):
defs = []
for name, ty, value, pragmas in r_define.findall(text):
if pattern.search(name) is not None:
enum = extract_enum(name, ty, value, pragmas)
defs.append(enum)
return {
"name": typename,
"defs": defs,
}
_top_level_structs = ["MuVM", "MuCtx", "MuIRBuilder"]
_enums = [(typename, re.compile(regex)) for typename, regex in [
("MuTrapHandlerResult", r'^MU_(THREAD|REBIND)'),
("MuDestKind", r'^MU_DEST_'),
("MuBinOpStatus", r'^MU_BOS_'),
("MuBinOptr", r'^MU_BINOP_'),
("MuCmpOptr", r'^MU_CMP_'),
("MuConvOptr", r'^MU_CONV_'),
("MuMemOrd", r'^MU_ORD_'),
("MuAtomicRMWOptr", r'^MU_ARMW_'),
("MuCallConv", r'^MU_CC_'),
("MuCommInst", r'^MU_CI_'),
]]
def extract_typedefs(text):
typedefs = {}
typedefs_order = []
for m in r_typedef.finditer(text):
const_qualifier, expand_to, name = m.groups()
expand_to = expand_to.replace(" ","")
typedefs[name] = expand_to
typedefs_order.append((name, expand_to))
return typedefs, typedefs_order
def parse_muapi(text):
structs = []
for sn in _top_level_structs:
b = extract_struct(text, sn)
methods = extract_methods(b)
structs.append({"name": sn, "methods": methods})
enums = []
for tn,pat in _enums:
enums.append(extract_enums(text, tn, pat))
typedefs, typedefs_order = extract_typedefs(text)
return {
"structs": structs,
"enums": enums,
"typedefs": typedefs,
"typedefs_order": typedefs_order,
}
if __name__=='__main__':
import sys, pprint, shutil
width = 80
try:
width, height = shutil.get_terminal_size((80, 25))
except:
pass
text = sys.stdin.read()
pprint.pprint(parse_muapi(text), width=width)
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment