Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
TASTE
asn1-value-editor
Commits
f1e29439
Commit
f1e29439
authored
Jul 15, 2015
by
Maxime Perrotin
Browse files
Add VDM value notation parser
parent
926013c4
Changes
1
Hide whitespace changes
Inline
Side-by-side
asn1_value_editor/vdm_vn.py
0 → 100644
View file @
f1e29439
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
ASN.1 Value Editor - Parse VDM Value notation
Mapping of ASN.1 types:
----------------------
Boolean : true, false
Integer: numbers
Real: numbers dot numbers
Sequence: mk_typename(val1, val2..)
Set of: { a, b. c }
Sequence of: [a, b, c]
Enum: <value>
Choice: ?
Bit string literal : ?
Octet string literal : ?
String Literal : ?
Copyright (c) 2012-2015 European Space Agency
Designed and implemented by Maxime Perrotin
Contact: maxime.perrotin@esa.int
License is LGPLv3 - Check the LICENSE file
"""
from
pyparsing
import
(
Word
,
QuotedString
,
Literal
,
Combine
,
Optional
,
oneOf
,
OneOrMore
,
srange
,
nums
,
nestedExpr
,
Forward
,
delimitedList
,
Keyword
,
ParseException
)
import
string
# TBC in VDM - Keep ASN.1 Value Notation
BitStringLiteral
=
(
'"'
+
Word
(
"01"
)
+
'"B'
).
setResultsName
(
'BITSTRING'
)
# TBC in VDM - Keep ASN.1 Value Notation
OctetStringLiteral
=
(
'"'
+
Word
(
string
.
hexdigits
)
+
'"H'
).
setResultsName
(
'OCTETSTRING'
)
# QuotedString's first parameter = quote type..+multiline to support \n => OK
# Support embedded quotes as per GSER standard (""embedded quoted text"")
StringLiteral
=
QuotedString
(
'"'
,
escQuote
=
'""'
,
multiline
=
True
,).
setResultsName
(
'QUOTEDSTRING'
)
TRUE
=
Literal
(
"true"
).
setResultsName
(
'TRUE'
)
FALSE
=
Literal
(
"false"
).
setResultsName
(
'FALSE'
)
# TBC in VDM
NULL
=
Literal
(
"NULL"
).
setResultsName
(
'NULL'
)
COMMA
=
Literal
(
','
).
suppress
()
# TBC in VDM
PLUS_INFINITY
=
Literal
(
"PLUS-INFINITY"
).
setResultsName
(
'PLUS-INFINITY'
)
MINUS_INFINITY
=
Literal
(
"MINUS-INFINITY"
).
setResultsName
(
'MINUS-INFINITY'
)
# EMPTY_LIST:
LBRACKET
=
Literal
(
'{'
).
suppress
()
RBRACKET
=
Literal
(
'}'
).
suppress
()
# Square brackets
LSBRACKET
=
Literal
(
'['
).
suppress
()
RSBRACKET
=
Literal
(
']'
).
suppress
()
# Parenthesis
LPAREN
=
Literal
(
'('
).
suppress
()
RPAREN
=
Literal
(
')'
).
suppress
()
# INTEGER
INT
=
Combine
(
Optional
(
oneOf
(
"+ -"
))
+
(
Word
(
srange
(
"[1-9]"
),
nums
)
|
Literal
(
'0'
))).
setResultsName
(
'INTEGER'
)
# This one is correct:
# a lowercase followed by a-zA-z and -
#LID = Word(string.lowercase, string.letters+string.digits+'-')
# Update MP 21/11/12: made it tolerant to uppercase for the first character
LID
=
Word
(
string
.
letters
,
string
.
letters
+
string
.
digits
+
'-'
)
identifier
=
LID
.
setResultsName
(
'IDENTIFIER'
)
# Value reference is in practice used to identify enumerated values
valuereference
=
LID
.
setResultsName
(
'VALUE_REF'
)
# FloatingPointLiteral
DOT
=
Literal
(
'.'
)
DOUBLE_DOT
=
Literal
(
'..'
)
# Exponent (e.g. e+10)
Exponent
=
oneOf
(
"e E"
)
+
Optional
(
oneOf
(
"+ -"
))
+
OneOrMore
(
Word
(
nums
))
# Thefirst (INT + DOUBLE_DOT) is unclear (e.g. "5..").
FloatingPointLiteral
=
(
INT
+
DOUBLE_DOT
|
Combine
(
INT
+
DOT
+
Optional
(
Word
(
nums
))
+
Optional
(
Exponent
))
|
INT
).
setResultsName
(
'REAL1'
)
# Numeric value2 (eg. { mantissa 3, base 4, exponent 5 })
# Does this noataion exist in VDM ?
MANTISSA
=
Literal
(
'mantissa'
).
suppress
()
BASE
=
Literal
(
'base'
).
suppress
()
EXPONENT
=
Literal
(
'exponent'
).
suppress
()
NUMERIC_VALUE2
=
(
nestedExpr
(
'{'
,
'}'
,
MANTISSA
+
INT
+
COMMA
+
BASE
+
INT
+
COMMA
+
EXPONENT
+
INT
)).
setResultsName
(
'REAL2'
)
# Forward allows to use recursive constructs
NAMED_VALUE_LIST
=
Forward
()
SETOF
=
Forward
()
VALUE_LIST
=
Forward
()
choiceValue
=
Forward
()
# Support for "Any" value (using a star)
AnyValueLiteral
=
Keyword
(
'*'
).
setResultsName
(
'ANY'
)
# ASN.1 Value Notation: only missing construct are OID references
# Ambiguity: FloatingPointLiteral contains INT -> INT will never be parsed as REAL
value
=
(
BitStringLiteral
|
OctetStringLiteral
|
TRUE
|
FALSE
|
StringLiteral
|
NULL
|
PLUS_INFINITY
|
MINUS_INFINITY
|
NAMED_VALUE_LIST
|
VALUE_LIST
|
SETOF
|
(
valuereference
^
choiceValue
)
|
(
INT
^
FloatingPointLiteral
)
|
NUMERIC_VALUE2
|
AnyValueLiteral
)
# ASN.1 CHOICE --> TBC in VDM
choiceValue
<<
(
identifier
+
':'
+
value
).
setResultsName
(
'CHOICE'
)
# SEQUENCE
NAMED_VALUE_LIST
<<
LPAREN
+
delimitedList
(
value
)
+
RPAREN
# VDM SEQUENCE OF
VALUE_LIST
<<
LSBRACKET
+
Optional
(
delimitedList
(
value
))
+
RSBRACKET
# SET OF
SETOF
<<
LBRACKET
+
Optional
(
delimitedList
(
value
))
+
RBRACKET
# Parse actions allow to modify the AST to be compliant with the ASN.1 Editor input
NAMED_VALUE_LIST
.
setParseAction
(
lambda
s
,
l
,
t
:
[
t
.
asList
()])
VALUE_LIST
.
setParseAction
(
lambda
s
,
l
,
t
:
[
t
.
asList
()])
SETOF
.
setParseAction
(
lambda
s
,
l
,
t
:
[
t
.
asList
()])
choiceValue
.
setParseAction
(
lambda
s
,
l
,
t
:
{
'Choice'
:
t
[
0
].
replace
(
'-'
,
'_'
),
t
[
0
].
replace
(
'-'
,
'_'
):
t
[
2
]})
valuereference
.
setParseAction
(
lambda
s
,
l
,
t
:
{
'Enum'
:
t
[
0
].
replace
(
'-'
,
'_'
)})
FloatingPointLiteral
.
setParseAction
(
lambda
s
,
l
,
t
:
float
(
t
[
0
]))
INT
.
setParseAction
(
lambda
s
,
l
,
t
:
int
(
t
[
0
]))
TRUE
.
setParseAction
(
lambda
s
,
l
,
t
:
True
)
FALSE
.
setParseAction
(
lambda
s
,
l
,
t
:
False
)
AnyValueLiteral
.
setParseAction
(
lambda
s
,
l
,
t
:
{
'Any'
:
t
[
0
]})
OctetStringLiteral
.
setParseAction
(
lambda
s
,
l
,
t
:
''
.
join
([
chr
(
int
(
t
[
1
][
i
:
i
+
2
],
16
))
for
i
in
range
(
0
,
len
(
t
[
1
]),
2
)]))
# BitString Warning: will only work with series of 8 bits
BitStringLiteral
.
setParseAction
(
lambda
s
,
l
,
t
:
''
.
join
([
chr
(
int
(
t
[
1
][
i
:
i
+
9
],
2
))
for
i
in
range
(
0
,
len
(
t
[
1
]),
2
)]))
def
parse_vdm
(
varName
=
'vdm'
,
string
=
''
):
''' Return a variable compatible with the ASN.1 Editor from a VDM string'''
try
:
return
{
varName
:
value
.
parseString
(
string
,
True
)[
0
]}
except
ParseException
as
err
:
print
'[parse_vdm] Parsing error:'
,
string
raise
def
toASN1ValueNotation
(
val
):
''' Create a GSER reprentation of the Python variable '''
result
=
''
if
isinstance
(
val
,
dict
):
if
'Choice'
in
val
:
result
+=
val
[
'Choice'
].
replace
(
'_'
,
'-'
).
strip
()
+
':'
result
+=
toASN1ValueNotation
(
val
[
val
[
'Choice'
]])
elif
'Enum'
in
val
:
result
+=
unicode
(
val
[
'Enum'
]).
replace
(
'_'
,
'-'
).
strip
()
else
:
# SEQUENCE and SET
result
+=
' { '
needsComa
=
False
for
seqElem
in
val
:
if
needsComa
:
result
+=
', '
result
+=
seqElem
.
replace
(
'_'
,
'-'
).
strip
()
+
' '
result
+=
toASN1ValueNotation
(
val
[
seqElem
])
needsComa
=
True
result
+=
' }'
elif
isinstance
(
val
,
list
):
# SEQUENCE OF or SET OF
result
+=
' { '
needsComa
=
False
for
seqOfElem
in
val
:
if
needsComa
:
result
+=
', '
result
+=
toASN1ValueNotation
(
seqOfElem
)
needsComa
=
True
result
+=
' }'
elif
isinstance
(
val
,
bool
):
# boolean are in upper case in ASN.1
result
+=
str
(
val
).
upper
()
elif
isinstance
(
val
,
basestring
):
# Strings: add quotes, and if string contain non-displayable chars, use repr() to get a visible string
if
all
(
c
in
string
.
printable
for
c
in
val
):
result
=
'"'
+
val
+
'"'
else
:
result
=
'"'
for
c
in
val
:
result
+=
'%02x'
%
ord
(
c
)
result
+=
'"H'
else
:
result
=
str
(
val
)
# INTEGER and REAL
return
result
def
valueNotationToSwig
(
gser
,
dest
,
sort
,
ASN1Swig
,
ASN1_AST
,
var
=
None
):
''' Parse a GSER value and fill a SWIG variable with it
Inputs:
gser : input GSER string
dest : output SWIG instance to be filled
ASN1Swig : python module containing SWIG DV access
sort: ASN1 typename, with dashes, no underscores
ASN1_AST : AST generated by ASN1SCC
var : optional already pyside-converted GSER string
Outputs:
none - "dest" is modified by this function
'''
var
=
var
or
value
.
parseString
(
gser
,
True
)[
0
]
def
reach
(
field
,
orig
,
idx
=
True
):
''' Helper: move swig pointer to the next field, and optionaly
index (if idx=True)
Inputs: field is a string with optional index (e.g. "a[0]")
orig is the swig pointer
idx: set to true if you want the index to be reached
'''
split
=
field
.
strip
(
']'
).
split
(
'['
)
ptr
=
getattr
(
orig
,
split
[
0
])
if
split
else
orig
if
len
(
split
)
>
1
and
idx
:
# SEQOF index
ptr
=
ptr
[
int
(
split
[
1
])]
def
rec
(
inp
,
outp
,
sort
):
''' Recursively fill up the value '''
if
sort
.
kind
==
'ReferenceType'
:
sort
=
ASN1_AST
[
sort
.
ReferencedTypeName
]
if
isinstance
(
inp
,
list
):
# SEQUENCE OF
# get the path to the sequence of
_
,
params
,
path
=
outp
.
GetState
()
if
path
:
path
=
path
.
strip
(
'.'
).
split
(
'.'
)
for
i
in
range
(
len
(
inp
)):
outp
.
Reset
()
for
each
in
path
:
reach
(
each
,
outp
)
# Follow the ASN.1 type in the AST from ASN1SCC
rec
(
inp
[
i
],
outp
[
i
],
sort
.
type
.
type
)
if
sort
.
type
.
Min
!=
sort
.
type
.
Max
:
outp
.
Reset
()
for
each
in
path
:
reach
(
each
,
outp
)
# The ASN1SCC AST only knows if the list has a fixed length
outp
.
SetLength
(
len
(
inp
))
elif
isinstance
(
inp
,
(
int
,
float
,
bool
)):
outp
.
Set
(
inp
)
elif
isinstance
(
inp
,
dict
):
if
'Enum'
in
inp
:
# Get proper enum id from the ASN1SCC AST
enum_id
=
sort
.
type
.
EnumValues
[
inp
[
'Enum'
].
replace
(
'_'
,
'-'
)].
EnumID
val
=
getattr
(
ASN1Swig
.
DV
,
enum_id
)
outp
.
Set
(
val
)
elif
'Choice'
in
inp
:
child_name
=
inp
[
'Choice'
]
ch_ty
=
sort
.
type
.
Children
[
child_name
.
replace
(
'_'
,
'-'
)]
enum_val
=
getattr
(
ASN1Swig
.
DV
,
ch_ty
.
EnumID
)
outp
.
kind
.
Set
(
enum_val
)
rec
(
inp
[
child_name
],
getattr
(
outp
,
child_name
.
replace
(
'-'
,
'_'
)),
ch_ty
.
type
)
else
:
# SEQUENCE
# get the path to the sequence
_
,
params
,
path
=
outp
.
GetState
()
if
path
:
path
=
path
.
strip
(
'.'
).
split
(
'.'
)
for
field
,
data
in
inp
.
viewitems
():
outp
.
Reset
()
for
each
in
path
:
# Reach the path, including indexes
reach
(
each
,
outp
)
# Then get the field itself
reach
(
field
.
replace
(
'-'
,
'_'
),
outp
)
field_ty
=
sort
.
type
.
Children
[
field
.
replace
(
'_'
,
'-'
)]
rec
(
data
,
outp
,
field_ty
.
type
)
# move back to original path
outp
.
Reset
()
if
len
(
path
):
reach
(
path
[
0
],
outp
)
else
:
# Unsupported type
pass
rec
(
var
,
dest
,
sort
)
if
__name__
==
'__main__'
:
''' Test application '''
import
sys
if
len
(
sys
.
argv
)
==
2
:
print
parse_vdm
(
string
=
sys
.
argv
[
1
])
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment