Specman - how to perform sub-type multi-extend, also for different kinds of sub-types - verification

I want to achieve the following functionality:
extend RED GREEN BLUE packet {...}
this line will cause the struct members in the curly brackets,
to be added to all the specified subtypes of a certain enumerated type.
the result will look like this:
extend RED packet {...}
extend BLUE packet {...}
extend GREEN packet {...}
extend BIG MEDIUM RED BLUE GREEN packet {...}
this line will extend all possible combinations of the
items from each enumerated type with the struct members
that appear in the curly brackets.
the result will look like this:
extend MEDIUM RED packet {...}
extend MEDIUM BLUE packet {...}
extend MEDIUM GREEN packet {...}
extend BIG RED packet {...}
extend BIG BLUE packet {...}
extend BIG GREEN packet {...}
Thanks,

This macro solves this problem, yet there is a small limitation.
Since this macro is ‘define as computed’ , the struct you are going to apply it on should be defined in a different file than the file that uses the macro.
A simple use case is shown here: (suppose this macro is in a file called dac.e):
define <multi_when'statement> "ext_s \[<detr'name>,...\] <base'type> \{<sm'exp>,...\}" as computed {
var our_struct:rf_struct=rf_manager.get_type_by_name(<base'type>).as_a(rf_struct);
var fields:list of rf_field = our_struct.as_a(rf_struct).get_declared_fields();
var rf_type_list:list of rf_type;
var list_of_0_index:list of uint;
var field_names:list of string;
var list_of_enums:list of rf_enum;
var temp_index:uint=0;
var used_types_list:list of rf_type;
var enumerations:list of string;
var indices:list of uint;
var num_of_each_enum:list of uint;
var size_of_final_list_of_enumerations_to_be_used:uint=1;
var enum_items_list:list of rf_enum_item;
var final_list_of_enumerations_to_be_used: list of string;
var multiplication_list_algrtm1:list of uint;
var multiplication_list_algrtm2:list of uint;
var multiplication_uint_algrtm:uint=1;
if (<detr'names>.is_empty()){
error("you did not supply any when subtypes");
};
for each (field) in fields{
rf_type_list.add(field.get_type());
field_names.add(field.get_name());
};
for each (typ) in rf_type_list{
if (rf_type_list[index] is not a rf_enum){
rf_type_list.delete(index);
field_names.delete(index);
};
};
if (rf_type_list.is_empty()){
error("the type ",<base'type>," does not have any enumerated type fields.");
};
for each (typ) using index (typ_index) in rf_type_list {
num_of_each_enum.add(0);
if(indices.is_empty()){
indices.add(0);
}else {
indices.add(indices[typ_index-1])
};
enum_items_list = typ.as_a(rf_enum).get_items();
for each (enum_item) in <detr'names> {
if (enum_items_list.has(it.get_name()==enum_item)){
out(enum_item, " is found in ",typ.get_name());
enumerations.add(append(enum_item,"'",field_names[typ_index]));
indices[typ_index]+=1;
num_of_each_enum[typ_index]+=1;
};
};
};
for each in num_of_each_enum do { // avoid enums that are not used - to
if (it==0){
list_of_0_index.add(index);
};
};
if (!list_of_0_index.is_empty()){
list_of_0_index=list_of_0_index.reverse();
for each in list_of_0_index {
num_of_each_enum.delete(it);
indices.delete(it);
field_names.delete(it);
}
};
enumerations=enumerations.unique(it);
if (enumerations.is_empty()){
error("no legal enumerated values were used in the ext_s macro, please check that the arguments in square brackets are in the form of [<enum_item1>,<enum_item2>,...]");
};
//remove the last index (not relevant - and add 0 in the indices[0]
indices.add0(0);
indices.delete(indices.size()-1);
for each in num_of_each_enum do {
size_of_final_list_of_enumerations_to_be_used*=it;
};
for each in num_of_each_enum do {
multiplication_uint_algrtm*=it;
multiplication_list_algrtm1.add(size_of_final_list_of_enumerations_to_be_used/multiplication_uint_algrtm);
multiplication_list_algrtm2.add(size_of_final_list_of_enumerations_to_be_used/multiplication_list_algrtm1[index]);
};
//build the final list of string to be used in the extend statement:
for i from 1 to size_of_final_list_of_enumerations_to_be_used{
final_list_of_enumerations_to_be_used.add("");
};
for k from 0 to indices.size()-1 do {
temp_index=0;
for j from 0 to multiplication_list_algrtm2[k]-1 do {
for i from 0 to multiplication_list_algrtm1[k]-1 do {
final_list_of_enumerations_to_be_used[temp_index]=append(final_list_of_enumerations_to_be_used[temp_index]," ",enumerations[indices[k]+j%num_of_each_enum[k]]);
temp_index+=1;
};
};
};
for each in final_list_of_enumerations_to_be_used do {
result = appendf("%s extend %s %s {",result,it, <base'type> );
for each in <sm'exps> do {
result= appendf("%s %s;",result,it);
};
result = append(result , "};");
};
print result;
};
Note that this macro solves an interesting problem:
Suppose you have a list of a bunch of items of certain types (for example : {a1,a2,b1,b2,c1,c2,c3…}),
And you do not preliminarily know how many types are there in this list (in this example there are 3 types-a,b,c - but there could be more or less). So question is, how do you create a list of all possible combinations of all items from all type (for example: 0. a1-b1-c1 1.a1-b1-c2…..11.a2-b2-c3), without knowing how many types are there in the list? You can follow the code and figure out the algorithm to do that (using list of indices, how many items are there from each type and so….).
The file that should be loaded prior to macro (dac.e) is :
Struct.e:
<'
type t1:[A1,A2,A3,A4,A5];
type t2:[B1,B2,B3];
type t3:[C1,C2,C3];
struct s{
a:uint(bits:4);
t_1:t1;
t_2:t2;
t_3:t3;
};
'>
And the test file is :
<'
Import dac.e;
import struct.e;
//use the macro
ext_s [A1,A2,B1,B2] s {x:int,keep x==6,y:int,keep y==10};
extend sys{
s1:A1 B1 s;
s2:A2 B1 s;
s3:A1 s;
run() is also{
print s1;
print s2;
print s3;
};
};
'>
Please comment if you have any question.

Related

Extract value out of Kotlin arrow Either type and assign it to const

It would be a basic question, but I couldn't figure out a solution. I need to initialize a constant out of the right-side value of below either type.
val test: Either<String, Int> = 1.right()
I tried something like below but it shrinks the scope of the constant.
when(test) {
is Either.Right -> {val get:Int = test.b}
is Either.Left -> println(test.a)
}
I want that get to be scoped outside of when statement. Is there any way to do it or Arrow Either is not made for this purpose?
The important question is: what should happen if the Either is Left. In this example it is created close to where it's used, so it is obvious to you as a developer. But to the compiler what is inside the Either can be either an Int or a String.
You can extract the value using for example fold:
val x = test.fold({ 0 }, {it}) // provide 0 as default in case the Either was a `Left`
// x = 1
another option is getOrElse
val test = 1.right()
val x = test.getOrElse { 42 } // again, default in case it was a `Left`
// x = 42
You can also work with it without unwrapping it:
val test = 1.right()
val testPlus10 = test.map { it + 10 } // adds 10 to `test` if it is `Right`, does nothing otherwise
val x = testPlus10.getOrElse { 0 } // unwrap by providing a default value
// x = 11
For more example check the official docs.
Recommended reading: How do I get the value out of my Monad

How to properly iterate over arrays in kotlin

I am currently learning kotlin and therefore following the kotlin track on exercism. The following exercise required me to calculate the Hamming difference between two Strings (so basically just counting the number of differences).
I got to the solution with the following code:
object Hamming {
fun compute(dnaOne: String, dnaTwo: String): Int {
if (dnaOne.length != dnaTwo.length) throw IllegalArgumentException("left and right strands must be of equal length.")
var counter = 0
for ((index, letter) in dnaOne.toCharArray().withIndex()) {
if (letter != dnaTwo.toCharArray()[index]) {
counter++
}
}
return counter
}
}
however, in the beginning I tried to do dnaOne.split("").withIndex() instead of dnaOne.toCharArray().withIndex() which did not work, it would literally stop after the first iteration and the following example
Hamming.compute("GGACGGATTCTG", "AGGACGGATTCT") would return 1 instead of the correct integer 9 (which only gets returned when using toCharArray)
I would appreciate any explanation
I was able to simplify this by using the built-in CharSequence.zip function because StringimplementsCharSequence` in Kotlin.
According to the documentation for zip:
Returns a list of pairs built from the characters of this and the [other] char sequences with the same index
The returned list has length of the shortest char sequence.
Which means we will get a List<Pair<Char,Char>> back (a list of pairs of letters in the same positions). Now that we have this, we can use Iterable.count to determine how many of them are different.
I implemented this as an extension function on String rather than in an object:
fun String.hamming(other: String): Int =
if(this.length != other.length) {
throw IllegalArgumentException("String lengths must match")
} else {
this.zip(other).count { it.first != it.second }
}
This also becomes a single expression now.
And to call this:
val ham = "GGACGGATTCTG".hamming("AGGACGGATTCT")
println("Hamming distance: $ham")

Specman - Assign uint decimal number to sequence

I have the following sequence:
extend CONFIG_ADC_CLK ocp_master_sequence_q {
divide_by : uint(bits:4);
align_by : uint(bits:4);
body()#driver.clock is {
var div : uint(bits:3);
case divide_by {
1 : { div = 0; };
2 : { div = 1; };
4 : { div = 2; };
8 : { div = 3; };
16 : { div = 4; };
default : { dut_error(divide_by," is not a legal Clock division for ADC"); };
};
gad_regs.gad_clk_gen.clk_algn = align_by;
gad_regs.gad_clk_gen.clk_dev = div;
do WR_REG seq keeping {.reg==gad_regs.gad_clk_gen;};
};
};//extend CONFIG_ADC_CLK ocp_master_sequence_q {
In the test I use the sequence :
do CONFIG_ADC_CLK seq keeping {.divide_by== 3;.align_by==0;};
For some reason the compiler refer the number of the field divide_byas hex number instead of decimal.
How can I ensure that it will refer it as decimal?
This is not related to sequences and not related to how numbers are assigned to fields. It's just about how numeric values are formatted in printing and string operations. The actual value of a field has nothing to do with how it is printed.
By default, dut_error(), message(), out(), append() and other string formatting routines use the current setting of config print -radix. So, you probably have it set to HEX in your environment.
If you need this specific dut_error() to always use decimal format, no matter what the config setting is, you can use dec(), like this:
dut_error(dec(divide_by)," is not a legal Clock division for ADC");
By the way, when using the second variant of those routines, such as dut_errorf() or appendf(), you can determine the radix by providing the right % parameter, e.g., %d for decimals or %x for hexa, for example, the above dut_error() might be rewritten as:
dut_errorf("%d is not a legal Clock division for ADC", divide_by);
Here, you can also use %s, in which case the config radix setting is still used.

Adding user mode types for Perl 6 NativeCall structs

The Perl 6 docs list a bunch of types. Some of them, such as Str, have more complicated box/unbox behaviors.
Is it possible to define my own type, specifying my own routines for the box/unboxing? For a particular project, I have a bunch of types I'm reusing, and basically cut/pasting my accessor functions over and over.
For example, the C Struct uses a time_t, and I plug in accessor methods to go to/from a DateTime. Another example is a comma-separated list, I'd like to go to/from an Array and take care of the split/join automagically.
Is there a better way to do this?
Edit: Add Example:
constant time_t = uint64;
constant FooType_t = uint16;
enum FooType <A B C>;
class Foo is repr('CStruct') is rw
{
has uint32 $.id;
has Str $.name;
has FooType_t $.type;
has time_t $.time;
method name(Str $n?) {
$!name := $n with $n;
$!name;
}
method type(FooType $t?) {
$!type = $t with $t;
FooType($!type);
}
method time(DateTime $d?) {
$!time = .Instant.to-posix[0].Int with $d;
DateTime.new($!time)
}
}
my $f = Foo.new;
$f.id = 12;
$f.name('myname');
$f.type(B);
$f.time(DateTime.new('2000-01-01T12:34:56Z'));
say "$f.id() $f.name() $f.type() $f.time()";
# 12 myname B 2000-01-01T12:34:56Z
This works, I can set the various fields of the CStruct in Perl-ish ways (no lvalue, but I can pass them in as parameters).
Now I want to use time_t, FooType_t, etc. for many fields in a lot of structs and have them act the same way. Is there a better way other than to just copy those methods over and over?
Maybe macros could help here? I haven't mastered them yet.
You could write a trait that handles automatic attribute conversion on fetching or storing the attribute. The following should get you started:
multi sub trait_mod:<is>(Attribute:D $attr, :$autoconv!) {
use nqp;
my $name := $attr.name;
$attr.package.^add_method: $name.substr(2), do given $attr.type {
when .REPR eq 'P6int' {
method () is rw {
my $self := self;
Proxy.new:
FETCH => method () {
$autoconv.out(nqp::getattr_i($self, $self.WHAT, $name));
},
STORE => method ($_) {
nqp::bindattr_i($self, $self.WHAT, $name,
nqp::decont($autoconv.in($_)));
}
}
}
default {
die "FIXME: no idea how to handle {.^name}";
}
}
}
For example, take your use case of time_t:
constant time_t = uint64;
class CTimeConversion {
multi method in(Int $_ --> time_t) { $_ }
multi method in(DateTime $_ --> time_t) { .posix }
method out(time_t $_ --> DateTime) { DateTime.new($_) }
}
class CTimeSpan is repr<CStruct> {
has time_t $.start is autoconv(CTimeConversion);
has time_t $.end is autoconv(CTimeConversion);
}
Finally, some example code to show it works:
my $span = CTimeSpan.new;
say $span;
say $span.end;
$span.end = DateTime.now;
say $span;
say $span.end;

ANTLR4 change listener during parse

I have an ANTLR4 listener which handles a standard and well-formed grammar, however am struggling with how to deal the non-standard implementations. Although all of the variants go through the lexer without problems the parse stage is a lot trickier.
A traditional way of doing this would be something like
// Header of document
variant = STANDARD;
if (header.indexOf("microsoft") != -1) {
variant = MICROSOFT;
} else if (header.indexOf("google") != -1) {
variant = GOOGLE;
}
...
// Parsing a particular element
if (variant.equals(MICROSOFT)) {
// Microsoft-specific stuff
} else if (variant.equals(GOOGLE)) {
// Google-specific stuff
} else {
// Standard stuff
}
but this quickly becomes unmaintainable. The obvious solution is to have a ParseTreeListener for the standard implementation and then subclass it for each variant, but I don't know which variant it is until I've started the parse.
So how can I either switch from one listener to another part-way through the parse, or restart the parse with a new listener once I know which variant I'm dealing with?
If these variants occur frequently, you might want to consider embedding custom code to handle context sensitive parsing by using predicates (the {...}? construct in the following pseudo grammar):
rule
: { boolean-expression-a }? a-alternative
| { boolean-expression-b }? b-alternative
| /* fall through */ not-a-or-b-alternative
;
Let's say you want to parse a file containing chunks. A chunk consists of a header and a data row. In the header you can set your variant. The data of a normal variant contains 3 NUMBERs, Google's variant contains 2 NUMBERs and Microsoft's variant contains a single NUMBER. An example of such a file would look like this:
header: none
data: 1 2 3
header: google
data: 4 5
header: microsoft
data: 6
And here's a demo of a context sensitive ANTLR v4 grammar able to parse this:
grammar T;
#parser::members {
enum Variant {
GOOGLE,
MICROSOFT,
OTHER;
public static Variant tryValueOf(String name) {
try {
return Variant.valueOf(name.toUpperCase());
}
catch(Exception e) {
return OTHER;
}
}
}
private Variant variant = Variant.OTHER;
}
parse
: chunk+ EOF
;
chunk
: header data
;
header
: K_HEADER COLON NAME {variant = Variant.tryValueOf($NAME.text);}
;
data
: {variant == Variant.MICROSOFT}? K_DATA COLON NUMBER #MicrosoftData
| {variant == Variant.GOOGLE}? K_DATA COLON NUMBER NUMBER #GoogleData
| K_DATA COLON NUMBER NUMBER NUMBER #OtherData
;
K_DATA : 'data';
K_HEADER : 'header';
NAME : [a-zA-Z]+;
NUMBER : [0-9]+;
COLON : ':';
SPACE : [ \t\r\n] -> skip;
Resulting in the following parse: