Commit 0441d625 authored by Maxime Perrotin's avatar Maxime Perrotin

Work on the new iterator generator in python

parent aacae1a9
......@@ -13,10 +13,338 @@
"""
__version__ = "3.0.0"
import signal
import argparse
import traceback
import logging
from typing import List
LOG = logging.getLogger(__name__)
import opengeode.Asn1scc as asn1scc
import opengeode.ogParser as parser
LF = '\n'
def indent(nb: int, phrase: List[str], sep=""):
# offer indenting of multiple line strings when referenced inside f-strings
result = LF.join(phrase)
return (sep+LF+nb*" ").join(result.splitlines())
def adb_template(packages: List[str]) -> str:
''' Top-level template to generate the .adb file '''
return f'''-- This file was generated by TASTE: DO NOT EDIT
with Ada.Strings,
Ada.Strings.Fixed,
Ada.Srings.Unbounded,
Ada.Characters.Handling;
use Ada.Strings,
Ada.Strings.Fixed,
Ada.Strings.Unbounded,
Ada.Characters.Handling;
package body ASN1_Ada_Iterators.Iterators is
{indent(3, packages)}
end ASN1_Ada_Iterators.Iterators;
'''
def unsigned_int_adb_template (sort: str, minR: int, maxR: int) -> str:
''' Template for iterating on an unsigned integer '''
return f'''package body {sort}_Pkg is
procedure To_ASN1 (From : Interfaces.Unsigned_64;
To : out asn1Scc{sort}) is
begin
To := From;
end To_ASN1;
function Image (Elm : asn1Scc{sort}) return String is
(Ada.Strings.Fixed.Trim (Elm'Img, Both));
end {sort}_Pkg;'''
def signed_int_adb_template (sort: str, minR: int, maxR: int) -> str:
''' Template for iterating on a signed integer '''
return f'''package body {sort}_Pkg is
procedure To_ASN1 (From : Interfaces.Integer_64;
To : out asn1Scc{sort}) is
begin
To := From;
end To_ASN1;
function Image (Elm : asn1Scc{sort}) return String is
(Ada.Strings.Fixed.Trim (Elm'Img, Both));
end {sort}_Pkg;'''
def seq_of_adb_template (sort: str, inner: str, minR: int, maxR: int) -> str:
''' Template for iterating on an array '''
if minR == maxR:
return f'''package body {sort}_Pkg is
procedure To_ASN1 (From : Inner.{sort};
To : out asn1Scc{sort}) is
begin
for Idx in 1 .. From.Length loop
To.Data (Idx) := From.Data (Idx);
end loop;
end To_ASN1;
function Image (Elm : asn1Scc{sort}) return String is
Res : Unbounded_String;
begin
if Elm.Data'Length >= 1 then
Res := To_Unbounded_String (Image (Elm.Data (1)));
end if;
if Elm.Data'Length > 1 then
for Idx in 2 .. Elm.Data'Length loop
Res := Res & ", " & Image (Elm.Data (Idx));
end loop;
end if;
return To_String ("{" & Res & "}");
end Image;
end {sort}_Pkg;'''
else:
# Variable-size sequence of
return f'''package body {sort}_Pkg is
procedure To_ASN1 (From : Inner.{sort};
To : out asn1Scc{sort}) is
begin
for Idx in 1 .. From.Length loop
To.Data (Idx) := From.Data (Idx);
end loop;
To.Length := From.Length;
end To_ASN1;
function Image (Elm : asn1Scc{sort}) return String is
function Image_Rec (Elm : ASN1_Type) return String is
(if Elm.Length > 0 then
(Image (Elm.Data (1))
& (if Elm.Length > 1 then ", "
& Image_Rec (ASN1_Type'(Length => Elm.Length-1,
Data => Elm.Data(2 .. Elm.Length) &
Elm.Data(1 .. Elm.Data'Length - Elm.Length + 1)))
else ""))
else "");
begin
return "{ " & Image_Rec (Elm) & " }";
end Image;
end {sort}_Pkg;'''
def enum_adb_template (sort: str) -> str:
''' Template for iterating on an enumerated '''
return f'''package body {sort}_Pkg is
function Image (Elm : asn1Scc{sort}) return String is
(Delete (To_Lower (Elm'Img), From => 1, Through => 7)); -- remove "asn1scc" prefix
end Image;
end {sort}_Pkg;'''
def fields_adb_init (fields : dict) -> List [str]:
'''
Template for the fields of a sequence used in the Initialize function
'''
result = []
for name in fields.keys():
result.append (f"Container.{name}_it := P_{name}.Iterator (Container.{name}_val.Iterate);")
return result
def sequence_adb_template (sort: str, fields: dict) -> str:
''' Template for iterating on a record '''
return f'''package body {sort}_Pkg is
procedure Initialize (Container : in out {sort}) is
begin
{indent(6, fields_adb_init(fields))}
end Initialize;
function Has_Element (Position : Cursor) return Boolean is
(P_{fields_first(fields)} (Position.{fields_first(fields)}_it.Get_Cursor));
function Iterate (Container : {sort}) return {sort}_It'Class is
begin
return I: {sort}_It do
I.Position := Self'Unrestricted_Access;
end return;
end Iterate;
function First (Item : {sort}_It) return Cursor is
{indent(6, fields_adb_first(fields))}
begin
return Item.Position;
end First;
function Next (Item : {sort}_It;
Position : Cursor) return Cursor is
pragma Unreferenced (Item);
{indent(6, fields_adb_next(fields))}
begin
-- Iterate on the last field then on the previous ones
{indent(6, fields_adb_iterate(fields))}
return Position;
end Next;
function Image (Elm : asn1Scc{sort}) return String is
begin
return "{"
{fields_adb_image(13, fields)}
& "}";
end Image;
end {sort}_Pkg;'''
def ads_template(packages: List[str]) -> str:
''' Top-level template to generate the .ads file '''
return f'''-- This file was generated by TASTE: DO NOT EDIT
with Ada.Iterator_Interfaces,
Ada.Finalization,
Interfaces,
ASN1_Ada_Iterators,
ASN1_Iterators.Generic_Integer,
ASN1_Iterators.Generic_Unsigned_Integer,
ASN1_Iterators.Generic_Enumerated,
ASN1_Iterators.Generic_SeqOf;
pragma Unreferenced (ASN1_Ada_Iterators);
use Ada.Finalization,
Interfaces,
ASN1_Iterators;
package ASN1_Ada_Iterators.Iterators is
{indent(3, packages)}
end ASN1_Ada_Iterators.Iterators;
'''
def unsigned_int_ads_template (sort: str, minR: int, maxR: int) -> str:
''' Template for iterating on an unsigned integer '''
return f'''package {sort}_Pkg is
package Inner is new Generic_Unsigned_Integer (Min => {minR},
Max => {maxR});
package It renames Inner.It;
procedure To_ASN1 (From : Interfaces.Unsigned_64;
To : out asn1Scc{sort});
subtype Instance is Inner.Instance;
function Image (Elm : asn1Scc{sort}) return String;
end {sort}_Pkg;'''
def signed_int_ads_template (sort: str, minR: int, maxR: int) -> str:
''' Template for iterating on an signed integer '''
return f'''package {sort}_Pkg is
package Inner is new Generic_Integer (Min => {minR},
Max => {maxR});
package It renames Inner.It;
procedure To_ASN1 (From : Interfaces.Integer_64;
To : out asn1Scc{sort});
subtype Instance is Inner.Instance;
function Image (Elm : asn1Scc{sort}) return String;
end {sort}_Pkg;'''
def seq_of_ads_template (sort: str, inner: str, minR: int, maxR: int) -> str:
''' Template for iterating on an array '''
return f'''package {sort}_Pkg is
package Inner is new Generic_SeqOf (Min => {minR},
Max => {maxR},
Basic => {inner}_Pkg.It);
use {inner}_Pkg;
procedure To_ASN1 (From : Inner.{sort};
To : out asn1Scc{sort});
subtype Instance is Inner.Instance;
function Image (Elm : asn1Scc{sort}) return String;
end {sort}_Pkg;'''
def enum_ads_template (sort: str) -> str:
''' Template for iterating on an enumerated '''
return f'''package {sort}_Pkg is
package Inner is new Generic_Enumerated (Sort => asn1Scc{sort});
package It renames Inner.It;
subtype Instance is Inner.Instance;
function Image (Elm : asn1Scc{sort}) return String;
end {sort}_Pkg;'''
def fields_ads_pkg (fields : dict) -> List [str]:
''' for each field of a sequence, do a package renaming :
package P_<field> renames <Sort>_Pkg.It
This makes further code more readable '''
result = []
for name, sort in fields.items():
result.append (f"package P_{name} renames {sort}_Pkg.It;")
return result
def fields_ads_record (fields : dict) -> List [str]:
''' Declare the fields for iteration in an ASN.1 SEQUENCE type '''
result = []
for field in fields.keys():
result.append (f"{field}_val : P_{field}.Basic_ASN1_Iterator;")
result.append (f"{field}_it : P_{field}.Iterator;")
return result
def fields_ads_value (fields : dict) -> List [str]:
''' Code that return the value of an ASN.1 SEQUENCE type '''
result = []
for field in fields.keys():
result.append (f"{field} => Container.{field}_val.Elem_Value (Container.{field}_it.Position)")
return result
def sequence_ads_template (sort: str, fields: dict) -> str:
''' Template for iterating on a record '''
return f'''package {sort}_Pkg is
{indent(3, fields_ads_pkg(fields))}
type {sort}_It;
type {sort} is new Controlled with record
{indent(6, fields_ads_record(fields))}
end record
with Default_Iterator => Iterate,
Iterator_Element => asn1Scc{sort},
Constant_Indexing => Element;
type Cursor is access all {sort};
-- Constructor (called automatically)
procedure Initialize (Container : in out {sort});
function Has_Element (Position : Cursor) return Boolean;
package Iterators_{sort} is
new Ada.Iterator_Interfaces (Cursor, Has_Element);
type {sort}_It is new Iterators_{sort}.Forward_Iterator with record
Position : Cursor;
end record;
overriding function First (Item : {sort}_It) return Cursor;
overriding function Next (Item : {sort}_It;
Position : Cursor) return Cursor;
function Iterate (Container : {sort}) return {sort}_It'Class;
function Element (Container : {sort};
Position : Cursor) return asn1Scc{sort} is
({indent(7, fields_ads_value(fields), sep=",")});
subtype Instance is {sort};
function Image (Elm : asn1Scc{sort}) return String;
end {sort}_Pkg;'''
from opengeode import Asn1scc
def init_logging(options):
''' Initialize logging '''
......@@ -43,23 +371,106 @@ def parse_args():
help='Display debug information')
parser.add_argument(
'files',
metavar='dataview-uniq.asn',
metavar='file.asn',
type=str,
nargs='*',
help='ASN.1 file(s)')
return parser.parse_args()
def run(options):
asn1_files = options.files
if not asn1_files:
LOG.error ("You must specify at least one ASN.1 file")
return
try:
ast = asn1scc.parse_asn1(
asn1_files,
ast_version=asn1scc.ASN1.UniqueEnumeratedNames,
rename_policy=asn1scc.ASN1.NoRename,
flags=[asn1scc.ASN1.AstOnly],
pretty_print=False)
except (ImportError, NameError, TypeError) as err:
LOG.error(str(err))
LOG.error("ASN.1 Parsing error (run with -g for debug infomation)")
LOG.debug(traceback.format_exc())
return
modules = []
packages_ads, packages_adb = [], []
# Parse the ASN.1 AST to know the types and generate the corresponding code
sorts = {}
for module, types in ast.exportedTypes.items():
modules.append(module)
for sort in types:
sort_def = ast.types[sort]
basic = parser.find_basic_type (sort_def.type)
sorts[sort.replace("-", "_")] = basic
for name, basic in sorts.items():
if basic.kind == "IntegerType" and int(basic.Min) >= 0:
packages_ads.append(
unsigned_int_ads_template(name,
int(basic.Min),
int(basic.Max)))
packages_adb.append(
unsigned_int_adb_template(name,
int(basic.Min),
int(basic.Max)))
elif basic.kind == "IntegerType" and int(basic.Min < 0):
packages_ads.append(
signed_int_ads_template(name,
int(basic.Min),
int(basic.Max)))
packages_adb.append(
signed_int_adb_template(name,
int(basic.Min),
int(basic.Max)))
elif basic.kind == "EnumeratedType":
packages_ads.append(enum_ads_template(name))
packages_adb.append(enum_adb_template(name))
elif basic.kind == "SequenceOfType":
inner=basic.type.ReferencedTypeName.replace("-", "_")
packages_ads.append(
seq_of_ads_template (name,
inner,
int(basic.Min),
int(basic.Max)))
packages_adb.append(
seq_of_adb_template (name,
inner,
int(basic.Min),
int(basic.Max)))
elif basic.kind == "SequenceType":
fields = {}
for child in basic.Children.values():
fields[child.AdaName] = \
child.type.ReferencedTypeName.replace("-", "_")
packages_ads.append(sequence_ads_template(name, fields))
packages_adb.append(sequence_adb_template(name, fields))
else:
LOG.error("Unsupported type name: " + basic.kind)
return
ads = ads_template(packages_ads)
adb = adb_template(packages_adb)
# Write the .ads
with open ("asn1_ada_iterators-iterators.ads", "wb") as source:
source.write(ads.encode('latin1'))
with open ("asn1_ada_iterators-iterators.adb", "wb") as source:
source.write(adb.encode('latin1'))
def main():
''' Tool entry point '''
# Catch Ctrl-C to stop the app from the console
signal.signal(signal.SIGINT, signal.SIG_DFL)
options = parse_args()
init_logging(options)
asn1scc.LOG = LOG
LOG.info('Ada Iterators for ASN.1 - version ' + __version__)
run(options)
if __name__ == '__main__':
''' Run main application '''
main()
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