diff --git a/api/proto/sysml.pb.go b/api/proto/sysml.pb.go index be5e1161b..abde04985 100644 --- a/api/proto/sysml.pb.go +++ b/api/proto/sysml.pb.go @@ -3889,6 +3889,7 @@ type Value struct { // *Value_VectorQuantity // *Value_MeasurementRef // *Value_Infinity + // *Value_Function Kind isValue_Kind `protobuf_oneof:"kind"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -4075,6 +4076,15 @@ func (x *Value) GetInfinity() bool { return false } +func (x *Value) GetFunction() *Function { + if x != nil { + if x, ok := x.Kind.(*Value_Function); ok { + return x.Function + } + } + return nil +} + type isValue_Kind interface { isValue_Kind() } @@ -4148,6 +4158,10 @@ type Value_Infinity struct { Infinity bool `protobuf:"varint,16,opt,name=infinity,proto3,oneof"` } +type Value_Function struct { + Function *Function `protobuf:"bytes,17,opt,name=function,proto3,oneof"` // a calc as a value, named by its declaration +} + func (*Value_IntValue) isValue_Kind() {} func (*Value_RealValue) isValue_Kind() {} @@ -4180,6 +4194,72 @@ func (*Value_MeasurementRef) isValue_Kind() {} func (*Value_Infinity) isValue_Kind() {} +func (*Value_Function) isValue_Kind() {} + +// Function is a calc held as a value: a calc definition, or a calc usage with +// an input no read could supply, as `Sq` in `Fn(Sq, 3.0)` or the `f` of +// `in calc f {...}`. It crosses as the declaration it is a value of, which is +// its identity: two functions are the same exactly when calc_id and self_id +// are. A function closing over the bindings of the behavior body it is +// declared in has no wire form and crosses as the null arm. +type Function struct { + state protoimpl.MessageState `protogen:"open.v1"` + // FQN of the calc declaration ("Analysis::Sq"). Its identity. + CalcId string `protobuf:"bytes,1,opt,name=calc_id,json=calcId,proto3" json:"calc_id,omitempty"` + // ID of the object the calc's feature names resolve against, for a calc + // usage read off a part (`holder.scale`); 0 for a function closing over no + // object. An instance id, living only within the response that sent it: a + // request function with a non-zero self_id is rejected, since no later call + // holds that object. + SelfId int64 `protobuf:"varint,2,opt,name=self_id,json=selfId,proto3" json:"self_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Function) Reset() { + *x = Function{} + mi := &file_sysml_proto_msgTypes[48] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Function) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Function) ProtoMessage() {} + +func (x *Function) ProtoReflect() protoreflect.Message { + mi := &file_sysml_proto_msgTypes[48] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Function.ProtoReflect.Descriptor instead. +func (*Function) Descriptor() ([]byte, []int) { + return file_sysml_proto_rawDescGZIP(), []int{48} +} + +func (x *Function) GetCalcId() string { + if x != nil { + return x.CalcId + } + return "" +} + +func (x *Function) GetSelfId() int64 { + if x != nil { + return x.SelfId + } + return 0 +} + // Array is a Collections::Array: its elements flattened in row-major order // under its dimensions, compared by content rather than by the object read. type Array struct { @@ -4195,7 +4275,7 @@ type Array struct { func (x *Array) Reset() { *x = Array{} - mi := &file_sysml_proto_msgTypes[48] + mi := &file_sysml_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4207,7 +4287,7 @@ func (x *Array) String() string { func (*Array) ProtoMessage() {} func (x *Array) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[48] + mi := &file_sysml_proto_msgTypes[49] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4220,7 +4300,7 @@ func (x *Array) ProtoReflect() protoreflect.Message { // Deprecated: Use Array.ProtoReflect.Descriptor instead. func (*Array) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{48} + return file_sysml_proto_rawDescGZIP(), []int{49} } func (x *Array) GetDimensions() []int64 { @@ -4250,7 +4330,7 @@ type Vector struct { func (x *Vector) Reset() { *x = Vector{} - mi := &file_sysml_proto_msgTypes[49] + mi := &file_sysml_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4262,7 +4342,7 @@ func (x *Vector) String() string { func (*Vector) ProtoMessage() {} func (x *Vector) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[49] + mi := &file_sysml_proto_msgTypes[50] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4275,7 +4355,7 @@ func (x *Vector) ProtoReflect() protoreflect.Message { // Deprecated: Use Vector.ProtoReflect.Descriptor instead. func (*Vector) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{49} + return file_sysml_proto_rawDescGZIP(), []int{50} } func (x *Vector) GetComponents() []*Value { @@ -4298,7 +4378,7 @@ type VectorQuantity struct { func (x *VectorQuantity) Reset() { *x = VectorQuantity{} - mi := &file_sysml_proto_msgTypes[50] + mi := &file_sysml_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4310,7 +4390,7 @@ func (x *VectorQuantity) String() string { func (*VectorQuantity) ProtoMessage() {} func (x *VectorQuantity) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[50] + mi := &file_sysml_proto_msgTypes[51] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4323,7 +4403,7 @@ func (x *VectorQuantity) ProtoReflect() protoreflect.Message { // Deprecated: Use VectorQuantity.ProtoReflect.Descriptor instead. func (*VectorQuantity) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{50} + return file_sysml_proto_rawDescGZIP(), []int{51} } func (x *VectorQuantity) GetComponents() []*Quantity { @@ -4345,7 +4425,7 @@ type Complex struct { func (x *Complex) Reset() { *x = Complex{} - mi := &file_sysml_proto_msgTypes[51] + mi := &file_sysml_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4357,7 +4437,7 @@ func (x *Complex) String() string { func (*Complex) ProtoMessage() {} func (x *Complex) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[51] + mi := &file_sysml_proto_msgTypes[52] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4370,7 +4450,7 @@ func (x *Complex) ProtoReflect() protoreflect.Message { // Deprecated: Use Complex.ProtoReflect.Descriptor instead. func (*Complex) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{51} + return file_sysml_proto_rawDescGZIP(), []int{52} } func (x *Complex) GetReal() float64 { @@ -4404,7 +4484,7 @@ type EnumLiteral struct { func (x *EnumLiteral) Reset() { *x = EnumLiteral{} - mi := &file_sysml_proto_msgTypes[52] + mi := &file_sysml_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4416,7 +4496,7 @@ func (x *EnumLiteral) String() string { func (*EnumLiteral) ProtoMessage() {} func (x *EnumLiteral) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[52] + mi := &file_sysml_proto_msgTypes[53] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4429,7 +4509,7 @@ func (x *EnumLiteral) ProtoReflect() protoreflect.Message { // Deprecated: Use EnumLiteral.ProtoReflect.Descriptor instead. func (*EnumLiteral) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{52} + return file_sysml_proto_rawDescGZIP(), []int{53} } func (x *EnumLiteral) GetLiteralId() string { @@ -4462,7 +4542,7 @@ type ValueSequence struct { func (x *ValueSequence) Reset() { *x = ValueSequence{} - mi := &file_sysml_proto_msgTypes[53] + mi := &file_sysml_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4474,7 +4554,7 @@ func (x *ValueSequence) String() string { func (*ValueSequence) ProtoMessage() {} func (x *ValueSequence) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[53] + mi := &file_sysml_proto_msgTypes[54] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4487,7 +4567,7 @@ func (x *ValueSequence) ProtoReflect() protoreflect.Message { // Deprecated: Use ValueSequence.ProtoReflect.Descriptor instead. func (*ValueSequence) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{53} + return file_sysml_proto_rawDescGZIP(), []int{54} } func (x *ValueSequence) GetElements() []*Value { @@ -4521,7 +4601,7 @@ type Quantity struct { func (x *Quantity) Reset() { *x = Quantity{} - mi := &file_sysml_proto_msgTypes[54] + mi := &file_sysml_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4533,7 +4613,7 @@ func (x *Quantity) String() string { func (*Quantity) ProtoMessage() {} func (x *Quantity) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[54] + mi := &file_sysml_proto_msgTypes[55] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4546,7 +4626,7 @@ func (x *Quantity) ProtoReflect() protoreflect.Message { // Deprecated: Use Quantity.ProtoReflect.Descriptor instead. func (*Quantity) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{54} + return file_sysml_proto_rawDescGZIP(), []int{55} } func (x *Quantity) GetMagnitude() isQuantity_Magnitude { @@ -4632,7 +4712,7 @@ type MeasurementRef struct { func (x *MeasurementRef) Reset() { *x = MeasurementRef{} - mi := &file_sysml_proto_msgTypes[55] + mi := &file_sysml_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4644,7 +4724,7 @@ func (x *MeasurementRef) String() string { func (*MeasurementRef) ProtoMessage() {} func (x *MeasurementRef) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[55] + mi := &file_sysml_proto_msgTypes[56] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4657,7 +4737,7 @@ func (x *MeasurementRef) ProtoReflect() protoreflect.Message { // Deprecated: Use MeasurementRef.ProtoReflect.Descriptor instead. func (*MeasurementRef) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{55} + return file_sysml_proto_rawDescGZIP(), []int{56} } func (x *MeasurementRef) GetUnit() string { @@ -4696,7 +4776,7 @@ type UnitTerm struct { func (x *UnitTerm) Reset() { *x = UnitTerm{} - mi := &file_sysml_proto_msgTypes[56] + mi := &file_sysml_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4708,7 +4788,7 @@ func (x *UnitTerm) String() string { func (*UnitTerm) ProtoMessage() {} func (x *UnitTerm) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[56] + mi := &file_sysml_proto_msgTypes[57] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4721,7 +4801,7 @@ func (x *UnitTerm) ProtoReflect() protoreflect.Message { // Deprecated: Use UnitTerm.ProtoReflect.Descriptor instead. func (*UnitTerm) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{56} + return file_sysml_proto_rawDescGZIP(), []int{57} } func (x *UnitTerm) GetScaleNum() float64 { @@ -4757,7 +4837,7 @@ type UnitFactor struct { func (x *UnitFactor) Reset() { *x = UnitFactor{} - mi := &file_sysml_proto_msgTypes[57] + mi := &file_sysml_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4769,7 +4849,7 @@ func (x *UnitFactor) String() string { func (*UnitFactor) ProtoMessage() {} func (x *UnitFactor) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[57] + mi := &file_sysml_proto_msgTypes[58] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4782,7 +4862,7 @@ func (x *UnitFactor) ProtoReflect() protoreflect.Message { // Deprecated: Use UnitFactor.ProtoReflect.Descriptor instead. func (*UnitFactor) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{57} + return file_sysml_proto_rawDescGZIP(), []int{58} } func (x *UnitFactor) GetUnitId() string { @@ -4811,7 +4891,7 @@ type Diagnostic struct { func (x *Diagnostic) Reset() { *x = Diagnostic{} - mi := &file_sysml_proto_msgTypes[58] + mi := &file_sysml_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4823,7 +4903,7 @@ func (x *Diagnostic) String() string { func (*Diagnostic) ProtoMessage() {} func (x *Diagnostic) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[58] + mi := &file_sysml_proto_msgTypes[59] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4836,7 +4916,7 @@ func (x *Diagnostic) ProtoReflect() protoreflect.Message { // Deprecated: Use Diagnostic.ProtoReflect.Descriptor instead. func (*Diagnostic) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{58} + return file_sysml_proto_rawDescGZIP(), []int{59} } func (x *Diagnostic) GetSeverity() string { @@ -4874,7 +4954,7 @@ type Span struct { func (x *Span) Reset() { *x = Span{} - mi := &file_sysml_proto_msgTypes[59] + mi := &file_sysml_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4886,7 +4966,7 @@ func (x *Span) String() string { func (*Span) ProtoMessage() {} func (x *Span) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[59] + mi := &file_sysml_proto_msgTypes[60] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4899,7 +4979,7 @@ func (x *Span) ProtoReflect() protoreflect.Message { // Deprecated: Use Span.ProtoReflect.Descriptor instead. func (*Span) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{59} + return file_sysml_proto_rawDescGZIP(), []int{60} } func (x *Span) GetFile() string { @@ -4947,7 +5027,7 @@ type ServerInfoRequest struct { func (x *ServerInfoRequest) Reset() { *x = ServerInfoRequest{} - mi := &file_sysml_proto_msgTypes[60] + mi := &file_sysml_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4959,7 +5039,7 @@ func (x *ServerInfoRequest) String() string { func (*ServerInfoRequest) ProtoMessage() {} func (x *ServerInfoRequest) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[60] + mi := &file_sysml_proto_msgTypes[61] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4972,7 +5052,7 @@ func (x *ServerInfoRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ServerInfoRequest.ProtoReflect.Descriptor instead. func (*ServerInfoRequest) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{60} + return file_sysml_proto_rawDescGZIP(), []int{61} } // ServerInfoResponse describes the running service. @@ -5026,6 +5106,11 @@ type ServerInfoResponse struct { // refused with UNIMPLEMENTED rather than read as another // value. Separate from structured_values, which a client // built before this arm existed may already claim. + // "function_values" - a Value carries a calc held as a value as function, + // named by its declaration, rather than reporting it as an + // unsupported null, and one is accepted as an action input + // or calc argument; without it, one is refused with + // UNIMPLEMENTED rather than read as another value. // "apply_edits" - the ApplyEdits RPC edits a parsed model's own source, // preserving everything the edit did not touch. // "document_query" - the RunDocumentQuery RPC runs a named document query @@ -5039,7 +5124,7 @@ type ServerInfoResponse struct { func (x *ServerInfoResponse) Reset() { *x = ServerInfoResponse{} - mi := &file_sysml_proto_msgTypes[61] + mi := &file_sysml_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5051,7 +5136,7 @@ func (x *ServerInfoResponse) String() string { func (*ServerInfoResponse) ProtoMessage() {} func (x *ServerInfoResponse) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[61] + mi := &file_sysml_proto_msgTypes[62] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5064,7 +5149,7 @@ func (x *ServerInfoResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ServerInfoResponse.ProtoReflect.Descriptor instead. func (*ServerInfoResponse) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{61} + return file_sysml_proto_rawDescGZIP(), []int{62} } func (x *ServerInfoResponse) GetVersion() string { @@ -5094,7 +5179,7 @@ type QueryRequest struct { func (x *QueryRequest) Reset() { *x = QueryRequest{} - mi := &file_sysml_proto_msgTypes[62] + mi := &file_sysml_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5106,7 +5191,7 @@ func (x *QueryRequest) String() string { func (*QueryRequest) ProtoMessage() {} func (x *QueryRequest) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[62] + mi := &file_sysml_proto_msgTypes[63] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5119,7 +5204,7 @@ func (x *QueryRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use QueryRequest.ProtoReflect.Descriptor instead. func (*QueryRequest) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{62} + return file_sysml_proto_rawDescGZIP(), []int{63} } func (x *QueryRequest) GetModelHash() string { @@ -5156,7 +5241,7 @@ type QueryResponse struct { func (x *QueryResponse) Reset() { *x = QueryResponse{} - mi := &file_sysml_proto_msgTypes[63] + mi := &file_sysml_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5168,7 +5253,7 @@ func (x *QueryResponse) String() string { func (*QueryResponse) ProtoMessage() {} func (x *QueryResponse) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[63] + mi := &file_sysml_proto_msgTypes[64] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5181,7 +5266,7 @@ func (x *QueryResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use QueryResponse.ProtoReflect.Descriptor instead. func (*QueryResponse) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{63} + return file_sysml_proto_rawDescGZIP(), []int{64} } func (x *QueryResponse) GetElements() []*QueryResultElement { @@ -5211,7 +5296,7 @@ type Query struct { func (x *Query) Reset() { *x = Query{} - mi := &file_sysml_proto_msgTypes[64] + mi := &file_sysml_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5223,7 +5308,7 @@ func (x *Query) String() string { func (*Query) ProtoMessage() {} func (x *Query) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[64] + mi := &file_sysml_proto_msgTypes[65] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5236,7 +5321,7 @@ func (x *Query) ProtoReflect() protoreflect.Message { // Deprecated: Use Query.ProtoReflect.Descriptor instead. func (*Query) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{64} + return file_sysml_proto_rawDescGZIP(), []int{65} } func (x *Query) GetScope() []string { @@ -5275,7 +5360,7 @@ type Constraint struct { func (x *Constraint) Reset() { *x = Constraint{} - mi := &file_sysml_proto_msgTypes[65] + mi := &file_sysml_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5287,7 +5372,7 @@ func (x *Constraint) String() string { func (*Constraint) ProtoMessage() {} func (x *Constraint) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[65] + mi := &file_sysml_proto_msgTypes[66] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5300,7 +5385,7 @@ func (x *Constraint) ProtoReflect() protoreflect.Message { // Deprecated: Use Constraint.ProtoReflect.Descriptor instead. func (*Constraint) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{65} + return file_sysml_proto_rawDescGZIP(), []int{66} } func (x *Constraint) GetConstraint() isConstraint_Constraint { @@ -5363,7 +5448,7 @@ type PrimitiveConstraint struct { func (x *PrimitiveConstraint) Reset() { *x = PrimitiveConstraint{} - mi := &file_sysml_proto_msgTypes[66] + mi := &file_sysml_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5375,7 +5460,7 @@ func (x *PrimitiveConstraint) String() string { func (*PrimitiveConstraint) ProtoMessage() {} func (x *PrimitiveConstraint) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[66] + mi := &file_sysml_proto_msgTypes[67] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5388,7 +5473,7 @@ func (x *PrimitiveConstraint) ProtoReflect() protoreflect.Message { // Deprecated: Use PrimitiveConstraint.ProtoReflect.Descriptor instead. func (*PrimitiveConstraint) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{66} + return file_sysml_proto_rawDescGZIP(), []int{67} } func (x *PrimitiveConstraint) GetInverse() bool { @@ -5431,7 +5516,7 @@ type CompositeConstraint struct { func (x *CompositeConstraint) Reset() { *x = CompositeConstraint{} - mi := &file_sysml_proto_msgTypes[67] + mi := &file_sysml_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5443,7 +5528,7 @@ func (x *CompositeConstraint) String() string { func (*CompositeConstraint) ProtoMessage() {} func (x *CompositeConstraint) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[67] + mi := &file_sysml_proto_msgTypes[68] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5456,7 +5541,7 @@ func (x *CompositeConstraint) ProtoReflect() protoreflect.Message { // Deprecated: Use CompositeConstraint.ProtoReflect.Descriptor instead. func (*CompositeConstraint) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{67} + return file_sysml_proto_rawDescGZIP(), []int{68} } func (x *CompositeConstraint) GetOperator() CompositeOperator { @@ -5488,7 +5573,7 @@ type QueryResultElement struct { func (x *QueryResultElement) Reset() { *x = QueryResultElement{} - mi := &file_sysml_proto_msgTypes[68] + mi := &file_sysml_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5500,7 +5585,7 @@ func (x *QueryResultElement) String() string { func (*QueryResultElement) ProtoMessage() {} func (x *QueryResultElement) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[68] + mi := &file_sysml_proto_msgTypes[69] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5513,7 +5598,7 @@ func (x *QueryResultElement) ProtoReflect() protoreflect.Message { // Deprecated: Use QueryResultElement.ProtoReflect.Descriptor instead. func (*QueryResultElement) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{68} + return file_sysml_proto_rawDescGZIP(), []int{69} } func (x *QueryResultElement) GetId() string { @@ -5557,7 +5642,7 @@ type SweepRange struct { func (x *SweepRange) Reset() { *x = SweepRange{} - mi := &file_sysml_proto_msgTypes[69] + mi := &file_sysml_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5569,7 +5654,7 @@ func (x *SweepRange) String() string { func (*SweepRange) ProtoMessage() {} func (x *SweepRange) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[69] + mi := &file_sysml_proto_msgTypes[70] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5582,7 +5667,7 @@ func (x *SweepRange) ProtoReflect() protoreflect.Message { // Deprecated: Use SweepRange.ProtoReflect.Descriptor instead. func (*SweepRange) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{69} + return file_sysml_proto_rawDescGZIP(), []int{70} } func (x *SweepRange) GetParameter() string { @@ -5644,7 +5729,7 @@ type RunSweepRequest struct { func (x *RunSweepRequest) Reset() { *x = RunSweepRequest{} - mi := &file_sysml_proto_msgTypes[70] + mi := &file_sysml_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5656,7 +5741,7 @@ func (x *RunSweepRequest) String() string { func (*RunSweepRequest) ProtoMessage() {} func (x *RunSweepRequest) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[70] + mi := &file_sysml_proto_msgTypes[71] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5669,7 +5754,7 @@ func (x *RunSweepRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RunSweepRequest.ProtoReflect.Descriptor instead. func (*RunSweepRequest) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{70} + return file_sysml_proto_rawDescGZIP(), []int{71} } func (x *RunSweepRequest) GetModelHash() string { @@ -5752,7 +5837,7 @@ type SweepRow struct { func (x *SweepRow) Reset() { *x = SweepRow{} - mi := &file_sysml_proto_msgTypes[71] + mi := &file_sysml_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5764,7 +5849,7 @@ func (x *SweepRow) String() string { func (*SweepRow) ProtoMessage() {} func (x *SweepRow) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[71] + mi := &file_sysml_proto_msgTypes[72] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5777,7 +5862,7 @@ func (x *SweepRow) ProtoReflect() protoreflect.Message { // Deprecated: Use SweepRow.ProtoReflect.Descriptor instead. func (*SweepRow) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{71} + return file_sysml_proto_rawDescGZIP(), []int{72} } func (x *SweepRow) GetInputs() []*CalcOutput { @@ -5850,7 +5935,7 @@ type RunSweepResponse struct { func (x *RunSweepResponse) Reset() { *x = RunSweepResponse{} - mi := &file_sysml_proto_msgTypes[72] + mi := &file_sysml_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5862,7 +5947,7 @@ func (x *RunSweepResponse) String() string { func (*RunSweepResponse) ProtoMessage() {} func (x *RunSweepResponse) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[72] + mi := &file_sysml_proto_msgTypes[73] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5875,7 +5960,7 @@ func (x *RunSweepResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RunSweepResponse.ProtoReflect.Descriptor instead. func (*RunSweepResponse) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{72} + return file_sysml_proto_rawDescGZIP(), []int{73} } func (x *RunSweepResponse) GetRows() []*SweepRow { @@ -5953,7 +6038,7 @@ type RunDocumentQueryRequest struct { func (x *RunDocumentQueryRequest) Reset() { *x = RunDocumentQueryRequest{} - mi := &file_sysml_proto_msgTypes[73] + mi := &file_sysml_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5965,7 +6050,7 @@ func (x *RunDocumentQueryRequest) String() string { func (*RunDocumentQueryRequest) ProtoMessage() {} func (x *RunDocumentQueryRequest) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[73] + mi := &file_sysml_proto_msgTypes[74] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5978,7 +6063,7 @@ func (x *RunDocumentQueryRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RunDocumentQueryRequest.ProtoReflect.Descriptor instead. func (*RunDocumentQueryRequest) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{73} + return file_sysml_proto_rawDescGZIP(), []int{74} } func (x *RunDocumentQueryRequest) GetModelHash() string { @@ -6013,7 +6098,7 @@ type DocumentQueryBinding struct { func (x *DocumentQueryBinding) Reset() { *x = DocumentQueryBinding{} - mi := &file_sysml_proto_msgTypes[74] + mi := &file_sysml_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6025,7 +6110,7 @@ func (x *DocumentQueryBinding) String() string { func (*DocumentQueryBinding) ProtoMessage() {} func (x *DocumentQueryBinding) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[74] + mi := &file_sysml_proto_msgTypes[75] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6038,7 +6123,7 @@ func (x *DocumentQueryBinding) ProtoReflect() protoreflect.Message { // Deprecated: Use DocumentQueryBinding.ProtoReflect.Descriptor instead. func (*DocumentQueryBinding) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{74} + return file_sysml_proto_rawDescGZIP(), []int{75} } func (x *DocumentQueryBinding) GetParameter() string { @@ -6079,7 +6164,7 @@ type DocumentValue struct { func (x *DocumentValue) Reset() { *x = DocumentValue{} - mi := &file_sysml_proto_msgTypes[75] + mi := &file_sysml_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6091,7 +6176,7 @@ func (x *DocumentValue) String() string { func (*DocumentValue) ProtoMessage() {} func (x *DocumentValue) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[75] + mi := &file_sysml_proto_msgTypes[76] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6104,7 +6189,7 @@ func (x *DocumentValue) ProtoReflect() protoreflect.Message { // Deprecated: Use DocumentValue.ProtoReflect.Descriptor instead. func (*DocumentValue) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{75} + return file_sysml_proto_rawDescGZIP(), []int{76} } func (x *DocumentValue) GetKind() isDocumentValue_Kind { @@ -6240,7 +6325,7 @@ type DocumentQueryColumn struct { func (x *DocumentQueryColumn) Reset() { *x = DocumentQueryColumn{} - mi := &file_sysml_proto_msgTypes[76] + mi := &file_sysml_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6252,7 +6337,7 @@ func (x *DocumentQueryColumn) String() string { func (*DocumentQueryColumn) ProtoMessage() {} func (x *DocumentQueryColumn) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[76] + mi := &file_sysml_proto_msgTypes[77] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6265,7 +6350,7 @@ func (x *DocumentQueryColumn) ProtoReflect() protoreflect.Message { // Deprecated: Use DocumentQueryColumn.ProtoReflect.Descriptor instead. func (*DocumentQueryColumn) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{76} + return file_sysml_proto_rawDescGZIP(), []int{77} } func (x *DocumentQueryColumn) GetName() string { @@ -6285,7 +6370,7 @@ type DocumentQueryCell struct { func (x *DocumentQueryCell) Reset() { *x = DocumentQueryCell{} - mi := &file_sysml_proto_msgTypes[77] + mi := &file_sysml_proto_msgTypes[78] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6297,7 +6382,7 @@ func (x *DocumentQueryCell) String() string { func (*DocumentQueryCell) ProtoMessage() {} func (x *DocumentQueryCell) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[77] + mi := &file_sysml_proto_msgTypes[78] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6310,7 +6395,7 @@ func (x *DocumentQueryCell) ProtoReflect() protoreflect.Message { // Deprecated: Use DocumentQueryCell.ProtoReflect.Descriptor instead. func (*DocumentQueryCell) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{77} + return file_sysml_proto_rawDescGZIP(), []int{78} } func (x *DocumentQueryCell) GetValues() []*DocumentValue { @@ -6333,7 +6418,7 @@ type DocumentQueryRow struct { func (x *DocumentQueryRow) Reset() { *x = DocumentQueryRow{} - mi := &file_sysml_proto_msgTypes[78] + mi := &file_sysml_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6345,7 +6430,7 @@ func (x *DocumentQueryRow) String() string { func (*DocumentQueryRow) ProtoMessage() {} func (x *DocumentQueryRow) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[78] + mi := &file_sysml_proto_msgTypes[79] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6358,7 +6443,7 @@ func (x *DocumentQueryRow) ProtoReflect() protoreflect.Message { // Deprecated: Use DocumentQueryRow.ProtoReflect.Descriptor instead. func (*DocumentQueryRow) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{78} + return file_sysml_proto_rawDescGZIP(), []int{79} } func (x *DocumentQueryRow) GetElement() *DocumentValue { @@ -6389,7 +6474,7 @@ type RunDocumentQueryResponse struct { func (x *RunDocumentQueryResponse) Reset() { *x = RunDocumentQueryResponse{} - mi := &file_sysml_proto_msgTypes[79] + mi := &file_sysml_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6401,7 +6486,7 @@ func (x *RunDocumentQueryResponse) String() string { func (*RunDocumentQueryResponse) ProtoMessage() {} func (x *RunDocumentQueryResponse) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[79] + mi := &file_sysml_proto_msgTypes[80] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6414,7 +6499,7 @@ func (x *RunDocumentQueryResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RunDocumentQueryResponse.ProtoReflect.Descriptor instead. func (*RunDocumentQueryResponse) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{79} + return file_sysml_proto_rawDescGZIP(), []int{80} } func (x *RunDocumentQueryResponse) GetColumns() []*DocumentQueryColumn { @@ -6446,7 +6531,7 @@ type RenderDocumentRequest struct { func (x *RenderDocumentRequest) Reset() { *x = RenderDocumentRequest{} - mi := &file_sysml_proto_msgTypes[80] + mi := &file_sysml_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6458,7 +6543,7 @@ func (x *RenderDocumentRequest) String() string { func (*RenderDocumentRequest) ProtoMessage() {} func (x *RenderDocumentRequest) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[80] + mi := &file_sysml_proto_msgTypes[81] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6471,7 +6556,7 @@ func (x *RenderDocumentRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RenderDocumentRequest.ProtoReflect.Descriptor instead. func (*RenderDocumentRequest) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{80} + return file_sysml_proto_rawDescGZIP(), []int{81} } func (x *RenderDocumentRequest) GetModelHash() string { @@ -6499,7 +6584,7 @@ type RenderDocumentResponse struct { func (x *RenderDocumentResponse) Reset() { *x = RenderDocumentResponse{} - mi := &file_sysml_proto_msgTypes[81] + mi := &file_sysml_proto_msgTypes[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6511,7 +6596,7 @@ func (x *RenderDocumentResponse) String() string { func (*RenderDocumentResponse) ProtoMessage() {} func (x *RenderDocumentResponse) ProtoReflect() protoreflect.Message { - mi := &file_sysml_proto_msgTypes[81] + mi := &file_sysml_proto_msgTypes[82] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6524,7 +6609,7 @@ func (x *RenderDocumentResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RenderDocumentResponse.ProtoReflect.Descriptor instead. func (*RenderDocumentResponse) Descriptor() ([]byte, []int) { - return file_sysml_proto_rawDescGZIP(), []int{81} + return file_sysml_proto_rawDescGZIP(), []int{82} } func (x *RenderDocumentResponse) GetMarkdown() string { @@ -6832,7 +6917,7 @@ const file_sysml_proto_rawDesc = "" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n" + "\x04type\x18\x02 \x01(\tR\x04type\x12\"\n" + "\x05value\x18\x03 \x01(\v2\f.sysml.ValueR\x05value\x12\x12\n" + - "\x04unit\x18\x04 \x01(\tR\x04unit\"\x9f\x05\n" + + "\x04unit\x18\x04 \x01(\tR\x04unit\"\xce\x05\n" + "\x05Value\x12\x1d\n" + "\tint_value\x18\x01 \x01(\x03H\x00R\bintValue\x12\x1f\n" + "\n" + @@ -6853,8 +6938,12 @@ const file_sysml_proto_rawDesc = "" + "\x06vector\x18\r \x01(\v2\r.sysml.VectorH\x00R\x06vector\x12@\n" + "\x0fvector_quantity\x18\x0e \x01(\v2\x15.sysml.VectorQuantityH\x00R\x0evectorQuantity\x12@\n" + "\x0fmeasurement_ref\x18\x0f \x01(\v2\x15.sysml.MeasurementRefH\x00R\x0emeasurementRef\x12\x1c\n" + - "\binfinity\x18\x10 \x01(\bH\x00R\binfinityB\x06\n" + - "\x04kind\"Q\n" + + "\binfinity\x18\x10 \x01(\bH\x00R\binfinity\x12-\n" + + "\bfunction\x18\x11 \x01(\v2\x0f.sysml.FunctionH\x00R\bfunctionB\x06\n" + + "\x04kind\"<\n" + + "\bFunction\x12\x17\n" + + "\acalc_id\x18\x01 \x01(\tR\x06calcId\x12\x17\n" + + "\aself_id\x18\x02 \x01(\x03R\x06selfId\"Q\n" + "\x05Array\x12\x1e\n" + "\n" + "dimensions\x18\x01 \x03(\x03R\n" + @@ -7093,7 +7182,7 @@ func file_sysml_proto_rawDescGZIP() []byte { } var file_sysml_proto_enumTypes = make([]protoimpl.EnumInfo, 4) -var file_sysml_proto_msgTypes = make([]protoimpl.MessageInfo, 90) +var file_sysml_proto_msgTypes = make([]protoimpl.MessageInfo, 91) var file_sysml_proto_goTypes = []any{ (FailureReason)(0), // 0: sysml.FailureReason (EditFailure)(0), // 1: sysml.EditFailure @@ -7147,98 +7236,99 @@ var file_sysml_proto_goTypes = []any{ (*MultiplicityInfo)(nil), // 49: sysml.MultiplicityInfo (*AttributeInfo)(nil), // 50: sysml.AttributeInfo (*Value)(nil), // 51: sysml.Value - (*Array)(nil), // 52: sysml.Array - (*Vector)(nil), // 53: sysml.Vector - (*VectorQuantity)(nil), // 54: sysml.VectorQuantity - (*Complex)(nil), // 55: sysml.Complex - (*EnumLiteral)(nil), // 56: sysml.EnumLiteral - (*ValueSequence)(nil), // 57: sysml.ValueSequence - (*Quantity)(nil), // 58: sysml.Quantity - (*MeasurementRef)(nil), // 59: sysml.MeasurementRef - (*UnitTerm)(nil), // 60: sysml.UnitTerm - (*UnitFactor)(nil), // 61: sysml.UnitFactor - (*Diagnostic)(nil), // 62: sysml.Diagnostic - (*Span)(nil), // 63: sysml.Span - (*ServerInfoRequest)(nil), // 64: sysml.ServerInfoRequest - (*ServerInfoResponse)(nil), // 65: sysml.ServerInfoResponse - (*QueryRequest)(nil), // 66: sysml.QueryRequest - (*QueryResponse)(nil), // 67: sysml.QueryResponse - (*Query)(nil), // 68: sysml.Query - (*Constraint)(nil), // 69: sysml.Constraint - (*PrimitiveConstraint)(nil), // 70: sysml.PrimitiveConstraint - (*CompositeConstraint)(nil), // 71: sysml.CompositeConstraint - (*QueryResultElement)(nil), // 72: sysml.QueryResultElement - (*SweepRange)(nil), // 73: sysml.SweepRange - (*RunSweepRequest)(nil), // 74: sysml.RunSweepRequest - (*SweepRow)(nil), // 75: sysml.SweepRow - (*RunSweepResponse)(nil), // 76: sysml.RunSweepResponse - (*RunDocumentQueryRequest)(nil), // 77: sysml.RunDocumentQueryRequest - (*DocumentQueryBinding)(nil), // 78: sysml.DocumentQueryBinding - (*DocumentValue)(nil), // 79: sysml.DocumentValue - (*DocumentQueryColumn)(nil), // 80: sysml.DocumentQueryColumn - (*DocumentQueryCell)(nil), // 81: sysml.DocumentQueryCell - (*DocumentQueryRow)(nil), // 82: sysml.DocumentQueryRow - (*RunDocumentQueryResponse)(nil), // 83: sysml.RunDocumentQueryResponse - (*RenderDocumentRequest)(nil), // 84: sysml.RenderDocumentRequest - (*RenderDocumentResponse)(nil), // 85: sysml.RenderDocumentResponse - nil, // 86: sysml.RunAnalysisRequest.NamedArgumentsEntry - nil, // 87: sysml.Instance.FeatureValuesEntry - nil, // 88: sysml.ExecuteActionRequest.InputsEntry - nil, // 89: sysml.ExecuteActionResponse.OutputsEntry - nil, // 90: sysml.ExecuteStateResponse.FinalContextEntry - nil, // 91: sysml.SymbolInfo.MetadataEntry - nil, // 92: sysml.QueryResultElement.PropertiesEntry - nil, // 93: sysml.RunSweepRequest.NamedArgumentsEntry + (*Function)(nil), // 52: sysml.Function + (*Array)(nil), // 53: sysml.Array + (*Vector)(nil), // 54: sysml.Vector + (*VectorQuantity)(nil), // 55: sysml.VectorQuantity + (*Complex)(nil), // 56: sysml.Complex + (*EnumLiteral)(nil), // 57: sysml.EnumLiteral + (*ValueSequence)(nil), // 58: sysml.ValueSequence + (*Quantity)(nil), // 59: sysml.Quantity + (*MeasurementRef)(nil), // 60: sysml.MeasurementRef + (*UnitTerm)(nil), // 61: sysml.UnitTerm + (*UnitFactor)(nil), // 62: sysml.UnitFactor + (*Diagnostic)(nil), // 63: sysml.Diagnostic + (*Span)(nil), // 64: sysml.Span + (*ServerInfoRequest)(nil), // 65: sysml.ServerInfoRequest + (*ServerInfoResponse)(nil), // 66: sysml.ServerInfoResponse + (*QueryRequest)(nil), // 67: sysml.QueryRequest + (*QueryResponse)(nil), // 68: sysml.QueryResponse + (*Query)(nil), // 69: sysml.Query + (*Constraint)(nil), // 70: sysml.Constraint + (*PrimitiveConstraint)(nil), // 71: sysml.PrimitiveConstraint + (*CompositeConstraint)(nil), // 72: sysml.CompositeConstraint + (*QueryResultElement)(nil), // 73: sysml.QueryResultElement + (*SweepRange)(nil), // 74: sysml.SweepRange + (*RunSweepRequest)(nil), // 75: sysml.RunSweepRequest + (*SweepRow)(nil), // 76: sysml.SweepRow + (*RunSweepResponse)(nil), // 77: sysml.RunSweepResponse + (*RunDocumentQueryRequest)(nil), // 78: sysml.RunDocumentQueryRequest + (*DocumentQueryBinding)(nil), // 79: sysml.DocumentQueryBinding + (*DocumentValue)(nil), // 80: sysml.DocumentValue + (*DocumentQueryColumn)(nil), // 81: sysml.DocumentQueryColumn + (*DocumentQueryCell)(nil), // 82: sysml.DocumentQueryCell + (*DocumentQueryRow)(nil), // 83: sysml.DocumentQueryRow + (*RunDocumentQueryResponse)(nil), // 84: sysml.RunDocumentQueryResponse + (*RenderDocumentRequest)(nil), // 85: sysml.RenderDocumentRequest + (*RenderDocumentResponse)(nil), // 86: sysml.RenderDocumentResponse + nil, // 87: sysml.RunAnalysisRequest.NamedArgumentsEntry + nil, // 88: sysml.Instance.FeatureValuesEntry + nil, // 89: sysml.ExecuteActionRequest.InputsEntry + nil, // 90: sysml.ExecuteActionResponse.OutputsEntry + nil, // 91: sysml.ExecuteStateResponse.FinalContextEntry + nil, // 92: sysml.SymbolInfo.MetadataEntry + nil, // 93: sysml.QueryResultElement.PropertiesEntry + nil, // 94: sysml.RunSweepRequest.NamedArgumentsEntry } var file_sysml_proto_depIdxs = []int32{ 0, // 0: sysml.Verdict.failure_reason:type_name -> sysml.FailureReason 4, // 1: sysml.VerifyConstraintResponse.verdict:type_name -> sysml.Verdict 28, // 2: sysml.VerifyConstraintResponse.instances:type_name -> sysml.Instance - 62, // 3: sysml.VerifyConstraintResponse.diagnostics:type_name -> sysml.Diagnostic + 63, // 3: sysml.VerifyConstraintResponse.diagnostics:type_name -> sysml.Diagnostic 4, // 4: sysml.VerifyRequirementResponse.verdict:type_name -> sysml.Verdict 28, // 5: sysml.VerifyRequirementResponse.instances:type_name -> sysml.Instance - 62, // 6: sysml.VerifyRequirementResponse.diagnostics:type_name -> sysml.Diagnostic + 63, // 6: sysml.VerifyRequirementResponse.diagnostics:type_name -> sysml.Diagnostic 8, // 7: sysml.VerifyRequirementResponse.verification_verdicts:type_name -> sysml.VerificationVerdict 4, // 8: sysml.VerifySatisfactionResponse.verdicts:type_name -> sysml.Verdict 28, // 9: sysml.VerifySatisfactionResponse.instances:type_name -> sysml.Instance - 62, // 10: sysml.VerifySatisfactionResponse.diagnostics:type_name -> sysml.Diagnostic + 63, // 10: sysml.VerifySatisfactionResponse.diagnostics:type_name -> sysml.Diagnostic 0, // 11: sysml.VerifySatisfactionResponse.failure_reason:type_name -> sysml.FailureReason 8, // 12: sysml.VerifySatisfactionResponse.verification_verdicts:type_name -> sysml.VerificationVerdict 51, // 13: sysml.EvaluateCalcRequest.arguments:type_name -> sysml.Value 51, // 14: sysml.EvaluateCalcResponse.result:type_name -> sysml.Value 14, // 15: sysml.EvaluateCalcResponse.outputs:type_name -> sysml.CalcOutput - 62, // 16: sysml.EvaluateCalcResponse.diagnostics:type_name -> sysml.Diagnostic + 63, // 16: sysml.EvaluateCalcResponse.diagnostics:type_name -> sysml.Diagnostic 0, // 17: sysml.EvaluateCalcResponse.failure_reason:type_name -> sysml.FailureReason 51, // 18: sysml.CalcOutput.value:type_name -> sysml.Value 51, // 19: sysml.RunAnalysisRequest.arguments:type_name -> sysml.Value - 86, // 20: sysml.RunAnalysisRequest.named_arguments:type_name -> sysml.RunAnalysisRequest.NamedArgumentsEntry + 87, // 20: sysml.RunAnalysisRequest.named_arguments:type_name -> sysml.RunAnalysisRequest.NamedArgumentsEntry 14, // 21: sysml.RunAnalysisResponse.outputs:type_name -> sysml.CalcOutput 4, // 22: sysml.RunAnalysisResponse.verdicts:type_name -> sysml.Verdict 28, // 23: sysml.RunAnalysisResponse.instances:type_name -> sysml.Instance - 62, // 24: sysml.RunAnalysisResponse.diagnostics:type_name -> sysml.Diagnostic + 63, // 24: sysml.RunAnalysisResponse.diagnostics:type_name -> sysml.Diagnostic 0, // 25: sysml.RunAnalysisResponse.failure_reason:type_name -> sysml.FailureReason 8, // 26: sysml.RunAnalysisResponse.verification_verdicts:type_name -> sysml.VerificationVerdict 18, // 27: sysml.ParseSourcesRequest.documents:type_name -> sysml.SourceDocument 46, // 28: sysml.ParseSourcesResponse.roots:type_name -> sysml.SymbolInfo - 62, // 29: sysml.ParseSourcesResponse.diagnostics:type_name -> sysml.Diagnostic + 63, // 29: sysml.ParseSourcesResponse.diagnostics:type_name -> sysml.Diagnostic 46, // 30: sysml.ParseFileResponse.root:type_name -> sysml.SymbolInfo - 62, // 31: sysml.ParseFileResponse.diagnostics:type_name -> sysml.Diagnostic + 63, // 31: sysml.ParseFileResponse.diagnostics:type_name -> sysml.Diagnostic 46, // 32: sysml.SymbolResponse.symbol:type_name -> sysml.SymbolInfo - 62, // 33: sysml.DiagnosticsResponse.diagnostics:type_name -> sysml.Diagnostic + 63, // 33: sysml.DiagnosticsResponse.diagnostics:type_name -> sysml.Diagnostic 51, // 34: sysml.EvaluateResponse.result:type_name -> sysml.Value - 62, // 35: sysml.EvaluateResponse.diagnostics:type_name -> sysml.Diagnostic - 87, // 36: sysml.Instance.feature_values:type_name -> sysml.Instance.FeatureValuesEntry + 63, // 35: sysml.EvaluateResponse.diagnostics:type_name -> sysml.Diagnostic + 88, // 36: sysml.Instance.feature_values:type_name -> sysml.Instance.FeatureValuesEntry 51, // 37: sysml.FeatureValue.value:type_name -> sysml.Value 51, // 38: sysml.FeatureValue.values:type_name -> sysml.Value 28, // 39: sysml.InstantiateResponse.instance:type_name -> sysml.Instance - 62, // 40: sysml.InstantiateResponse.diagnostics:type_name -> sysml.Diagnostic + 63, // 40: sysml.InstantiateResponse.diagnostics:type_name -> sysml.Diagnostic 28, // 41: sysml.InstantiateResponse.instances:type_name -> sysml.Instance - 88, // 42: sysml.ExecuteActionRequest.inputs:type_name -> sysml.ExecuteActionRequest.InputsEntry - 89, // 43: sysml.ExecuteActionResponse.outputs:type_name -> sysml.ExecuteActionResponse.OutputsEntry - 62, // 44: sysml.ExecuteActionResponse.diagnostics:type_name -> sysml.Diagnostic - 90, // 45: sysml.ExecuteStateResponse.final_context:type_name -> sysml.ExecuteStateResponse.FinalContextEntry - 62, // 46: sysml.ExecuteStateResponse.diagnostics:type_name -> sysml.Diagnostic - 62, // 47: sysml.ConvertResponse.diagnostics:type_name -> sysml.Diagnostic + 89, // 42: sysml.ExecuteActionRequest.inputs:type_name -> sysml.ExecuteActionRequest.InputsEntry + 90, // 43: sysml.ExecuteActionResponse.outputs:type_name -> sysml.ExecuteActionResponse.OutputsEntry + 63, // 44: sysml.ExecuteActionResponse.diagnostics:type_name -> sysml.Diagnostic + 91, // 45: sysml.ExecuteStateResponse.final_context:type_name -> sysml.ExecuteStateResponse.FinalContextEntry + 63, // 46: sysml.ExecuteStateResponse.diagnostics:type_name -> sysml.Diagnostic + 63, // 47: sysml.ConvertResponse.diagnostics:type_name -> sysml.Diagnostic 39, // 48: sysml.ApplyEditsRequest.operations:type_name -> sysml.EditOperation 42, // 49: sysml.EditOperation.set_value:type_name -> sysml.SetValueEdit 43, // 50: sysml.EditOperation.rename:type_name -> sysml.RenameEdit @@ -7246,111 +7336,112 @@ var file_sysml_proto_depIdxs = []int32{ 41, // 52: sysml.EditOperation.delete:type_name -> sysml.DeleteEdit 45, // 53: sysml.ApplyEditsResponse.applied:type_name -> sysml.AppliedEdit 1, // 54: sysml.ApplyEditsResponse.failure:type_name -> sysml.EditFailure - 62, // 55: sysml.ApplyEditsResponse.diagnostics:type_name -> sysml.Diagnostic - 91, // 56: sysml.SymbolInfo.metadata:type_name -> sysml.SymbolInfo.MetadataEntry + 63, // 55: sysml.ApplyEditsResponse.diagnostics:type_name -> sysml.Diagnostic + 92, // 56: sysml.SymbolInfo.metadata:type_name -> sysml.SymbolInfo.MetadataEntry 50, // 57: sysml.SymbolInfo.attributes:type_name -> sysml.AttributeInfo 48, // 58: sysml.SymbolInfo.type_info:type_name -> sysml.TypeInfo 49, // 59: sysml.SymbolInfo.multiplicity:type_name -> sysml.MultiplicityInfo 47, // 60: sysml.SymbolInfo.specializations:type_name -> sysml.Specialization 51, // 61: sysml.AttributeInfo.value:type_name -> sysml.Value - 57, // 62: sysml.Value.sequence:type_name -> sysml.ValueSequence - 58, // 63: sysml.Value.quantity:type_name -> sysml.Quantity - 56, // 64: sysml.Value.enum_literal:type_name -> sysml.EnumLiteral - 55, // 65: sysml.Value.complex:type_name -> sysml.Complex - 52, // 66: sysml.Value.array:type_name -> sysml.Array - 53, // 67: sysml.Value.vector:type_name -> sysml.Vector - 54, // 68: sysml.Value.vector_quantity:type_name -> sysml.VectorQuantity - 59, // 69: sysml.Value.measurement_ref:type_name -> sysml.MeasurementRef - 51, // 70: sysml.Array.elements:type_name -> sysml.Value - 51, // 71: sysml.Vector.components:type_name -> sysml.Value - 58, // 72: sysml.VectorQuantity.components:type_name -> sysml.Quantity - 51, // 73: sysml.ValueSequence.elements:type_name -> sysml.Value - 60, // 74: sysml.Quantity.unit_term:type_name -> sysml.UnitTerm - 60, // 75: sysml.MeasurementRef.unit_term:type_name -> sysml.UnitTerm - 61, // 76: sysml.UnitTerm.factors:type_name -> sysml.UnitFactor - 63, // 77: sysml.Diagnostic.span:type_name -> sysml.Span - 68, // 78: sysml.QueryRequest.query:type_name -> sysml.Query - 72, // 79: sysml.QueryResponse.elements:type_name -> sysml.QueryResultElement - 69, // 80: sysml.Query.where:type_name -> sysml.Constraint - 70, // 81: sysml.Constraint.primitive:type_name -> sysml.PrimitiveConstraint - 71, // 82: sysml.Constraint.composite:type_name -> sysml.CompositeConstraint - 2, // 83: sysml.PrimitiveConstraint.operator:type_name -> sysml.PrimitiveOperator - 3, // 84: sysml.CompositeConstraint.operator:type_name -> sysml.CompositeOperator - 69, // 85: sysml.CompositeConstraint.constraint:type_name -> sysml.Constraint - 92, // 86: sysml.QueryResultElement.properties:type_name -> sysml.QueryResultElement.PropertiesEntry - 51, // 87: sysml.SweepRange.start:type_name -> sysml.Value - 51, // 88: sysml.SweepRange.end:type_name -> sysml.Value - 51, // 89: sysml.SweepRange.step:type_name -> sysml.Value - 51, // 90: sysml.RunSweepRequest.arguments:type_name -> sysml.Value - 93, // 91: sysml.RunSweepRequest.named_arguments:type_name -> sysml.RunSweepRequest.NamedArgumentsEntry - 73, // 92: sysml.RunSweepRequest.ranges:type_name -> sysml.SweepRange - 14, // 93: sysml.SweepRow.inputs:type_name -> sysml.CalcOutput - 14, // 94: sysml.SweepRow.outputs:type_name -> sysml.CalcOutput - 4, // 95: sysml.SweepRow.verdicts:type_name -> sysml.Verdict - 0, // 96: sysml.SweepRow.failure_reason:type_name -> sysml.FailureReason - 75, // 97: sysml.RunSweepResponse.rows:type_name -> sysml.SweepRow - 62, // 98: sysml.RunSweepResponse.diagnostics:type_name -> sysml.Diagnostic - 0, // 99: sysml.RunSweepResponse.failure_reason:type_name -> sysml.FailureReason - 28, // 100: sysml.RunSweepResponse.instances:type_name -> sysml.Instance - 78, // 101: sysml.RunDocumentQueryRequest.bindings:type_name -> sysml.DocumentQueryBinding - 79, // 102: sysml.DocumentQueryBinding.values:type_name -> sysml.DocumentValue - 58, // 103: sysml.DocumentValue.quantity:type_name -> sysml.Quantity - 79, // 104: sysml.DocumentQueryCell.values:type_name -> sysml.DocumentValue - 79, // 105: sysml.DocumentQueryRow.element:type_name -> sysml.DocumentValue - 81, // 106: sysml.DocumentQueryRow.cells:type_name -> sysml.DocumentQueryCell - 80, // 107: sysml.RunDocumentQueryResponse.columns:type_name -> sysml.DocumentQueryColumn - 82, // 108: sysml.RunDocumentQueryResponse.rows:type_name -> sysml.DocumentQueryRow - 51, // 109: sysml.RunAnalysisRequest.NamedArgumentsEntry.value:type_name -> sysml.Value - 29, // 110: sysml.Instance.FeatureValuesEntry.value:type_name -> sysml.FeatureValue - 51, // 111: sysml.ExecuteActionRequest.InputsEntry.value:type_name -> sysml.Value - 51, // 112: sysml.ExecuteActionResponse.OutputsEntry.value:type_name -> sysml.Value - 51, // 113: sysml.ExecuteStateResponse.FinalContextEntry.value:type_name -> sysml.Value - 51, // 114: sysml.RunSweepRequest.NamedArgumentsEntry.value:type_name -> sysml.Value - 64, // 115: sysml.SysMLService.GetServerInfo:input_type -> sysml.ServerInfoRequest - 17, // 116: sysml.SysMLService.ParseFile:input_type -> sysml.ParseFileRequest - 19, // 117: sysml.SysMLService.ParseSources:input_type -> sysml.ParseSourcesRequest - 22, // 118: sysml.SysMLService.GetSymbol:input_type -> sysml.GetSymbolRequest - 24, // 119: sysml.SysMLService.GetDiagnostics:input_type -> sysml.DiagnosticsRequest - 26, // 120: sysml.SysMLService.Evaluate:input_type -> sysml.EvaluateRequest - 30, // 121: sysml.SysMLService.Instantiate:input_type -> sysml.InstantiateRequest - 32, // 122: sysml.SysMLService.ExecuteAction:input_type -> sysml.ExecuteActionRequest - 34, // 123: sysml.SysMLService.ExecuteState:input_type -> sysml.ExecuteStateRequest - 36, // 124: sysml.SysMLService.Convert:input_type -> sysml.ConvertRequest - 38, // 125: sysml.SysMLService.ApplyEdits:input_type -> sysml.ApplyEditsRequest - 5, // 126: sysml.SysMLService.VerifyConstraint:input_type -> sysml.VerifyConstraintRequest - 7, // 127: sysml.SysMLService.VerifyRequirement:input_type -> sysml.VerifyRequirementRequest - 10, // 128: sysml.SysMLService.VerifySatisfaction:input_type -> sysml.VerifySatisfactionRequest - 12, // 129: sysml.SysMLService.EvaluateCalc:input_type -> sysml.EvaluateCalcRequest - 15, // 130: sysml.SysMLService.RunAnalysis:input_type -> sysml.RunAnalysisRequest - 74, // 131: sysml.SysMLService.RunSweep:input_type -> sysml.RunSweepRequest - 66, // 132: sysml.SysMLService.Query:input_type -> sysml.QueryRequest - 77, // 133: sysml.SysMLService.RunDocumentQuery:input_type -> sysml.RunDocumentQueryRequest - 84, // 134: sysml.SysMLService.RenderDocument:input_type -> sysml.RenderDocumentRequest - 65, // 135: sysml.SysMLService.GetServerInfo:output_type -> sysml.ServerInfoResponse - 21, // 136: sysml.SysMLService.ParseFile:output_type -> sysml.ParseFileResponse - 20, // 137: sysml.SysMLService.ParseSources:output_type -> sysml.ParseSourcesResponse - 23, // 138: sysml.SysMLService.GetSymbol:output_type -> sysml.SymbolResponse - 25, // 139: sysml.SysMLService.GetDiagnostics:output_type -> sysml.DiagnosticsResponse - 27, // 140: sysml.SysMLService.Evaluate:output_type -> sysml.EvaluateResponse - 31, // 141: sysml.SysMLService.Instantiate:output_type -> sysml.InstantiateResponse - 33, // 142: sysml.SysMLService.ExecuteAction:output_type -> sysml.ExecuteActionResponse - 35, // 143: sysml.SysMLService.ExecuteState:output_type -> sysml.ExecuteStateResponse - 37, // 144: sysml.SysMLService.Convert:output_type -> sysml.ConvertResponse - 44, // 145: sysml.SysMLService.ApplyEdits:output_type -> sysml.ApplyEditsResponse - 6, // 146: sysml.SysMLService.VerifyConstraint:output_type -> sysml.VerifyConstraintResponse - 9, // 147: sysml.SysMLService.VerifyRequirement:output_type -> sysml.VerifyRequirementResponse - 11, // 148: sysml.SysMLService.VerifySatisfaction:output_type -> sysml.VerifySatisfactionResponse - 13, // 149: sysml.SysMLService.EvaluateCalc:output_type -> sysml.EvaluateCalcResponse - 16, // 150: sysml.SysMLService.RunAnalysis:output_type -> sysml.RunAnalysisResponse - 76, // 151: sysml.SysMLService.RunSweep:output_type -> sysml.RunSweepResponse - 67, // 152: sysml.SysMLService.Query:output_type -> sysml.QueryResponse - 83, // 153: sysml.SysMLService.RunDocumentQuery:output_type -> sysml.RunDocumentQueryResponse - 85, // 154: sysml.SysMLService.RenderDocument:output_type -> sysml.RenderDocumentResponse - 135, // [135:155] is the sub-list for method output_type - 115, // [115:135] is the sub-list for method input_type - 115, // [115:115] is the sub-list for extension type_name - 115, // [115:115] is the sub-list for extension extendee - 0, // [0:115] is the sub-list for field type_name + 58, // 62: sysml.Value.sequence:type_name -> sysml.ValueSequence + 59, // 63: sysml.Value.quantity:type_name -> sysml.Quantity + 57, // 64: sysml.Value.enum_literal:type_name -> sysml.EnumLiteral + 56, // 65: sysml.Value.complex:type_name -> sysml.Complex + 53, // 66: sysml.Value.array:type_name -> sysml.Array + 54, // 67: sysml.Value.vector:type_name -> sysml.Vector + 55, // 68: sysml.Value.vector_quantity:type_name -> sysml.VectorQuantity + 60, // 69: sysml.Value.measurement_ref:type_name -> sysml.MeasurementRef + 52, // 70: sysml.Value.function:type_name -> sysml.Function + 51, // 71: sysml.Array.elements:type_name -> sysml.Value + 51, // 72: sysml.Vector.components:type_name -> sysml.Value + 59, // 73: sysml.VectorQuantity.components:type_name -> sysml.Quantity + 51, // 74: sysml.ValueSequence.elements:type_name -> sysml.Value + 61, // 75: sysml.Quantity.unit_term:type_name -> sysml.UnitTerm + 61, // 76: sysml.MeasurementRef.unit_term:type_name -> sysml.UnitTerm + 62, // 77: sysml.UnitTerm.factors:type_name -> sysml.UnitFactor + 64, // 78: sysml.Diagnostic.span:type_name -> sysml.Span + 69, // 79: sysml.QueryRequest.query:type_name -> sysml.Query + 73, // 80: sysml.QueryResponse.elements:type_name -> sysml.QueryResultElement + 70, // 81: sysml.Query.where:type_name -> sysml.Constraint + 71, // 82: sysml.Constraint.primitive:type_name -> sysml.PrimitiveConstraint + 72, // 83: sysml.Constraint.composite:type_name -> sysml.CompositeConstraint + 2, // 84: sysml.PrimitiveConstraint.operator:type_name -> sysml.PrimitiveOperator + 3, // 85: sysml.CompositeConstraint.operator:type_name -> sysml.CompositeOperator + 70, // 86: sysml.CompositeConstraint.constraint:type_name -> sysml.Constraint + 93, // 87: sysml.QueryResultElement.properties:type_name -> sysml.QueryResultElement.PropertiesEntry + 51, // 88: sysml.SweepRange.start:type_name -> sysml.Value + 51, // 89: sysml.SweepRange.end:type_name -> sysml.Value + 51, // 90: sysml.SweepRange.step:type_name -> sysml.Value + 51, // 91: sysml.RunSweepRequest.arguments:type_name -> sysml.Value + 94, // 92: sysml.RunSweepRequest.named_arguments:type_name -> sysml.RunSweepRequest.NamedArgumentsEntry + 74, // 93: sysml.RunSweepRequest.ranges:type_name -> sysml.SweepRange + 14, // 94: sysml.SweepRow.inputs:type_name -> sysml.CalcOutput + 14, // 95: sysml.SweepRow.outputs:type_name -> sysml.CalcOutput + 4, // 96: sysml.SweepRow.verdicts:type_name -> sysml.Verdict + 0, // 97: sysml.SweepRow.failure_reason:type_name -> sysml.FailureReason + 76, // 98: sysml.RunSweepResponse.rows:type_name -> sysml.SweepRow + 63, // 99: sysml.RunSweepResponse.diagnostics:type_name -> sysml.Diagnostic + 0, // 100: sysml.RunSweepResponse.failure_reason:type_name -> sysml.FailureReason + 28, // 101: sysml.RunSweepResponse.instances:type_name -> sysml.Instance + 79, // 102: sysml.RunDocumentQueryRequest.bindings:type_name -> sysml.DocumentQueryBinding + 80, // 103: sysml.DocumentQueryBinding.values:type_name -> sysml.DocumentValue + 59, // 104: sysml.DocumentValue.quantity:type_name -> sysml.Quantity + 80, // 105: sysml.DocumentQueryCell.values:type_name -> sysml.DocumentValue + 80, // 106: sysml.DocumentQueryRow.element:type_name -> sysml.DocumentValue + 82, // 107: sysml.DocumentQueryRow.cells:type_name -> sysml.DocumentQueryCell + 81, // 108: sysml.RunDocumentQueryResponse.columns:type_name -> sysml.DocumentQueryColumn + 83, // 109: sysml.RunDocumentQueryResponse.rows:type_name -> sysml.DocumentQueryRow + 51, // 110: sysml.RunAnalysisRequest.NamedArgumentsEntry.value:type_name -> sysml.Value + 29, // 111: sysml.Instance.FeatureValuesEntry.value:type_name -> sysml.FeatureValue + 51, // 112: sysml.ExecuteActionRequest.InputsEntry.value:type_name -> sysml.Value + 51, // 113: sysml.ExecuteActionResponse.OutputsEntry.value:type_name -> sysml.Value + 51, // 114: sysml.ExecuteStateResponse.FinalContextEntry.value:type_name -> sysml.Value + 51, // 115: sysml.RunSweepRequest.NamedArgumentsEntry.value:type_name -> sysml.Value + 65, // 116: sysml.SysMLService.GetServerInfo:input_type -> sysml.ServerInfoRequest + 17, // 117: sysml.SysMLService.ParseFile:input_type -> sysml.ParseFileRequest + 19, // 118: sysml.SysMLService.ParseSources:input_type -> sysml.ParseSourcesRequest + 22, // 119: sysml.SysMLService.GetSymbol:input_type -> sysml.GetSymbolRequest + 24, // 120: sysml.SysMLService.GetDiagnostics:input_type -> sysml.DiagnosticsRequest + 26, // 121: sysml.SysMLService.Evaluate:input_type -> sysml.EvaluateRequest + 30, // 122: sysml.SysMLService.Instantiate:input_type -> sysml.InstantiateRequest + 32, // 123: sysml.SysMLService.ExecuteAction:input_type -> sysml.ExecuteActionRequest + 34, // 124: sysml.SysMLService.ExecuteState:input_type -> sysml.ExecuteStateRequest + 36, // 125: sysml.SysMLService.Convert:input_type -> sysml.ConvertRequest + 38, // 126: sysml.SysMLService.ApplyEdits:input_type -> sysml.ApplyEditsRequest + 5, // 127: sysml.SysMLService.VerifyConstraint:input_type -> sysml.VerifyConstraintRequest + 7, // 128: sysml.SysMLService.VerifyRequirement:input_type -> sysml.VerifyRequirementRequest + 10, // 129: sysml.SysMLService.VerifySatisfaction:input_type -> sysml.VerifySatisfactionRequest + 12, // 130: sysml.SysMLService.EvaluateCalc:input_type -> sysml.EvaluateCalcRequest + 15, // 131: sysml.SysMLService.RunAnalysis:input_type -> sysml.RunAnalysisRequest + 75, // 132: sysml.SysMLService.RunSweep:input_type -> sysml.RunSweepRequest + 67, // 133: sysml.SysMLService.Query:input_type -> sysml.QueryRequest + 78, // 134: sysml.SysMLService.RunDocumentQuery:input_type -> sysml.RunDocumentQueryRequest + 85, // 135: sysml.SysMLService.RenderDocument:input_type -> sysml.RenderDocumentRequest + 66, // 136: sysml.SysMLService.GetServerInfo:output_type -> sysml.ServerInfoResponse + 21, // 137: sysml.SysMLService.ParseFile:output_type -> sysml.ParseFileResponse + 20, // 138: sysml.SysMLService.ParseSources:output_type -> sysml.ParseSourcesResponse + 23, // 139: sysml.SysMLService.GetSymbol:output_type -> sysml.SymbolResponse + 25, // 140: sysml.SysMLService.GetDiagnostics:output_type -> sysml.DiagnosticsResponse + 27, // 141: sysml.SysMLService.Evaluate:output_type -> sysml.EvaluateResponse + 31, // 142: sysml.SysMLService.Instantiate:output_type -> sysml.InstantiateResponse + 33, // 143: sysml.SysMLService.ExecuteAction:output_type -> sysml.ExecuteActionResponse + 35, // 144: sysml.SysMLService.ExecuteState:output_type -> sysml.ExecuteStateResponse + 37, // 145: sysml.SysMLService.Convert:output_type -> sysml.ConvertResponse + 44, // 146: sysml.SysMLService.ApplyEdits:output_type -> sysml.ApplyEditsResponse + 6, // 147: sysml.SysMLService.VerifyConstraint:output_type -> sysml.VerifyConstraintResponse + 9, // 148: sysml.SysMLService.VerifyRequirement:output_type -> sysml.VerifyRequirementResponse + 11, // 149: sysml.SysMLService.VerifySatisfaction:output_type -> sysml.VerifySatisfactionResponse + 13, // 150: sysml.SysMLService.EvaluateCalc:output_type -> sysml.EvaluateCalcResponse + 16, // 151: sysml.SysMLService.RunAnalysis:output_type -> sysml.RunAnalysisResponse + 77, // 152: sysml.SysMLService.RunSweep:output_type -> sysml.RunSweepResponse + 68, // 153: sysml.SysMLService.Query:output_type -> sysml.QueryResponse + 84, // 154: sysml.SysMLService.RunDocumentQuery:output_type -> sysml.RunDocumentQueryResponse + 86, // 155: sysml.SysMLService.RenderDocument:output_type -> sysml.RenderDocumentResponse + 136, // [136:156] is the sub-list for method output_type + 116, // [116:136] is the sub-list for method input_type + 116, // [116:116] is the sub-list for extension type_name + 116, // [116:116] is the sub-list for extension extendee + 0, // [0:116] is the sub-list for field type_name } func init() { file_sysml_proto_init() } @@ -7394,16 +7485,17 @@ func file_sysml_proto_init() { (*Value_VectorQuantity)(nil), (*Value_MeasurementRef)(nil), (*Value_Infinity)(nil), + (*Value_Function)(nil), } - file_sysml_proto_msgTypes[54].OneofWrappers = []any{ + file_sysml_proto_msgTypes[55].OneofWrappers = []any{ (*Quantity_IntMagnitude)(nil), (*Quantity_RealMagnitude)(nil), } - file_sysml_proto_msgTypes[65].OneofWrappers = []any{ + file_sysml_proto_msgTypes[66].OneofWrappers = []any{ (*Constraint_Primitive)(nil), (*Constraint_Composite)(nil), } - file_sysml_proto_msgTypes[75].OneofWrappers = []any{ + file_sysml_proto_msgTypes[76].OneofWrappers = []any{ (*DocumentValue_ElementId)(nil), (*DocumentValue_StringValue)(nil), (*DocumentValue_IntValue)(nil), @@ -7418,7 +7510,7 @@ func file_sysml_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_sysml_proto_rawDesc), len(file_sysml_proto_rawDesc)), NumEnums: 4, - NumMessages: 90, + NumMessages: 91, NumExtensions: 0, NumServices: 1, }, diff --git a/api/proto/sysml.proto b/api/proto/sysml.proto index 9ff7359ae..ecd20bd8a 100644 --- a/api/proto/sysml.proto +++ b/api/proto/sysml.proto @@ -704,9 +704,27 @@ message Value { // every finite magnitude and refused by arithmetic. Always true when set, // as DocumentValue.infinity is. bool infinity = 16; + Function function = 17; // a calc as a value, named by its declaration } } +// Function is a calc held as a value: a calc definition, or a calc usage with +// an input no read could supply, as `Sq` in `Fn(Sq, 3.0)` or the `f` of +// `in calc f {...}`. It crosses as the declaration it is a value of, which is +// its identity: two functions are the same exactly when calc_id and self_id +// are. A function closing over the bindings of the behavior body it is +// declared in has no wire form and crosses as the null arm. +message Function { + // FQN of the calc declaration ("Analysis::Sq"). Its identity. + string calc_id = 1; + // ID of the object the calc's feature names resolve against, for a calc + // usage read off a part (`holder.scale`); 0 for a function closing over no + // object. An instance id, living only within the response that sent it: a + // request function with a non-zero self_id is rejected, since no later call + // holds that object. + int64 self_id = 2; +} + // Array is a Collections::Array: its elements flattened in row-major order // under its dimensions, compared by content rather than by the object read. message Array { @@ -882,6 +900,11 @@ message ServerInfoResponse { // refused with UNIMPLEMENTED rather than read as another // value. Separate from structured_values, which a client // built before this arm existed may already claim. + // "function_values" - a Value carries a calc held as a value as function, + // named by its declaration, rather than reporting it as an + // unsupported null, and one is accepted as an action input + // or calc argument; without it, one is refused with + // UNIMPLEMENTED rather than read as another value. // "apply_edits" - the ApplyEdits RPC edits a parsed model's own source, // preserving everything the edit did not touch. // "document_query" - the RunDocumentQuery RPC runs a named document query diff --git a/changes/unreleased/function-values.added.md b/changes/unreleased/function-values.added.md new file mode 100644 index 000000000..f7235facb --- /dev/null +++ b/changes/unreleased/function-values.added.md @@ -0,0 +1,2 @@ +- **A calc is a value.** A calc definition, a calc usage awaiting an input, or an `in calc` parameter named where a value is expected is a function value: the calc together with the scope and object it was read in, invoked through a calc-typed parameter (`calc def Fn { in calc f { in v : Real; return : Real; } in a : Real; return : Real = f(a); }` makes `Fn(Sq, 3.0)` `9.0`), passed positionally or by name, read off a part, returned from a calc, compared and adopted. `SampledFunctions::Sample` now samples a user calc, and a library function the runtime implements (`RealFunctions::sqrt`) is a value too. A calc declared in a behavior body closes over the innermost active run of that behavior alone — never a caller's parameters, and nothing when no such run is active. Calling a non-function, an arity mismatch and an unbound calc parameter are typed errors; `in calc` parameters parse in action bodies as they do in calc bodies. A call through a feature chain (`holder.scale(3.0)`) is now checked statically as a direct call is — arguments against the calc feature's inputs, the result against the declared type it binds to — applies the calc even when defaults supply every input or it has none (`holder.scaled()`, where the bare read `holder.scaled` computes its result), and a typed feature valued by such a call (`item t : Tallied = picker.pick(lead, trail)`) classifies the argument the calc returns as one valued by a direct call does. +- **Function values cross the API.** `Value.function` carries the calc's qualified name and the id of the object it was read off, under the new `function_values` capability, which the Go, Python, Node, Rust and Java clients expose as a typed value and refuse to send to a service without the capability. A function closing over a behavior body's bindings crosses as an unsupported null, since no name reconstructs it; one read off an object is refused as an argument to a later call, since that object lived only within the response that sent it. Native compilation refuses a calc that binds or applies a function value with a typed error. diff --git a/client/opensysml/README.md b/client/opensysml/README.md index 4f533e3f5..e62d11423 100644 --- a/client/opensysml/README.md +++ b/client/opensysml/README.md @@ -202,13 +202,14 @@ answering implementation supports, and `ServerInfo.Has` checks one. A request that asks for an unavailable capability is refused with `CodeUnimplemented`; capabilities that describe response population instead omit the fields they name. Check the list first for an operation-specific error (the `Capability*` -constants name the known ones). Three capabilities are checked for you: a `Complex` +constants name the known ones). Four capabilities are checked for you: a `Complex` among `ExecuteAction` inputs or `EvaluateCalc`/`RunAnalysis` arguments needs `complex_values`, an `Array`, `Vector` or `VectorQuantity` needs -`structured_values`, and a `MeasurementRef` needs `measurement_refs` — each at -the top level or nested in a sequence or array; a service without them would read -the value as null, so the client refuses with `CodeUnimplemented` before sending -anything. +`structured_values`, a `MeasurementRef` needs `measurement_refs`, and a `Function` +(a calc held as a value, sent back to bind a calc-typed parameter) needs +`function_values` — each at the top level or nested in a sequence or array; a +service without them would read the value as null, so the client refuses with +`CodeUnimplemented` before sending anything. ## Stability diff --git a/client/opensysml/client.go b/client/opensysml/client.go index 9f5339c5d..e2227073f 100644 --- a/client/opensysml/client.go +++ b/client/opensysml/client.go @@ -62,8 +62,9 @@ type Client interface { // ExecuteAction executes the named action with the inputs given, bound by // parameter name, and reports the outputs it produced. A Complex input // requires the complex_values capability, an Array, Vector or - // VectorQuantity input the structured_values one and a MeasurementRef - // input the measurement_refs one, checked before anything is sent. + // VectorQuantity input the structured_values one, a MeasurementRef input + // the measurement_refs one and a Function input the function_values one, + // checked before anything is sent. ExecuteAction(ctx context.Context, model *Model, actionSymbolID string, inputs map[string]Value) (*ActionRun, error) // ExecuteState runs the named state machine, feeding it the events in @@ -85,9 +86,10 @@ type Client interface { // EvaluateCalc invokes the named calculation with positional arguments, or, // given none, evaluates a calc usage from its own members. Requires the - // verification capability, and the complex_values, structured_values or - // measurement_refs capability for a Complex, a structured or a - // MeasurementRef argument, checked before anything is sent. + // verification capability, and the complex_values, structured_values, + // measurement_refs or function_values capability for a Complex, a + // structured, a MeasurementRef or a Function argument, checked before + // anything is sent. EvaluateCalc(ctx context.Context, model *Model, symbolID string, arguments ...Value) (*Calculation, error) // RunAnalysis runs the named analysis case — a definition or a usage — and @@ -505,7 +507,8 @@ func (c *client) call(model *Model) (string, error) { // requireValueCapabilities refuses to send a value of a kind whose capability // the service lacks — a Complex without complex_values, an Array, Vector or // VectorQuantity without structured_values, a MeasurementRef without -// measurement_refs — which would read it as null rather than refuse it. +// measurement_refs, a Function without function_values — which would read it +// as null rather than refuse it. func (c *client) requireValueCapabilities(ctx context.Context, values ...Value) error { var needed []string if slices.ContainsFunc(values, carriesComplex) { @@ -517,6 +520,9 @@ func (c *client) requireValueCapabilities(ctx context.Context, values ...Value) if slices.ContainsFunc(values, carriesMeasurementRef) { needed = append(needed, CapabilityMeasurementRefs) } + if slices.ContainsFunc(values, carriesFunction) { + needed = append(needed, CapabilityFunctionValues) + } if len(needed) == 0 { return nil } @@ -586,6 +592,15 @@ func carriesMeasurementRef(value Value) bool { return slices.ContainsFunc(nestedValues(value), carriesMeasurementRef) } +// carriesFunction reports whether a value, or any value nested in it, is a +// Function. +func carriesFunction(value Value) bool { + if _, ok := value.(Function); ok { + return true + } + return slices.ContainsFunc(nestedValues(value), carriesFunction) +} + // nestedValues are the values a value holds: a sequence's elements, an array's. func nestedValues(value Value) []Value { switch v := value.(type) { diff --git a/client/opensysml/convert.go b/client/opensysml/convert.go index aaae9ea96..eee63be49 100644 --- a/client/opensysml/convert.go +++ b/client/opensysml/convert.go @@ -165,6 +165,11 @@ func valueFromProto(value *pb.Value) Value { return Null("unsupported: measurement reference without its reduction") } return MeasurementRef{Unit: ref.GetUnit(), Term: unitTermFromProto(ref.GetUnitTerm()), UnitID: ref.GetUnitId()} + case *pb.Value_Function: + if kind.Function.GetCalcId() == "" { + return Null("unsupported: function naming no calc") + } + return Function{CalcID: kind.Function.GetCalcId(), Self: InstanceID(kind.Function.GetSelfId())} default: // A newer service's arm parses as an unknown field: no kind at all. return Null("unsupported: a value arm this client does not know") @@ -245,6 +250,11 @@ func valueToProto(value Value) (*pb.Value, error) { UnitTerm: unitTermToProto(v.Term), UnitId: v.UnitID, }}}, nil + case Function: + if v.CalcID == "" { + return nil, &StatusError{Code: CodeInvalidArgument, Message: "a function names no calc"} + } + return &pb.Value{Kind: &pb.Value_Function{Function: &pb.Function{CalcId: v.CalcID, SelfId: int64(v.Self)}}}, nil case Unset: return nil, &StatusError{ Code: CodeInvalidArgument, diff --git a/client/opensysml/function_test.go b/client/opensysml/function_test.go new file mode 100644 index 000000000..d7196aeaa --- /dev/null +++ b/client/opensysml/function_test.go @@ -0,0 +1,202 @@ +package opensysml_test + +import ( + "context" + "errors" + "fmt" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/Open-MBEE/OpenSysML/api/proto/protoconnect" + "github.com/Open-MBEE/OpenSysML/client/opensysml" + sysmlgrpc "github.com/Open-MBEE/OpenSysML/internal/grpc" +) + +const functionSource = `package F { + private import ScalarValues::*; + calc def Sq { in v : Real; return : Real = v * v; } + calc def Cube { in v : Real; return : Real = v * v * v; } + calc def Apply { in calc f { in v : Real; return : Real; } in a : Real; return : Real = f(a); } + calc apply : Apply; + calc def Identity { in calc f { in v : Real; return : Real; } return r = f; } + attribute pick = Identity(Sq); + attribute nine = Apply(Sq, 3.0); + part def Scaler { + attribute k : Real = 2.0; + calc scale { in x : Real; return : Real = x * k; } + } + part holder : Scaler; + attribute scaler = holder.scale; + + action applyTwice { + in calc f { in v : Real; return : Real; } + in a : Real; + out y : Real; + first start; + action inner { assign y := f(f(a)); } + then done; + succession first start then inner; + } +}` + +// A calc read as a value arrives as a Function naming it over every transport, +// one read off an object naming that object too, and a Function sent back binds +// the calc-typed parameter the calc invokes. +func TestFunctionsCrossEveryTransport(t *testing.T) { + address := startService(t) + for name, client := range map[string]opensysml.Client{ + "in-process": newClient(t), + "connect-proto": dialClient(t, address), + "connect-json": dialClient(t, address, opensysml.WithJSONBody()), + } { + t.Run(name, func(t *testing.T) { + ctx := context.Background() + info, err := client.ServerInfo(ctx) + if err != nil { + t.Fatalf("ServerInfo: %v", err) + } + if !info.Has(opensysml.CapabilityFunctionValues) { + t.Errorf("capabilities %v do not name %s", info.Capabilities, opensysml.CapabilityFunctionValues) + } + model := parse(t, client, functionSource) + + pick, err := client.Evaluate(ctx, model, "F::pick") + if err != nil { + t.Fatalf("Evaluate(F::pick): %v", err) + } + if pick != (opensysml.Function{CalcID: "F::Sq"}) { + t.Errorf("F::pick = %#v, want the function F::Sq bound to no object", pick) + } + + scaler, err := client.Evaluate(ctx, model, "F::scaler") + if err != nil { + t.Fatalf("Evaluate(F::scaler): %v", err) + } + fn, ok := scaler.(opensysml.Function) + if !ok || fn.CalcID != "F::Scaler::scale" || fn.Self == 0 { + t.Errorf("F::scaler = %#v, want the function F::Scaler::scale bound to holder", scaler) + } + + nine, err := client.Evaluate(ctx, model, "F::nine") + if err != nil { + t.Fatalf("Evaluate(F::nine): %v", err) + } + if nine != opensysml.Real(9) { + t.Errorf("F::nine = %#v, want 9.0", nine) + } + + calc, err := client.EvaluateCalc(ctx, model, "F::apply", opensysml.Function{CalcID: "F::Cube"}, opensysml.Real(2)) + if err != nil { + t.Fatalf("EvaluateCalc(apply): %v", err) + } + if calc.Result != opensysml.Real(8) { + t.Errorf("apply(Cube, 2.0) = %#v, want 8.0", calc.Result) + } + + run, err := client.ExecuteAction(ctx, model, "F::applyTwice", map[string]opensysml.Value{ + "f": opensysml.Function{CalcID: "F::Sq"}, "a": opensysml.Real(3), + }) + if err != nil { + t.Fatalf("ExecuteAction: %v", err) + } + if run.Outputs["y"] != opensysml.Real(81) { + t.Errorf("y = %#v, want 81.0", run.Outputs["y"]) + } + + // A function naming no calc, or an object this call did not + // create, is refused in band rather than invoked. + for label, bad := range map[string]opensysml.Function{ + "an unknown calc": {CalcID: "F::Nothing"}, + "a non-calc": {CalcID: "F::holder"}, + "an unknown object": {CalcID: "F::Scaler::scale", Self: 99}, + } { + if _, err := client.EvaluateCalc(ctx, model, "F::apply", bad, opensysml.Real(2)); err == nil { + t.Errorf("EvaluateCalc with a function naming %s succeeded", label) + } + } + var status *opensysml.StatusError + _, err = client.EvaluateCalc(ctx, model, "F::apply", opensysml.Function{}, opensysml.Real(2)) + if !errors.As(err, &status) || status.Code != opensysml.CodeInvalidArgument { + t.Errorf("EvaluateCalc with an empty function: err = %v, want CodeInvalidArgument", err) + } + }) + } +} + +// A service without function_values would read a function input as null, so +// the client refuses to send one, however deeply nested; and what such a +// service reports for a calc read as a value is an unsupported null naming it. +func TestFunctionInputNeedsFunctionValues(t *testing.T) { + svc, err := sysmlgrpc.NewServiceWithUnavailableCapabilitiesForTesting(16, "test", []string{opensysml.CapabilityFunctionValues}) + if err != nil { + t.Fatalf("NewServiceWithUnavailableCapabilitiesForTesting: %v", err) + } + t.Cleanup(svc.Close) + mux := http.NewServeMux() + mux.Handle(protoconnect.NewSysMLServiceHandler(sysmlgrpc.NewConnectAdapter(svc))) + server := httptest.NewServer(mux) + t.Cleanup(server.Close) + + sq := opensysml.Function{CalcID: "F::Sq"} + for name, client := range map[string]opensysml.Client{ + "connect-proto": dialClient(t, server.URL), + "connect-json": dialClient(t, server.URL, opensysml.WithJSONBody()), + } { + t.Run(name, func(t *testing.T) { + ctx := context.Background() + model := parse(t, client, functionSource) + for label, input := range map[string]opensysml.Value{ + "function": sq, + "nested": opensysml.Sequence{opensysml.Int(1), opensysml.Sequence{sq}}, + "in an array": opensysml.Array{Dimensions: []int64{1}, Elements: []opensysml.Value{sq}}, + } { + _, err := client.ExecuteAction(ctx, model, "F::applyTwice", map[string]opensysml.Value{"f": input, "a": opensysml.Real(3)}) + wantFunctionValuesRefusal(t, "ExecuteAction "+label, err) + _, err = client.EvaluateCalc(ctx, model, "F::apply", input, opensysml.Real(2)) + wantFunctionValuesRefusal(t, "EvaluateCalc "+label, err) + } + + // Invocation inside the model needs no capability: only the + // value crossing the boundary does. + nine, err := client.Evaluate(ctx, model, "F::nine") + if err != nil { + t.Fatalf("Evaluate(F::nine): %v", err) + } + if nine != opensysml.Real(9) { + t.Errorf("F::nine without function_values = %#v, want 9.0", nine) + } + pick, err := client.Evaluate(ctx, model, "F::pick") + if err != nil { + t.Fatalf("Evaluate(F::pick): %v", err) + } + if pick != opensysml.Null("unsupported: function F::Sq") { + t.Errorf("F::pick without function_values = %#v, want an unsupported null naming F::Sq", pick) + } + }) + } +} + +func wantFunctionValuesRefusal(t *testing.T, op string, err error) { + t.Helper() + var status *opensysml.StatusError + if !errors.As(err, &status) || status.Code != opensysml.CodeUnimplemented || !strings.Contains(status.Message, opensysml.CapabilityFunctionValues) { + t.Errorf("%s: err = %v, want CodeUnimplemented naming %s", op, err, opensysml.CapabilityFunctionValues) + } +} + +func TestFunctionRendersAsItsCalc(t *testing.T) { + for _, testcase := range []struct { + value opensysml.Value + want string + }{ + {opensysml.Function{CalcID: "F::Sq"}, "F::Sq"}, + {opensysml.Function{CalcID: "F::Scaler::scale", Self: 1}, "F::Scaler::scale"}, + {opensysml.Sequence{opensysml.Function{CalcID: "F::Sq"}, opensysml.Function{CalcID: "F::Cube"}}, "[F::Sq F::Cube]"}, + } { + if got := fmt.Sprintf("%v", testcase.value); got != testcase.want { + t.Errorf("%#v renders as %q, want %q", testcase.value, got, testcase.want) + } + } +} diff --git a/client/opensysml/types.go b/client/opensysml/types.go index 57630c95e..4a039c438 100644 --- a/client/opensysml/types.go +++ b/client/opensysml/types.go @@ -11,27 +11,27 @@ import ( // service so negotiating on them needs no other import. Capabilities are only // ever added, never renamed or removed with their behaviour intact. const ( - CapabilityTypeFacts = sysmlgrpc.CapabilityTypeFacts - CapabilityConvert = sysmlgrpc.CapabilityConvert - CapabilityVerification = sysmlgrpc.CapabilityVerification - CapabilityQuery = sysmlgrpc.CapabilityQuery - CapabilityOSLCQuery = sysmlgrpc.CapabilityOSLCQuery - CapabilityDocumentQuery = sysmlgrpc.CapabilityDocumentQuery - CapabilityRenderDocument = sysmlgrpc.CapabilityRenderDocument - CapabilityEnumValues = sysmlgrpc.CapabilityEnumValues - CapabilityEvaluateSubject = sysmlgrpc.CapabilityEvaluateSubject - CapabilitySymbolAttributes = sysmlgrpc.CapabilitySymbolAttributes - CapabilityUnsetValue = sysmlgrpc.CapabilityUnsetValue - CapabilityFeatureValues = sysmlgrpc.CapabilityFeatureValues - CapabilityApplyEdits = sysmlgrpc.CapabilityApplyEdits - CapabilityAuthoring = sysmlgrpc.CapabilityAuthoring - CapabilityInlineLanguage = sysmlgrpc.CapabilityInlineLanguage - CapabilityStrictConformance = sysmlgrpc.CapabilityStrictConformance - CapabilityParseSources = sysmlgrpc.CapabilityParseSources - CapabilityComplexValues = sysmlgrpc.CapabilityComplexValues - CapabilityStructuredValues = sysmlgrpc.CapabilityStructuredValues - CapabilityMeasurementRefs = sysmlgrpc.CapabilityMeasurementRefs - + CapabilityTypeFacts = sysmlgrpc.CapabilityTypeFacts + CapabilityConvert = sysmlgrpc.CapabilityConvert + CapabilityVerification = sysmlgrpc.CapabilityVerification + CapabilityQuery = sysmlgrpc.CapabilityQuery + CapabilityOSLCQuery = sysmlgrpc.CapabilityOSLCQuery + CapabilityDocumentQuery = sysmlgrpc.CapabilityDocumentQuery + CapabilityRenderDocument = sysmlgrpc.CapabilityRenderDocument + CapabilityEnumValues = sysmlgrpc.CapabilityEnumValues + CapabilityEvaluateSubject = sysmlgrpc.CapabilityEvaluateSubject + CapabilitySymbolAttributes = sysmlgrpc.CapabilitySymbolAttributes + CapabilityUnsetValue = sysmlgrpc.CapabilityUnsetValue + CapabilityFeatureValues = sysmlgrpc.CapabilityFeatureValues + CapabilityApplyEdits = sysmlgrpc.CapabilityApplyEdits + CapabilityAuthoring = sysmlgrpc.CapabilityAuthoring + CapabilityInlineLanguage = sysmlgrpc.CapabilityInlineLanguage + CapabilityStrictConformance = sysmlgrpc.CapabilityStrictConformance + CapabilityParseSources = sysmlgrpc.CapabilityParseSources + CapabilityComplexValues = sysmlgrpc.CapabilityComplexValues + CapabilityStructuredValues = sysmlgrpc.CapabilityStructuredValues + CapabilityMeasurementRefs = sysmlgrpc.CapabilityMeasurementRefs + CapabilityFunctionValues = sysmlgrpc.CapabilityFunctionValues CapabilityVerificationVerdicts = sysmlgrpc.CapabilityVerificationVerdicts ) diff --git a/client/opensysml/value.go b/client/opensysml/value.go index 2071a0165..73d6a1493 100644 --- a/client/opensysml/value.go +++ b/client/opensysml/value.go @@ -9,8 +9,8 @@ import ( // Value is one evaluated SysML value. It is a sealed sum: the concrete types // are Int, Real, Complex, Bool, String, InstanceID, Sequence, Null, Unset, -// Quantity, EnumLiteral, Array, Vector, VectorQuantity and MeasurementRef, and -// a type switch over them is exhaustive. +// Quantity, EnumLiteral, Array, Vector, VectorQuantity, MeasurementRef and +// Function, and a type switch over them is exhaustive. type Value interface { isValue() } @@ -97,6 +97,19 @@ type MeasurementRef struct { UnitID string } +// Function is a calc held as a value: a calc definition, a calc usage or an +// `in calc` parameter read where a value is expected. It names the calc and, for +// one read off an object, that object; sent as an argument it is invoked through +// the calc-typed parameter it binds. Identity is the calc together with Self. +type Function struct { + // CalcID is the FQN of the calc declaration ("M::Sq"). + CalcID string + // Self is the object the calc computes over, an id of the answer that + // reported it; 0 for a calc bound to no object. As any InstanceID it lives + // only within that answer: sent as an argument, a non-zero Self is refused. + Self InstanceID +} + // EnumLiteral is one literal of an enumeration definition. A literal is its // own identity: two values are the same literal exactly when LiteralID is. type EnumLiteral struct { @@ -214,6 +227,11 @@ func (t UnitTerm) String() string { return strings.Join(parts, "·") } +// String names the calc the function is a value of. +func (f Function) String() string { + return f.CalcID +} + // String is the literal as a reader writes it, the Name the service reported. func (e EnumLiteral) String() string { return e.Name @@ -248,6 +266,7 @@ func (Array) isValue() { /* marker: closed Value set */ } func (Vector) isValue() { /* marker: closed Value set */ } func (VectorQuantity) isValue() { /* marker: closed Value set */ } func (MeasurementRef) isValue() { /* marker: closed Value set */ } +func (Function) isValue() { /* marker: closed Value set */ } func (Int) isNumber() { /* marker: closed Number set */ } func (Real) isNumber() { /* marker: closed Number set */ } diff --git a/clients/java/README.md b/clients/java/README.md index 5625d1b6f..cf3e63bf4 100644 --- a/clients/java/README.md +++ b/clients/java/README.md @@ -36,7 +36,7 @@ try (Connection connection = Connection.open()) { // starts a private sysml Every value the API answers with is immutable: `Value` is a sealed interface over records (`IntegerValue`, `RealValue`, `ComplexValue`, `QuantityValue`, `ArrayValue`, -`VectorValue`, `VectorQuantityValue`, `MeasurementRefValue`, `EnumerationValue`, +`VectorValue`, `VectorQuantityValue`, `MeasurementRefValue`, `FunctionValue`, `EnumerationValue`, `InstanceReference`, `Sequence`, `NullValue`, `UnsetValue`, …), and `Symbol`, `Diagnostic`, `Instance` and `Instantiation` are records with copied collections. No generated protobuf message or builder appears in the public API. diff --git a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/Capabilities.java b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/Capabilities.java index 9105e27a4..13debef96 100644 --- a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/Capabilities.java +++ b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/Capabilities.java @@ -47,6 +47,9 @@ public final class Capabilities { /** A bare measurement unit ({@code SI::m}, {@code m / s}) travels as itself rather than as an unsupported null. */ public static final String MEASUREMENT_REFS = "measurement_refs"; + /** A calc held as a value travels as the {@code function} naming its declaration rather than as an unsupported null. */ + public static final String FUNCTION_VALUES = "function_values"; + /** The unbounded value {@code *} travels as itself rather than as an unsupported null. */ public static final String INFINITY_VALUE = "infinity_value"; diff --git a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/Value.java b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/Value.java index d0c816efc..b3ec37638 100644 --- a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/Value.java +++ b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/Value.java @@ -142,6 +142,36 @@ record MeasurementRefValue(String unit, Quantity.UnitTerm reduction, OptionalIt is the declaration it is a value of, which is its identity: two functions are equal + * exactly when both components are. A function closing over the bindings of the behavior body + * it is declared in has no wire form; the service sends it as an unsupported {@link NullValue}, + * as does a service without the {@code function_values} capability for every function. + * + * @param calcId FQN of the calc declaration ({@code "Analysis::Sq"}), never empty + * @param selfId id of the object the calc's feature names resolve against, for a calc usage read + * off a part ({@code holder.scale}); absent for a function closing over no object + */ + record FunctionValue(String calcId, Optional selfId) implements Value { + /** + * Creates a function. + * + * @param calcId the calc declaration, never {@code null} or empty + * @param selfId the object read against, never {@code null} + * @throws IllegalArgumentException if {@code calcId} is empty + */ + public FunctionValue { + Objects.requireNonNull(calcId, "calcId"); + Objects.requireNonNull(selfId, "selfId"); + if (calcId.isEmpty()) { + throw new IllegalArgumentException("a function names no calc"); + } + } + } + /** * One literal of an enumeration definition. * diff --git a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/internal/Protos.java b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/internal/Protos.java index 093d32821..6dfb81c77 100644 --- a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/internal/Protos.java +++ b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/internal/Protos.java @@ -59,6 +59,7 @@ public static Optional value(org.openmbee.opensysml.proto.Value value) { case VECTOR -> Optional.of(vector(value.getVector())); case VECTOR_QUANTITY -> Optional.of(vectorQuantity(value.getVectorQuantity())); case MEASUREMENT_REF -> Optional.of(measurementRef(value.getMeasurementRef())); + case FUNCTION -> Optional.of(function(value.getFunction())); case KIND_NOT_SET -> Optional.empty(); }; } @@ -133,6 +134,16 @@ private static Value measurementRef(org.openmbee.opensysml.proto.MeasurementRef ref.getUnit(), unitTerm(ref.getUnitTerm()), present(ref.getUnitId())); } + private static Value function(org.openmbee.opensysml.proto.Function function) { + if (function.getCalcId().isEmpty()) { + throw new TransportException( + "the service answered a malformed function: it names no calc", null); + } + return new Value.FunctionValue( + function.getCalcId(), + function.getSelfId() == 0 ? Optional.empty() : Optional.of(function.getSelfId())); + } + private static Value sequence(org.openmbee.opensysml.proto.Value value) { List elements = new ArrayList<>(); for (org.openmbee.opensysml.proto.Value element : value.getSequence().getElementsList()) { diff --git a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/Function.java b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/Function.java new file mode 100644 index 000000000..b1cbc7c69 --- /dev/null +++ b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/Function.java @@ -0,0 +1,647 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: sysml.proto +// Protobuf Java Version: 4.33.1 + +package org.openmbee.opensysml.proto; + +/** + *
+ * Function is a calc held as a value: a calc definition, or a calc usage with
+ * an input no read could supply, as `Sq` in `Fn(Sq, 3.0)` or the `f` of
+ * `in calc f {...}`. It crosses as the declaration it is a value of, which is
+ * its identity: two functions are the same exactly when calc_id and self_id
+ * are. A function closing over the bindings of the behavior body it is
+ * declared in has no wire form and crosses as the null arm.
+ * 
+ * + * Protobuf type {@code sysml.Function} + */ +@com.google.protobuf.Generated +public final class Function extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:sysml.Function) + FunctionOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 33, + /* patch= */ 1, + /* suffix= */ "", + "Function"); + } + // Use Function.newBuilder() to construct. + private Function(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private Function() { + calcId_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.openmbee.opensysml.proto.Sysml.internal_static_sysml_Function_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.openmbee.opensysml.proto.Sysml.internal_static_sysml_Function_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.openmbee.opensysml.proto.Function.class, org.openmbee.opensysml.proto.Function.Builder.class); + } + + public static final int CALC_ID_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object calcId_ = ""; + /** + *
+   * FQN of the calc declaration ("Analysis::Sq"). Its identity.
+   * 
+ * + * string calc_id = 1 [json_name = "calcId"]; + * @return The calcId. + */ + @java.lang.Override + public java.lang.String getCalcId() { + java.lang.Object ref = calcId_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + calcId_ = s; + return s; + } + } + /** + *
+   * FQN of the calc declaration ("Analysis::Sq"). Its identity.
+   * 
+ * + * string calc_id = 1 [json_name = "calcId"]; + * @return The bytes for calcId. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getCalcIdBytes() { + java.lang.Object ref = calcId_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + calcId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int SELF_ID_FIELD_NUMBER = 2; + private long selfId_ = 0L; + /** + *
+   * ID of the object the calc's feature names resolve against, for a calc
+   * usage read off a part (`holder.scale`); 0 for a function closing over no
+   * object. An instance id, living only within the response that sent it: a
+   * request function with a non-zero self_id is rejected, since no later call
+   * holds that object.
+   * 
+ * + * int64 self_id = 2 [json_name = "selfId"]; + * @return The selfId. + */ + @java.lang.Override + public long getSelfId() { + return selfId_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(calcId_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, calcId_); + } + if (selfId_ != 0L) { + output.writeInt64(2, selfId_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(calcId_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, calcId_); + } + if (selfId_ != 0L) { + size += com.google.protobuf.CodedOutputStream + .computeInt64Size(2, selfId_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof org.openmbee.opensysml.proto.Function)) { + return super.equals(obj); + } + org.openmbee.opensysml.proto.Function other = (org.openmbee.opensysml.proto.Function) obj; + + if (!getCalcId() + .equals(other.getCalcId())) return false; + if (getSelfId() + != other.getSelfId()) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + CALC_ID_FIELD_NUMBER; + hash = (53 * hash) + getCalcId().hashCode(); + hash = (37 * hash) + SELF_ID_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashLong( + getSelfId()); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static org.openmbee.opensysml.proto.Function parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.openmbee.opensysml.proto.Function parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.openmbee.opensysml.proto.Function parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.openmbee.opensysml.proto.Function parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.openmbee.opensysml.proto.Function parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.openmbee.opensysml.proto.Function parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.openmbee.opensysml.proto.Function parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static org.openmbee.opensysml.proto.Function parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static org.openmbee.opensysml.proto.Function parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static org.openmbee.opensysml.proto.Function parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static org.openmbee.opensysml.proto.Function parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static org.openmbee.opensysml.proto.Function parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(org.openmbee.opensysml.proto.Function prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * Function is a calc held as a value: a calc definition, or a calc usage with
+   * an input no read could supply, as `Sq` in `Fn(Sq, 3.0)` or the `f` of
+   * `in calc f {...}`. It crosses as the declaration it is a value of, which is
+   * its identity: two functions are the same exactly when calc_id and self_id
+   * are. A function closing over the bindings of the behavior body it is
+   * declared in has no wire form and crosses as the null arm.
+   * 
+ * + * Protobuf type {@code sysml.Function} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:sysml.Function) + org.openmbee.opensysml.proto.FunctionOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.openmbee.opensysml.proto.Sysml.internal_static_sysml_Function_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.openmbee.opensysml.proto.Sysml.internal_static_sysml_Function_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.openmbee.opensysml.proto.Function.class, org.openmbee.opensysml.proto.Function.Builder.class); + } + + // Construct using org.openmbee.opensysml.proto.Function.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + calcId_ = ""; + selfId_ = 0L; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.openmbee.opensysml.proto.Sysml.internal_static_sysml_Function_descriptor; + } + + @java.lang.Override + public org.openmbee.opensysml.proto.Function getDefaultInstanceForType() { + return org.openmbee.opensysml.proto.Function.getDefaultInstance(); + } + + @java.lang.Override + public org.openmbee.opensysml.proto.Function build() { + org.openmbee.opensysml.proto.Function result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public org.openmbee.opensysml.proto.Function buildPartial() { + org.openmbee.opensysml.proto.Function result = new org.openmbee.opensysml.proto.Function(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(org.openmbee.opensysml.proto.Function result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.calcId_ = calcId_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.selfId_ = selfId_; + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.openmbee.opensysml.proto.Function) { + return mergeFrom((org.openmbee.opensysml.proto.Function)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.openmbee.opensysml.proto.Function other) { + if (other == org.openmbee.opensysml.proto.Function.getDefaultInstance()) return this; + if (!other.getCalcId().isEmpty()) { + calcId_ = other.calcId_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (other.getSelfId() != 0L) { + setSelfId(other.getSelfId()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + calcId_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 16: { + selfId_ = input.readInt64(); + bitField0_ |= 0x00000002; + break; + } // case 16 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object calcId_ = ""; + /** + *
+     * FQN of the calc declaration ("Analysis::Sq"). Its identity.
+     * 
+ * + * string calc_id = 1 [json_name = "calcId"]; + * @return The calcId. + */ + public java.lang.String getCalcId() { + java.lang.Object ref = calcId_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + calcId_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * FQN of the calc declaration ("Analysis::Sq"). Its identity.
+     * 
+ * + * string calc_id = 1 [json_name = "calcId"]; + * @return The bytes for calcId. + */ + public com.google.protobuf.ByteString + getCalcIdBytes() { + java.lang.Object ref = calcId_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + calcId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * FQN of the calc declaration ("Analysis::Sq"). Its identity.
+     * 
+ * + * string calc_id = 1 [json_name = "calcId"]; + * @param value The calcId to set. + * @return This builder for chaining. + */ + public Builder setCalcId( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + calcId_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * FQN of the calc declaration ("Analysis::Sq"). Its identity.
+     * 
+ * + * string calc_id = 1 [json_name = "calcId"]; + * @return This builder for chaining. + */ + public Builder clearCalcId() { + calcId_ = getDefaultInstance().getCalcId(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * FQN of the calc declaration ("Analysis::Sq"). Its identity.
+     * 
+ * + * string calc_id = 1 [json_name = "calcId"]; + * @param value The bytes for calcId to set. + * @return This builder for chaining. + */ + public Builder setCalcIdBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + calcId_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private long selfId_ ; + /** + *
+     * ID of the object the calc's feature names resolve against, for a calc
+     * usage read off a part (`holder.scale`); 0 for a function closing over no
+     * object. An instance id, living only within the response that sent it: a
+     * request function with a non-zero self_id is rejected, since no later call
+     * holds that object.
+     * 
+ * + * int64 self_id = 2 [json_name = "selfId"]; + * @return The selfId. + */ + @java.lang.Override + public long getSelfId() { + return selfId_; + } + /** + *
+     * ID of the object the calc's feature names resolve against, for a calc
+     * usage read off a part (`holder.scale`); 0 for a function closing over no
+     * object. An instance id, living only within the response that sent it: a
+     * request function with a non-zero self_id is rejected, since no later call
+     * holds that object.
+     * 
+ * + * int64 self_id = 2 [json_name = "selfId"]; + * @param value The selfId to set. + * @return This builder for chaining. + */ + public Builder setSelfId(long value) { + + selfId_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * ID of the object the calc's feature names resolve against, for a calc
+     * usage read off a part (`holder.scale`); 0 for a function closing over no
+     * object. An instance id, living only within the response that sent it: a
+     * request function with a non-zero self_id is rejected, since no later call
+     * holds that object.
+     * 
+ * + * int64 self_id = 2 [json_name = "selfId"]; + * @return This builder for chaining. + */ + public Builder clearSelfId() { + bitField0_ = (bitField0_ & ~0x00000002); + selfId_ = 0L; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:sysml.Function) + } + + // @@protoc_insertion_point(class_scope:sysml.Function) + private static final org.openmbee.opensysml.proto.Function DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new org.openmbee.opensysml.proto.Function(); + } + + public static org.openmbee.opensysml.proto.Function getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public Function parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public org.openmbee.opensysml.proto.Function getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/FunctionOrBuilder.java b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/FunctionOrBuilder.java new file mode 100644 index 000000000..d1f0bc5d1 --- /dev/null +++ b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/FunctionOrBuilder.java @@ -0,0 +1,46 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: sysml.proto +// Protobuf Java Version: 4.33.1 + +package org.openmbee.opensysml.proto; + +@com.google.protobuf.Generated +public interface FunctionOrBuilder extends + // @@protoc_insertion_point(interface_extends:sysml.Function) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * FQN of the calc declaration ("Analysis::Sq"). Its identity.
+   * 
+ * + * string calc_id = 1 [json_name = "calcId"]; + * @return The calcId. + */ + java.lang.String getCalcId(); + /** + *
+   * FQN of the calc declaration ("Analysis::Sq"). Its identity.
+   * 
+ * + * string calc_id = 1 [json_name = "calcId"]; + * @return The bytes for calcId. + */ + com.google.protobuf.ByteString + getCalcIdBytes(); + + /** + *
+   * ID of the object the calc's feature names resolve against, for a calc
+   * usage read off a part (`holder.scale`); 0 for a function closing over no
+   * object. An instance id, living only within the response that sent it: a
+   * request function with a non-zero self_id is rejected, since no later call
+   * holds that object.
+   * 
+ * + * int64 self_id = 2 [json_name = "selfId"]; + * @return The selfId. + */ + long getSelfId(); +} diff --git a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ServerInfoResponse.java b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ServerInfoResponse.java index 78e9e331b..c4311fadf 100644 --- a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ServerInfoResponse.java +++ b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ServerInfoResponse.java @@ -150,6 +150,11 @@ public java.lang.String getVersion() { * refused with UNIMPLEMENTED rather than read as another * value. Separate from structured_values, which a client * built before this arm existed may already claim. + * "function_values" - a Value carries a calc held as a value as function, + * named by its declaration, rather than reporting it as an + * unsupported null, and one is accepted as an action input + * or calc argument; without it, one is refused with + * UNIMPLEMENTED rather than read as another value. * "apply_edits" - the ApplyEdits RPC edits a parsed model's own source, * preserving everything the edit did not touch. * "document_query" - the RunDocumentQuery RPC runs a named document query @@ -210,6 +215,11 @@ public java.lang.String getVersion() { * refused with UNIMPLEMENTED rather than read as another * value. Separate from structured_values, which a client * built before this arm existed may already claim. + * "function_values" - a Value carries a calc held as a value as function, + * named by its declaration, rather than reporting it as an + * unsupported null, and one is accepted as an action input + * or calc argument; without it, one is refused with + * UNIMPLEMENTED rather than read as another value. * "apply_edits" - the ApplyEdits RPC edits a parsed model's own source, * preserving everything the edit did not touch. * "document_query" - the RunDocumentQuery RPC runs a named document query @@ -269,6 +279,11 @@ public int getCapabilitiesCount() { * refused with UNIMPLEMENTED rather than read as another * value. Separate from structured_values, which a client * built before this arm existed may already claim. + * "function_values" - a Value carries a calc held as a value as function, + * named by its declaration, rather than reporting it as an + * unsupported null, and one is accepted as an action input + * or calc argument; without it, one is refused with + * UNIMPLEMENTED rather than read as another value. * "apply_edits" - the ApplyEdits RPC edits a parsed model's own source, * preserving everything the edit did not touch. * "document_query" - the RunDocumentQuery RPC runs a named document query @@ -329,6 +344,11 @@ public java.lang.String getCapabilities(int index) { * refused with UNIMPLEMENTED rather than read as another * value. Separate from structured_values, which a client * built before this arm existed may already claim. + * "function_values" - a Value carries a calc held as a value as function, + * named by its declaration, rather than reporting it as an + * unsupported null, and one is accepted as an action input + * or calc argument; without it, one is refused with + * UNIMPLEMENTED rather than read as another value. * "apply_edits" - the ApplyEdits RPC edits a parsed model's own source, * preserving everything the edit did not touch. * "document_query" - the RunDocumentQuery RPC runs a named document query @@ -838,6 +858,11 @@ private void ensureCapabilitiesIsMutable() { * refused with UNIMPLEMENTED rather than read as another * value. Separate from structured_values, which a client * built before this arm existed may already claim. + * "function_values" - a Value carries a calc held as a value as function, + * named by its declaration, rather than reporting it as an + * unsupported null, and one is accepted as an action input + * or calc argument; without it, one is refused with + * UNIMPLEMENTED rather than read as another value. * "apply_edits" - the ApplyEdits RPC edits a parsed model's own source, * preserving everything the edit did not touch. * "document_query" - the RunDocumentQuery RPC runs a named document query @@ -899,6 +924,11 @@ private void ensureCapabilitiesIsMutable() { * refused with UNIMPLEMENTED rather than read as another * value. Separate from structured_values, which a client * built before this arm existed may already claim. + * "function_values" - a Value carries a calc held as a value as function, + * named by its declaration, rather than reporting it as an + * unsupported null, and one is accepted as an action input + * or calc argument; without it, one is refused with + * UNIMPLEMENTED rather than read as another value. * "apply_edits" - the ApplyEdits RPC edits a parsed model's own source, * preserving everything the edit did not touch. * "document_query" - the RunDocumentQuery RPC runs a named document query @@ -958,6 +988,11 @@ public int getCapabilitiesCount() { * refused with UNIMPLEMENTED rather than read as another * value. Separate from structured_values, which a client * built before this arm existed may already claim. + * "function_values" - a Value carries a calc held as a value as function, + * named by its declaration, rather than reporting it as an + * unsupported null, and one is accepted as an action input + * or calc argument; without it, one is refused with + * UNIMPLEMENTED rather than read as another value. * "apply_edits" - the ApplyEdits RPC edits a parsed model's own source, * preserving everything the edit did not touch. * "document_query" - the RunDocumentQuery RPC runs a named document query @@ -1018,6 +1053,11 @@ public java.lang.String getCapabilities(int index) { * refused with UNIMPLEMENTED rather than read as another * value. Separate from structured_values, which a client * built before this arm existed may already claim. + * "function_values" - a Value carries a calc held as a value as function, + * named by its declaration, rather than reporting it as an + * unsupported null, and one is accepted as an action input + * or calc argument; without it, one is refused with + * UNIMPLEMENTED rather than read as another value. * "apply_edits" - the ApplyEdits RPC edits a parsed model's own source, * preserving everything the edit did not touch. * "document_query" - the RunDocumentQuery RPC runs a named document query @@ -1079,6 +1119,11 @@ public java.lang.String getCapabilities(int index) { * refused with UNIMPLEMENTED rather than read as another * value. Separate from structured_values, which a client * built before this arm existed may already claim. + * "function_values" - a Value carries a calc held as a value as function, + * named by its declaration, rather than reporting it as an + * unsupported null, and one is accepted as an action input + * or calc argument; without it, one is refused with + * UNIMPLEMENTED rather than read as another value. * "apply_edits" - the ApplyEdits RPC edits a parsed model's own source, * preserving everything the edit did not touch. * "document_query" - the RunDocumentQuery RPC runs a named document query @@ -1146,6 +1191,11 @@ public Builder setCapabilities( * refused with UNIMPLEMENTED rather than read as another * value. Separate from structured_values, which a client * built before this arm existed may already claim. + * "function_values" - a Value carries a calc held as a value as function, + * named by its declaration, rather than reporting it as an + * unsupported null, and one is accepted as an action input + * or calc argument; without it, one is refused with + * UNIMPLEMENTED rather than read as another value. * "apply_edits" - the ApplyEdits RPC edits a parsed model's own source, * preserving everything the edit did not touch. * "document_query" - the RunDocumentQuery RPC runs a named document query @@ -1212,6 +1262,11 @@ public Builder addCapabilities( * refused with UNIMPLEMENTED rather than read as another * value. Separate from structured_values, which a client * built before this arm existed may already claim. + * "function_values" - a Value carries a calc held as a value as function, + * named by its declaration, rather than reporting it as an + * unsupported null, and one is accepted as an action input + * or calc argument; without it, one is refused with + * UNIMPLEMENTED rather than read as another value. * "apply_edits" - the ApplyEdits RPC edits a parsed model's own source, * preserving everything the edit did not touch. * "document_query" - the RunDocumentQuery RPC runs a named document query @@ -1278,6 +1333,11 @@ public Builder addAllCapabilities( * refused with UNIMPLEMENTED rather than read as another * value. Separate from structured_values, which a client * built before this arm existed may already claim. + * "function_values" - a Value carries a calc held as a value as function, + * named by its declaration, rather than reporting it as an + * unsupported null, and one is accepted as an action input + * or calc argument; without it, one is refused with + * UNIMPLEMENTED rather than read as another value. * "apply_edits" - the ApplyEdits RPC edits a parsed model's own source, * preserving everything the edit did not touch. * "document_query" - the RunDocumentQuery RPC runs a named document query @@ -1341,6 +1401,11 @@ public Builder clearCapabilities() { * refused with UNIMPLEMENTED rather than read as another * value. Separate from structured_values, which a client * built before this arm existed may already claim. + * "function_values" - a Value carries a calc held as a value as function, + * named by its declaration, rather than reporting it as an + * unsupported null, and one is accepted as an action input + * or calc argument; without it, one is refused with + * UNIMPLEMENTED rather than read as another value. * "apply_edits" - the ApplyEdits RPC edits a parsed model's own source, * preserving everything the edit did not touch. * "document_query" - the RunDocumentQuery RPC runs a named document query diff --git a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ServerInfoResponseOrBuilder.java b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ServerInfoResponseOrBuilder.java index 81b55f359..d38de77f3 100644 --- a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ServerInfoResponseOrBuilder.java +++ b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ServerInfoResponseOrBuilder.java @@ -79,6 +79,11 @@ public interface ServerInfoResponseOrBuilder extends * refused with UNIMPLEMENTED rather than read as another * value. Separate from structured_values, which a client * built before this arm existed may already claim. + * "function_values" - a Value carries a calc held as a value as function, + * named by its declaration, rather than reporting it as an + * unsupported null, and one is accepted as an action input + * or calc argument; without it, one is refused with + * UNIMPLEMENTED rather than read as another value. * "apply_edits" - the ApplyEdits RPC edits a parsed model's own source, * preserving everything the edit did not touch. * "document_query" - the RunDocumentQuery RPC runs a named document query @@ -137,6 +142,11 @@ public interface ServerInfoResponseOrBuilder extends * refused with UNIMPLEMENTED rather than read as another * value. Separate from structured_values, which a client * built before this arm existed may already claim. + * "function_values" - a Value carries a calc held as a value as function, + * named by its declaration, rather than reporting it as an + * unsupported null, and one is accepted as an action input + * or calc argument; without it, one is refused with + * UNIMPLEMENTED rather than read as another value. * "apply_edits" - the ApplyEdits RPC edits a parsed model's own source, * preserving everything the edit did not touch. * "document_query" - the RunDocumentQuery RPC runs a named document query @@ -194,6 +204,11 @@ public interface ServerInfoResponseOrBuilder extends * refused with UNIMPLEMENTED rather than read as another * value. Separate from structured_values, which a client * built before this arm existed may already claim. + * "function_values" - a Value carries a calc held as a value as function, + * named by its declaration, rather than reporting it as an + * unsupported null, and one is accepted as an action input + * or calc argument; without it, one is refused with + * UNIMPLEMENTED rather than read as another value. * "apply_edits" - the ApplyEdits RPC edits a parsed model's own source, * preserving everything the edit did not touch. * "document_query" - the RunDocumentQuery RPC runs a named document query @@ -252,6 +267,11 @@ public interface ServerInfoResponseOrBuilder extends * refused with UNIMPLEMENTED rather than read as another * value. Separate from structured_values, which a client * built before this arm existed may already claim. + * "function_values" - a Value carries a calc held as a value as function, + * named by its declaration, rather than reporting it as an + * unsupported null, and one is accepted as an action input + * or calc argument; without it, one is refused with + * UNIMPLEMENTED rather than read as another value. * "apply_edits" - the ApplyEdits RPC edits a parsed model's own source, * preserving everything the edit did not touch. * "document_query" - the RunDocumentQuery RPC runs a named document query diff --git a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/Sysml.java b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/Sysml.java index 1a843e626..37099f9c3 100644 --- a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/Sysml.java +++ b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/Sysml.java @@ -296,6 +296,11 @@ public static void registerAllExtensions( static final com.google.protobuf.GeneratedMessage.FieldAccessorTable internal_static_sysml_Value_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_sysml_Function_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_sysml_Function_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_sysml_Array_descriptor; static final @@ -699,7 +704,7 @@ public static void registerAllExtensions( "\030\001 \001(\tR\005lower\022\024\n\005upper\030\002 \001(\tR\005upper\"o\n\rA" + "ttributeInfo\022\022\n\004name\030\001 \001(\tR\004name\022\022\n\004type" + "\030\002 \001(\tR\004type\022\"\n\005value\030\003 \001(\0132\014.sysml.Valu" + - "eR\005value\022\022\n\004unit\030\004 \001(\tR\004unit\"\237\005\n\005Value\022\035" + + "eR\005value\022\022\n\004unit\030\004 \001(\tR\004unit\"\316\005\n\005Value\022\035" + "\n\tint_value\030\001 \001(\003H\000R\010intValue\022\037\n\nreal_va" + "lue\030\002 \001(\001H\000R\trealValue\022\037\n\nbool_value\030\003 \001" + "(\010H\000R\tboolValue\022#\n\014string_value\030\004 \001(\tH\000R" + @@ -716,178 +721,181 @@ public static void registerAllExtensions( "sysml.VectorQuantityH\000R\016vectorQuantity\022@" + "\n\017measurement_ref\030\017 \001(\0132\025.sysml.Measurem" + "entRefH\000R\016measurementRef\022\034\n\010infinity\030\020 \001" + - "(\010H\000R\010infinityB\006\n\004kind\"Q\n\005Array\022\036\n\ndimen" + - "sions\030\001 \003(\003R\ndimensions\022(\n\010elements\030\002 \003(" + - "\0132\014.sysml.ValueR\010elements\"6\n\006Vector\022,\n\nc" + - "omponents\030\001 \003(\0132\014.sysml.ValueR\ncomponent" + - "s\"A\n\016VectorQuantity\022/\n\ncomponents\030\001 \003(\0132" + - "\017.sysml.QuantityR\ncomponents\";\n\007Complex\022" + - "\022\n\004real\030\001 \001(\001R\004real\022\034\n\timaginary\030\002 \001(\001R\t" + - "imaginary\"g\n\013EnumLiteral\022\035\n\nliteral_id\030\001" + - " \001(\tR\tliteralId\022%\n\016enumeration_id\030\002 \001(\tR" + - "\renumerationId\022\022\n\004name\030\003 \001(\tR\004name\"9\n\rVa" + - "lueSequence\022(\n\010elements\030\001 \003(\0132\014.sysml.Va" + - "lueR\010elements\"\251\001\n\010Quantity\022%\n\rint_magnit" + - "ude\030\001 \001(\003H\000R\014intMagnitude\022\'\n\016real_magnit" + - "ude\030\002 \001(\001H\000R\rrealMagnitude\022\022\n\004unit\030\003 \001(\t" + - "R\004unit\022,\n\tunit_term\030\004 \001(\0132\017.sysml.UnitTe" + - "rmR\010unitTermB\013\n\tmagnitude\"k\n\016Measurement" + - "Ref\022\022\n\004unit\030\001 \001(\tR\004unit\022,\n\tunit_term\030\002 \001" + - "(\0132\017.sysml.UnitTermR\010unitTerm\022\027\n\007unit_id" + - "\030\003 \001(\tR\006unitId\"q\n\010UnitTerm\022\033\n\tscale_num\030" + - "\001 \001(\001R\010scaleNum\022\033\n\tscale_den\030\002 \001(\001R\010scal" + - "eDen\022+\n\007factors\030\003 \003(\0132\021.sysml.UnitFactor" + - "R\007factors\"A\n\nUnitFactor\022\027\n\007unit_id\030\001 \001(\t" + - "R\006unitId\022\032\n\010exponent\030\002 \001(\001R\010exponent\"c\n\n" + - "Diagnostic\022\032\n\010severity\030\001 \001(\tR\010severity\022\030" + - "\n\007message\030\002 \001(\tR\007message\022\037\n\004span\030\003 \001(\0132\013" + - ".sysml.SpanR\004span\"\212\001\n\004Span\022\022\n\004file\030\001 \001(\t" + - "R\004file\022\035\n\nstart_line\030\002 \001(\005R\tstartLine\022\033\n" + - "\tstart_col\030\003 \001(\005R\010startCol\022\031\n\010end_line\030\004" + - " \001(\005R\007endLine\022\027\n\007end_col\030\005 \001(\005R\006endCol\"\023" + - "\n\021ServerInfoRequest\"R\n\022ServerInfoRespons" + - "e\022\030\n\007version\030\001 \001(\tR\007version\022\"\n\014capabilit" + - "ies\030\002 \003(\tR\014capabilities\"p\n\014QueryRequest\022" + - "\035\n\nmodel_hash\030\001 \001(\tR\tmodelHash\022\"\n\005query\030" + - "\002 \001(\0132\014.sysml.QueryR\005query\022\035\n\noslc_query" + - "\030\003 \001(\tR\toslcQuery\"F\n\rQueryResponse\0225\n\010el" + - "ements\030\001 \003(\0132\031.sysml.QueryResultElementR" + - "\010elements\"^\n\005Query\022\024\n\005scope\030\001 \003(\tR\005scope" + - "\022\026\n\006select\030\002 \003(\tR\006select\022\'\n\005where\030\003 \001(\0132" + - "\021.sysml.ConstraintR\005where\"\222\001\n\nConstraint" + - "\022:\n\tprimitive\030\001 \001(\0132\032.sysml.PrimitiveCon" + - "straintH\000R\tprimitive\022:\n\tcomposite\030\002 \001(\0132" + - "\032.sysml.CompositeConstraintH\000R\tcomposite" + - "B\014\n\nconstraint\"\227\001\n\023PrimitiveConstraint\022\030" + - "\n\007inverse\030\001 \001(\010R\007inverse\022\032\n\010property\030\002 \001" + - "(\tR\010property\0224\n\010operator\030\003 \001(\0162\030.sysml.P" + - "rimitiveOperatorR\010operator\022\024\n\005value\030\004 \003(" + - "\tR\005value\"~\n\023CompositeConstraint\0224\n\010opera" + - "tor\030\001 \001(\0162\030.sysml.CompositeOperatorR\010ope" + - "rator\0221\n\nconstraint\030\002 \003(\0132\021.sysml.Constr" + - "aintR\nconstraint\"\302\001\n\022QueryResultElement\022" + - "\016\n\002id\030\001 \001(\tR\002id\022\022\n\004type\030\002 \001(\tR\004type\022I\n\np" + - "roperties\030\003 \003(\0132).sysml.QueryResultEleme" + - "nt.PropertiesEntryR\nproperties\032=\n\017Proper" + - "tiesEntry\022\020\n\003key\030\001 \001(\tR\003key\022\024\n\005value\030\002 \001" + - "(\tR\005value:\0028\001\"\220\001\n\nSweepRange\022\034\n\tparamete" + - "r\030\001 \001(\tR\tparameter\022\"\n\005start\030\002 \001(\0132\014.sysm" + - "l.ValueR\005start\022\036\n\003end\030\003 \001(\0132\014.sysml.Valu" + - "eR\003end\022 \n\004step\030\004 \001(\0132\014.sysml.ValueR\004step" + - "\"\244\003\n\017RunSweepRequest\022\035\n\nmodel_hash\030\001 \001(\t" + - "R\tmodelHash\022\033\n\tsymbol_id\030\002 \001(\tR\010symbolId" + - "\022*\n\021subject_symbol_id\030\003 \001(\tR\017subjectSymb" + - "olId\022*\n\targuments\030\004 \003(\0132\014.sysml.ValueR\ta" + - "rguments\022S\n\017named_arguments\030\005 \003(\0132*.sysm" + - "l.RunSweepRequest.NamedArgumentsEntryR\016n" + - "amedArguments\022)\n\006ranges\030\006 \003(\0132\021.sysml.Sw" + - "eepRangeR\006ranges\022\030\n\007samples\030\007 \001(\003R\007sampl" + - "es\022\022\n\004seed\030\010 \001(\004R\004seed\032O\n\023NamedArguments" + - "Entry\022\020\n\003key\030\001 \001(\tR\003key\022\"\n\005value\030\002 \001(\0132\014" + - ".sysml.ValueR\005value:\0028\001\"\210\002\n\010SweepRow\022)\n\006" + - "inputs\030\001 \003(\0132\021.sysml.CalcOutputR\006inputs\022" + - "+\n\007outputs\030\002 \003(\0132\021.sysml.CalcOutputR\007out" + - "puts\022*\n\010verdicts\030\003 \003(\0132\016.sysml.VerdictR\010" + - "verdicts\022%\n\016elapsed_micros\030\004 \001(\003R\relapse" + - "dMicros\022\024\n\005error\030\005 \001(\tR\005error\022;\n\016failure" + - "_reason\030\006 \001(\0162\024.sysml.FailureReasonR\rfai" + - "lureReason\"\274\002\n\020RunSweepResponse\022#\n\004rows\030" + - "\001 \003(\0132\017.sysml.SweepRowR\004rows\022\036\n\nparamete" + - "rs\030\002 \003(\tR\nparameters\022\030\n\007sampled\030\003 \001(\010R\007s" + - "ampled\022\022\n\004seed\030\004 \001(\004R\004seed\022\024\n\005error\030\005 \001(" + - "\tR\005error\0223\n\013diagnostics\030\006 \003(\0132\021.sysml.Di" + - "agnosticR\013diagnostics\022;\n\016failure_reason\030" + - "\007 \001(\0162\024.sysml.FailureReasonR\rfailureReas" + - "on\022-\n\tinstances\030\010 \003(\0132\017.sysml.InstanceR\t" + - "instances\"\214\001\n\027RunDocumentQueryRequest\022\035\n" + - "\nmodel_hash\030\001 \001(\tR\tmodelHash\022\031\n\010query_id" + - "\030\002 \001(\tR\007queryId\0227\n\010bindings\030\003 \003(\0132\033.sysm" + - "l.DocumentQueryBindingR\010bindings\"b\n\024Docu" + - "mentQueryBinding\022\034\n\tparameter\030\001 \001(\tR\tpar" + - "ameter\022,\n\006values\030\002 \003(\0132\024.sysml.DocumentV" + - "alueR\006values\"\256\002\n\rDocumentValue\022\037\n\nelemen" + - "t_id\030\001 \001(\tH\000R\telementId\022#\n\014string_value\030" + - "\002 \001(\tH\000R\013stringValue\022\035\n\tint_value\030\003 \001(\003H" + - "\000R\010intValue\022\037\n\nreal_value\030\004 \001(\001H\000R\trealV" + - "alue\022\037\n\nbool_value\030\005 \001(\010H\000R\tboolValue\022\034\n" + - "\010infinity\030\006 \001(\010H\000R\010infinity\022-\n\010quantity\030" + - "\010 \001(\0132\017.sysml.QuantityH\000R\010quantity\022!\n\014el" + - "ement_type\030\007 \001(\tR\013elementTypeB\006\n\004kind\")\n" + - "\023DocumentQueryColumn\022\022\n\004name\030\001 \001(\tR\004name" + - "\"A\n\021DocumentQueryCell\022,\n\006values\030\001 \003(\0132\024." + - "sysml.DocumentValueR\006values\"r\n\020DocumentQ" + - "ueryRow\022.\n\007element\030\001 \001(\0132\024.sysml.Documen" + - "tValueR\007element\022.\n\005cells\030\002 \003(\0132\030.sysml.D" + - "ocumentQueryCellR\005cells\"}\n\030RunDocumentQu" + - "eryResponse\0224\n\007columns\030\001 \003(\0132\032.sysml.Doc" + - "umentQueryColumnR\007columns\022+\n\004rows\030\002 \003(\0132" + - "\027.sysml.DocumentQueryRowR\004rows\"W\n\025Render" + - "DocumentRequest\022\035\n\nmodel_hash\030\001 \001(\tR\tmod" + - "elHash\022\037\n\013document_id\030\002 \001(\tR\ndocumentId\"" + - "4\n\026RenderDocumentResponse\022\032\n\010markdown\030\001 " + - "\001(\tR\010markdown*\223\001\n\rFailureReason\022\036\n\032FAILU" + - "RE_REASON_UNSPECIFIED\020\000\022\035\n\031FAILURE_REASO" + - "N_EVALUATION\020\001\022\035\n\031FAILURE_REASON_WRONG_K" + - "IND\020\002\022$\n FAILURE_REASON_AMBIGUOUS_SUBJEC" + - "T\020\003*\235\004\n\013EditFailure\022\034\n\030EDIT_FAILURE_UNSP" + - "ECIFIED\020\000\022\036\n\032EDIT_FAILURE_NO_OPERATIONS\020" + - "\001\022\037\n\033EDIT_FAILURE_UNKNOWN_TARGET\020\002\022!\n\035ED" + - "IT_FAILURE_AMBIGUOUS_TARGET\020\003\022\033\n\027EDIT_FA" + - "ILURE_NOT_VALUED\020\004\022\036\n\032EDIT_FAILURE_INVAL" + - "ID_VALUE\020\005\022\035\n\031EDIT_FAILURE_INVALID_NAME\020" + - "\006\022\032\n\026EDIT_FAILURE_NOT_NAMED\020\007\022\"\n\036EDIT_FA" + - "ILURE_RENAME_REFERENCED\020\010\022\"\n\036EDIT_FAILUR" + - "E_OVERLAPPING_EDITS\020\t\022\037\n\033EDIT_FAILURE_RE" + - "SULT_INVALID\020\n\022\036\n\032EDIT_FAILURE_OWNER_UNK" + - "NOWN\020\013\022$\n EDIT_FAILURE_OWNER_NOT_NAMESPA" + - "CE\020\014\022\035\n\031EDIT_FAILURE_ILLEGAL_KIND\020\r\022\"\n\036E" + - "DIT_FAILURE_MEMBER_NAME_TAKEN\020\016\022\"\n\036EDIT_" + - "FAILURE_DELETE_REFERENCED\020\017*\222\001\n\021Primitiv" + - "eOperator\022\"\n\036PRIMITIVE_OPERATOR_UNSPECIF" + - "IED\020\000\022\034\n\030PRIMITIVE_OPERATOR_EQUAL\020\001\022\036\n\032P" + - "RIMITIVE_OPERATOR_GREATER\020\002\022\033\n\027PRIMITIVE" + - "_OPERATOR_LESS\020\003*n\n\021CompositeOperator\022\"\n" + - "\036COMPOSITE_OPERATOR_UNSPECIFIED\020\000\022\032\n\026COM" + - "POSITE_OPERATOR_AND\020\001\022\031\n\025COMPOSITE_OPERA" + - "TOR_OR\020\0022\244\013\n\014SysMLService\022D\n\rGetServerIn" + - "fo\022\030.sysml.ServerInfoRequest\032\031.sysml.Ser" + - "verInfoResponse\022>\n\tParseFile\022\027.sysml.Par" + - "seFileRequest\032\030.sysml.ParseFileResponse\022" + - "G\n\014ParseSources\022\032.sysml.ParseSourcesRequ" + - "est\032\033.sysml.ParseSourcesResponse\022;\n\tGetS" + - "ymbol\022\027.sysml.GetSymbolRequest\032\025.sysml.S" + - "ymbolResponse\022G\n\016GetDiagnostics\022\031.sysml." + - "DiagnosticsRequest\032\032.sysml.DiagnosticsRe" + - "sponse\022;\n\010Evaluate\022\026.sysml.EvaluateReque" + - "st\032\027.sysml.EvaluateResponse\022D\n\013Instantia" + - "te\022\031.sysml.InstantiateRequest\032\032.sysml.In" + - "stantiateResponse\022J\n\rExecuteAction\022\033.sys" + - "ml.ExecuteActionRequest\032\034.sysml.ExecuteA" + - "ctionResponse\022G\n\014ExecuteState\022\032.sysml.Ex" + - "ecuteStateRequest\032\033.sysml.ExecuteStateRe" + - "sponse\0228\n\007Convert\022\025.sysml.ConvertRequest" + - "\032\026.sysml.ConvertResponse\022A\n\nApplyEdits\022\030" + - ".sysml.ApplyEditsRequest\032\031.sysml.ApplyEd" + - "itsResponse\022S\n\020VerifyConstraint\022\036.sysml." + - "VerifyConstraintRequest\032\037.sysml.VerifyCo" + - "nstraintResponse\022V\n\021VerifyRequirement\022\037." + - "sysml.VerifyRequirementRequest\032 .sysml.V" + - "erifyRequirementResponse\022Y\n\022VerifySatisf" + - "action\022 .sysml.VerifySatisfactionRequest" + - "\032!.sysml.VerifySatisfactionResponse\022G\n\014E" + - "valuateCalc\022\032.sysml.EvaluateCalcRequest\032" + - "\033.sysml.EvaluateCalcResponse\022D\n\013RunAnaly" + - "sis\022\031.sysml.RunAnalysisRequest\032\032.sysml.R" + - "unAnalysisResponse\022;\n\010RunSweep\022\026.sysml.R" + - "unSweepRequest\032\027.sysml.RunSweepResponse\022" + - "2\n\005Query\022\023.sysml.QueryRequest\032\024.sysml.Qu" + - "eryResponse\022S\n\020RunDocumentQuery\022\036.sysml." + - "RunDocumentQueryRequest\032\037.sysml.RunDocum" + - "entQueryResponse\022M\n\016RenderDocument\022\034.sys" + - "ml.RenderDocumentRequest\032\035.sysml.RenderD", - "ocumentResponseBJ\n\034org.openmbee.opensysm" + - "l.protoP\001Z(github.com/Open-MBEE/OpenSysM" + - "L/api/protob\006proto3" + "(\010H\000R\010infinity\022-\n\010function\030\021 \001(\0132\017.sysml" + + ".FunctionH\000R\010functionB\006\n\004kind\"<\n\010Functio" + + "n\022\027\n\007calc_id\030\001 \001(\tR\006calcId\022\027\n\007self_id\030\002 " + + "\001(\003R\006selfId\"Q\n\005Array\022\036\n\ndimensions\030\001 \003(\003" + + "R\ndimensions\022(\n\010elements\030\002 \003(\0132\014.sysml.V" + + "alueR\010elements\"6\n\006Vector\022,\n\ncomponents\030\001" + + " \003(\0132\014.sysml.ValueR\ncomponents\"A\n\016Vector" + + "Quantity\022/\n\ncomponents\030\001 \003(\0132\017.sysml.Qua" + + "ntityR\ncomponents\";\n\007Complex\022\022\n\004real\030\001 \001" + + "(\001R\004real\022\034\n\timaginary\030\002 \001(\001R\timaginary\"g" + + "\n\013EnumLiteral\022\035\n\nliteral_id\030\001 \001(\tR\tliter" + + "alId\022%\n\016enumeration_id\030\002 \001(\tR\renumeratio" + + "nId\022\022\n\004name\030\003 \001(\tR\004name\"9\n\rValueSequence" + + "\022(\n\010elements\030\001 \003(\0132\014.sysml.ValueR\010elemen" + + "ts\"\251\001\n\010Quantity\022%\n\rint_magnitude\030\001 \001(\003H\000" + + "R\014intMagnitude\022\'\n\016real_magnitude\030\002 \001(\001H\000" + + "R\rrealMagnitude\022\022\n\004unit\030\003 \001(\tR\004unit\022,\n\tu" + + "nit_term\030\004 \001(\0132\017.sysml.UnitTermR\010unitTer" + + "mB\013\n\tmagnitude\"k\n\016MeasurementRef\022\022\n\004unit" + + "\030\001 \001(\tR\004unit\022,\n\tunit_term\030\002 \001(\0132\017.sysml." + + "UnitTermR\010unitTerm\022\027\n\007unit_id\030\003 \001(\tR\006uni" + + "tId\"q\n\010UnitTerm\022\033\n\tscale_num\030\001 \001(\001R\010scal" + + "eNum\022\033\n\tscale_den\030\002 \001(\001R\010scaleDen\022+\n\007fac" + + "tors\030\003 \003(\0132\021.sysml.UnitFactorR\007factors\"A" + + "\n\nUnitFactor\022\027\n\007unit_id\030\001 \001(\tR\006unitId\022\032\n" + + "\010exponent\030\002 \001(\001R\010exponent\"c\n\nDiagnostic\022" + + "\032\n\010severity\030\001 \001(\tR\010severity\022\030\n\007message\030\002" + + " \001(\tR\007message\022\037\n\004span\030\003 \001(\0132\013.sysml.Span" + + "R\004span\"\212\001\n\004Span\022\022\n\004file\030\001 \001(\tR\004file\022\035\n\ns" + + "tart_line\030\002 \001(\005R\tstartLine\022\033\n\tstart_col\030" + + "\003 \001(\005R\010startCol\022\031\n\010end_line\030\004 \001(\005R\007endLi" + + "ne\022\027\n\007end_col\030\005 \001(\005R\006endCol\"\023\n\021ServerInf" + + "oRequest\"R\n\022ServerInfoResponse\022\030\n\007versio" + + "n\030\001 \001(\tR\007version\022\"\n\014capabilities\030\002 \003(\tR\014" + + "capabilities\"p\n\014QueryRequest\022\035\n\nmodel_ha" + + "sh\030\001 \001(\tR\tmodelHash\022\"\n\005query\030\002 \001(\0132\014.sys" + + "ml.QueryR\005query\022\035\n\noslc_query\030\003 \001(\tR\tosl" + + "cQuery\"F\n\rQueryResponse\0225\n\010elements\030\001 \003(" + + "\0132\031.sysml.QueryResultElementR\010elements\"^" + + "\n\005Query\022\024\n\005scope\030\001 \003(\tR\005scope\022\026\n\006select\030" + + "\002 \003(\tR\006select\022\'\n\005where\030\003 \001(\0132\021.sysml.Con" + + "straintR\005where\"\222\001\n\nConstraint\022:\n\tprimiti" + + "ve\030\001 \001(\0132\032.sysml.PrimitiveConstraintH\000R\t" + + "primitive\022:\n\tcomposite\030\002 \001(\0132\032.sysml.Com" + + "positeConstraintH\000R\tcompositeB\014\n\nconstra" + + "int\"\227\001\n\023PrimitiveConstraint\022\030\n\007inverse\030\001" + + " \001(\010R\007inverse\022\032\n\010property\030\002 \001(\tR\010propert" + + "y\0224\n\010operator\030\003 \001(\0162\030.sysml.PrimitiveOpe" + + "ratorR\010operator\022\024\n\005value\030\004 \003(\tR\005value\"~\n" + + "\023CompositeConstraint\0224\n\010operator\030\001 \001(\0162\030" + + ".sysml.CompositeOperatorR\010operator\0221\n\nco" + + "nstraint\030\002 \003(\0132\021.sysml.ConstraintR\nconst" + + "raint\"\302\001\n\022QueryResultElement\022\016\n\002id\030\001 \001(\t" + + "R\002id\022\022\n\004type\030\002 \001(\tR\004type\022I\n\nproperties\030\003" + + " \003(\0132).sysml.QueryResultElement.Properti" + + "esEntryR\nproperties\032=\n\017PropertiesEntry\022\020" + + "\n\003key\030\001 \001(\tR\003key\022\024\n\005value\030\002 \001(\tR\005value:\002" + + "8\001\"\220\001\n\nSweepRange\022\034\n\tparameter\030\001 \001(\tR\tpa" + + "rameter\022\"\n\005start\030\002 \001(\0132\014.sysml.ValueR\005st" + + "art\022\036\n\003end\030\003 \001(\0132\014.sysml.ValueR\003end\022 \n\004s" + + "tep\030\004 \001(\0132\014.sysml.ValueR\004step\"\244\003\n\017RunSwe" + + "epRequest\022\035\n\nmodel_hash\030\001 \001(\tR\tmodelHash" + + "\022\033\n\tsymbol_id\030\002 \001(\tR\010symbolId\022*\n\021subject" + + "_symbol_id\030\003 \001(\tR\017subjectSymbolId\022*\n\targ" + + "uments\030\004 \003(\0132\014.sysml.ValueR\targuments\022S\n" + + "\017named_arguments\030\005 \003(\0132*.sysml.RunSweepR" + + "equest.NamedArgumentsEntryR\016namedArgumen" + + "ts\022)\n\006ranges\030\006 \003(\0132\021.sysml.SweepRangeR\006r" + + "anges\022\030\n\007samples\030\007 \001(\003R\007samples\022\022\n\004seed\030" + + "\010 \001(\004R\004seed\032O\n\023NamedArgumentsEntry\022\020\n\003ke" + + "y\030\001 \001(\tR\003key\022\"\n\005value\030\002 \001(\0132\014.sysml.Valu" + + "eR\005value:\0028\001\"\210\002\n\010SweepRow\022)\n\006inputs\030\001 \003(" + + "\0132\021.sysml.CalcOutputR\006inputs\022+\n\007outputs\030" + + "\002 \003(\0132\021.sysml.CalcOutputR\007outputs\022*\n\010ver" + + "dicts\030\003 \003(\0132\016.sysml.VerdictR\010verdicts\022%\n" + + "\016elapsed_micros\030\004 \001(\003R\relapsedMicros\022\024\n\005" + + "error\030\005 \001(\tR\005error\022;\n\016failure_reason\030\006 \001" + + "(\0162\024.sysml.FailureReasonR\rfailureReason\"" + + "\274\002\n\020RunSweepResponse\022#\n\004rows\030\001 \003(\0132\017.sys" + + "ml.SweepRowR\004rows\022\036\n\nparameters\030\002 \003(\tR\np" + + "arameters\022\030\n\007sampled\030\003 \001(\010R\007sampled\022\022\n\004s" + + "eed\030\004 \001(\004R\004seed\022\024\n\005error\030\005 \001(\tR\005error\0223\n" + + "\013diagnostics\030\006 \003(\0132\021.sysml.DiagnosticR\013d" + + "iagnostics\022;\n\016failure_reason\030\007 \001(\0162\024.sys" + + "ml.FailureReasonR\rfailureReason\022-\n\tinsta" + + "nces\030\010 \003(\0132\017.sysml.InstanceR\tinstances\"\214" + + "\001\n\027RunDocumentQueryRequest\022\035\n\nmodel_hash" + + "\030\001 \001(\tR\tmodelHash\022\031\n\010query_id\030\002 \001(\tR\007que" + + "ryId\0227\n\010bindings\030\003 \003(\0132\033.sysml.DocumentQ" + + "ueryBindingR\010bindings\"b\n\024DocumentQueryBi" + + "nding\022\034\n\tparameter\030\001 \001(\tR\tparameter\022,\n\006v" + + "alues\030\002 \003(\0132\024.sysml.DocumentValueR\006value" + + "s\"\256\002\n\rDocumentValue\022\037\n\nelement_id\030\001 \001(\tH" + + "\000R\telementId\022#\n\014string_value\030\002 \001(\tH\000R\013st" + + "ringValue\022\035\n\tint_value\030\003 \001(\003H\000R\010intValue" + + "\022\037\n\nreal_value\030\004 \001(\001H\000R\trealValue\022\037\n\nboo" + + "l_value\030\005 \001(\010H\000R\tboolValue\022\034\n\010infinity\030\006" + + " \001(\010H\000R\010infinity\022-\n\010quantity\030\010 \001(\0132\017.sys" + + "ml.QuantityH\000R\010quantity\022!\n\014element_type\030" + + "\007 \001(\tR\013elementTypeB\006\n\004kind\")\n\023DocumentQu" + + "eryColumn\022\022\n\004name\030\001 \001(\tR\004name\"A\n\021Documen" + + "tQueryCell\022,\n\006values\030\001 \003(\0132\024.sysml.Docum" + + "entValueR\006values\"r\n\020DocumentQueryRow\022.\n\007" + + "element\030\001 \001(\0132\024.sysml.DocumentValueR\007ele" + + "ment\022.\n\005cells\030\002 \003(\0132\030.sysml.DocumentQuer" + + "yCellR\005cells\"}\n\030RunDocumentQueryResponse" + + "\0224\n\007columns\030\001 \003(\0132\032.sysml.DocumentQueryC" + + "olumnR\007columns\022+\n\004rows\030\002 \003(\0132\027.sysml.Doc" + + "umentQueryRowR\004rows\"W\n\025RenderDocumentReq" + + "uest\022\035\n\nmodel_hash\030\001 \001(\tR\tmodelHash\022\037\n\013d" + + "ocument_id\030\002 \001(\tR\ndocumentId\"4\n\026RenderDo" + + "cumentResponse\022\032\n\010markdown\030\001 \001(\tR\010markdo" + + "wn*\223\001\n\rFailureReason\022\036\n\032FAILURE_REASON_U" + + "NSPECIFIED\020\000\022\035\n\031FAILURE_REASON_EVALUATIO" + + "N\020\001\022\035\n\031FAILURE_REASON_WRONG_KIND\020\002\022$\n FA" + + "ILURE_REASON_AMBIGUOUS_SUBJECT\020\003*\235\004\n\013Edi" + + "tFailure\022\034\n\030EDIT_FAILURE_UNSPECIFIED\020\000\022\036" + + "\n\032EDIT_FAILURE_NO_OPERATIONS\020\001\022\037\n\033EDIT_F" + + "AILURE_UNKNOWN_TARGET\020\002\022!\n\035EDIT_FAILURE_" + + "AMBIGUOUS_TARGET\020\003\022\033\n\027EDIT_FAILURE_NOT_V" + + "ALUED\020\004\022\036\n\032EDIT_FAILURE_INVALID_VALUE\020\005\022" + + "\035\n\031EDIT_FAILURE_INVALID_NAME\020\006\022\032\n\026EDIT_F" + + "AILURE_NOT_NAMED\020\007\022\"\n\036EDIT_FAILURE_RENAM" + + "E_REFERENCED\020\010\022\"\n\036EDIT_FAILURE_OVERLAPPI" + + "NG_EDITS\020\t\022\037\n\033EDIT_FAILURE_RESULT_INVALI" + + "D\020\n\022\036\n\032EDIT_FAILURE_OWNER_UNKNOWN\020\013\022$\n E" + + "DIT_FAILURE_OWNER_NOT_NAMESPACE\020\014\022\035\n\031EDI" + + "T_FAILURE_ILLEGAL_KIND\020\r\022\"\n\036EDIT_FAILURE" + + "_MEMBER_NAME_TAKEN\020\016\022\"\n\036EDIT_FAILURE_DEL" + + "ETE_REFERENCED\020\017*\222\001\n\021PrimitiveOperator\022\"" + + "\n\036PRIMITIVE_OPERATOR_UNSPECIFIED\020\000\022\034\n\030PR" + + "IMITIVE_OPERATOR_EQUAL\020\001\022\036\n\032PRIMITIVE_OP" + + "ERATOR_GREATER\020\002\022\033\n\027PRIMITIVE_OPERATOR_L" + + "ESS\020\003*n\n\021CompositeOperator\022\"\n\036COMPOSITE_" + + "OPERATOR_UNSPECIFIED\020\000\022\032\n\026COMPOSITE_OPER" + + "ATOR_AND\020\001\022\031\n\025COMPOSITE_OPERATOR_OR\020\0022\244\013" + + "\n\014SysMLService\022D\n\rGetServerInfo\022\030.sysml." + + "ServerInfoRequest\032\031.sysml.ServerInfoResp" + + "onse\022>\n\tParseFile\022\027.sysml.ParseFileReque" + + "st\032\030.sysml.ParseFileResponse\022G\n\014ParseSou" + + "rces\022\032.sysml.ParseSourcesRequest\032\033.sysml" + + ".ParseSourcesResponse\022;\n\tGetSymbol\022\027.sys" + + "ml.GetSymbolRequest\032\025.sysml.SymbolRespon" + + "se\022G\n\016GetDiagnostics\022\031.sysml.Diagnostics" + + "Request\032\032.sysml.DiagnosticsResponse\022;\n\010E" + + "valuate\022\026.sysml.EvaluateRequest\032\027.sysml." + + "EvaluateResponse\022D\n\013Instantiate\022\031.sysml." + + "InstantiateRequest\032\032.sysml.InstantiateRe" + + "sponse\022J\n\rExecuteAction\022\033.sysml.ExecuteA" + + "ctionRequest\032\034.sysml.ExecuteActionRespon" + + "se\022G\n\014ExecuteState\022\032.sysml.ExecuteStateR" + + "equest\032\033.sysml.ExecuteStateResponse\0228\n\007C" + + "onvert\022\025.sysml.ConvertRequest\032\026.sysml.Co" + + "nvertResponse\022A\n\nApplyEdits\022\030.sysml.Appl" + + "yEditsRequest\032\031.sysml.ApplyEditsResponse" + + "\022S\n\020VerifyConstraint\022\036.sysml.VerifyConst" + + "raintRequest\032\037.sysml.VerifyConstraintRes" + + "ponse\022V\n\021VerifyRequirement\022\037.sysml.Verif" + + "yRequirementRequest\032 .sysml.VerifyRequir" + + "ementResponse\022Y\n\022VerifySatisfaction\022 .sy" + + "sml.VerifySatisfactionRequest\032!.sysml.Ve" + + "rifySatisfactionResponse\022G\n\014EvaluateCalc" + + "\022\032.sysml.EvaluateCalcRequest\032\033.sysml.Eva" + + "luateCalcResponse\022D\n\013RunAnalysis\022\031.sysml" + + ".RunAnalysisRequest\032\032.sysml.RunAnalysisR" + + "esponse\022;\n\010RunSweep\022\026.sysml.RunSweepRequ" + + "est\032\027.sysml.RunSweepResponse\0222\n\005Query\022\023." + + "sysml.QueryRequest\032\024.sysml.QueryResponse" + + "\022S\n\020RunDocumentQuery\022\036.sysml.RunDocument", + "QueryRequest\032\037.sysml.RunDocumentQueryRes" + + "ponse\022M\n\016RenderDocument\022\034.sysml.RenderDo" + + "cumentRequest\032\035.sysml.RenderDocumentResp" + + "onseBJ\n\034org.openmbee.opensysml.protoP\001Z(" + + "github.com/Open-MBEE/OpenSysML/api/proto" + + "b\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, @@ -1216,129 +1224,135 @@ public static void registerAllExtensions( internal_static_sysml_Value_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_Value_descriptor, - new java.lang.String[] { "IntValue", "RealValue", "BoolValue", "StringValue", "InstanceId", "Sequence", "Null", "Quantity", "EnumLiteral", "Unset", "Complex", "Array", "Vector", "VectorQuantity", "MeasurementRef", "Infinity", "Kind", }); - internal_static_sysml_Array_descriptor = + new java.lang.String[] { "IntValue", "RealValue", "BoolValue", "StringValue", "InstanceId", "Sequence", "Null", "Quantity", "EnumLiteral", "Unset", "Complex", "Array", "Vector", "VectorQuantity", "MeasurementRef", "Infinity", "Function", "Kind", }); + internal_static_sysml_Function_descriptor = getDescriptor().getMessageType(48); + internal_static_sysml_Function_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_sysml_Function_descriptor, + new java.lang.String[] { "CalcId", "SelfId", }); + internal_static_sysml_Array_descriptor = + getDescriptor().getMessageType(49); internal_static_sysml_Array_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_Array_descriptor, new java.lang.String[] { "Dimensions", "Elements", }); internal_static_sysml_Vector_descriptor = - getDescriptor().getMessageType(49); + getDescriptor().getMessageType(50); internal_static_sysml_Vector_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_Vector_descriptor, new java.lang.String[] { "Components", }); internal_static_sysml_VectorQuantity_descriptor = - getDescriptor().getMessageType(50); + getDescriptor().getMessageType(51); internal_static_sysml_VectorQuantity_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_VectorQuantity_descriptor, new java.lang.String[] { "Components", }); internal_static_sysml_Complex_descriptor = - getDescriptor().getMessageType(51); + getDescriptor().getMessageType(52); internal_static_sysml_Complex_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_Complex_descriptor, new java.lang.String[] { "Real", "Imaginary", }); internal_static_sysml_EnumLiteral_descriptor = - getDescriptor().getMessageType(52); + getDescriptor().getMessageType(53); internal_static_sysml_EnumLiteral_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_EnumLiteral_descriptor, new java.lang.String[] { "LiteralId", "EnumerationId", "Name", }); internal_static_sysml_ValueSequence_descriptor = - getDescriptor().getMessageType(53); + getDescriptor().getMessageType(54); internal_static_sysml_ValueSequence_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_ValueSequence_descriptor, new java.lang.String[] { "Elements", }); internal_static_sysml_Quantity_descriptor = - getDescriptor().getMessageType(54); + getDescriptor().getMessageType(55); internal_static_sysml_Quantity_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_Quantity_descriptor, new java.lang.String[] { "IntMagnitude", "RealMagnitude", "Unit", "UnitTerm", "Magnitude", }); internal_static_sysml_MeasurementRef_descriptor = - getDescriptor().getMessageType(55); + getDescriptor().getMessageType(56); internal_static_sysml_MeasurementRef_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_MeasurementRef_descriptor, new java.lang.String[] { "Unit", "UnitTerm", "UnitId", }); internal_static_sysml_UnitTerm_descriptor = - getDescriptor().getMessageType(56); + getDescriptor().getMessageType(57); internal_static_sysml_UnitTerm_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_UnitTerm_descriptor, new java.lang.String[] { "ScaleNum", "ScaleDen", "Factors", }); internal_static_sysml_UnitFactor_descriptor = - getDescriptor().getMessageType(57); + getDescriptor().getMessageType(58); internal_static_sysml_UnitFactor_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_UnitFactor_descriptor, new java.lang.String[] { "UnitId", "Exponent", }); internal_static_sysml_Diagnostic_descriptor = - getDescriptor().getMessageType(58); + getDescriptor().getMessageType(59); internal_static_sysml_Diagnostic_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_Diagnostic_descriptor, new java.lang.String[] { "Severity", "Message", "Span", }); internal_static_sysml_Span_descriptor = - getDescriptor().getMessageType(59); + getDescriptor().getMessageType(60); internal_static_sysml_Span_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_Span_descriptor, new java.lang.String[] { "File", "StartLine", "StartCol", "EndLine", "EndCol", }); internal_static_sysml_ServerInfoRequest_descriptor = - getDescriptor().getMessageType(60); + getDescriptor().getMessageType(61); internal_static_sysml_ServerInfoRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_ServerInfoRequest_descriptor, new java.lang.String[] { }); internal_static_sysml_ServerInfoResponse_descriptor = - getDescriptor().getMessageType(61); + getDescriptor().getMessageType(62); internal_static_sysml_ServerInfoResponse_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_ServerInfoResponse_descriptor, new java.lang.String[] { "Version", "Capabilities", }); internal_static_sysml_QueryRequest_descriptor = - getDescriptor().getMessageType(62); + getDescriptor().getMessageType(63); internal_static_sysml_QueryRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_QueryRequest_descriptor, new java.lang.String[] { "ModelHash", "Query", "OslcQuery", }); internal_static_sysml_QueryResponse_descriptor = - getDescriptor().getMessageType(63); + getDescriptor().getMessageType(64); internal_static_sysml_QueryResponse_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_QueryResponse_descriptor, new java.lang.String[] { "Elements", }); internal_static_sysml_Query_descriptor = - getDescriptor().getMessageType(64); + getDescriptor().getMessageType(65); internal_static_sysml_Query_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_Query_descriptor, new java.lang.String[] { "Scope", "Select", "Where", }); internal_static_sysml_Constraint_descriptor = - getDescriptor().getMessageType(65); + getDescriptor().getMessageType(66); internal_static_sysml_Constraint_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_Constraint_descriptor, new java.lang.String[] { "Primitive", "Composite", "Constraint", }); internal_static_sysml_PrimitiveConstraint_descriptor = - getDescriptor().getMessageType(66); + getDescriptor().getMessageType(67); internal_static_sysml_PrimitiveConstraint_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_PrimitiveConstraint_descriptor, new java.lang.String[] { "Inverse", "Property", "Operator", "Value", }); internal_static_sysml_CompositeConstraint_descriptor = - getDescriptor().getMessageType(67); + getDescriptor().getMessageType(68); internal_static_sysml_CompositeConstraint_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_CompositeConstraint_descriptor, new java.lang.String[] { "Operator", "Constraint", }); internal_static_sysml_QueryResultElement_descriptor = - getDescriptor().getMessageType(68); + getDescriptor().getMessageType(69); internal_static_sysml_QueryResultElement_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_QueryResultElement_descriptor, @@ -1350,13 +1364,13 @@ public static void registerAllExtensions( internal_static_sysml_QueryResultElement_PropertiesEntry_descriptor, new java.lang.String[] { "Key", "Value", }); internal_static_sysml_SweepRange_descriptor = - getDescriptor().getMessageType(69); + getDescriptor().getMessageType(70); internal_static_sysml_SweepRange_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_SweepRange_descriptor, new java.lang.String[] { "Parameter", "Start", "End", "Step", }); internal_static_sysml_RunSweepRequest_descriptor = - getDescriptor().getMessageType(70); + getDescriptor().getMessageType(71); internal_static_sysml_RunSweepRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_RunSweepRequest_descriptor, @@ -1368,67 +1382,67 @@ public static void registerAllExtensions( internal_static_sysml_RunSweepRequest_NamedArgumentsEntry_descriptor, new java.lang.String[] { "Key", "Value", }); internal_static_sysml_SweepRow_descriptor = - getDescriptor().getMessageType(71); + getDescriptor().getMessageType(72); internal_static_sysml_SweepRow_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_SweepRow_descriptor, new java.lang.String[] { "Inputs", "Outputs", "Verdicts", "ElapsedMicros", "Error", "FailureReason", }); internal_static_sysml_RunSweepResponse_descriptor = - getDescriptor().getMessageType(72); + getDescriptor().getMessageType(73); internal_static_sysml_RunSweepResponse_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_RunSweepResponse_descriptor, new java.lang.String[] { "Rows", "Parameters", "Sampled", "Seed", "Error", "Diagnostics", "FailureReason", "Instances", }); internal_static_sysml_RunDocumentQueryRequest_descriptor = - getDescriptor().getMessageType(73); + getDescriptor().getMessageType(74); internal_static_sysml_RunDocumentQueryRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_RunDocumentQueryRequest_descriptor, new java.lang.String[] { "ModelHash", "QueryId", "Bindings", }); internal_static_sysml_DocumentQueryBinding_descriptor = - getDescriptor().getMessageType(74); + getDescriptor().getMessageType(75); internal_static_sysml_DocumentQueryBinding_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_DocumentQueryBinding_descriptor, new java.lang.String[] { "Parameter", "Values", }); internal_static_sysml_DocumentValue_descriptor = - getDescriptor().getMessageType(75); + getDescriptor().getMessageType(76); internal_static_sysml_DocumentValue_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_DocumentValue_descriptor, new java.lang.String[] { "ElementId", "StringValue", "IntValue", "RealValue", "BoolValue", "Infinity", "Quantity", "ElementType", "Kind", }); internal_static_sysml_DocumentQueryColumn_descriptor = - getDescriptor().getMessageType(76); + getDescriptor().getMessageType(77); internal_static_sysml_DocumentQueryColumn_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_DocumentQueryColumn_descriptor, new java.lang.String[] { "Name", }); internal_static_sysml_DocumentQueryCell_descriptor = - getDescriptor().getMessageType(77); + getDescriptor().getMessageType(78); internal_static_sysml_DocumentQueryCell_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_DocumentQueryCell_descriptor, new java.lang.String[] { "Values", }); internal_static_sysml_DocumentQueryRow_descriptor = - getDescriptor().getMessageType(78); + getDescriptor().getMessageType(79); internal_static_sysml_DocumentQueryRow_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_DocumentQueryRow_descriptor, new java.lang.String[] { "Element", "Cells", }); internal_static_sysml_RunDocumentQueryResponse_descriptor = - getDescriptor().getMessageType(79); + getDescriptor().getMessageType(80); internal_static_sysml_RunDocumentQueryResponse_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_RunDocumentQueryResponse_descriptor, new java.lang.String[] { "Columns", "Rows", }); internal_static_sysml_RenderDocumentRequest_descriptor = - getDescriptor().getMessageType(80); + getDescriptor().getMessageType(81); internal_static_sysml_RenderDocumentRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_RenderDocumentRequest_descriptor, new java.lang.String[] { "ModelHash", "DocumentId", }); internal_static_sysml_RenderDocumentResponse_descriptor = - getDescriptor().getMessageType(81); + getDescriptor().getMessageType(82); internal_static_sysml_RenderDocumentResponse_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_sysml_RenderDocumentResponse_descriptor, diff --git a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/Value.java b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/Value.java index 78aec21c3..c0161a70d 100644 --- a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/Value.java +++ b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/Value.java @@ -69,6 +69,7 @@ public enum KindCase VECTOR_QUANTITY(14), MEASUREMENT_REF(15), INFINITY(16), + FUNCTION(17), KIND_NOT_SET(0); private final int value; private KindCase(int value) { @@ -102,6 +103,7 @@ public static KindCase forNumber(int value) { case 14: return VECTOR_QUANTITY; case 15: return MEASUREMENT_REF; case 16: return INFINITY; + case 17: return FUNCTION; case 0: return KIND_NOT_SET; default: return null; } @@ -709,6 +711,49 @@ public boolean getInfinity() { return false; } + public static final int FUNCTION_FIELD_NUMBER = 17; + /** + *
+   * a calc as a value, named by its declaration
+   * 
+ * + * .sysml.Function function = 17 [json_name = "function"]; + * @return Whether the function field is set. + */ + @java.lang.Override + public boolean hasFunction() { + return kindCase_ == 17; + } + /** + *
+   * a calc as a value, named by its declaration
+   * 
+ * + * .sysml.Function function = 17 [json_name = "function"]; + * @return The function. + */ + @java.lang.Override + public org.openmbee.opensysml.proto.Function getFunction() { + if (kindCase_ == 17) { + return (org.openmbee.opensysml.proto.Function) kind_; + } + return org.openmbee.opensysml.proto.Function.getDefaultInstance(); + } + /** + *
+   * a calc as a value, named by its declaration
+   * 
+ * + * .sysml.Function function = 17 [json_name = "function"]; + */ + @java.lang.Override + public org.openmbee.opensysml.proto.FunctionOrBuilder getFunctionOrBuilder() { + if (kindCase_ == 17) { + return (org.openmbee.opensysml.proto.Function) kind_; + } + return org.openmbee.opensysml.proto.Function.getDefaultInstance(); + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -777,6 +822,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) output.writeBool( 16, (boolean)((java.lang.Boolean) kind_)); } + if (kindCase_ == 17) { + output.writeMessage(17, (org.openmbee.opensysml.proto.Function) kind_); + } getUnknownFields().writeTo(output); } @@ -854,6 +902,10 @@ public int getSerializedSize() { .computeBoolSize( 16, (boolean)((java.lang.Boolean) kind_)); } + if (kindCase_ == 17) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(17, (org.openmbee.opensysml.proto.Function) kind_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -936,6 +988,10 @@ public boolean equals(final java.lang.Object obj) { if (getInfinity() != other.getInfinity()) return false; break; + case 17: + if (!getFunction() + .equals(other.getFunction())) return false; + break; case 0: default: } @@ -1021,6 +1077,10 @@ public int hashCode() { hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( getInfinity()); break; + case 17: + hash = (37 * hash) + FUNCTION_FIELD_NUMBER; + hash = (53 * hash) + getFunction().hashCode(); + break; case 0: default: } @@ -1183,6 +1243,9 @@ public Builder clear() { if (measurementRefBuilder_ != null) { measurementRefBuilder_.clear(); } + if (functionBuilder_ != null) { + functionBuilder_.clear(); + } kindCase_ = 0; kind_ = null; return this; @@ -1256,6 +1319,10 @@ private void buildPartialOneofs(org.openmbee.opensysml.proto.Value result) { measurementRefBuilder_ != null) { result.kind_ = measurementRefBuilder_.build(); } + if (kindCase_ == 17 && + functionBuilder_ != null) { + result.kind_ = functionBuilder_.build(); + } } @java.lang.Override @@ -1339,6 +1406,10 @@ public Builder mergeFrom(org.openmbee.opensysml.proto.Value other) { setInfinity(other.getInfinity()); break; } + case FUNCTION: { + mergeFunction(other.getFunction()); + break; + } case KIND_NOT_SET: { break; } @@ -1467,6 +1538,13 @@ public Builder mergeFrom( kindCase_ = 16; break; } // case 128 + case 138: { + input.readMessage( + internalGetFunctionFieldBuilder().getBuilder(), + extensionRegistry); + kindCase_ = 17; + break; + } // case 138 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -3373,6 +3451,184 @@ public Builder clearInfinity() { return this; } + private com.google.protobuf.SingleFieldBuilder< + org.openmbee.opensysml.proto.Function, org.openmbee.opensysml.proto.Function.Builder, org.openmbee.opensysml.proto.FunctionOrBuilder> functionBuilder_; + /** + *
+     * a calc as a value, named by its declaration
+     * 
+ * + * .sysml.Function function = 17 [json_name = "function"]; + * @return Whether the function field is set. + */ + @java.lang.Override + public boolean hasFunction() { + return kindCase_ == 17; + } + /** + *
+     * a calc as a value, named by its declaration
+     * 
+ * + * .sysml.Function function = 17 [json_name = "function"]; + * @return The function. + */ + @java.lang.Override + public org.openmbee.opensysml.proto.Function getFunction() { + if (functionBuilder_ == null) { + if (kindCase_ == 17) { + return (org.openmbee.opensysml.proto.Function) kind_; + } + return org.openmbee.opensysml.proto.Function.getDefaultInstance(); + } else { + if (kindCase_ == 17) { + return functionBuilder_.getMessage(); + } + return org.openmbee.opensysml.proto.Function.getDefaultInstance(); + } + } + /** + *
+     * a calc as a value, named by its declaration
+     * 
+ * + * .sysml.Function function = 17 [json_name = "function"]; + */ + public Builder setFunction(org.openmbee.opensysml.proto.Function value) { + if (functionBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + kind_ = value; + onChanged(); + } else { + functionBuilder_.setMessage(value); + } + kindCase_ = 17; + return this; + } + /** + *
+     * a calc as a value, named by its declaration
+     * 
+ * + * .sysml.Function function = 17 [json_name = "function"]; + */ + public Builder setFunction( + org.openmbee.opensysml.proto.Function.Builder builderForValue) { + if (functionBuilder_ == null) { + kind_ = builderForValue.build(); + onChanged(); + } else { + functionBuilder_.setMessage(builderForValue.build()); + } + kindCase_ = 17; + return this; + } + /** + *
+     * a calc as a value, named by its declaration
+     * 
+ * + * .sysml.Function function = 17 [json_name = "function"]; + */ + public Builder mergeFunction(org.openmbee.opensysml.proto.Function value) { + if (functionBuilder_ == null) { + if (kindCase_ == 17 && + kind_ != org.openmbee.opensysml.proto.Function.getDefaultInstance()) { + kind_ = org.openmbee.opensysml.proto.Function.newBuilder((org.openmbee.opensysml.proto.Function) kind_) + .mergeFrom(value).buildPartial(); + } else { + kind_ = value; + } + onChanged(); + } else { + if (kindCase_ == 17) { + functionBuilder_.mergeFrom(value); + } else { + functionBuilder_.setMessage(value); + } + } + kindCase_ = 17; + return this; + } + /** + *
+     * a calc as a value, named by its declaration
+     * 
+ * + * .sysml.Function function = 17 [json_name = "function"]; + */ + public Builder clearFunction() { + if (functionBuilder_ == null) { + if (kindCase_ == 17) { + kindCase_ = 0; + kind_ = null; + onChanged(); + } + } else { + if (kindCase_ == 17) { + kindCase_ = 0; + kind_ = null; + } + functionBuilder_.clear(); + } + return this; + } + /** + *
+     * a calc as a value, named by its declaration
+     * 
+ * + * .sysml.Function function = 17 [json_name = "function"]; + */ + public org.openmbee.opensysml.proto.Function.Builder getFunctionBuilder() { + return internalGetFunctionFieldBuilder().getBuilder(); + } + /** + *
+     * a calc as a value, named by its declaration
+     * 
+ * + * .sysml.Function function = 17 [json_name = "function"]; + */ + @java.lang.Override + public org.openmbee.opensysml.proto.FunctionOrBuilder getFunctionOrBuilder() { + if ((kindCase_ == 17) && (functionBuilder_ != null)) { + return functionBuilder_.getMessageOrBuilder(); + } else { + if (kindCase_ == 17) { + return (org.openmbee.opensysml.proto.Function) kind_; + } + return org.openmbee.opensysml.proto.Function.getDefaultInstance(); + } + } + /** + *
+     * a calc as a value, named by its declaration
+     * 
+ * + * .sysml.Function function = 17 [json_name = "function"]; + */ + private com.google.protobuf.SingleFieldBuilder< + org.openmbee.opensysml.proto.Function, org.openmbee.opensysml.proto.Function.Builder, org.openmbee.opensysml.proto.FunctionOrBuilder> + internalGetFunctionFieldBuilder() { + if (functionBuilder_ == null) { + if (!(kindCase_ == 17)) { + kind_ = org.openmbee.opensysml.proto.Function.getDefaultInstance(); + } + functionBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.openmbee.opensysml.proto.Function, org.openmbee.opensysml.proto.Function.Builder, org.openmbee.opensysml.proto.FunctionOrBuilder>( + (org.openmbee.opensysml.proto.Function) kind_, + getParentForChildren(), + isClean()); + kind_ = null; + } + kindCase_ = 17; + onChanged(); + return functionBuilder_; + } + // @@protoc_insertion_point(builder_scope:sysml.Value) } diff --git a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ValueOrBuilder.java b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ValueOrBuilder.java index 82dc6529d..00f57684d 100644 --- a/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ValueOrBuilder.java +++ b/clients/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto/ValueOrBuilder.java @@ -344,5 +344,32 @@ public interface ValueOrBuilder extends */ boolean getInfinity(); + /** + *
+   * a calc as a value, named by its declaration
+   * 
+ * + * .sysml.Function function = 17 [json_name = "function"]; + * @return Whether the function field is set. + */ + boolean hasFunction(); + /** + *
+   * a calc as a value, named by its declaration
+   * 
+ * + * .sysml.Function function = 17 [json_name = "function"]; + * @return The function. + */ + org.openmbee.opensysml.proto.Function getFunction(); + /** + *
+   * a calc as a value, named by its declaration
+   * 
+ * + * .sysml.Function function = 17 [json_name = "function"]; + */ + org.openmbee.opensysml.proto.FunctionOrBuilder getFunctionOrBuilder(); + org.openmbee.opensysml.proto.Value.KindCase getKindCase(); } diff --git a/clients/java/opensysml-client/src/test/java/org/openmbee/opensysml/ApiIntegrationTest.java b/clients/java/opensysml-client/src/test/java/org/openmbee/opensysml/ApiIntegrationTest.java index 898c8b32c..1e78196c1 100644 --- a/clients/java/opensysml-client/src/test/java/org/openmbee/opensysml/ApiIntegrationTest.java +++ b/clients/java/opensysml-client/src/test/java/org/openmbee/opensysml/ApiIntegrationTest.java @@ -234,6 +234,41 @@ void aBareMeasurementReferenceArrivesWithItsReductionAndDeclarationOverProtobufA } } + private static final String FUNCTIONS = + """ + package Demo { + private import ScalarValues::*; + calc def Sq { in v : Real; return : Real = v * v; } + calc def Fn { in calc f { in v : Real; return : Real; } in a : Real; return : Real = f(a); } + calc def Identity { in calc f { in v : Real; return : Real; } return r = f; } + attribute pick = Identity(Sq); + attribute nine = Fn(Sq, 3.0); + part def Scaler { + attribute k : Real = 2.0; + calc scale { in x : Real; return : Real = x * k; } + } + part holder : Scaler; + attribute scaler = holder.scale; + } + """; + + @Test + void aCalcHeldAsAValueArrivesAsTheFunctionItNamesOverProtobufAndJson() { + assertTrue(connection.capabilities().has(Capabilities.FUNCTION_VALUES)); + try (Connection json = + Connection.open(ServiceBinary.options().encoding(Encoding.JSON).build())) { + for (Connection each : List.of(connection, json)) { + Model model = each.parse(FUNCTIONS); + assertEquals( + new Value.FunctionValue("Demo::Sq", Optional.empty()), model.eval("Demo::pick")); + assertEquals(new Value.RealValue(9.0), model.eval("Demo::nine")); + Value.FunctionValue scale = (Value.FunctionValue) model.eval("Demo::scaler"); + assertEquals("Demo::Scaler::scale", scale.calcId()); + assertTrue(scale.selfId().orElseThrow() > 0); + } + } + } + @Test void aModelTheServiceDoesNotHoldIsRefused() { Model absent = connection.model("sha256:0000000000000000"); diff --git a/clients/java/opensysml-client/src/test/java/org/openmbee/opensysml/internal/ProtosTest.java b/clients/java/opensysml-client/src/test/java/org/openmbee/opensysml/internal/ProtosTest.java index ca9629e01..d29119f19 100644 --- a/clients/java/opensysml-client/src/test/java/org/openmbee/opensysml/internal/ProtosTest.java +++ b/clients/java/opensysml-client/src/test/java/org/openmbee/opensysml/internal/ProtosTest.java @@ -13,6 +13,7 @@ import org.openmbee.opensysml.proto.AttributeInfo; import org.openmbee.opensysml.proto.Complex; import org.openmbee.opensysml.proto.FeatureValue; +import org.openmbee.opensysml.proto.Function; import org.openmbee.opensysml.proto.MeasurementRef; import org.openmbee.opensysml.proto.SymbolInfo; import org.openmbee.opensysml.proto.UnitFactor; @@ -319,6 +320,39 @@ void aMeasurementReferenceReadsItsUnitItsReductionAndTheDeclarationItNames() { assertThrows(TransportException.class, () -> Protos.value(nested)); } + private static org.openmbee.opensysml.proto.Value function(String calcId, long selfId) { + return org.openmbee.opensysml.proto.Value.newBuilder() + .setFunction(Function.newBuilder().setCalcId(calcId).setSelfId(selfId)) + .build(); + } + + @Test + void aFunctionIsTheCalcItNamesReadAgainstAnObjectOrNone() { + assertEquals( + Optional.of(new Value.FunctionValue("Demo::Sq", Optional.empty())), + Protos.value(function("Demo::Sq", 0))); + assertEquals( + Optional.of(new Value.FunctionValue("Demo::Scaler::scale", Optional.of(7L))), + Protos.value(function("Demo::Scaler::scale", 7))); + // The object is part of the identity: another object's read is another value. + assertNotEquals( + Protos.value(function("Demo::Scaler::scale", 7)), + Protos.value(function("Demo::Scaler::scale", 8))); + + // Naming no calc is malformed at any depth, on the wire and in the record. + org.openmbee.opensysml.proto.Value noCalc = function("", 0); + TransportException nothing = + assertThrows(TransportException.class, () -> Protos.value(noCalc)); + assertTrue(nothing.getMessage().contains("names no calc"), nothing.getMessage()); + org.openmbee.opensysml.proto.Value nested = + org.openmbee.opensysml.proto.Value.newBuilder() + .setSequence(ValueSequence.newBuilder().addElements(function("", 3))) + .build(); + assertThrows(TransportException.class, () -> Protos.value(nested)); + assertThrows( + IllegalArgumentException.class, () -> new Value.FunctionValue("", Optional.empty())); + } + @Test void aQuantityWithoutAMagnitudeIsRefusedRatherThanReadAsZero() { org.openmbee.opensysml.proto.Quantity noMagnitude = diff --git a/clients/java/opensysml-conformance/src/main/java/org/openmbee/opensysml/conformance/Normalizer.java b/clients/java/opensysml-conformance/src/main/java/org/openmbee/opensysml/conformance/Normalizer.java index 2e5f1600b..18331c3b1 100644 --- a/clients/java/opensysml-conformance/src/main/java/org/openmbee/opensysml/conformance/Normalizer.java +++ b/clients/java/opensysml-conformance/src/main/java/org/openmbee/opensysml/conformance/Normalizer.java @@ -27,7 +27,11 @@ final class Normalizer { /** The int64 fields carrying a runtime instance id, which is assigned per call. */ private static final Set NORMALIZED_IDS = - Set.of("sysml.Instance.id", "sysml.Value.instance_id", "sysml.Verdict.instance_id"); + Set.of( + "sysml.Instance.id", + "sysml.Value.instance_id", + "sysml.Verdict.instance_id", + "sysml.Function.self_id"); private final String modelHash; private final Map labels = new HashMap<>(); diff --git a/clients/java/opensysml-conformance/src/main/java/org/openmbee/opensysml/conformance/Rendering.java b/clients/java/opensysml-conformance/src/main/java/org/openmbee/opensysml/conformance/Rendering.java index 4ab23f099..8430d77c4 100644 --- a/clients/java/opensysml-conformance/src/main/java/org/openmbee/opensysml/conformance/Rendering.java +++ b/clients/java/opensysml-conformance/src/main/java/org/openmbee/opensysml/conformance/Rendering.java @@ -87,6 +87,11 @@ static org.openmbee.opensysml.proto.Value value(Value value) { .setUnitTerm(unitTerm(ref.reduction())); ref.unitId().ifPresent(reference::setUnitId); builder.setMeasurementRef(reference); + } else if (value instanceof Value.FunctionValue function) { + builder.setFunction( + org.openmbee.opensysml.proto.Function.newBuilder() + .setCalcId(function.calcId()) + .setSelfId(function.selfId().orElse(0L))); } else { throw new IllegalStateException("no rendering for " + value.getClass()); } diff --git a/clients/java/opensysml-conformance/src/test/java/org/openmbee/opensysml/conformance/NormalizerTest.java b/clients/java/opensysml-conformance/src/test/java/org/openmbee/opensysml/conformance/NormalizerTest.java index 2269e59f4..8b1c3d1aa 100644 --- a/clients/java/opensysml-conformance/src/test/java/org/openmbee/opensysml/conformance/NormalizerTest.java +++ b/clients/java/opensysml-conformance/src/test/java/org/openmbee/opensysml/conformance/NormalizerTest.java @@ -5,6 +5,7 @@ import org.openmbee.opensysml.proto.Diagnostic; import org.openmbee.opensysml.proto.EvaluateResponse; import org.openmbee.opensysml.proto.FeatureValue; +import org.openmbee.opensysml.proto.Function; import org.openmbee.opensysml.proto.Instance; import org.openmbee.opensysml.proto.InstantiateResponse; import org.openmbee.opensysml.proto.ParseFileResponse; @@ -75,6 +76,15 @@ void instanceIdsAreLabelledInOrderOfFirstAppearance() { FeatureValue.newBuilder() .setFeatureName("part") .setValue(Value.newBuilder().setInstanceId(7)) + .build()) + .putFeatureValues( + "scale", + FeatureValue.newBuilder() + .setFeatureName("scale") + .setValue( + Value.newBuilder() + .setFunction( + Function.newBuilder().setCalcId("T::scale").setSelfId(41))) .build())) .addInstances(Instance.newBuilder().setId(41)) .addInstances(Instance.newBuilder().setId(7)) @@ -86,6 +96,9 @@ void instanceIdsAreLabelledInOrderOfFirstAppearance() { Map features = (Map) root.get("feature_values"); Map value = (Map) ((Map) features.get("part")).get("value"); assertEquals("@2", value.get("instance_id")); + Map function = + (Map) ((Map) ((Map) features.get("scale")).get("value")).get("function"); + assertEquals("@1", function.get("self_id")); List instances = (List) normalized.get("instances"); assertEquals("@1", ((Map) instances.get(0)).get("id")); diff --git a/clients/node/README.md b/clients/node/README.md index e49a6dbad..d500b1af2 100644 --- a/clients/node/README.md +++ b/clients/node/README.md @@ -57,6 +57,7 @@ switch (value.kind) { case "string": value.value; case "quantity": value.magnitude; value.unit; // 1500.0 [kg] case "measurementRef": value.unit; value.unitTerm; value.unitId; // a bare unit: km, reduced to 1000·metre + case "function": value.calcId; value.selfId; // a calc as a value: Demo::Sq, or holder.scale read off an object case "array": value.dimensions; value.elements; // row-major, an element is any SysMLValue case "vector": value.components; // { kind: "int" | "real" }[] case "vectorQuantity": value.components; // QuantityValue[], a unit per component @@ -185,9 +186,11 @@ The client checks the advertised list **before** making such a call so it can raise a `MissingCapabilityError` naming the service, its version and the way to get one that has it. A direct capability-gated request to a service without the capability is refused with `UNIMPLEMENTED`; response-population capabilities -instead omit the fields they name. A service without `structured_values` or -`measurement_refs` sends the value kinds those name (`array`, `vector`, -`vectorQuantity`; `measurementRef`) as `null` with an `unsupported: …` reason. +instead omit the fields they name. A service without `structured_values`, +`measurement_refs` or `function_values` sends the value kinds those name (`array`, +`vector`, `vectorQuantity`; `measurementRef`; `function`) as `null` with an +`unsupported: …` reason. A function closing over the bindings of a behavior body +has no wire form and is sent as `null` by every service. ## Failures are typed diff --git a/clients/node/conformance/normalize.ts b/clients/node/conformance/normalize.ts index a3767a0e8..4910da76f 100644 --- a/clients/node/conformance/normalize.ts +++ b/clients/node/conformance/normalize.ts @@ -16,6 +16,7 @@ const NORMALIZED_IDS = new Set([ "sysml.Instance.id", "sysml.Value.instance_id", "sysml.Verdict.instance_id", + "sysml.Function.self_id", ]); /** diff --git a/clients/node/src/core/capabilities.ts b/clients/node/src/core/capabilities.ts index fb65578a7..2b7ad68f1 100644 --- a/clients/node/src/core/capabilities.ts +++ b/clients/node/src/core/capabilities.ts @@ -22,6 +22,8 @@ export const CAPABILITY_COMPLEX_VALUES = "complex_values"; export const CAPABILITY_STRUCTURED_VALUES = "structured_values"; /** A bare measurement unit (`SI::m`, `m / s`) as `Value.measurement_ref`, rather than an unsupported null. */ export const CAPABILITY_MEASUREMENT_REFS = "measurement_refs"; +/** A calc held as a value as `Value.function`, named by its declaration, rather than an unsupported null. */ +export const CAPABILITY_FUNCTION_VALUES = "function_values"; /** The unbounded value `*` as `Value.infinity`, rather than an unsupported null. */ export const CAPABILITY_INFINITY_VALUE = "infinity_value"; /** `ParseFileRequest.language`, which declares the language of inline content. */ diff --git a/clients/node/src/core/index.ts b/clients/node/src/core/index.ts index 7ecdae194..da1e56e26 100644 --- a/clients/node/src/core/index.ts +++ b/clients/node/src/core/index.ts @@ -28,6 +28,7 @@ export { CAPABILITY_ENUM_VALUES, CAPABILITY_EVALUATE_SUBJECT, CAPABILITY_FEATURE_VALUES, + CAPABILITY_FUNCTION_VALUES, CAPABILITY_INFINITY_VALUE, CAPABILITY_INLINE_LANGUAGE, CAPABILITY_MEASUREMENT_REFS, @@ -72,6 +73,7 @@ export type { ArrayValue, ComplexValue, EnumValue, + FunctionValue, Magnitude, MeasurementRefValue, QuantityValue, diff --git a/clients/node/src/core/values.ts b/clients/node/src/core/values.ts index e1a84e1e6..c7b377eb3 100644 --- a/clients/node/src/core/values.ts +++ b/clients/node/src/core/values.ts @@ -5,6 +5,7 @@ import { create } from "@bufbuild/protobuf"; import type { Array as ArrayMessage, EnumLiteral, + Function as FunctionMessage, MeasurementRef, Quantity, UnitTerm, @@ -18,6 +19,7 @@ import { ComplexSchema, EnumLiteralSchema, FailureReason, + FunctionSchema, MeasurementRefSchema, QuantitySchema, UnitFactorSchema, @@ -77,6 +79,17 @@ export interface MeasurementRefValue { unitId?: string; } +/** + * A calc held as a value — a calc definition, or a calc usage with an input no + * read could supply — named by the FQN of its declaration, which is its identity. + * `selfId` is the object its feature names resolve against, for a calc usage + * read off a part (`holder.scale`); absent for a function closing over no object. + */ +export interface FunctionValue { + calcId: string; + selfId?: bigint; +} + /** * A multidimensional array: `dimensions` gives the extent of each dimension and * `elements` the elements flattened row-major, the last dimension varying @@ -103,6 +116,7 @@ export type SysMLValue = | { kind: "sequence"; elements: SysMLValue[] } | ({ kind: "quantity" } & QuantityValue) | ({ kind: "measurementRef" } & MeasurementRefValue) + | ({ kind: "function" } & FunctionValue) | { kind: "enum"; value: EnumValue } | ({ kind: "array" } & ArrayValue) | { kind: "vector"; components: Magnitude[] } @@ -167,6 +181,8 @@ export function decodeValue(value: Value | undefined): SysMLValue { return { kind: "quantity", ...decodeQuantity(kind.value) }; case "measurementRef": return { kind: "measurementRef", ...decodeMeasurementRef(kind.value) }; + case "function": + return { kind: "function", ...decodeFunction(kind.value) }; case "enumLiteral": return { kind: "enum", value: decodeEnumLiteral(kind.value) }; case "array": @@ -226,6 +242,8 @@ export function encodeValue(value: SysMLValue): Value { return create(ValueSchema, { kind: { case: "measurementRef", value: encodeMeasurementRef(value) }, }); + case "function": + return create(ValueSchema, { kind: { case: "function", value: encodeFunction(value) } }); case "enum": return create(ValueSchema, { kind: { case: "enumLiteral", value: create(EnumLiteralSchema, value.value) }, @@ -328,6 +346,8 @@ export function formatValue(value: SysMLValue): string { } case "measurementRef": return value.unit === "" ? formatUnitTerm(value.unitTerm) : value.unit; + case "function": + return value.calcId; case "enum": return value.value.name; case "array": @@ -416,6 +436,20 @@ function encodeMeasurementRef(ref: MeasurementRefValue): MeasurementRef { }); } +function decodeFunction(fn: FunctionMessage): FunctionValue { + if (fn.calcId === "") { + throw new MalformedValueError("a function names no calc"); + } + return { calcId: fn.calcId, ...(fn.selfId === 0n ? {} : { selfId: fn.selfId }) }; +} + +function encodeFunction(fn: FunctionValue): FunctionMessage { + if (fn.calcId === "") { + throw new MalformedValueError("a function names no calc"); + } + return create(FunctionSchema, { calcId: fn.calcId, selfId: fn.selfId ?? 0n }); +} + function encodeUnitTerm(term: UnitFactorization): UnitTerm { return create(UnitTermSchema, { scaleNum: term.scaleNum, diff --git a/clients/node/src/generated/sysml_pb.ts b/clients/node/src/generated/sysml_pb.ts index 1fbafc31f..2079572ab 100644 --- a/clients/node/src/generated/sysml_pb.ts +++ b/clients/node/src/generated/sysml_pb.ts @@ -10,7 +10,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file sysml.proto. */ export const file_sysml: GenFile = /*@__PURE__*/ - fileDesc("CgtzeXNtbC5wcm90bxIFc3lzbWwi4gEKB1ZlcmRpY3QSDAoEa2luZBgBIAEoCRISCgplbGVtZW50X2lkGAIgASgJEg8KB2VsZW1lbnQYAyABKAkSDQoFaG9sZHMYBCABKAgSEQoJY29uZGl0aW9uGAUgASgJEhMKC2luc3RhbmNlX2lkGAYgASgDEhgKEGluc3RhbmNlX3R5cGVfaWQYByABKAkSDQoFZXJyb3IYCCABKAkSLAoOZmFpbHVyZV9yZWFzb24YCSABKA4yFC5zeXNtbC5GYWlsdXJlUmVhc29uEhYKDnJlcXVpcmVtZW50X2lkGAogASgJIlsKF1ZlcmlmeUNvbnN0cmFpbnRSZXF1ZXN0EhIKCm1vZGVsX2hhc2gYASABKAkSEQoJc3ltYm9sX2lkGAIgASgJEhkKEXN1YmplY3Rfc3ltYm9sX2lkGAMgASgJIpYBChhWZXJpZnlDb25zdHJhaW50UmVzcG9uc2USHwoHdmVyZGljdBgBIAEoCzIOLnN5c21sLlZlcmRpY3QSIgoJaW5zdGFuY2VzGAIgAygLMg8uc3lzbWwuSW5zdGFuY2USDQoFZXJyb3IYAyABKAkSJgoLZGlhZ25vc3RpY3MYBCADKAsyES5zeXNtbC5EaWFnbm9zdGljIlwKGFZlcmlmeVJlcXVpcmVtZW50UmVxdWVzdBISCgptb2RlbF9oYXNoGAEgASgJEhEKCXN5bWJvbF9pZBgCIAEoCRIZChFzdWJqZWN0X3N5bWJvbF9pZBgDIAEoCSJtChNWZXJpZmljYXRpb25WZXJkaWN0Eg8KB2Nhc2VfaWQYASABKAkSDAoEa2luZBgCIAEoCRIOCgZkZXRhaWwYAyABKAkSDwoHc3ViY2FzZRgEIAEoCBIWCg5yZXF1aXJlbWVudF9pZBgFIAEoCSLSAQoZVmVyaWZ5UmVxdWlyZW1lbnRSZXNwb25zZRIfCgd2ZXJkaWN0GAEgASgLMg4uc3lzbWwuVmVyZGljdBIiCglpbnN0YW5jZXMYAiADKAsyDy5zeXNtbC5JbnN0YW5jZRINCgVlcnJvchgDIAEoCRImCgtkaWFnbm9zdGljcxgEIAMoCzIRLnN5c21sLkRpYWdub3N0aWMSOQoVdmVyaWZpY2F0aW9uX3ZlcmRpY3RzGAUgAygLMhouc3lzbWwuVmVyaWZpY2F0aW9uVmVyZGljdCJCChlWZXJpZnlTYXRpc2ZhY3Rpb25SZXF1ZXN0EhIKCm1vZGVsX2hhc2gYASABKAkSEQoJc3ltYm9sX2lkGAIgASgJIoICChpWZXJpZnlTYXRpc2ZhY3Rpb25SZXNwb25zZRIgCgh2ZXJkaWN0cxgBIAMoCzIOLnN5c21sLlZlcmRpY3QSIgoJaW5zdGFuY2VzGAIgAygLMg8uc3lzbWwuSW5zdGFuY2USDQoFZXJyb3IYAyABKAkSJgoLZGlhZ25vc3RpY3MYBCADKAsyES5zeXNtbC5EaWFnbm9zdGljEiwKDmZhaWx1cmVfcmVhc29uGAUgASgOMhQuc3lzbWwuRmFpbHVyZVJlYXNvbhI5ChV2ZXJpZmljYXRpb25fdmVyZGljdHMYBiADKAsyGi5zeXNtbC5WZXJpZmljYXRpb25WZXJkaWN0Il0KE0V2YWx1YXRlQ2FsY1JlcXVlc3QSEgoKbW9kZWxfaGFzaBgBIAEoCRIRCglzeW1ib2xfaWQYAiABKAkSHwoJYXJndW1lbnRzGAMgAygLMgwuc3lzbWwuVmFsdWUivQEKFEV2YWx1YXRlQ2FsY1Jlc3BvbnNlEhwKBnJlc3VsdBgBIAEoCzIMLnN5c21sLlZhbHVlEiIKB291dHB1dHMYAiADKAsyES5zeXNtbC5DYWxjT3V0cHV0Eg0KBWVycm9yGAMgASgJEiYKC2RpYWdub3N0aWNzGAQgAygLMhEuc3lzbWwuRGlhZ25vc3RpYxIsCg5mYWlsdXJlX3JlYXNvbhgFIAEoDjIULnN5c21sLkZhaWx1cmVSZWFzb24iNwoKQ2FsY091dHB1dBIMCgRuYW1lGAEgASgJEhsKBXZhbHVlGAIgASgLMgwuc3lzbWwuVmFsdWUihAIKElJ1bkFuYWx5c2lzUmVxdWVzdBISCgptb2RlbF9oYXNoGAEgASgJEhEKCXN5bWJvbF9pZBgCIAEoCRIZChFzdWJqZWN0X3N5bWJvbF9pZBgDIAEoCRIfCglhcmd1bWVudHMYBCADKAsyDC5zeXNtbC5WYWx1ZRJGCg9uYW1lZF9hcmd1bWVudHMYBSADKAsyLS5zeXNtbC5SdW5BbmFseXNpc1JlcXVlc3QuTmFtZWRBcmd1bWVudHNFbnRyeRpDChNOYW1lZEFyZ3VtZW50c0VudHJ5EgsKA2tleRgBIAEoCRIbCgV2YWx1ZRgCIAEoCzIMLnN5c21sLlZhbHVlOgI4ASKfAgoTUnVuQW5hbHlzaXNSZXNwb25zZRIiCgdvdXRwdXRzGAEgAygLMhEuc3lzbWwuQ2FsY091dHB1dBIgCgh2ZXJkaWN0cxgCIAMoCzIOLnN5c21sLlZlcmRpY3QSIgoJaW5zdGFuY2VzGAMgAygLMg8uc3lzbWwuSW5zdGFuY2USDQoFZXJyb3IYBCABKAkSJgoLZGlhZ25vc3RpY3MYBSADKAsyES5zeXNtbC5EaWFnbm9zdGljEiwKDmZhaWx1cmVfcmVhc29uGAYgASgOMhQuc3lzbWwuRmFpbHVyZVJlYXNvbhI5ChV2ZXJpZmljYXRpb25fdmVyZGljdHMYByADKAsyGi5zeXNtbC5WZXJpZmljYXRpb25WZXJkaWN0IowBChBQYXJzZUZpbGVSZXF1ZXN0EhMKCWZpbGVfcGF0aBgBIAEoCUgAEhEKB2NvbnRlbnQYAiABKAlIABIYCgxjb250ZW50X2hhc2gYAyABKAlCAhgBEhAKCGxhbmd1YWdlGAQgASgJEhoKEnN0cmljdF9jb25mb3JtYW5jZRgFIAEoCEIICgZzb3VyY2UiYgoOU291cmNlRG9jdW1lbnQSEwoJZmlsZV9wYXRoGAEgASgJSAASEQoHY29udGVudBgCIAEoCUgAEhAKCGxhbmd1YWdlGAMgASgJEgwKBG5hbWUYBCABKAlCCAoGc291cmNlIlsKE1BhcnNlU291cmNlc1JlcXVlc3QSKAoJZG9jdW1lbnRzGAEgAygLMhUuc3lzbWwuU291cmNlRG9jdW1lbnQSGgoSc3RyaWN0X2NvbmZvcm1hbmNlGAIgASgIIoMBChRQYXJzZVNvdXJjZXNSZXNwb25zZRISCgptb2RlbF9oYXNoGAEgASgJEiAKBXJvb3RzGAIgAygLMhEuc3lzbWwuU3ltYm9sSW5mbxImCgtkaWFnbm9zdGljcxgDIAMoCzIRLnN5c21sLkRpYWdub3N0aWMSDQoFZXJyb3IYBCABKAkifwoRUGFyc2VGaWxlUmVzcG9uc2USEgoKbW9kZWxfaGFzaBgBIAEoCRIfCgRyb290GAIgASgLMhEuc3lzbWwuU3ltYm9sSW5mbxImCgtkaWFnbm9zdGljcxgDIAMoCzIRLnN5c21sLkRpYWdub3N0aWMSDQoFZXJyb3IYBCABKAkiOQoQR2V0U3ltYm9sUmVxdWVzdBISCgptb2RlbF9oYXNoGAEgASgJEhEKCXN5bWJvbF9pZBgCIAEoCSJCCg5TeW1ib2xSZXNwb25zZRIhCgZzeW1ib2wYASABKAsyES5zeXNtbC5TeW1ib2xJbmZvEg0KBWVycm9yGAIgASgJIigKEkRpYWdub3N0aWNzUmVxdWVzdBISCgptb2RlbF9oYXNoGAEgASgJIkwKE0RpYWdub3N0aWNzUmVzcG9uc2USJgoLZGlhZ25vc3RpY3MYASADKAsyES5zeXNtbC5EaWFnbm9zdGljEg0KBWVycm9yGAIgASgJIm8KD0V2YWx1YXRlUmVxdWVzdBISCgptb2RlbF9oYXNoGAEgASgJEhIKCmV4cHJlc3Npb24YAiABKAkSGQoRY29udGV4dF9zeW1ib2xfaWQYAyABKAkSGQoRc3ViamVjdF9zeW1ib2xfaWQYBCABKAkiZwoQRXZhbHVhdGVSZXNwb25zZRIcCgZyZXN1bHQYASABKAsyDC5zeXNtbC5WYWx1ZRINCgVlcnJvchgCIAEoCRImCgtkaWFnbm9zdGljcxgDIAMoCzIRLnN5c21sLkRpYWdub3N0aWMiwgEKCEluc3RhbmNlEgoKAmlkGAEgASgDEhYKDnR5cGVfc3ltYm9sX2lkGAIgASgJEjoKDmZlYXR1cmVfdmFsdWVzGAQgAygLMiIuc3lzbWwuSW5zdGFuY2UuRmVhdHVyZVZhbHVlc0VudHJ5GkkKEkZlYXR1cmVWYWx1ZXNFbnRyeRILCgNrZXkYASABKAkSIgoFdmFsdWUYAiABKAsyEy5zeXNtbC5GZWF0dXJlVmFsdWU6AjgBSgQIAxAEUgVzbG90cyKEAQoMRmVhdHVyZVZhbHVlEhQKDGZlYXR1cmVfbmFtZRgBIAEoCRIbCgV2YWx1ZRgCIAEoCzIMLnN5c21sLlZhbHVlEhwKBnZhbHVlcxgDIAMoCzIMLnN5c21sLlZhbHVlEhQKDG1hdGVyaWFsaXplZBgEIAEoCBINCgVlcnJvchgFIAEoCSI7ChJJbnN0YW50aWF0ZVJlcXVlc3QSEgoKbW9kZWxfaGFzaBgBIAEoCRIRCglzeW1ib2xfaWQYAiABKAkikwEKE0luc3RhbnRpYXRlUmVzcG9uc2USIQoIaW5zdGFuY2UYASABKAsyDy5zeXNtbC5JbnN0YW5jZRINCgVlcnJvchgCIAEoCRImCgtkaWFnbm9zdGljcxgDIAMoCzIRLnN5c21sLkRpYWdub3N0aWMSIgoJaW5zdGFuY2VzGAQgAygLMg8uc3lzbWwuSW5zdGFuY2UiugEKFEV4ZWN1dGVBY3Rpb25SZXF1ZXN0EhIKCm1vZGVsX2hhc2gYASABKAkSGAoQYWN0aW9uX3N5bWJvbF9pZBgCIAEoCRI3CgZpbnB1dHMYAyADKAsyJy5zeXNtbC5FeGVjdXRlQWN0aW9uUmVxdWVzdC5JbnB1dHNFbnRyeRo7CgtJbnB1dHNFbnRyeRILCgNrZXkYASABKAkSGwoFdmFsdWUYAiABKAsyDC5zeXNtbC5WYWx1ZToCOAEiyAEKFUV4ZWN1dGVBY3Rpb25SZXNwb25zZRI6CgdvdXRwdXRzGAEgAygLMikuc3lzbWwuRXhlY3V0ZUFjdGlvblJlc3BvbnNlLk91dHB1dHNFbnRyeRINCgVlcnJvchgCIAEoCRImCgtkaWFnbm9zdGljcxgDIAMoCzIRLnN5c21sLkRpYWdub3N0aWMaPAoMT3V0cHV0c0VudHJ5EgsKA2tleRgBIAEoCRIbCgV2YWx1ZRgCIAEoCzIMLnN5c21sLlZhbHVlOgI4ASJaChNFeGVjdXRlU3RhdGVSZXF1ZXN0EhIKCm1vZGVsX2hhc2gYASABKAkSHwoXc3RhdGVfbWFjaGluZV9zeW1ib2xfaWQYAiABKAkSDgoGZXZlbnRzGAMgAygJIu4BChRFeGVjdXRlU3RhdGVSZXNwb25zZRIWCg5zdGF0ZXNfdmlzaXRlZBgBIAMoCRJECg1maW5hbF9jb250ZXh0GAIgAygLMi0uc3lzbWwuRXhlY3V0ZVN0YXRlUmVzcG9uc2UuRmluYWxDb250ZXh0RW50cnkSDQoFZXJyb3IYAyABKAkSJgoLZGlhZ25vc3RpY3MYBCADKAsyES5zeXNtbC5EaWFnbm9zdGljGkEKEUZpbmFsQ29udGV4dEVudHJ5EgsKA2tleRgBIAEoCRIbCgV2YWx1ZRgCIAEoCzIMLnN5c21sLlZhbHVlOgI4ASKgAQoOQ29udmVydFJlcXVlc3QSEwoJZmlsZV9wYXRoGAEgASgJSAASEQoHY29udGVudBgCIAEoCUgAEhQKCm1vZGVsX2hhc2gYBiABKAlIABITCgtmcm9tX2Zvcm1hdBgDIAEoCRIRCgl0b19mb3JtYXQYBCABKAkSHgoWdG9sZXJhdGVfc3ludGF4X2Vycm9ycxgFIAEoCEIICgZzb3VyY2UitAEKD0NvbnZlcnRSZXNwb25zZRIPCgdjb250ZW50GAEgASgJEhMKC2Zyb21fZm9ybWF0GAIgASgJEhEKCXRvX2Zvcm1hdBgDIAEoCRINCgVlcnJvchgEIAEoCRImCgtkaWFnbm9zdGljcxgFIAMoCzIRLnN5c21sLkRpYWdub3N0aWMSFAoMZXhwZXJpbWVudGFsGAYgASgIEhsKE2V4cGVyaW1lbnRhbF9ub3RpY2UYByABKAkiUQoRQXBwbHlFZGl0c1JlcXVlc3QSEgoKbW9kZWxfaGFzaBgBIAEoCRIoCgpvcGVyYXRpb25zGAIgAygLMhQuc3lzbWwuRWRpdE9wZXJhdGlvbiK8AQoNRWRpdE9wZXJhdGlvbhIoCglzZXRfdmFsdWUYASABKAsyEy5zeXNtbC5TZXRWYWx1ZUVkaXRIABIjCgZyZW5hbWUYAiABKAsyES5zeXNtbC5SZW5hbWVFZGl0SAASKgoKYWRkX21lbWJlchgDIAEoCzIULnN5c21sLkFkZE1lbWJlckVkaXRIABIjCgZkZWxldGUYBCABKAsyES5zeXNtbC5EZWxldGVFZGl0SABCCwoJb3BlcmF0aW9uIoIBCg1BZGRNZW1iZXJFZGl0Eg0KBW93bmVyGAEgASgJEgwKBGtpbmQYAiABKAkSDAoEbmFtZRgDIAEoCRIMCgR0eXBlGAQgASgJEhQKDG11bHRpcGxpY2l0eRgFIAEoCRINCgV2YWx1ZRgGIAEoCRITCgtzcGVjaWFsaXplcxgHIAMoCSItCgpEZWxldGVFZGl0Eg4KBnRhcmdldBgBIAEoCRIPCgdjYXNjYWRlGAIgASgIIi0KDFNldFZhbHVlRWRpdBIOCgZ0YXJnZXQYASABKAkSDQoFdmFsdWUYAiABKAkiLgoKUmVuYW1lRWRpdBIOCgZ0YXJnZXQYASABKAkSEAoIbmV3X25hbWUYAiABKAkiwgEKEkFwcGx5RWRpdHNSZXNwb25zZRIPCgdjb250ZW50GAEgASgJEiMKB2FwcGxpZWQYAiADKAsyEi5zeXNtbC5BcHBsaWVkRWRpdBINCgVlcnJvchgDIAEoCRIjCgdmYWlsdXJlGAQgASgOMhIuc3lzbWwuRWRpdEZhaWx1cmUSJgoLZGlhZ25vc3RpY3MYBSADKAsyES5zeXNtbC5EaWFnbm9zdGljEhoKEnJlZmVycmluZ19lbGVtZW50cxgGIAMoCSJ6CgtBcHBsaWVkRWRpdBIXCg9vcGVyYXRpb25faW5kZXgYASABKAUSDgoGdGFyZ2V0GAIgASgJEg4KBm9mZnNldBgDIAEoBRIOCgZsZW5ndGgYBCABKAUSEAoIb2xkX3RleHQYBSABKAkSEAoIbmV3X3RleHQYBiABKAki/QIKClN5bWJvbEluZm8SCgoCaWQYASABKAkSDAoEbmFtZRgCIAEoCRIMCgRraW5kGAMgASgJEjEKCG1ldGFkYXRhGAQgAygLMh8uc3lzbWwuU3ltYm9sSW5mby5NZXRhZGF0YUVudHJ5EhEKCWNoaWxkX2lkcxgFIAMoCRIoCgphdHRyaWJ1dGVzGAYgAygLMhQuc3lzbWwuQXR0cmlidXRlSW5mbxIiCgl0eXBlX2luZm8YByABKAsyDy5zeXNtbC5UeXBlSW5mbxItCgxtdWx0aXBsaWNpdHkYCCABKAsyFy5zeXNtbC5NdWx0aXBsaWNpdHlJbmZvEi4KD3NwZWNpYWxpemF0aW9ucxgJIAMoCzIVLnN5c21sLlNwZWNpYWxpemF0aW9uEiMKG3dpdGhoZWxkX2xpYnJhcnlfYXR0cmlidXRlcxgKIAEoBRovCg1NZXRhZGF0YUVudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEiWAoOU3BlY2lhbGl6YXRpb24SDAoEa2luZBgBIAEoCRIQCghkZWNsYXJlZBgCIAEoCRIRCgl0YXJnZXRfaWQYAyABKAkSEwoLdGFyZ2V0X2tpbmQYBCABKAkilQEKCFR5cGVJbmZvEhAKCGRlY2xhcmVkGAEgASgJEhMKC3Jlc29sdmVkX2lkGAIgASgJEhUKDXJlc29sdmVkX2tpbmQYAyABKAkSEQoJcHJpbWl0aXZlGAQgASgJEhgKEHByaW1pdGl2ZV9zb3VyY2UYBSABKAkSEAoIcXVhbnRpdHkYBiABKAgSDAoEdW5pdBgHIAEoCSIwChBNdWx0aXBsaWNpdHlJbmZvEg0KBWxvd2VyGAEgASgJEg0KBXVwcGVyGAIgASgJIlYKDUF0dHJpYnV0ZUluZm8SDAoEbmFtZRgBIAEoCRIMCgR0eXBlGAIgASgJEhsKBXZhbHVlGAMgASgLMgwuc3lzbWwuVmFsdWUSDAoEdW5pdBgEIAEoCSL2AwoFVmFsdWUSEwoJaW50X3ZhbHVlGAEgASgDSAASFAoKcmVhbF92YWx1ZRgCIAEoAUgAEhQKCmJvb2xfdmFsdWUYAyABKAhIABIWCgxzdHJpbmdfdmFsdWUYBCABKAlIABIVCgtpbnN0YW5jZV9pZBgFIAEoA0gAEigKCHNlcXVlbmNlGAYgASgLMhQuc3lzbWwuVmFsdWVTZXF1ZW5jZUgAEg4KBG51bGwYByABKAlIABIjCghxdWFudGl0eRgIIAEoCzIPLnN5c21sLlF1YW50aXR5SAASKgoMZW51bV9saXRlcmFsGAkgASgLMhIuc3lzbWwuRW51bUxpdGVyYWxIABIPCgV1bnNldBgKIAEoCEgAEiEKB2NvbXBsZXgYCyABKAsyDi5zeXNtbC5Db21wbGV4SAASHQoFYXJyYXkYDCABKAsyDC5zeXNtbC5BcnJheUgAEh8KBnZlY3RvchgNIAEoCzINLnN5c21sLlZlY3RvckgAEjAKD3ZlY3Rvcl9xdWFudGl0eRgOIAEoCzIVLnN5c21sLlZlY3RvclF1YW50aXR5SAASMAoPbWVhc3VyZW1lbnRfcmVmGA8gASgLMhUuc3lzbWwuTWVhc3VyZW1lbnRSZWZIABISCghpbmZpbml0eRgQIAEoCEgAQgYKBGtpbmQiOwoFQXJyYXkSEgoKZGltZW5zaW9ucxgBIAMoAxIeCghlbGVtZW50cxgCIAMoCzIMLnN5c21sLlZhbHVlIioKBlZlY3RvchIgCgpjb21wb25lbnRzGAEgAygLMgwuc3lzbWwuVmFsdWUiNQoOVmVjdG9yUXVhbnRpdHkSIwoKY29tcG9uZW50cxgBIAMoCzIPLnN5c21sLlF1YW50aXR5IioKB0NvbXBsZXgSDAoEcmVhbBgBIAEoARIRCglpbWFnaW5hcnkYAiABKAEiRwoLRW51bUxpdGVyYWwSEgoKbGl0ZXJhbF9pZBgBIAEoCRIWCg5lbnVtZXJhdGlvbl9pZBgCIAEoCRIMCgRuYW1lGAMgASgJIi8KDVZhbHVlU2VxdWVuY2USHgoIZWxlbWVudHMYASADKAsyDC5zeXNtbC5WYWx1ZSJ8CghRdWFudGl0eRIXCg1pbnRfbWFnbml0dWRlGAEgASgDSAASGAoOcmVhbF9tYWduaXR1ZGUYAiABKAFIABIMCgR1bml0GAMgASgJEiIKCXVuaXRfdGVybRgEIAEoCzIPLnN5c21sLlVuaXRUZXJtQgsKCW1hZ25pdHVkZSJTCg5NZWFzdXJlbWVudFJlZhIMCgR1bml0GAEgASgJEiIKCXVuaXRfdGVybRgCIAEoCzIPLnN5c21sLlVuaXRUZXJtEg8KB3VuaXRfaWQYAyABKAkiVAoIVW5pdFRlcm0SEQoJc2NhbGVfbnVtGAEgASgBEhEKCXNjYWxlX2RlbhgCIAEoARIiCgdmYWN0b3JzGAMgAygLMhEuc3lzbWwuVW5pdEZhY3RvciIvCgpVbml0RmFjdG9yEg8KB3VuaXRfaWQYASABKAkSEAoIZXhwb25lbnQYAiABKAEiSgoKRGlhZ25vc3RpYxIQCghzZXZlcml0eRgBIAEoCRIPCgdtZXNzYWdlGAIgASgJEhkKBHNwYW4YAyABKAsyCy5zeXNtbC5TcGFuIl4KBFNwYW4SDAoEZmlsZRgBIAEoCRISCgpzdGFydF9saW5lGAIgASgFEhEKCXN0YXJ0X2NvbBgDIAEoBRIQCghlbmRfbGluZRgEIAEoBRIPCgdlbmRfY29sGAUgASgFIhMKEVNlcnZlckluZm9SZXF1ZXN0IjsKElNlcnZlckluZm9SZXNwb25zZRIPCgd2ZXJzaW9uGAEgASgJEhQKDGNhcGFiaWxpdGllcxgCIAMoCSJTCgxRdWVyeVJlcXVlc3QSEgoKbW9kZWxfaGFzaBgBIAEoCRIbCgVxdWVyeRgCIAEoCzIMLnN5c21sLlF1ZXJ5EhIKCm9zbGNfcXVlcnkYAyABKAkiPAoNUXVlcnlSZXNwb25zZRIrCghlbGVtZW50cxgBIAMoCzIZLnN5c21sLlF1ZXJ5UmVzdWx0RWxlbWVudCJICgVRdWVyeRINCgVzY29wZRgBIAMoCRIOCgZzZWxlY3QYAiADKAkSIAoFd2hlcmUYAyABKAsyES5zeXNtbC5Db25zdHJhaW50InwKCkNvbnN0cmFpbnQSLwoJcHJpbWl0aXZlGAEgASgLMhouc3lzbWwuUHJpbWl0aXZlQ29uc3RyYWludEgAEi8KCWNvbXBvc2l0ZRgCIAEoCzIaLnN5c21sLkNvbXBvc2l0ZUNvbnN0cmFpbnRIAEIMCgpjb25zdHJhaW50InMKE1ByaW1pdGl2ZUNvbnN0cmFpbnQSDwoHaW52ZXJzZRgBIAEoCBIQCghwcm9wZXJ0eRgCIAEoCRIqCghvcGVyYXRvchgDIAEoDjIYLnN5c21sLlByaW1pdGl2ZU9wZXJhdG9yEg0KBXZhbHVlGAQgAygJImgKE0NvbXBvc2l0ZUNvbnN0cmFpbnQSKgoIb3BlcmF0b3IYASABKA4yGC5zeXNtbC5Db21wb3NpdGVPcGVyYXRvchIlCgpjb25zdHJhaW50GAIgAygLMhEuc3lzbWwuQ29uc3RyYWludCKgAQoSUXVlcnlSZXN1bHRFbGVtZW50EgoKAmlkGAEgASgJEgwKBHR5cGUYAiABKAkSPQoKcHJvcGVydGllcxgDIAMoCzIpLnN5c21sLlF1ZXJ5UmVzdWx0RWxlbWVudC5Qcm9wZXJ0aWVzRW50cnkaMQoPUHJvcGVydGllc0VudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEicwoKU3dlZXBSYW5nZRIRCglwYXJhbWV0ZXIYASABKAkSGwoFc3RhcnQYAiABKAsyDC5zeXNtbC5WYWx1ZRIZCgNlbmQYAyABKAsyDC5zeXNtbC5WYWx1ZRIaCgRzdGVwGAQgASgLMgwuc3lzbWwuVmFsdWUiwAIKD1J1blN3ZWVwUmVxdWVzdBISCgptb2RlbF9oYXNoGAEgASgJEhEKCXN5bWJvbF9pZBgCIAEoCRIZChFzdWJqZWN0X3N5bWJvbF9pZBgDIAEoCRIfCglhcmd1bWVudHMYBCADKAsyDC5zeXNtbC5WYWx1ZRJDCg9uYW1lZF9hcmd1bWVudHMYBSADKAsyKi5zeXNtbC5SdW5Td2VlcFJlcXVlc3QuTmFtZWRBcmd1bWVudHNFbnRyeRIhCgZyYW5nZXMYBiADKAsyES5zeXNtbC5Td2VlcFJhbmdlEg8KB3NhbXBsZXMYByABKAMSDAoEc2VlZBgIIAEoBBpDChNOYW1lZEFyZ3VtZW50c0VudHJ5EgsKA2tleRgBIAEoCRIbCgV2YWx1ZRgCIAEoCzIMLnN5c21sLlZhbHVlOgI4ASLIAQoIU3dlZXBSb3cSIQoGaW5wdXRzGAEgAygLMhEuc3lzbWwuQ2FsY091dHB1dBIiCgdvdXRwdXRzGAIgAygLMhEuc3lzbWwuQ2FsY091dHB1dBIgCgh2ZXJkaWN0cxgDIAMoCzIOLnN5c21sLlZlcmRpY3QSFgoOZWxhcHNlZF9taWNyb3MYBCABKAMSDQoFZXJyb3IYBSABKAkSLAoOZmFpbHVyZV9yZWFzb24YBiABKA4yFC5zeXNtbC5GYWlsdXJlUmVhc29uIu0BChBSdW5Td2VlcFJlc3BvbnNlEh0KBHJvd3MYASADKAsyDy5zeXNtbC5Td2VlcFJvdxISCgpwYXJhbWV0ZXJzGAIgAygJEg8KB3NhbXBsZWQYAyABKAgSDAoEc2VlZBgEIAEoBBINCgVlcnJvchgFIAEoCRImCgtkaWFnbm9zdGljcxgGIAMoCzIRLnN5c21sLkRpYWdub3N0aWMSLAoOZmFpbHVyZV9yZWFzb24YByABKA4yFC5zeXNtbC5GYWlsdXJlUmVhc29uEiIKCWluc3RhbmNlcxgIIAMoCzIPLnN5c21sLkluc3RhbmNlIm4KF1J1bkRvY3VtZW50UXVlcnlSZXF1ZXN0EhIKCm1vZGVsX2hhc2gYASABKAkSEAoIcXVlcnlfaWQYAiABKAkSLQoIYmluZGluZ3MYAyADKAsyGy5zeXNtbC5Eb2N1bWVudFF1ZXJ5QmluZGluZyJPChREb2N1bWVudFF1ZXJ5QmluZGluZxIRCglwYXJhbWV0ZXIYASABKAkSJAoGdmFsdWVzGAIgAygLMhQuc3lzbWwuRG9jdW1lbnRWYWx1ZSLVAQoNRG9jdW1lbnRWYWx1ZRIUCgplbGVtZW50X2lkGAEgASgJSAASFgoMc3RyaW5nX3ZhbHVlGAIgASgJSAASEwoJaW50X3ZhbHVlGAMgASgDSAASFAoKcmVhbF92YWx1ZRgEIAEoAUgAEhQKCmJvb2xfdmFsdWUYBSABKAhIABISCghpbmZpbml0eRgGIAEoCEgAEiMKCHF1YW50aXR5GAggASgLMg8uc3lzbWwuUXVhbnRpdHlIABIUCgxlbGVtZW50X3R5cGUYByABKAlCBgoEa2luZCIjChNEb2N1bWVudFF1ZXJ5Q29sdW1uEgwKBG5hbWUYASABKAkiOQoRRG9jdW1lbnRRdWVyeUNlbGwSJAoGdmFsdWVzGAEgAygLMhQuc3lzbWwuRG9jdW1lbnRWYWx1ZSJiChBEb2N1bWVudFF1ZXJ5Um93EiUKB2VsZW1lbnQYASABKAsyFC5zeXNtbC5Eb2N1bWVudFZhbHVlEicKBWNlbGxzGAIgAygLMhguc3lzbWwuRG9jdW1lbnRRdWVyeUNlbGwibgoYUnVuRG9jdW1lbnRRdWVyeVJlc3BvbnNlEisKB2NvbHVtbnMYASADKAsyGi5zeXNtbC5Eb2N1bWVudFF1ZXJ5Q29sdW1uEiUKBHJvd3MYAiADKAsyFy5zeXNtbC5Eb2N1bWVudFF1ZXJ5Um93IkAKFVJlbmRlckRvY3VtZW50UmVxdWVzdBISCgptb2RlbF9oYXNoGAEgASgJEhMKC2RvY3VtZW50X2lkGAIgASgJIioKFlJlbmRlckRvY3VtZW50UmVzcG9uc2USEAoIbWFya2Rvd24YASABKAkqkwEKDUZhaWx1cmVSZWFzb24SHgoaRkFJTFVSRV9SRUFTT05fVU5TUEVDSUZJRUQQABIdChlGQUlMVVJFX1JFQVNPTl9FVkFMVUFUSU9OEAESHQoZRkFJTFVSRV9SRUFTT05fV1JPTkdfS0lORBACEiQKIEZBSUxVUkVfUkVBU09OX0FNQklHVU9VU19TVUJKRUNUEAMqnQQKC0VkaXRGYWlsdXJlEhwKGEVESVRfRkFJTFVSRV9VTlNQRUNJRklFRBAAEh4KGkVESVRfRkFJTFVSRV9OT19PUEVSQVRJT05TEAESHwobRURJVF9GQUlMVVJFX1VOS05PV05fVEFSR0VUEAISIQodRURJVF9GQUlMVVJFX0FNQklHVU9VU19UQVJHRVQQAxIbChdFRElUX0ZBSUxVUkVfTk9UX1ZBTFVFRBAEEh4KGkVESVRfRkFJTFVSRV9JTlZBTElEX1ZBTFVFEAUSHQoZRURJVF9GQUlMVVJFX0lOVkFMSURfTkFNRRAGEhoKFkVESVRfRkFJTFVSRV9OT1RfTkFNRUQQBxIiCh5FRElUX0ZBSUxVUkVfUkVOQU1FX1JFRkVSRU5DRUQQCBIiCh5FRElUX0ZBSUxVUkVfT1ZFUkxBUFBJTkdfRURJVFMQCRIfChtFRElUX0ZBSUxVUkVfUkVTVUxUX0lOVkFMSUQQChIeChpFRElUX0ZBSUxVUkVfT1dORVJfVU5LTk9XThALEiQKIEVESVRfRkFJTFVSRV9PV05FUl9OT1RfTkFNRVNQQUNFEAwSHQoZRURJVF9GQUlMVVJFX0lMTEVHQUxfS0lORBANEiIKHkVESVRfRkFJTFVSRV9NRU1CRVJfTkFNRV9UQUtFThAOEiIKHkVESVRfRkFJTFVSRV9ERUxFVEVfUkVGRVJFTkNFRBAPKpIBChFQcmltaXRpdmVPcGVyYXRvchIiCh5QUklNSVRJVkVfT1BFUkFUT1JfVU5TUEVDSUZJRUQQABIcChhQUklNSVRJVkVfT1BFUkFUT1JfRVFVQUwQARIeChpQUklNSVRJVkVfT1BFUkFUT1JfR1JFQVRFUhACEhsKF1BSSU1JVElWRV9PUEVSQVRPUl9MRVNTEAMqbgoRQ29tcG9zaXRlT3BlcmF0b3ISIgoeQ09NUE9TSVRFX09QRVJBVE9SX1VOU1BFQ0lGSUVEEAASGgoWQ09NUE9TSVRFX09QRVJBVE9SX0FORBABEhkKFUNPTVBPU0lURV9PUEVSQVRPUl9PUhACMqQLCgxTeXNNTFNlcnZpY2USRAoNR2V0U2VydmVySW5mbxIYLnN5c21sLlNlcnZlckluZm9SZXF1ZXN0Ghkuc3lzbWwuU2VydmVySW5mb1Jlc3BvbnNlEj4KCVBhcnNlRmlsZRIXLnN5c21sLlBhcnNlRmlsZVJlcXVlc3QaGC5zeXNtbC5QYXJzZUZpbGVSZXNwb25zZRJHCgxQYXJzZVNvdXJjZXMSGi5zeXNtbC5QYXJzZVNvdXJjZXNSZXF1ZXN0Ghsuc3lzbWwuUGFyc2VTb3VyY2VzUmVzcG9uc2USOwoJR2V0U3ltYm9sEhcuc3lzbWwuR2V0U3ltYm9sUmVxdWVzdBoVLnN5c21sLlN5bWJvbFJlc3BvbnNlEkcKDkdldERpYWdub3N0aWNzEhkuc3lzbWwuRGlhZ25vc3RpY3NSZXF1ZXN0Ghouc3lzbWwuRGlhZ25vc3RpY3NSZXNwb25zZRI7CghFdmFsdWF0ZRIWLnN5c21sLkV2YWx1YXRlUmVxdWVzdBoXLnN5c21sLkV2YWx1YXRlUmVzcG9uc2USRAoLSW5zdGFudGlhdGUSGS5zeXNtbC5JbnN0YW50aWF0ZVJlcXVlc3QaGi5zeXNtbC5JbnN0YW50aWF0ZVJlc3BvbnNlEkoKDUV4ZWN1dGVBY3Rpb24SGy5zeXNtbC5FeGVjdXRlQWN0aW9uUmVxdWVzdBocLnN5c21sLkV4ZWN1dGVBY3Rpb25SZXNwb25zZRJHCgxFeGVjdXRlU3RhdGUSGi5zeXNtbC5FeGVjdXRlU3RhdGVSZXF1ZXN0Ghsuc3lzbWwuRXhlY3V0ZVN0YXRlUmVzcG9uc2USOAoHQ29udmVydBIVLnN5c21sLkNvbnZlcnRSZXF1ZXN0GhYuc3lzbWwuQ29udmVydFJlc3BvbnNlEkEKCkFwcGx5RWRpdHMSGC5zeXNtbC5BcHBseUVkaXRzUmVxdWVzdBoZLnN5c21sLkFwcGx5RWRpdHNSZXNwb25zZRJTChBWZXJpZnlDb25zdHJhaW50Eh4uc3lzbWwuVmVyaWZ5Q29uc3RyYWludFJlcXVlc3QaHy5zeXNtbC5WZXJpZnlDb25zdHJhaW50UmVzcG9uc2USVgoRVmVyaWZ5UmVxdWlyZW1lbnQSHy5zeXNtbC5WZXJpZnlSZXF1aXJlbWVudFJlcXVlc3QaIC5zeXNtbC5WZXJpZnlSZXF1aXJlbWVudFJlc3BvbnNlElkKElZlcmlmeVNhdGlzZmFjdGlvbhIgLnN5c21sLlZlcmlmeVNhdGlzZmFjdGlvblJlcXVlc3QaIS5zeXNtbC5WZXJpZnlTYXRpc2ZhY3Rpb25SZXNwb25zZRJHCgxFdmFsdWF0ZUNhbGMSGi5zeXNtbC5FdmFsdWF0ZUNhbGNSZXF1ZXN0Ghsuc3lzbWwuRXZhbHVhdGVDYWxjUmVzcG9uc2USRAoLUnVuQW5hbHlzaXMSGS5zeXNtbC5SdW5BbmFseXNpc1JlcXVlc3QaGi5zeXNtbC5SdW5BbmFseXNpc1Jlc3BvbnNlEjsKCFJ1blN3ZWVwEhYuc3lzbWwuUnVuU3dlZXBSZXF1ZXN0Ghcuc3lzbWwuUnVuU3dlZXBSZXNwb25zZRIyCgVRdWVyeRITLnN5c21sLlF1ZXJ5UmVxdWVzdBoULnN5c21sLlF1ZXJ5UmVzcG9uc2USUwoQUnVuRG9jdW1lbnRRdWVyeRIeLnN5c21sLlJ1bkRvY3VtZW50UXVlcnlSZXF1ZXN0Gh8uc3lzbWwuUnVuRG9jdW1lbnRRdWVyeVJlc3BvbnNlEk0KDlJlbmRlckRvY3VtZW50Ehwuc3lzbWwuUmVuZGVyRG9jdW1lbnRSZXF1ZXN0Gh0uc3lzbWwuUmVuZGVyRG9jdW1lbnRSZXNwb25zZUIqWihnaXRodWIuY29tL09wZW4tTUJFRS9PcGVuU3lzTUwvYXBpL3Byb3RvYgZwcm90bzM"); + fileDesc("CgtzeXNtbC5wcm90bxIFc3lzbWwi4gEKB1ZlcmRpY3QSDAoEa2luZBgBIAEoCRISCgplbGVtZW50X2lkGAIgASgJEg8KB2VsZW1lbnQYAyABKAkSDQoFaG9sZHMYBCABKAgSEQoJY29uZGl0aW9uGAUgASgJEhMKC2luc3RhbmNlX2lkGAYgASgDEhgKEGluc3RhbmNlX3R5cGVfaWQYByABKAkSDQoFZXJyb3IYCCABKAkSLAoOZmFpbHVyZV9yZWFzb24YCSABKA4yFC5zeXNtbC5GYWlsdXJlUmVhc29uEhYKDnJlcXVpcmVtZW50X2lkGAogASgJIlsKF1ZlcmlmeUNvbnN0cmFpbnRSZXF1ZXN0EhIKCm1vZGVsX2hhc2gYASABKAkSEQoJc3ltYm9sX2lkGAIgASgJEhkKEXN1YmplY3Rfc3ltYm9sX2lkGAMgASgJIpYBChhWZXJpZnlDb25zdHJhaW50UmVzcG9uc2USHwoHdmVyZGljdBgBIAEoCzIOLnN5c21sLlZlcmRpY3QSIgoJaW5zdGFuY2VzGAIgAygLMg8uc3lzbWwuSW5zdGFuY2USDQoFZXJyb3IYAyABKAkSJgoLZGlhZ25vc3RpY3MYBCADKAsyES5zeXNtbC5EaWFnbm9zdGljIlwKGFZlcmlmeVJlcXVpcmVtZW50UmVxdWVzdBISCgptb2RlbF9oYXNoGAEgASgJEhEKCXN5bWJvbF9pZBgCIAEoCRIZChFzdWJqZWN0X3N5bWJvbF9pZBgDIAEoCSJtChNWZXJpZmljYXRpb25WZXJkaWN0Eg8KB2Nhc2VfaWQYASABKAkSDAoEa2luZBgCIAEoCRIOCgZkZXRhaWwYAyABKAkSDwoHc3ViY2FzZRgEIAEoCBIWCg5yZXF1aXJlbWVudF9pZBgFIAEoCSLSAQoZVmVyaWZ5UmVxdWlyZW1lbnRSZXNwb25zZRIfCgd2ZXJkaWN0GAEgASgLMg4uc3lzbWwuVmVyZGljdBIiCglpbnN0YW5jZXMYAiADKAsyDy5zeXNtbC5JbnN0YW5jZRINCgVlcnJvchgDIAEoCRImCgtkaWFnbm9zdGljcxgEIAMoCzIRLnN5c21sLkRpYWdub3N0aWMSOQoVdmVyaWZpY2F0aW9uX3ZlcmRpY3RzGAUgAygLMhouc3lzbWwuVmVyaWZpY2F0aW9uVmVyZGljdCJCChlWZXJpZnlTYXRpc2ZhY3Rpb25SZXF1ZXN0EhIKCm1vZGVsX2hhc2gYASABKAkSEQoJc3ltYm9sX2lkGAIgASgJIoICChpWZXJpZnlTYXRpc2ZhY3Rpb25SZXNwb25zZRIgCgh2ZXJkaWN0cxgBIAMoCzIOLnN5c21sLlZlcmRpY3QSIgoJaW5zdGFuY2VzGAIgAygLMg8uc3lzbWwuSW5zdGFuY2USDQoFZXJyb3IYAyABKAkSJgoLZGlhZ25vc3RpY3MYBCADKAsyES5zeXNtbC5EaWFnbm9zdGljEiwKDmZhaWx1cmVfcmVhc29uGAUgASgOMhQuc3lzbWwuRmFpbHVyZVJlYXNvbhI5ChV2ZXJpZmljYXRpb25fdmVyZGljdHMYBiADKAsyGi5zeXNtbC5WZXJpZmljYXRpb25WZXJkaWN0Il0KE0V2YWx1YXRlQ2FsY1JlcXVlc3QSEgoKbW9kZWxfaGFzaBgBIAEoCRIRCglzeW1ib2xfaWQYAiABKAkSHwoJYXJndW1lbnRzGAMgAygLMgwuc3lzbWwuVmFsdWUivQEKFEV2YWx1YXRlQ2FsY1Jlc3BvbnNlEhwKBnJlc3VsdBgBIAEoCzIMLnN5c21sLlZhbHVlEiIKB291dHB1dHMYAiADKAsyES5zeXNtbC5DYWxjT3V0cHV0Eg0KBWVycm9yGAMgASgJEiYKC2RpYWdub3N0aWNzGAQgAygLMhEuc3lzbWwuRGlhZ25vc3RpYxIsCg5mYWlsdXJlX3JlYXNvbhgFIAEoDjIULnN5c21sLkZhaWx1cmVSZWFzb24iNwoKQ2FsY091dHB1dBIMCgRuYW1lGAEgASgJEhsKBXZhbHVlGAIgASgLMgwuc3lzbWwuVmFsdWUihAIKElJ1bkFuYWx5c2lzUmVxdWVzdBISCgptb2RlbF9oYXNoGAEgASgJEhEKCXN5bWJvbF9pZBgCIAEoCRIZChFzdWJqZWN0X3N5bWJvbF9pZBgDIAEoCRIfCglhcmd1bWVudHMYBCADKAsyDC5zeXNtbC5WYWx1ZRJGCg9uYW1lZF9hcmd1bWVudHMYBSADKAsyLS5zeXNtbC5SdW5BbmFseXNpc1JlcXVlc3QuTmFtZWRBcmd1bWVudHNFbnRyeRpDChNOYW1lZEFyZ3VtZW50c0VudHJ5EgsKA2tleRgBIAEoCRIbCgV2YWx1ZRgCIAEoCzIMLnN5c21sLlZhbHVlOgI4ASKfAgoTUnVuQW5hbHlzaXNSZXNwb25zZRIiCgdvdXRwdXRzGAEgAygLMhEuc3lzbWwuQ2FsY091dHB1dBIgCgh2ZXJkaWN0cxgCIAMoCzIOLnN5c21sLlZlcmRpY3QSIgoJaW5zdGFuY2VzGAMgAygLMg8uc3lzbWwuSW5zdGFuY2USDQoFZXJyb3IYBCABKAkSJgoLZGlhZ25vc3RpY3MYBSADKAsyES5zeXNtbC5EaWFnbm9zdGljEiwKDmZhaWx1cmVfcmVhc29uGAYgASgOMhQuc3lzbWwuRmFpbHVyZVJlYXNvbhI5ChV2ZXJpZmljYXRpb25fdmVyZGljdHMYByADKAsyGi5zeXNtbC5WZXJpZmljYXRpb25WZXJkaWN0IowBChBQYXJzZUZpbGVSZXF1ZXN0EhMKCWZpbGVfcGF0aBgBIAEoCUgAEhEKB2NvbnRlbnQYAiABKAlIABIYCgxjb250ZW50X2hhc2gYAyABKAlCAhgBEhAKCGxhbmd1YWdlGAQgASgJEhoKEnN0cmljdF9jb25mb3JtYW5jZRgFIAEoCEIICgZzb3VyY2UiYgoOU291cmNlRG9jdW1lbnQSEwoJZmlsZV9wYXRoGAEgASgJSAASEQoHY29udGVudBgCIAEoCUgAEhAKCGxhbmd1YWdlGAMgASgJEgwKBG5hbWUYBCABKAlCCAoGc291cmNlIlsKE1BhcnNlU291cmNlc1JlcXVlc3QSKAoJZG9jdW1lbnRzGAEgAygLMhUuc3lzbWwuU291cmNlRG9jdW1lbnQSGgoSc3RyaWN0X2NvbmZvcm1hbmNlGAIgASgIIoMBChRQYXJzZVNvdXJjZXNSZXNwb25zZRISCgptb2RlbF9oYXNoGAEgASgJEiAKBXJvb3RzGAIgAygLMhEuc3lzbWwuU3ltYm9sSW5mbxImCgtkaWFnbm9zdGljcxgDIAMoCzIRLnN5c21sLkRpYWdub3N0aWMSDQoFZXJyb3IYBCABKAkifwoRUGFyc2VGaWxlUmVzcG9uc2USEgoKbW9kZWxfaGFzaBgBIAEoCRIfCgRyb290GAIgASgLMhEuc3lzbWwuU3ltYm9sSW5mbxImCgtkaWFnbm9zdGljcxgDIAMoCzIRLnN5c21sLkRpYWdub3N0aWMSDQoFZXJyb3IYBCABKAkiOQoQR2V0U3ltYm9sUmVxdWVzdBISCgptb2RlbF9oYXNoGAEgASgJEhEKCXN5bWJvbF9pZBgCIAEoCSJCCg5TeW1ib2xSZXNwb25zZRIhCgZzeW1ib2wYASABKAsyES5zeXNtbC5TeW1ib2xJbmZvEg0KBWVycm9yGAIgASgJIigKEkRpYWdub3N0aWNzUmVxdWVzdBISCgptb2RlbF9oYXNoGAEgASgJIkwKE0RpYWdub3N0aWNzUmVzcG9uc2USJgoLZGlhZ25vc3RpY3MYASADKAsyES5zeXNtbC5EaWFnbm9zdGljEg0KBWVycm9yGAIgASgJIm8KD0V2YWx1YXRlUmVxdWVzdBISCgptb2RlbF9oYXNoGAEgASgJEhIKCmV4cHJlc3Npb24YAiABKAkSGQoRY29udGV4dF9zeW1ib2xfaWQYAyABKAkSGQoRc3ViamVjdF9zeW1ib2xfaWQYBCABKAkiZwoQRXZhbHVhdGVSZXNwb25zZRIcCgZyZXN1bHQYASABKAsyDC5zeXNtbC5WYWx1ZRINCgVlcnJvchgCIAEoCRImCgtkaWFnbm9zdGljcxgDIAMoCzIRLnN5c21sLkRpYWdub3N0aWMiwgEKCEluc3RhbmNlEgoKAmlkGAEgASgDEhYKDnR5cGVfc3ltYm9sX2lkGAIgASgJEjoKDmZlYXR1cmVfdmFsdWVzGAQgAygLMiIuc3lzbWwuSW5zdGFuY2UuRmVhdHVyZVZhbHVlc0VudHJ5GkkKEkZlYXR1cmVWYWx1ZXNFbnRyeRILCgNrZXkYASABKAkSIgoFdmFsdWUYAiABKAsyEy5zeXNtbC5GZWF0dXJlVmFsdWU6AjgBSgQIAxAEUgVzbG90cyKEAQoMRmVhdHVyZVZhbHVlEhQKDGZlYXR1cmVfbmFtZRgBIAEoCRIbCgV2YWx1ZRgCIAEoCzIMLnN5c21sLlZhbHVlEhwKBnZhbHVlcxgDIAMoCzIMLnN5c21sLlZhbHVlEhQKDG1hdGVyaWFsaXplZBgEIAEoCBINCgVlcnJvchgFIAEoCSI7ChJJbnN0YW50aWF0ZVJlcXVlc3QSEgoKbW9kZWxfaGFzaBgBIAEoCRIRCglzeW1ib2xfaWQYAiABKAkikwEKE0luc3RhbnRpYXRlUmVzcG9uc2USIQoIaW5zdGFuY2UYASABKAsyDy5zeXNtbC5JbnN0YW5jZRINCgVlcnJvchgCIAEoCRImCgtkaWFnbm9zdGljcxgDIAMoCzIRLnN5c21sLkRpYWdub3N0aWMSIgoJaW5zdGFuY2VzGAQgAygLMg8uc3lzbWwuSW5zdGFuY2UiugEKFEV4ZWN1dGVBY3Rpb25SZXF1ZXN0EhIKCm1vZGVsX2hhc2gYASABKAkSGAoQYWN0aW9uX3N5bWJvbF9pZBgCIAEoCRI3CgZpbnB1dHMYAyADKAsyJy5zeXNtbC5FeGVjdXRlQWN0aW9uUmVxdWVzdC5JbnB1dHNFbnRyeRo7CgtJbnB1dHNFbnRyeRILCgNrZXkYASABKAkSGwoFdmFsdWUYAiABKAsyDC5zeXNtbC5WYWx1ZToCOAEiyAEKFUV4ZWN1dGVBY3Rpb25SZXNwb25zZRI6CgdvdXRwdXRzGAEgAygLMikuc3lzbWwuRXhlY3V0ZUFjdGlvblJlc3BvbnNlLk91dHB1dHNFbnRyeRINCgVlcnJvchgCIAEoCRImCgtkaWFnbm9zdGljcxgDIAMoCzIRLnN5c21sLkRpYWdub3N0aWMaPAoMT3V0cHV0c0VudHJ5EgsKA2tleRgBIAEoCRIbCgV2YWx1ZRgCIAEoCzIMLnN5c21sLlZhbHVlOgI4ASJaChNFeGVjdXRlU3RhdGVSZXF1ZXN0EhIKCm1vZGVsX2hhc2gYASABKAkSHwoXc3RhdGVfbWFjaGluZV9zeW1ib2xfaWQYAiABKAkSDgoGZXZlbnRzGAMgAygJIu4BChRFeGVjdXRlU3RhdGVSZXNwb25zZRIWCg5zdGF0ZXNfdmlzaXRlZBgBIAMoCRJECg1maW5hbF9jb250ZXh0GAIgAygLMi0uc3lzbWwuRXhlY3V0ZVN0YXRlUmVzcG9uc2UuRmluYWxDb250ZXh0RW50cnkSDQoFZXJyb3IYAyABKAkSJgoLZGlhZ25vc3RpY3MYBCADKAsyES5zeXNtbC5EaWFnbm9zdGljGkEKEUZpbmFsQ29udGV4dEVudHJ5EgsKA2tleRgBIAEoCRIbCgV2YWx1ZRgCIAEoCzIMLnN5c21sLlZhbHVlOgI4ASKgAQoOQ29udmVydFJlcXVlc3QSEwoJZmlsZV9wYXRoGAEgASgJSAASEQoHY29udGVudBgCIAEoCUgAEhQKCm1vZGVsX2hhc2gYBiABKAlIABITCgtmcm9tX2Zvcm1hdBgDIAEoCRIRCgl0b19mb3JtYXQYBCABKAkSHgoWdG9sZXJhdGVfc3ludGF4X2Vycm9ycxgFIAEoCEIICgZzb3VyY2UitAEKD0NvbnZlcnRSZXNwb25zZRIPCgdjb250ZW50GAEgASgJEhMKC2Zyb21fZm9ybWF0GAIgASgJEhEKCXRvX2Zvcm1hdBgDIAEoCRINCgVlcnJvchgEIAEoCRImCgtkaWFnbm9zdGljcxgFIAMoCzIRLnN5c21sLkRpYWdub3N0aWMSFAoMZXhwZXJpbWVudGFsGAYgASgIEhsKE2V4cGVyaW1lbnRhbF9ub3RpY2UYByABKAkiUQoRQXBwbHlFZGl0c1JlcXVlc3QSEgoKbW9kZWxfaGFzaBgBIAEoCRIoCgpvcGVyYXRpb25zGAIgAygLMhQuc3lzbWwuRWRpdE9wZXJhdGlvbiK8AQoNRWRpdE9wZXJhdGlvbhIoCglzZXRfdmFsdWUYASABKAsyEy5zeXNtbC5TZXRWYWx1ZUVkaXRIABIjCgZyZW5hbWUYAiABKAsyES5zeXNtbC5SZW5hbWVFZGl0SAASKgoKYWRkX21lbWJlchgDIAEoCzIULnN5c21sLkFkZE1lbWJlckVkaXRIABIjCgZkZWxldGUYBCABKAsyES5zeXNtbC5EZWxldGVFZGl0SABCCwoJb3BlcmF0aW9uIoIBCg1BZGRNZW1iZXJFZGl0Eg0KBW93bmVyGAEgASgJEgwKBGtpbmQYAiABKAkSDAoEbmFtZRgDIAEoCRIMCgR0eXBlGAQgASgJEhQKDG11bHRpcGxpY2l0eRgFIAEoCRINCgV2YWx1ZRgGIAEoCRITCgtzcGVjaWFsaXplcxgHIAMoCSItCgpEZWxldGVFZGl0Eg4KBnRhcmdldBgBIAEoCRIPCgdjYXNjYWRlGAIgASgIIi0KDFNldFZhbHVlRWRpdBIOCgZ0YXJnZXQYASABKAkSDQoFdmFsdWUYAiABKAkiLgoKUmVuYW1lRWRpdBIOCgZ0YXJnZXQYASABKAkSEAoIbmV3X25hbWUYAiABKAkiwgEKEkFwcGx5RWRpdHNSZXNwb25zZRIPCgdjb250ZW50GAEgASgJEiMKB2FwcGxpZWQYAiADKAsyEi5zeXNtbC5BcHBsaWVkRWRpdBINCgVlcnJvchgDIAEoCRIjCgdmYWlsdXJlGAQgASgOMhIuc3lzbWwuRWRpdEZhaWx1cmUSJgoLZGlhZ25vc3RpY3MYBSADKAsyES5zeXNtbC5EaWFnbm9zdGljEhoKEnJlZmVycmluZ19lbGVtZW50cxgGIAMoCSJ6CgtBcHBsaWVkRWRpdBIXCg9vcGVyYXRpb25faW5kZXgYASABKAUSDgoGdGFyZ2V0GAIgASgJEg4KBm9mZnNldBgDIAEoBRIOCgZsZW5ndGgYBCABKAUSEAoIb2xkX3RleHQYBSABKAkSEAoIbmV3X3RleHQYBiABKAki/QIKClN5bWJvbEluZm8SCgoCaWQYASABKAkSDAoEbmFtZRgCIAEoCRIMCgRraW5kGAMgASgJEjEKCG1ldGFkYXRhGAQgAygLMh8uc3lzbWwuU3ltYm9sSW5mby5NZXRhZGF0YUVudHJ5EhEKCWNoaWxkX2lkcxgFIAMoCRIoCgphdHRyaWJ1dGVzGAYgAygLMhQuc3lzbWwuQXR0cmlidXRlSW5mbxIiCgl0eXBlX2luZm8YByABKAsyDy5zeXNtbC5UeXBlSW5mbxItCgxtdWx0aXBsaWNpdHkYCCABKAsyFy5zeXNtbC5NdWx0aXBsaWNpdHlJbmZvEi4KD3NwZWNpYWxpemF0aW9ucxgJIAMoCzIVLnN5c21sLlNwZWNpYWxpemF0aW9uEiMKG3dpdGhoZWxkX2xpYnJhcnlfYXR0cmlidXRlcxgKIAEoBRovCg1NZXRhZGF0YUVudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEiWAoOU3BlY2lhbGl6YXRpb24SDAoEa2luZBgBIAEoCRIQCghkZWNsYXJlZBgCIAEoCRIRCgl0YXJnZXRfaWQYAyABKAkSEwoLdGFyZ2V0X2tpbmQYBCABKAkilQEKCFR5cGVJbmZvEhAKCGRlY2xhcmVkGAEgASgJEhMKC3Jlc29sdmVkX2lkGAIgASgJEhUKDXJlc29sdmVkX2tpbmQYAyABKAkSEQoJcHJpbWl0aXZlGAQgASgJEhgKEHByaW1pdGl2ZV9zb3VyY2UYBSABKAkSEAoIcXVhbnRpdHkYBiABKAgSDAoEdW5pdBgHIAEoCSIwChBNdWx0aXBsaWNpdHlJbmZvEg0KBWxvd2VyGAEgASgJEg0KBXVwcGVyGAIgASgJIlYKDUF0dHJpYnV0ZUluZm8SDAoEbmFtZRgBIAEoCRIMCgR0eXBlGAIgASgJEhsKBXZhbHVlGAMgASgLMgwuc3lzbWwuVmFsdWUSDAoEdW5pdBgEIAEoCSKbBAoFVmFsdWUSEwoJaW50X3ZhbHVlGAEgASgDSAASFAoKcmVhbF92YWx1ZRgCIAEoAUgAEhQKCmJvb2xfdmFsdWUYAyABKAhIABIWCgxzdHJpbmdfdmFsdWUYBCABKAlIABIVCgtpbnN0YW5jZV9pZBgFIAEoA0gAEigKCHNlcXVlbmNlGAYgASgLMhQuc3lzbWwuVmFsdWVTZXF1ZW5jZUgAEg4KBG51bGwYByABKAlIABIjCghxdWFudGl0eRgIIAEoCzIPLnN5c21sLlF1YW50aXR5SAASKgoMZW51bV9saXRlcmFsGAkgASgLMhIuc3lzbWwuRW51bUxpdGVyYWxIABIPCgV1bnNldBgKIAEoCEgAEiEKB2NvbXBsZXgYCyABKAsyDi5zeXNtbC5Db21wbGV4SAASHQoFYXJyYXkYDCABKAsyDC5zeXNtbC5BcnJheUgAEh8KBnZlY3RvchgNIAEoCzINLnN5c21sLlZlY3RvckgAEjAKD3ZlY3Rvcl9xdWFudGl0eRgOIAEoCzIVLnN5c21sLlZlY3RvclF1YW50aXR5SAASMAoPbWVhc3VyZW1lbnRfcmVmGA8gASgLMhUuc3lzbWwuTWVhc3VyZW1lbnRSZWZIABISCghpbmZpbml0eRgQIAEoCEgAEiMKCGZ1bmN0aW9uGBEgASgLMg8uc3lzbWwuRnVuY3Rpb25IAEIGCgRraW5kIiwKCEZ1bmN0aW9uEg8KB2NhbGNfaWQYASABKAkSDwoHc2VsZl9pZBgCIAEoAyI7CgVBcnJheRISCgpkaW1lbnNpb25zGAEgAygDEh4KCGVsZW1lbnRzGAIgAygLMgwuc3lzbWwuVmFsdWUiKgoGVmVjdG9yEiAKCmNvbXBvbmVudHMYASADKAsyDC5zeXNtbC5WYWx1ZSI1Cg5WZWN0b3JRdWFudGl0eRIjCgpjb21wb25lbnRzGAEgAygLMg8uc3lzbWwuUXVhbnRpdHkiKgoHQ29tcGxleBIMCgRyZWFsGAEgASgBEhEKCWltYWdpbmFyeRgCIAEoASJHCgtFbnVtTGl0ZXJhbBISCgpsaXRlcmFsX2lkGAEgASgJEhYKDmVudW1lcmF0aW9uX2lkGAIgASgJEgwKBG5hbWUYAyABKAkiLwoNVmFsdWVTZXF1ZW5jZRIeCghlbGVtZW50cxgBIAMoCzIMLnN5c21sLlZhbHVlInwKCFF1YW50aXR5EhcKDWludF9tYWduaXR1ZGUYASABKANIABIYCg5yZWFsX21hZ25pdHVkZRgCIAEoAUgAEgwKBHVuaXQYAyABKAkSIgoJdW5pdF90ZXJtGAQgASgLMg8uc3lzbWwuVW5pdFRlcm1CCwoJbWFnbml0dWRlIlMKDk1lYXN1cmVtZW50UmVmEgwKBHVuaXQYASABKAkSIgoJdW5pdF90ZXJtGAIgASgLMg8uc3lzbWwuVW5pdFRlcm0SDwoHdW5pdF9pZBgDIAEoCSJUCghVbml0VGVybRIRCglzY2FsZV9udW0YASABKAESEQoJc2NhbGVfZGVuGAIgASgBEiIKB2ZhY3RvcnMYAyADKAsyES5zeXNtbC5Vbml0RmFjdG9yIi8KClVuaXRGYWN0b3ISDwoHdW5pdF9pZBgBIAEoCRIQCghleHBvbmVudBgCIAEoASJKCgpEaWFnbm9zdGljEhAKCHNldmVyaXR5GAEgASgJEg8KB21lc3NhZ2UYAiABKAkSGQoEc3BhbhgDIAEoCzILLnN5c21sLlNwYW4iXgoEU3BhbhIMCgRmaWxlGAEgASgJEhIKCnN0YXJ0X2xpbmUYAiABKAUSEQoJc3RhcnRfY29sGAMgASgFEhAKCGVuZF9saW5lGAQgASgFEg8KB2VuZF9jb2wYBSABKAUiEwoRU2VydmVySW5mb1JlcXVlc3QiOwoSU2VydmVySW5mb1Jlc3BvbnNlEg8KB3ZlcnNpb24YASABKAkSFAoMY2FwYWJpbGl0aWVzGAIgAygJIlMKDFF1ZXJ5UmVxdWVzdBISCgptb2RlbF9oYXNoGAEgASgJEhsKBXF1ZXJ5GAIgASgLMgwuc3lzbWwuUXVlcnkSEgoKb3NsY19xdWVyeRgDIAEoCSI8Cg1RdWVyeVJlc3BvbnNlEisKCGVsZW1lbnRzGAEgAygLMhkuc3lzbWwuUXVlcnlSZXN1bHRFbGVtZW50IkgKBVF1ZXJ5Eg0KBXNjb3BlGAEgAygJEg4KBnNlbGVjdBgCIAMoCRIgCgV3aGVyZRgDIAEoCzIRLnN5c21sLkNvbnN0cmFpbnQifAoKQ29uc3RyYWludBIvCglwcmltaXRpdmUYASABKAsyGi5zeXNtbC5QcmltaXRpdmVDb25zdHJhaW50SAASLwoJY29tcG9zaXRlGAIgASgLMhouc3lzbWwuQ29tcG9zaXRlQ29uc3RyYWludEgAQgwKCmNvbnN0cmFpbnQicwoTUHJpbWl0aXZlQ29uc3RyYWludBIPCgdpbnZlcnNlGAEgASgIEhAKCHByb3BlcnR5GAIgASgJEioKCG9wZXJhdG9yGAMgASgOMhguc3lzbWwuUHJpbWl0aXZlT3BlcmF0b3ISDQoFdmFsdWUYBCADKAkiaAoTQ29tcG9zaXRlQ29uc3RyYWludBIqCghvcGVyYXRvchgBIAEoDjIYLnN5c21sLkNvbXBvc2l0ZU9wZXJhdG9yEiUKCmNvbnN0cmFpbnQYAiADKAsyES5zeXNtbC5Db25zdHJhaW50IqABChJRdWVyeVJlc3VsdEVsZW1lbnQSCgoCaWQYASABKAkSDAoEdHlwZRgCIAEoCRI9Cgpwcm9wZXJ0aWVzGAMgAygLMikuc3lzbWwuUXVlcnlSZXN1bHRFbGVtZW50LlByb3BlcnRpZXNFbnRyeRoxCg9Qcm9wZXJ0aWVzRW50cnkSCwoDa2V5GAEgASgJEg0KBXZhbHVlGAIgASgJOgI4ASJzCgpTd2VlcFJhbmdlEhEKCXBhcmFtZXRlchgBIAEoCRIbCgVzdGFydBgCIAEoCzIMLnN5c21sLlZhbHVlEhkKA2VuZBgDIAEoCzIMLnN5c21sLlZhbHVlEhoKBHN0ZXAYBCABKAsyDC5zeXNtbC5WYWx1ZSLAAgoPUnVuU3dlZXBSZXF1ZXN0EhIKCm1vZGVsX2hhc2gYASABKAkSEQoJc3ltYm9sX2lkGAIgASgJEhkKEXN1YmplY3Rfc3ltYm9sX2lkGAMgASgJEh8KCWFyZ3VtZW50cxgEIAMoCzIMLnN5c21sLlZhbHVlEkMKD25hbWVkX2FyZ3VtZW50cxgFIAMoCzIqLnN5c21sLlJ1blN3ZWVwUmVxdWVzdC5OYW1lZEFyZ3VtZW50c0VudHJ5EiEKBnJhbmdlcxgGIAMoCzIRLnN5c21sLlN3ZWVwUmFuZ2USDwoHc2FtcGxlcxgHIAEoAxIMCgRzZWVkGAggASgEGkMKE05hbWVkQXJndW1lbnRzRW50cnkSCwoDa2V5GAEgASgJEhsKBXZhbHVlGAIgASgLMgwuc3lzbWwuVmFsdWU6AjgBIsgBCghTd2VlcFJvdxIhCgZpbnB1dHMYASADKAsyES5zeXNtbC5DYWxjT3V0cHV0EiIKB291dHB1dHMYAiADKAsyES5zeXNtbC5DYWxjT3V0cHV0EiAKCHZlcmRpY3RzGAMgAygLMg4uc3lzbWwuVmVyZGljdBIWCg5lbGFwc2VkX21pY3JvcxgEIAEoAxINCgVlcnJvchgFIAEoCRIsCg5mYWlsdXJlX3JlYXNvbhgGIAEoDjIULnN5c21sLkZhaWx1cmVSZWFzb24i7QEKEFJ1blN3ZWVwUmVzcG9uc2USHQoEcm93cxgBIAMoCzIPLnN5c21sLlN3ZWVwUm93EhIKCnBhcmFtZXRlcnMYAiADKAkSDwoHc2FtcGxlZBgDIAEoCBIMCgRzZWVkGAQgASgEEg0KBWVycm9yGAUgASgJEiYKC2RpYWdub3N0aWNzGAYgAygLMhEuc3lzbWwuRGlhZ25vc3RpYxIsCg5mYWlsdXJlX3JlYXNvbhgHIAEoDjIULnN5c21sLkZhaWx1cmVSZWFzb24SIgoJaW5zdGFuY2VzGAggAygLMg8uc3lzbWwuSW5zdGFuY2UibgoXUnVuRG9jdW1lbnRRdWVyeVJlcXVlc3QSEgoKbW9kZWxfaGFzaBgBIAEoCRIQCghxdWVyeV9pZBgCIAEoCRItCghiaW5kaW5ncxgDIAMoCzIbLnN5c21sLkRvY3VtZW50UXVlcnlCaW5kaW5nIk8KFERvY3VtZW50UXVlcnlCaW5kaW5nEhEKCXBhcmFtZXRlchgBIAEoCRIkCgZ2YWx1ZXMYAiADKAsyFC5zeXNtbC5Eb2N1bWVudFZhbHVlItUBCg1Eb2N1bWVudFZhbHVlEhQKCmVsZW1lbnRfaWQYASABKAlIABIWCgxzdHJpbmdfdmFsdWUYAiABKAlIABITCglpbnRfdmFsdWUYAyABKANIABIUCgpyZWFsX3ZhbHVlGAQgASgBSAASFAoKYm9vbF92YWx1ZRgFIAEoCEgAEhIKCGluZmluaXR5GAYgASgISAASIwoIcXVhbnRpdHkYCCABKAsyDy5zeXNtbC5RdWFudGl0eUgAEhQKDGVsZW1lbnRfdHlwZRgHIAEoCUIGCgRraW5kIiMKE0RvY3VtZW50UXVlcnlDb2x1bW4SDAoEbmFtZRgBIAEoCSI5ChFEb2N1bWVudFF1ZXJ5Q2VsbBIkCgZ2YWx1ZXMYASADKAsyFC5zeXNtbC5Eb2N1bWVudFZhbHVlImIKEERvY3VtZW50UXVlcnlSb3cSJQoHZWxlbWVudBgBIAEoCzIULnN5c21sLkRvY3VtZW50VmFsdWUSJwoFY2VsbHMYAiADKAsyGC5zeXNtbC5Eb2N1bWVudFF1ZXJ5Q2VsbCJuChhSdW5Eb2N1bWVudFF1ZXJ5UmVzcG9uc2USKwoHY29sdW1ucxgBIAMoCzIaLnN5c21sLkRvY3VtZW50UXVlcnlDb2x1bW4SJQoEcm93cxgCIAMoCzIXLnN5c21sLkRvY3VtZW50UXVlcnlSb3ciQAoVUmVuZGVyRG9jdW1lbnRSZXF1ZXN0EhIKCm1vZGVsX2hhc2gYASABKAkSEwoLZG9jdW1lbnRfaWQYAiABKAkiKgoWUmVuZGVyRG9jdW1lbnRSZXNwb25zZRIQCghtYXJrZG93bhgBIAEoCSqTAQoNRmFpbHVyZVJlYXNvbhIeChpGQUlMVVJFX1JFQVNPTl9VTlNQRUNJRklFRBAAEh0KGUZBSUxVUkVfUkVBU09OX0VWQUxVQVRJT04QARIdChlGQUlMVVJFX1JFQVNPTl9XUk9OR19LSU5EEAISJAogRkFJTFVSRV9SRUFTT05fQU1CSUdVT1VTX1NVQkpFQ1QQAyqdBAoLRWRpdEZhaWx1cmUSHAoYRURJVF9GQUlMVVJFX1VOU1BFQ0lGSUVEEAASHgoaRURJVF9GQUlMVVJFX05PX09QRVJBVElPTlMQARIfChtFRElUX0ZBSUxVUkVfVU5LTk9XTl9UQVJHRVQQAhIhCh1FRElUX0ZBSUxVUkVfQU1CSUdVT1VTX1RBUkdFVBADEhsKF0VESVRfRkFJTFVSRV9OT1RfVkFMVUVEEAQSHgoaRURJVF9GQUlMVVJFX0lOVkFMSURfVkFMVUUQBRIdChlFRElUX0ZBSUxVUkVfSU5WQUxJRF9OQU1FEAYSGgoWRURJVF9GQUlMVVJFX05PVF9OQU1FRBAHEiIKHkVESVRfRkFJTFVSRV9SRU5BTUVfUkVGRVJFTkNFRBAIEiIKHkVESVRfRkFJTFVSRV9PVkVSTEFQUElOR19FRElUUxAJEh8KG0VESVRfRkFJTFVSRV9SRVNVTFRfSU5WQUxJRBAKEh4KGkVESVRfRkFJTFVSRV9PV05FUl9VTktOT1dOEAsSJAogRURJVF9GQUlMVVJFX09XTkVSX05PVF9OQU1FU1BBQ0UQDBIdChlFRElUX0ZBSUxVUkVfSUxMRUdBTF9LSU5EEA0SIgoeRURJVF9GQUlMVVJFX01FTUJFUl9OQU1FX1RBS0VOEA4SIgoeRURJVF9GQUlMVVJFX0RFTEVURV9SRUZFUkVOQ0VEEA8qkgEKEVByaW1pdGl2ZU9wZXJhdG9yEiIKHlBSSU1JVElWRV9PUEVSQVRPUl9VTlNQRUNJRklFRBAAEhwKGFBSSU1JVElWRV9PUEVSQVRPUl9FUVVBTBABEh4KGlBSSU1JVElWRV9PUEVSQVRPUl9HUkVBVEVSEAISGwoXUFJJTUlUSVZFX09QRVJBVE9SX0xFU1MQAypuChFDb21wb3NpdGVPcGVyYXRvchIiCh5DT01QT1NJVEVfT1BFUkFUT1JfVU5TUEVDSUZJRUQQABIaChZDT01QT1NJVEVfT1BFUkFUT1JfQU5EEAESGQoVQ09NUE9TSVRFX09QRVJBVE9SX09SEAIypAsKDFN5c01MU2VydmljZRJECg1HZXRTZXJ2ZXJJbmZvEhguc3lzbWwuU2VydmVySW5mb1JlcXVlc3QaGS5zeXNtbC5TZXJ2ZXJJbmZvUmVzcG9uc2USPgoJUGFyc2VGaWxlEhcuc3lzbWwuUGFyc2VGaWxlUmVxdWVzdBoYLnN5c21sLlBhcnNlRmlsZVJlc3BvbnNlEkcKDFBhcnNlU291cmNlcxIaLnN5c21sLlBhcnNlU291cmNlc1JlcXVlc3QaGy5zeXNtbC5QYXJzZVNvdXJjZXNSZXNwb25zZRI7CglHZXRTeW1ib2wSFy5zeXNtbC5HZXRTeW1ib2xSZXF1ZXN0GhUuc3lzbWwuU3ltYm9sUmVzcG9uc2USRwoOR2V0RGlhZ25vc3RpY3MSGS5zeXNtbC5EaWFnbm9zdGljc1JlcXVlc3QaGi5zeXNtbC5EaWFnbm9zdGljc1Jlc3BvbnNlEjsKCEV2YWx1YXRlEhYuc3lzbWwuRXZhbHVhdGVSZXF1ZXN0Ghcuc3lzbWwuRXZhbHVhdGVSZXNwb25zZRJECgtJbnN0YW50aWF0ZRIZLnN5c21sLkluc3RhbnRpYXRlUmVxdWVzdBoaLnN5c21sLkluc3RhbnRpYXRlUmVzcG9uc2USSgoNRXhlY3V0ZUFjdGlvbhIbLnN5c21sLkV4ZWN1dGVBY3Rpb25SZXF1ZXN0Ghwuc3lzbWwuRXhlY3V0ZUFjdGlvblJlc3BvbnNlEkcKDEV4ZWN1dGVTdGF0ZRIaLnN5c21sLkV4ZWN1dGVTdGF0ZVJlcXVlc3QaGy5zeXNtbC5FeGVjdXRlU3RhdGVSZXNwb25zZRI4CgdDb252ZXJ0EhUuc3lzbWwuQ29udmVydFJlcXVlc3QaFi5zeXNtbC5Db252ZXJ0UmVzcG9uc2USQQoKQXBwbHlFZGl0cxIYLnN5c21sLkFwcGx5RWRpdHNSZXF1ZXN0Ghkuc3lzbWwuQXBwbHlFZGl0c1Jlc3BvbnNlElMKEFZlcmlmeUNvbnN0cmFpbnQSHi5zeXNtbC5WZXJpZnlDb25zdHJhaW50UmVxdWVzdBofLnN5c21sLlZlcmlmeUNvbnN0cmFpbnRSZXNwb25zZRJWChFWZXJpZnlSZXF1aXJlbWVudBIfLnN5c21sLlZlcmlmeVJlcXVpcmVtZW50UmVxdWVzdBogLnN5c21sLlZlcmlmeVJlcXVpcmVtZW50UmVzcG9uc2USWQoSVmVyaWZ5U2F0aXNmYWN0aW9uEiAuc3lzbWwuVmVyaWZ5U2F0aXNmYWN0aW9uUmVxdWVzdBohLnN5c21sLlZlcmlmeVNhdGlzZmFjdGlvblJlc3BvbnNlEkcKDEV2YWx1YXRlQ2FsYxIaLnN5c21sLkV2YWx1YXRlQ2FsY1JlcXVlc3QaGy5zeXNtbC5FdmFsdWF0ZUNhbGNSZXNwb25zZRJECgtSdW5BbmFseXNpcxIZLnN5c21sLlJ1bkFuYWx5c2lzUmVxdWVzdBoaLnN5c21sLlJ1bkFuYWx5c2lzUmVzcG9uc2USOwoIUnVuU3dlZXASFi5zeXNtbC5SdW5Td2VlcFJlcXVlc3QaFy5zeXNtbC5SdW5Td2VlcFJlc3BvbnNlEjIKBVF1ZXJ5EhMuc3lzbWwuUXVlcnlSZXF1ZXN0GhQuc3lzbWwuUXVlcnlSZXNwb25zZRJTChBSdW5Eb2N1bWVudFF1ZXJ5Eh4uc3lzbWwuUnVuRG9jdW1lbnRRdWVyeVJlcXVlc3QaHy5zeXNtbC5SdW5Eb2N1bWVudFF1ZXJ5UmVzcG9uc2USTQoOUmVuZGVyRG9jdW1lbnQSHC5zeXNtbC5SZW5kZXJEb2N1bWVudFJlcXVlc3QaHS5zeXNtbC5SZW5kZXJEb2N1bWVudFJlc3BvbnNlQipaKGdpdGh1Yi5jb20vT3Blbi1NQkVFL09wZW5TeXNNTC9hcGkvcHJvdG9iBnByb3RvMw"); /** * Verdict is one verification's answer: whether the condition held, and, when @@ -2129,6 +2129,14 @@ export type Value = Message<"sysml.Value"> & { */ value: boolean; case: "infinity"; + } | { + /** + * a calc as a value, named by its declaration + * + * @generated from field: sysml.Function function = 17; + */ + value: Function; + case: "function"; } | { case: undefined; value?: undefined }; }; @@ -2139,6 +2147,43 @@ export type Value = Message<"sysml.Value"> & { export const ValueSchema: GenMessage = /*@__PURE__*/ messageDesc(file_sysml, 47); +/** + * Function is a calc held as a value: a calc definition, or a calc usage with + * an input no read could supply, as `Sq` in `Fn(Sq, 3.0)` or the `f` of + * `in calc f {...}`. It crosses as the declaration it is a value of, which is + * its identity: two functions are the same exactly when calc_id and self_id + * are. A function closing over the bindings of the behavior body it is + * declared in has no wire form and crosses as the null arm. + * + * @generated from message sysml.Function + */ +export type Function = Message<"sysml.Function"> & { + /** + * FQN of the calc declaration ("Analysis::Sq"). Its identity. + * + * @generated from field: string calc_id = 1; + */ + calcId: string; + + /** + * ID of the object the calc's feature names resolve against, for a calc + * usage read off a part (`holder.scale`); 0 for a function closing over no + * object. An instance id, living only within the response that sent it: a + * request function with a non-zero self_id is rejected, since no later call + * holds that object. + * + * @generated from field: int64 self_id = 2; + */ + selfId: bigint; +}; + +/** + * Describes the message sysml.Function. + * Use `create(FunctionSchema)` to create a new message. + */ +export const FunctionSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_sysml, 48); + /** * Array is a Collections::Array: its elements flattened in row-major order * under its dimensions, compared by content rather than by the object read. @@ -2167,7 +2212,7 @@ export type Array = Message<"sysml.Array"> & { * Use `create(ArraySchema)` to create a new message. */ export const ArraySchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 48); + messageDesc(file_sysml, 49); /** * Vector is a VectorValues::NumericalVectorValue: its components in order, @@ -2190,7 +2235,7 @@ export type Vector = Message<"sysml.Vector"> & { * Use `create(VectorSchema)` to create a new message. */ export const VectorSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 49); + messageDesc(file_sysml, 50); /** * VectorQuantity is a Quantities::VectorQuantityValue: one Quantity per axis, @@ -2213,7 +2258,7 @@ export type VectorQuantity = Message<"sysml.VectorQuantity"> & { * Use `create(VectorQuantitySchema)` to create a new message. */ export const VectorQuantitySchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 50); + messageDesc(file_sysml, 51); /** * Complex is one complex number in rectangular form. It crosses as one value @@ -2238,7 +2283,7 @@ export type Complex = Message<"sysml.Complex"> & { * Use `create(ComplexSchema)` to create a new message. */ export const ComplexSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 51); + messageDesc(file_sysml, 52); /** * EnumLiteral is one literal of an enumeration definition. A literal is its own @@ -2275,7 +2320,7 @@ export type EnumLiteral = Message<"sysml.EnumLiteral"> & { * Use `create(EnumLiteralSchema)` to create a new message. */ export const EnumLiteralSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 52); + messageDesc(file_sysml, 53); /** * @generated from message sysml.ValueSequence @@ -2292,7 +2337,7 @@ export type ValueSequence = Message<"sysml.ValueSequence"> & { * Use `create(ValueSequenceSchema)` to create a new message. */ export const ValueSequenceSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 53); + messageDesc(file_sysml, 54); /** * Quantity is a magnitude and the measurement reference it is expressed in, sent @@ -2343,7 +2388,7 @@ export type Quantity = Message<"sysml.Quantity"> & { * Use `create(QuantitySchema)` to create a new message. */ export const QuantitySchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 54); + messageDesc(file_sysml, 55); /** * MeasurementRef is a MeasurementReferences::ScalarMeasurementReference held as @@ -2391,7 +2436,7 @@ export type MeasurementRef = Message<"sysml.MeasurementRef"> & { * Use `create(MeasurementRefSchema)` to create a new message. */ export const MeasurementRefSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 55); + messageDesc(file_sysml, 56); /** * UnitTerm is a unit reduced to a scale factor over base units: `km/h` reduces @@ -2425,7 +2470,7 @@ export type UnitTerm = Message<"sysml.UnitTerm"> & { * Use `create(UnitTermSchema)` to create a new message. */ export const UnitTermSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 56); + messageDesc(file_sysml, 57); /** * UnitFactor is one base unit raised to an exponent. @@ -2451,7 +2496,7 @@ export type UnitFactor = Message<"sysml.UnitFactor"> & { * Use `create(UnitFactorSchema)` to create a new message. */ export const UnitFactorSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 57); + messageDesc(file_sysml, 58); /** * Diagnostic represents a parse/semantic error or warning @@ -2482,7 +2527,7 @@ export type Diagnostic = Message<"sysml.Diagnostic"> & { * Use `create(DiagnosticSchema)` to create a new message. */ export const DiagnosticSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 58); + messageDesc(file_sysml, 59); /** * Span represents a source location @@ -2521,7 +2566,7 @@ export type Span = Message<"sysml.Span"> & { * Use `create(SpanSchema)` to create a new message. */ export const SpanSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 59); + messageDesc(file_sysml, 60); /** * ServerInfoRequest asks the service to describe itself. It carries no fields; @@ -2537,7 +2582,7 @@ export type ServerInfoRequest = Message<"sysml.ServerInfoRequest"> & { * Use `create(ServerInfoRequestSchema)` to create a new message. */ export const ServerInfoRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 60); + messageDesc(file_sysml, 61); /** * ServerInfoResponse describes the running service. @@ -2598,6 +2643,11 @@ export type ServerInfoResponse = Message<"sysml.ServerInfoResponse"> & { * refused with UNIMPLEMENTED rather than read as another * value. Separate from structured_values, which a client * built before this arm existed may already claim. + * "function_values" - a Value carries a calc held as a value as function, + * named by its declaration, rather than reporting it as an + * unsupported null, and one is accepted as an action input + * or calc argument; without it, one is refused with + * UNIMPLEMENTED rather than read as another value. * "apply_edits" - the ApplyEdits RPC edits a parsed model's own source, * preserving everything the edit did not touch. * "document_query" - the RunDocumentQuery RPC runs a named document query @@ -2615,7 +2665,7 @@ export type ServerInfoResponse = Message<"sysml.ServerInfoResponse"> & { * Use `create(ServerInfoResponseSchema)` to create a new message. */ export const ServerInfoResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 61); + messageDesc(file_sysml, 62); /** * QueryRequest runs a Query against a model the service already parsed. @@ -2648,7 +2698,7 @@ export type QueryRequest = Message<"sysml.QueryRequest"> & { * Use `create(QueryRequestSchema)` to create a new message. */ export const QueryRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 62); + messageDesc(file_sysml, 63); /** * QueryResponse contains the elements the query selected, in the order they are @@ -2670,7 +2720,7 @@ export type QueryResponse = Message<"sysml.QueryResponse"> & { * Use `create(QueryResponseSchema)` to create a new message. */ export const QueryResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 63); + messageDesc(file_sysml, 64); /** * Query is the standard's Query resource (SysML v2 API & Services). Its `@type` @@ -2710,7 +2760,7 @@ export type Query = Message<"sysml.Query"> & { * Use `create(QuerySchema)` to create a new message. */ export const QuerySchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 64); + messageDesc(file_sysml, 65); /** * Constraint is the standard's Constraint, whose `@type` discriminates between @@ -2742,7 +2792,7 @@ export type Constraint = Message<"sysml.Constraint"> & { * Use `create(ConstraintSchema)` to create a new message. */ export const ConstraintSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 65); + messageDesc(file_sysml, 66); /** * PrimitiveConstraint compares one property of an element against a value. @@ -2785,7 +2835,7 @@ export type PrimitiveConstraint = Message<"sysml.PrimitiveConstraint"> & { * Use `create(PrimitiveConstraintSchema)` to create a new message. */ export const PrimitiveConstraintSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 66); + messageDesc(file_sysml, 67); /** * CompositeConstraint combines constraints. An empty constraint list fails the @@ -2810,7 +2860,7 @@ export type CompositeConstraint = Message<"sysml.CompositeConstraint"> & { * Use `create(CompositeConstraintSchema)` to create a new message. */ export const CompositeConstraintSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 67); + messageDesc(file_sysml, 68); /** * QueryResultElement is one matched element. `id` and `type` are always @@ -2846,7 +2896,7 @@ export type QueryResultElement = Message<"sysml.QueryResultElement"> & { * Use `create(QueryResultElementSchema)` to create a new message. */ export const QueryResultElementSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 68); + messageDesc(file_sysml, 69); /** * SweepRange is one parameter's range: the endpoints a swept run advances @@ -2889,7 +2939,7 @@ export type SweepRange = Message<"sysml.SweepRange"> & { * Use `create(SweepRangeSchema)` to create a new message. */ export const SweepRangeSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 69); + messageDesc(file_sysml, 70); /** * RunSweepRequest runs one analysis case or calc once per row of a sweep. Every @@ -2964,7 +3014,7 @@ export type RunSweepRequest = Message<"sysml.RunSweepRequest"> & { * Use `create(RunSweepRequestSchema)` to create a new message. */ export const RunSweepRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 70); + messageDesc(file_sysml, 71); /** * SweepRow is one run of a sweep: what it bound, what it produced, how long it @@ -3023,7 +3073,7 @@ export type SweepRow = Message<"sysml.SweepRow"> & { * Use `create(SweepRowSchema)` to create a new message. */ export const SweepRowSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 71); + messageDesc(file_sysml, 72); /** * RunSweepResponse carries the table, one row per run, in the order the runs @@ -3094,7 +3144,7 @@ export type RunSweepResponse = Message<"sysml.RunSweepResponse"> & { * Use `create(RunSweepResponseSchema)` to create a new message. */ export const RunSweepResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 72); + messageDesc(file_sysml, 73); /** * RunDocumentQueryRequest runs a named document query — a calc def @@ -3134,7 +3184,7 @@ export type RunDocumentQueryRequest = Message<"sysml.RunDocumentQueryRequest"> & * Use `create(RunDocumentQueryRequestSchema)` to create a new message. */ export const RunDocumentQueryRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 73); + messageDesc(file_sysml, 74); /** * DocumentQueryBinding binds one entry parameter of a document query. @@ -3158,7 +3208,7 @@ export type DocumentQueryBinding = Message<"sysml.DocumentQueryBinding"> & { * Use `create(DocumentQueryBindingSchema)` to create a new message. */ export const DocumentQueryBindingSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 74); + messageDesc(file_sysml, 75); /** * DocumentValue is one typed document-query value. A request binds a model @@ -3233,7 +3283,7 @@ export type DocumentValue = Message<"sysml.DocumentValue"> & { * Use `create(DocumentValueSchema)` to create a new message. */ export const DocumentValueSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 75); + messageDesc(file_sysml, 76); /** * DocumentQueryColumn is one projected property, in projection order. @@ -3252,7 +3302,7 @@ export type DocumentQueryColumn = Message<"sysml.DocumentQueryColumn"> & { * Use `create(DocumentQueryColumnSchema)` to create a new message. */ export const DocumentQueryColumnSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 76); + messageDesc(file_sysml, 77); /** * DocumentQueryCell is one row's values for one column, in the query's order. @@ -3271,7 +3321,7 @@ export type DocumentQueryCell = Message<"sysml.DocumentQueryCell"> & { * Use `create(DocumentQueryCellSchema)` to create a new message. */ export const DocumentQueryCellSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 77); + messageDesc(file_sysml, 78); /** * DocumentQueryRow is one selected element and its projected cells, one per @@ -3298,7 +3348,7 @@ export type DocumentQueryRow = Message<"sysml.DocumentQueryRow"> & { * Use `create(DocumentQueryRowSchema)` to create a new message. */ export const DocumentQueryRowSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 78); + messageDesc(file_sysml, 79); /** * RunDocumentQueryResponse is the query's answer: its projected columns and its @@ -3325,7 +3375,7 @@ export type RunDocumentQueryResponse = Message<"sysml.RunDocumentQueryResponse"> * Use `create(RunDocumentQueryResponseSchema)` to create a new message. */ export const RunDocumentQueryResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 79); + messageDesc(file_sysml, 80); /** * RenderDocumentRequest renders a named document — a part def specializing @@ -3356,7 +3406,7 @@ export type RenderDocumentRequest = Message<"sysml.RenderDocumentRequest"> & { * Use `create(RenderDocumentRequestSchema)` to create a new message. */ export const RenderDocumentRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 80); + messageDesc(file_sysml, 81); /** * RenderDocumentResponse carries the rendered Markdown, byte-for-byte what the @@ -3376,7 +3426,7 @@ export type RenderDocumentResponse = Message<"sysml.RenderDocumentResponse"> & { * Use `create(RenderDocumentResponseSchema)` to create a new message. */ export const RenderDocumentResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_sysml, 81); + messageDesc(file_sysml, 82); /** * FailureReason says what kind of failure an `error` reports, so a client acts diff --git a/clients/node/test/client.test.ts b/clients/node/test/client.test.ts index 6bcccf298..4e5b0970d 100644 --- a/clients/node/test/client.test.ts +++ b/clients/node/test/client.test.ts @@ -9,6 +9,7 @@ import { join } from "node:path"; import { after, before, test } from "node:test"; import { CAPABILITY_COMPLEX_VALUES, + CAPABILITY_FUNCTION_VALUES, CAPABILITY_MEASUREMENT_REFS, CAPABILITY_QUERY, CAPABILITY_STRUCTURED_VALUES, @@ -224,6 +225,37 @@ test("a bare measurement reference arrives as a unit with its reduction and decl } }); +const FUNCTION_MODEL = `package Demo { + private import ScalarValues::*; + calc def Sq { in v : Real; return : Real = v * v; } + calc def Fn { in calc f { in v : Real; return : Real; } in a : Real; return : Real = f(a); } + calc def Identity { in calc f { in v : Real; return : Real; } return r = f; } + attribute pick = Identity(Sq); + attribute nine = Fn(Sq, 3.0); + part def Scaler { + attribute k : Real = 2.0; + calc scale { in x : Real; return : Real = x * k; } + } + part holder : Scaler; + attribute scaler = holder.scale; +}`; + +test("a calc held as a value arrives as the function it names, with the object it was read off", async () => { + for (const options of [{ protocol: "grpc" as const }, {}, { encoding: "json" as const }]) { + await using connection = await connect(options); + assert.ok((await connection.serverInfo()).has(CAPABILITY_FUNCTION_VALUES)); + await using model = await connection.loads(FUNCTION_MODEL); + + assert.deepEqual(await model.eval("Demo::pick"), { kind: "function", calcId: "Demo::Sq" }); + assert.deepEqual(await model.eval("Demo::nine"), { kind: "real", value: 9 }); + const scale = await model.eval("Demo::scaler"); + assert.ok(scale.kind === "function"); + assert.equal(scale.calcId, "Demo::Scaler::scale"); + assert.ok(scale.selfId !== undefined && scale.selfId > 0n); + assert.equal(formatValue(scale), "Demo::Scaler::scale"); + } +}); + test("a file parses, and a syntax error is a diagnostic, not a thrown call", async () => { const dir = mkdtempSync(join(tmpdir(), "client-test-")); const path = join(dir, "sample.sysml"); diff --git a/clients/node/test/normalize.test.ts b/clients/node/test/normalize.test.ts index 4423cf3f8..b3867dea1 100644 --- a/clients/node/test/normalize.test.ts +++ b/clients/node/test/normalize.test.ts @@ -14,6 +14,7 @@ import { SymbolInfoSchema, ValueSchema, FeatureValueSchema, + FunctionSchema, } from "../src/generated/sysml_pb.js"; import { Integer, Normalizer, MODEL_HASH_PLACEHOLDER, PATH_PLACEHOLDER, VERSION_PLACEHOLDER } from "../conformance/normalize.js"; @@ -93,6 +94,37 @@ test("instance ids are relabelled in the order they appear, consistently", () => assert.equal((tree["instances"] as Record[])[0]?.["id"], "@2"); }); +test("the object a function was read off is labelled with the instance it names", () => { + const response = create(InstantiateResponseSchema, { + instance: create(InstanceSchema, { + id: 41n, + typeSymbolId: "Sample::Scaler", + featureValues: { + scale: create(FeatureValueSchema, { + featureName: "scale", + values: [ + create(ValueSchema, { + kind: { case: "function", value: create(FunctionSchema, { calcId: "Sample::Scaler::scale", selfId: 41n }) }, + }), + create(ValueSchema, { + kind: { case: "function", value: create(FunctionSchema, { calcId: "Sample::Sq" }) }, + }), + ], + }), + }, + }), + }); + const tree = new Normalizer(HASH).normalize(InstantiateResponseSchema, response); + const instance = tree["instance"] as Record; + const values = (instance["feature_values"] as Record>)["scale"]["values"] as Record< + string, + Record + >[]; + assert.deepEqual(values[0]?.["function"], { calc_id: "Sample::Scaler::scale", self_id: "@1" }); + // A function bound to no object leaves self_id unset, so it does not appear. + assert.deepEqual(values[1]?.["function"], { calc_id: "Sample::Sq" }); +}); + test("an integral field is an Integer, so it is never compared as a float", () => { const tree = new Normalizer(HASH).normalize( ParseFileResponseSchema, diff --git a/clients/node/test/values.test.ts b/clients/node/test/values.test.ts index 730177aa7..b84270de7 100644 --- a/clients/node/test/values.test.ts +++ b/clients/node/test/values.test.ts @@ -8,6 +8,7 @@ import { ComplexSchema, EnumLiteralSchema, FailureReason, + FunctionSchema, MeasurementRefSchema, QuantitySchema, UnitFactorSchema, @@ -332,6 +333,28 @@ test("a measurement reference keeps its unit, its reduction and the declaration assert.throws(() => decodeValue(nested), MalformedValueError); }); +test("a function is the calc it names, read against an object or none", () => { + const fn = (calcId: string, selfId: bigint) => + create(ValueSchema, { + kind: { case: "function", value: create(FunctionSchema, { calcId, selfId }) }, + }); + assert.deepEqual(decodeValue(fn("Demo::Sq", 0n)), { kind: "function", calcId: "Demo::Sq" }); + const scale = decodeValue(fn("Demo::Scaler::scale", 7n)); + assert.deepEqual(scale, { kind: "function", calcId: "Demo::Scaler::scale", selfId: 7n }); + assert.equal(formatValue(scale), "Demo::Scaler::scale"); + + // A function naming no calc is malformed, at any depth. + assert.throws( + () => decodeValue(fn("", 0n)), + (error: unknown) => error instanceof MalformedValueError && /names no calc/.test(error.message), + ); + const nested = create(ValueSchema, { + kind: { case: "sequence", value: create(ValueSequenceSchema, { elements: [fn("", 3n)] }) }, + }); + assert.throws(() => decodeValue(nested), MalformedValueError); + assert.throws(() => encodeValue({ kind: "function", calcId: "" }), MalformedValueError); +}); + test("encodeValue is the inverse of decodeValue, through the wire bytes", () => { const values: SysMLValue[] = [ { kind: "int", value: 9007199254740993n }, @@ -356,6 +379,8 @@ test("encodeValue is the inverse of decodeValue, through the wire bytes", () => }, }, { kind: "sequence", elements: [{ kind: "measurementRef", unit: "m", unitTerm: METRE, unitId: "SI::metre" }] }, + { kind: "function", calcId: "Demo::Sq" }, + { kind: "function", calcId: "Demo::Scaler::scale", selfId: 7n }, { kind: "enum", value: { name: "red", literalId: "P::Color::red", enumerationId: "P::Color" } }, { kind: "null", reason: "" }, { kind: "unset" }, diff --git a/clients/python/opensysml/__init__.py b/clients/python/opensysml/__init__.py index f60641337..920513260 100644 --- a/clients/python/opensysml/__init__.py +++ b/clients/python/opensysml/__init__.py @@ -23,7 +23,7 @@ TypeFacts, ) from opensysml.capabilities import MissingCapabilityError, ServerInfo -from opensysml.values import UNSET, Array, MeasurementRef, UnsetType, Vector, VectorQuantity +from opensysml.values import UNSET, Array, Function, MeasurementRef, UnsetType, Vector, VectorQuantity from opensysml.verdict import ( AnalysisResult, CalcResult, SweepRow, SweepTable, Verdict, VerificationVerdict, ) @@ -56,7 +56,7 @@ "AttributeFacts", "ServerInfo", "UNSET", "UnsetType", - "Array", "Vector", "VectorQuantity", "MeasurementRef", + "Array", "Vector", "VectorQuantity", "MeasurementRef", "Function", "Conversion", "FORMAT_SYSML", "FORMAT_TURTLE", "format_of_path", "ExperimentalFeatureWarning", "is_experimental", "Editor", "EditResult", "AppliedEdit", diff --git a/clients/python/opensysml/capabilities.py b/clients/python/opensysml/capabilities.py index abd9e0fd6..2948d179f 100644 --- a/clients/python/opensysml/capabilities.py +++ b/clients/python/opensysml/capabilities.py @@ -100,6 +100,13 @@ #: it with ``UNIMPLEMENTED``. CAPABILITY_MEASUREMENT_REFS = "measurement_refs" +#: A calc held as a value — a calc definition, or a calc usage with an input no +#: read could supply — as ``Value.function``, named by its declaration and read +#: as :class:`~opensysml.values.Function`. Without it the service sends an +#: unsupported null naming the calc, which is an error, and refuses one sent to +#: it with ``UNIMPLEMENTED``. +CAPABILITY_FUNCTION_VALUES = "function_values" + #: What the body of a verification case answered, as the #: ``verification_verdicts`` of a requirement, satisfaction or analysis #: response, read as :class:`~opensysml.verdict.VerificationVerdict`. Without it diff --git a/clients/python/opensysml/connection.py b/clients/python/opensysml/connection.py index 225b08551..5e123c251 100644 --- a/clients/python/opensysml/connection.py +++ b/clients/python/opensysml/connection.py @@ -21,6 +21,7 @@ CAPABILITY_DOCUMENT_QUERY, CAPABILITY_EVALUATE_SUBJECT, CAPABILITY_FEATURE_VALUES, + CAPABILITY_FUNCTION_VALUES, CAPABILITY_INFINITY_VALUE, CAPABILITY_MEASUREMENT_REFS, CAPABILITY_QUERY, @@ -59,6 +60,7 @@ from opensysml.query import build_query, elements_of from opensysml.values import ( Array, + Function, MeasurementRef, Quantity, Vector, @@ -1180,7 +1182,8 @@ def execute_action(self, action_symbol_id, model_hash, inputs=None): :class:`~opensysml.values.Vector` or :class:`~opensysml.values.VectorQuantity` and the service predates ``structured_values``, or a :class:`~opensysml.values.MeasurementRef` and the service predates - ``measurement_refs``; nothing is sent + ``measurement_refs``, or a :class:`~opensysml.values.Function` + and the service predates ``function_values``; nothing is sent """ # Convert Python inputs to protobuf Values pb_inputs = {name: self._python_to_value(val) for name, val in (inputs or {}).items()} @@ -1378,8 +1381,9 @@ def calc(self, symbol_id, model_hash, arguments=None): MissingCapabilityError: If the service cannot verify, or an argument holds a ``complex`` and the service predates ``complex_values``, an array, vector or vector quantity and - the service predates ``structured_values``, or a measurement - reference and the service predates ``measurement_refs``; nothing + the service predates ``structured_values``, a measurement + reference and the service predates ``measurement_refs``, or a + function and the service predates ``function_values``; nothing is sent ModelNotFoundError: If the service no longer holds the model """ @@ -1395,6 +1399,7 @@ def calc(self, symbol_id, model_hash, arguments=None): CAPABILITY_COMPLEX_VALUES, CAPABILITY_STRUCTURED_VALUES, CAPABILITY_MEASUREMENT_REFS, + CAPABILITY_FUNCTION_VALUES, )) ): response = self._stub.EvaluateCalc(request) @@ -1662,6 +1667,14 @@ def _require_measurement_refs(self): upgrade_remedy(CAPABILITY_MEASUREMENT_REFS), ) + def _require_function_values(self): + """Refuse to send a function a service without ``function_values`` would read as null.""" + require( + self.server_info(), + CAPABILITY_FUNCTION_VALUES, + upgrade_remedy(CAPABILITY_FUNCTION_VALUES), + ) + def _require_infinity_value(self): """Refuse to send the unbounded value ``*`` a service without ``infinity_value`` would read as null.""" require( @@ -1720,6 +1733,9 @@ def _python_to_value(self, py_value): elif isinstance(py_value, MeasurementRef): self._require_measurement_refs() return sysml_pb2.Value(measurement_ref=py_value.to_pb()) + elif isinstance(py_value, Function): + self._require_function_values() + return sysml_pb2.Value(function=py_value.to_pb()) elif isinstance(py_value, Array): self._require_structured_values() return sysml_pb2.Value(array=py_value.to_pb(self._python_to_value)) diff --git a/clients/python/opensysml/proto/sysml_pb2.py b/clients/python/opensysml/proto/sysml_pb2.py index 391c35afc..4b0e92de3 100644 --- a/clients/python/opensysml/proto/sysml_pb2.py +++ b/clients/python/opensysml/proto/sysml_pb2.py @@ -24,7 +24,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0bsysml.proto\x12\x05sysml\"\xe2\x01\n\x07Verdict\x12\x0c\n\x04kind\x18\x01 \x01(\t\x12\x12\n\nelement_id\x18\x02 \x01(\t\x12\x0f\n\x07\x65lement\x18\x03 \x01(\t\x12\r\n\x05holds\x18\x04 \x01(\x08\x12\x11\n\tcondition\x18\x05 \x01(\t\x12\x13\n\x0binstance_id\x18\x06 \x01(\x03\x12\x18\n\x10instance_type_id\x18\x07 \x01(\t\x12\r\n\x05\x65rror\x18\x08 \x01(\t\x12,\n\x0e\x66\x61ilure_reason\x18\t \x01(\x0e\x32\x14.sysml.FailureReason\x12\x16\n\x0erequirement_id\x18\n \x01(\t\"[\n\x17VerifyConstraintRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x11\n\tsymbol_id\x18\x02 \x01(\t\x12\x19\n\x11subject_symbol_id\x18\x03 \x01(\t\"\x96\x01\n\x18VerifyConstraintResponse\x12\x1f\n\x07verdict\x18\x01 \x01(\x0b\x32\x0e.sysml.Verdict\x12\"\n\tinstances\x18\x02 \x03(\x0b\x32\x0f.sysml.Instance\x12\r\n\x05\x65rror\x18\x03 \x01(\t\x12&\n\x0b\x64iagnostics\x18\x04 \x03(\x0b\x32\x11.sysml.Diagnostic\"\\\n\x18VerifyRequirementRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x11\n\tsymbol_id\x18\x02 \x01(\t\x12\x19\n\x11subject_symbol_id\x18\x03 \x01(\t\"m\n\x13VerificationVerdict\x12\x0f\n\x07\x63\x61se_id\x18\x01 \x01(\t\x12\x0c\n\x04kind\x18\x02 \x01(\t\x12\x0e\n\x06\x64\x65tail\x18\x03 \x01(\t\x12\x0f\n\x07subcase\x18\x04 \x01(\x08\x12\x16\n\x0erequirement_id\x18\x05 \x01(\t\"\xd2\x01\n\x19VerifyRequirementResponse\x12\x1f\n\x07verdict\x18\x01 \x01(\x0b\x32\x0e.sysml.Verdict\x12\"\n\tinstances\x18\x02 \x03(\x0b\x32\x0f.sysml.Instance\x12\r\n\x05\x65rror\x18\x03 \x01(\t\x12&\n\x0b\x64iagnostics\x18\x04 \x03(\x0b\x32\x11.sysml.Diagnostic\x12\x39\n\x15verification_verdicts\x18\x05 \x03(\x0b\x32\x1a.sysml.VerificationVerdict\"B\n\x19VerifySatisfactionRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x11\n\tsymbol_id\x18\x02 \x01(\t\"\x82\x02\n\x1aVerifySatisfactionResponse\x12 \n\x08verdicts\x18\x01 \x03(\x0b\x32\x0e.sysml.Verdict\x12\"\n\tinstances\x18\x02 \x03(\x0b\x32\x0f.sysml.Instance\x12\r\n\x05\x65rror\x18\x03 \x01(\t\x12&\n\x0b\x64iagnostics\x18\x04 \x03(\x0b\x32\x11.sysml.Diagnostic\x12,\n\x0e\x66\x61ilure_reason\x18\x05 \x01(\x0e\x32\x14.sysml.FailureReason\x12\x39\n\x15verification_verdicts\x18\x06 \x03(\x0b\x32\x1a.sysml.VerificationVerdict\"]\n\x13\x45valuateCalcRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x11\n\tsymbol_id\x18\x02 \x01(\t\x12\x1f\n\targuments\x18\x03 \x03(\x0b\x32\x0c.sysml.Value\"\xbd\x01\n\x14\x45valuateCalcResponse\x12\x1c\n\x06result\x18\x01 \x01(\x0b\x32\x0c.sysml.Value\x12\"\n\x07outputs\x18\x02 \x03(\x0b\x32\x11.sysml.CalcOutput\x12\r\n\x05\x65rror\x18\x03 \x01(\t\x12&\n\x0b\x64iagnostics\x18\x04 \x03(\x0b\x32\x11.sysml.Diagnostic\x12,\n\x0e\x66\x61ilure_reason\x18\x05 \x01(\x0e\x32\x14.sysml.FailureReason\"7\n\nCalcOutput\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x1b\n\x05value\x18\x02 \x01(\x0b\x32\x0c.sysml.Value\"\x84\x02\n\x12RunAnalysisRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x11\n\tsymbol_id\x18\x02 \x01(\t\x12\x19\n\x11subject_symbol_id\x18\x03 \x01(\t\x12\x1f\n\targuments\x18\x04 \x03(\x0b\x32\x0c.sysml.Value\x12\x46\n\x0fnamed_arguments\x18\x05 \x03(\x0b\x32-.sysml.RunAnalysisRequest.NamedArgumentsEntry\x1a\x43\n\x13NamedArgumentsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1b\n\x05value\x18\x02 \x01(\x0b\x32\x0c.sysml.Value:\x02\x38\x01\"\x9f\x02\n\x13RunAnalysisResponse\x12\"\n\x07outputs\x18\x01 \x03(\x0b\x32\x11.sysml.CalcOutput\x12 \n\x08verdicts\x18\x02 \x03(\x0b\x32\x0e.sysml.Verdict\x12\"\n\tinstances\x18\x03 \x03(\x0b\x32\x0f.sysml.Instance\x12\r\n\x05\x65rror\x18\x04 \x01(\t\x12&\n\x0b\x64iagnostics\x18\x05 \x03(\x0b\x32\x11.sysml.Diagnostic\x12,\n\x0e\x66\x61ilure_reason\x18\x06 \x01(\x0e\x32\x14.sysml.FailureReason\x12\x39\n\x15verification_verdicts\x18\x07 \x03(\x0b\x32\x1a.sysml.VerificationVerdict\"\x8c\x01\n\x10ParseFileRequest\x12\x13\n\tfile_path\x18\x01 \x01(\tH\x00\x12\x11\n\x07\x63ontent\x18\x02 \x01(\tH\x00\x12\x18\n\x0c\x63ontent_hash\x18\x03 \x01(\tB\x02\x18\x01\x12\x10\n\x08language\x18\x04 \x01(\t\x12\x1a\n\x12strict_conformance\x18\x05 \x01(\x08\x42\x08\n\x06source\"b\n\x0eSourceDocument\x12\x13\n\tfile_path\x18\x01 \x01(\tH\x00\x12\x11\n\x07\x63ontent\x18\x02 \x01(\tH\x00\x12\x10\n\x08language\x18\x03 \x01(\t\x12\x0c\n\x04name\x18\x04 \x01(\tB\x08\n\x06source\"[\n\x13ParseSourcesRequest\x12(\n\tdocuments\x18\x01 \x03(\x0b\x32\x15.sysml.SourceDocument\x12\x1a\n\x12strict_conformance\x18\x02 \x01(\x08\"\x83\x01\n\x14ParseSourcesResponse\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12 \n\x05roots\x18\x02 \x03(\x0b\x32\x11.sysml.SymbolInfo\x12&\n\x0b\x64iagnostics\x18\x03 \x03(\x0b\x32\x11.sysml.Diagnostic\x12\r\n\x05\x65rror\x18\x04 \x01(\t\"\x7f\n\x11ParseFileResponse\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x1f\n\x04root\x18\x02 \x01(\x0b\x32\x11.sysml.SymbolInfo\x12&\n\x0b\x64iagnostics\x18\x03 \x03(\x0b\x32\x11.sysml.Diagnostic\x12\r\n\x05\x65rror\x18\x04 \x01(\t\"9\n\x10GetSymbolRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x11\n\tsymbol_id\x18\x02 \x01(\t\"B\n\x0eSymbolResponse\x12!\n\x06symbol\x18\x01 \x01(\x0b\x32\x11.sysml.SymbolInfo\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"(\n\x12\x44iagnosticsRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\"L\n\x13\x44iagnosticsResponse\x12&\n\x0b\x64iagnostics\x18\x01 \x03(\x0b\x32\x11.sysml.Diagnostic\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"o\n\x0f\x45valuateRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x12\n\nexpression\x18\x02 \x01(\t\x12\x19\n\x11\x63ontext_symbol_id\x18\x03 \x01(\t\x12\x19\n\x11subject_symbol_id\x18\x04 \x01(\t\"g\n\x10\x45valuateResponse\x12\x1c\n\x06result\x18\x01 \x01(\x0b\x32\x0c.sysml.Value\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12&\n\x0b\x64iagnostics\x18\x03 \x03(\x0b\x32\x11.sysml.Diagnostic\"\xc2\x01\n\x08Instance\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x16\n\x0etype_symbol_id\x18\x02 \x01(\t\x12:\n\x0e\x66\x65\x61ture_values\x18\x04 \x03(\x0b\x32\".sysml.Instance.FeatureValuesEntry\x1aI\n\x12\x46\x65\x61tureValuesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.sysml.FeatureValue:\x02\x38\x01J\x04\x08\x03\x10\x04R\x05slots\"\x84\x01\n\x0c\x46\x65\x61tureValue\x12\x14\n\x0c\x66\x65\x61ture_name\x18\x01 \x01(\t\x12\x1b\n\x05value\x18\x02 \x01(\x0b\x32\x0c.sysml.Value\x12\x1c\n\x06values\x18\x03 \x03(\x0b\x32\x0c.sysml.Value\x12\x14\n\x0cmaterialized\x18\x04 \x01(\x08\x12\r\n\x05\x65rror\x18\x05 \x01(\t\";\n\x12InstantiateRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x11\n\tsymbol_id\x18\x02 \x01(\t\"\x93\x01\n\x13InstantiateResponse\x12!\n\x08instance\x18\x01 \x01(\x0b\x32\x0f.sysml.Instance\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12&\n\x0b\x64iagnostics\x18\x03 \x03(\x0b\x32\x11.sysml.Diagnostic\x12\"\n\tinstances\x18\x04 \x03(\x0b\x32\x0f.sysml.Instance\"\xba\x01\n\x14\x45xecuteActionRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x18\n\x10\x61\x63tion_symbol_id\x18\x02 \x01(\t\x12\x37\n\x06inputs\x18\x03 \x03(\x0b\x32\'.sysml.ExecuteActionRequest.InputsEntry\x1a;\n\x0bInputsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1b\n\x05value\x18\x02 \x01(\x0b\x32\x0c.sysml.Value:\x02\x38\x01\"\xc8\x01\n\x15\x45xecuteActionResponse\x12:\n\x07outputs\x18\x01 \x03(\x0b\x32).sysml.ExecuteActionResponse.OutputsEntry\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12&\n\x0b\x64iagnostics\x18\x03 \x03(\x0b\x32\x11.sysml.Diagnostic\x1a<\n\x0cOutputsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1b\n\x05value\x18\x02 \x01(\x0b\x32\x0c.sysml.Value:\x02\x38\x01\"Z\n\x13\x45xecuteStateRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x1f\n\x17state_machine_symbol_id\x18\x02 \x01(\t\x12\x0e\n\x06\x65vents\x18\x03 \x03(\t\"\xee\x01\n\x14\x45xecuteStateResponse\x12\x16\n\x0estates_visited\x18\x01 \x03(\t\x12\x44\n\rfinal_context\x18\x02 \x03(\x0b\x32-.sysml.ExecuteStateResponse.FinalContextEntry\x12\r\n\x05\x65rror\x18\x03 \x01(\t\x12&\n\x0b\x64iagnostics\x18\x04 \x03(\x0b\x32\x11.sysml.Diagnostic\x1a\x41\n\x11\x46inalContextEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1b\n\x05value\x18\x02 \x01(\x0b\x32\x0c.sysml.Value:\x02\x38\x01\"\xa0\x01\n\x0e\x43onvertRequest\x12\x13\n\tfile_path\x18\x01 \x01(\tH\x00\x12\x11\n\x07\x63ontent\x18\x02 \x01(\tH\x00\x12\x14\n\nmodel_hash\x18\x06 \x01(\tH\x00\x12\x13\n\x0b\x66rom_format\x18\x03 \x01(\t\x12\x11\n\tto_format\x18\x04 \x01(\t\x12\x1e\n\x16tolerate_syntax_errors\x18\x05 \x01(\x08\x42\x08\n\x06source\"\xb4\x01\n\x0f\x43onvertResponse\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\x12\x13\n\x0b\x66rom_format\x18\x02 \x01(\t\x12\x11\n\tto_format\x18\x03 \x01(\t\x12\r\n\x05\x65rror\x18\x04 \x01(\t\x12&\n\x0b\x64iagnostics\x18\x05 \x03(\x0b\x32\x11.sysml.Diagnostic\x12\x14\n\x0c\x65xperimental\x18\x06 \x01(\x08\x12\x1b\n\x13\x65xperimental_notice\x18\x07 \x01(\t\"Q\n\x11\x41pplyEditsRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12(\n\noperations\x18\x02 \x03(\x0b\x32\x14.sysml.EditOperation\"\xbc\x01\n\rEditOperation\x12(\n\tset_value\x18\x01 \x01(\x0b\x32\x13.sysml.SetValueEditH\x00\x12#\n\x06rename\x18\x02 \x01(\x0b\x32\x11.sysml.RenameEditH\x00\x12*\n\nadd_member\x18\x03 \x01(\x0b\x32\x14.sysml.AddMemberEditH\x00\x12#\n\x06\x64\x65lete\x18\x04 \x01(\x0b\x32\x11.sysml.DeleteEditH\x00\x42\x0b\n\toperation\"\x82\x01\n\rAddMemberEdit\x12\r\n\x05owner\x18\x01 \x01(\t\x12\x0c\n\x04kind\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x0c\n\x04type\x18\x04 \x01(\t\x12\x14\n\x0cmultiplicity\x18\x05 \x01(\t\x12\r\n\x05value\x18\x06 \x01(\t\x12\x13\n\x0bspecializes\x18\x07 \x03(\t\"-\n\nDeleteEdit\x12\x0e\n\x06target\x18\x01 \x01(\t\x12\x0f\n\x07\x63\x61scade\x18\x02 \x01(\x08\"-\n\x0cSetValueEdit\x12\x0e\n\x06target\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\".\n\nRenameEdit\x12\x0e\n\x06target\x18\x01 \x01(\t\x12\x10\n\x08new_name\x18\x02 \x01(\t\"\xc2\x01\n\x12\x41pplyEditsResponse\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\x12#\n\x07\x61pplied\x18\x02 \x03(\x0b\x32\x12.sysml.AppliedEdit\x12\r\n\x05\x65rror\x18\x03 \x01(\t\x12#\n\x07\x66\x61ilure\x18\x04 \x01(\x0e\x32\x12.sysml.EditFailure\x12&\n\x0b\x64iagnostics\x18\x05 \x03(\x0b\x32\x11.sysml.Diagnostic\x12\x1a\n\x12referring_elements\x18\x06 \x03(\t\"z\n\x0b\x41ppliedEdit\x12\x17\n\x0foperation_index\x18\x01 \x01(\x05\x12\x0e\n\x06target\x18\x02 \x01(\t\x12\x0e\n\x06offset\x18\x03 \x01(\x05\x12\x0e\n\x06length\x18\x04 \x01(\x05\x12\x10\n\x08old_text\x18\x05 \x01(\t\x12\x10\n\x08new_text\x18\x06 \x01(\t\"\xfd\x02\n\nSymbolInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0c\n\x04kind\x18\x03 \x01(\t\x12\x31\n\x08metadata\x18\x04 \x03(\x0b\x32\x1f.sysml.SymbolInfo.MetadataEntry\x12\x11\n\tchild_ids\x18\x05 \x03(\t\x12(\n\nattributes\x18\x06 \x03(\x0b\x32\x14.sysml.AttributeInfo\x12\"\n\ttype_info\x18\x07 \x01(\x0b\x32\x0f.sysml.TypeInfo\x12-\n\x0cmultiplicity\x18\x08 \x01(\x0b\x32\x17.sysml.MultiplicityInfo\x12.\n\x0fspecializations\x18\t \x03(\x0b\x32\x15.sysml.Specialization\x12#\n\x1bwithheld_library_attributes\x18\n \x01(\x05\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"X\n\x0eSpecialization\x12\x0c\n\x04kind\x18\x01 \x01(\t\x12\x10\n\x08\x64\x65\x63lared\x18\x02 \x01(\t\x12\x11\n\ttarget_id\x18\x03 \x01(\t\x12\x13\n\x0btarget_kind\x18\x04 \x01(\t\"\x95\x01\n\x08TypeInfo\x12\x10\n\x08\x64\x65\x63lared\x18\x01 \x01(\t\x12\x13\n\x0bresolved_id\x18\x02 \x01(\t\x12\x15\n\rresolved_kind\x18\x03 \x01(\t\x12\x11\n\tprimitive\x18\x04 \x01(\t\x12\x18\n\x10primitive_source\x18\x05 \x01(\t\x12\x10\n\x08quantity\x18\x06 \x01(\x08\x12\x0c\n\x04unit\x18\x07 \x01(\t\"0\n\x10MultiplicityInfo\x12\r\n\x05lower\x18\x01 \x01(\t\x12\r\n\x05upper\x18\x02 \x01(\t\"V\n\rAttributeInfo\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04type\x18\x02 \x01(\t\x12\x1b\n\x05value\x18\x03 \x01(\x0b\x32\x0c.sysml.Value\x12\x0c\n\x04unit\x18\x04 \x01(\t\"\xf6\x03\n\x05Value\x12\x13\n\tint_value\x18\x01 \x01(\x03H\x00\x12\x14\n\nreal_value\x18\x02 \x01(\x01H\x00\x12\x14\n\nbool_value\x18\x03 \x01(\x08H\x00\x12\x16\n\x0cstring_value\x18\x04 \x01(\tH\x00\x12\x15\n\x0binstance_id\x18\x05 \x01(\x03H\x00\x12(\n\x08sequence\x18\x06 \x01(\x0b\x32\x14.sysml.ValueSequenceH\x00\x12\x0e\n\x04null\x18\x07 \x01(\tH\x00\x12#\n\x08quantity\x18\x08 \x01(\x0b\x32\x0f.sysml.QuantityH\x00\x12*\n\x0c\x65num_literal\x18\t \x01(\x0b\x32\x12.sysml.EnumLiteralH\x00\x12\x0f\n\x05unset\x18\n \x01(\x08H\x00\x12!\n\x07\x63omplex\x18\x0b \x01(\x0b\x32\x0e.sysml.ComplexH\x00\x12\x1d\n\x05\x61rray\x18\x0c \x01(\x0b\x32\x0c.sysml.ArrayH\x00\x12\x1f\n\x06vector\x18\r \x01(\x0b\x32\r.sysml.VectorH\x00\x12\x30\n\x0fvector_quantity\x18\x0e \x01(\x0b\x32\x15.sysml.VectorQuantityH\x00\x12\x30\n\x0fmeasurement_ref\x18\x0f \x01(\x0b\x32\x15.sysml.MeasurementRefH\x00\x12\x12\n\x08infinity\x18\x10 \x01(\x08H\x00\x42\x06\n\x04kind\";\n\x05\x41rray\x12\x12\n\ndimensions\x18\x01 \x03(\x03\x12\x1e\n\x08\x65lements\x18\x02 \x03(\x0b\x32\x0c.sysml.Value\"*\n\x06Vector\x12 \n\ncomponents\x18\x01 \x03(\x0b\x32\x0c.sysml.Value\"5\n\x0eVectorQuantity\x12#\n\ncomponents\x18\x01 \x03(\x0b\x32\x0f.sysml.Quantity\"*\n\x07\x43omplex\x12\x0c\n\x04real\x18\x01 \x01(\x01\x12\x11\n\timaginary\x18\x02 \x01(\x01\"G\n\x0b\x45numLiteral\x12\x12\n\nliteral_id\x18\x01 \x01(\t\x12\x16\n\x0e\x65numeration_id\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\"/\n\rValueSequence\x12\x1e\n\x08\x65lements\x18\x01 \x03(\x0b\x32\x0c.sysml.Value\"|\n\x08Quantity\x12\x17\n\rint_magnitude\x18\x01 \x01(\x03H\x00\x12\x18\n\x0ereal_magnitude\x18\x02 \x01(\x01H\x00\x12\x0c\n\x04unit\x18\x03 \x01(\t\x12\"\n\tunit_term\x18\x04 \x01(\x0b\x32\x0f.sysml.UnitTermB\x0b\n\tmagnitude\"S\n\x0eMeasurementRef\x12\x0c\n\x04unit\x18\x01 \x01(\t\x12\"\n\tunit_term\x18\x02 \x01(\x0b\x32\x0f.sysml.UnitTerm\x12\x0f\n\x07unit_id\x18\x03 \x01(\t\"T\n\x08UnitTerm\x12\x11\n\tscale_num\x18\x01 \x01(\x01\x12\x11\n\tscale_den\x18\x02 \x01(\x01\x12\"\n\x07\x66\x61\x63tors\x18\x03 \x03(\x0b\x32\x11.sysml.UnitFactor\"/\n\nUnitFactor\x12\x0f\n\x07unit_id\x18\x01 \x01(\t\x12\x10\n\x08\x65xponent\x18\x02 \x01(\x01\"J\n\nDiagnostic\x12\x10\n\x08severity\x18\x01 \x01(\t\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x19\n\x04span\x18\x03 \x01(\x0b\x32\x0b.sysml.Span\"^\n\x04Span\x12\x0c\n\x04\x66ile\x18\x01 \x01(\t\x12\x12\n\nstart_line\x18\x02 \x01(\x05\x12\x11\n\tstart_col\x18\x03 \x01(\x05\x12\x10\n\x08\x65nd_line\x18\x04 \x01(\x05\x12\x0f\n\x07\x65nd_col\x18\x05 \x01(\x05\"\x13\n\x11ServerInfoRequest\";\n\x12ServerInfoResponse\x12\x0f\n\x07version\x18\x01 \x01(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x02 \x03(\t\"S\n\x0cQueryRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x1b\n\x05query\x18\x02 \x01(\x0b\x32\x0c.sysml.Query\x12\x12\n\noslc_query\x18\x03 \x01(\t\"<\n\rQueryResponse\x12+\n\x08\x65lements\x18\x01 \x03(\x0b\x32\x19.sysml.QueryResultElement\"H\n\x05Query\x12\r\n\x05scope\x18\x01 \x03(\t\x12\x0e\n\x06select\x18\x02 \x03(\t\x12 \n\x05where\x18\x03 \x01(\x0b\x32\x11.sysml.Constraint\"|\n\nConstraint\x12/\n\tprimitive\x18\x01 \x01(\x0b\x32\x1a.sysml.PrimitiveConstraintH\x00\x12/\n\tcomposite\x18\x02 \x01(\x0b\x32\x1a.sysml.CompositeConstraintH\x00\x42\x0c\n\nconstraint\"s\n\x13PrimitiveConstraint\x12\x0f\n\x07inverse\x18\x01 \x01(\x08\x12\x10\n\x08property\x18\x02 \x01(\t\x12*\n\x08operator\x18\x03 \x01(\x0e\x32\x18.sysml.PrimitiveOperator\x12\r\n\x05value\x18\x04 \x03(\t\"h\n\x13\x43ompositeConstraint\x12*\n\x08operator\x18\x01 \x01(\x0e\x32\x18.sysml.CompositeOperator\x12%\n\nconstraint\x18\x02 \x03(\x0b\x32\x11.sysml.Constraint\"\xa0\x01\n\x12QueryResultElement\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04type\x18\x02 \x01(\t\x12=\n\nproperties\x18\x03 \x03(\x0b\x32).sysml.QueryResultElement.PropertiesEntry\x1a\x31\n\x0fPropertiesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"s\n\nSweepRange\x12\x11\n\tparameter\x18\x01 \x01(\t\x12\x1b\n\x05start\x18\x02 \x01(\x0b\x32\x0c.sysml.Value\x12\x19\n\x03\x65nd\x18\x03 \x01(\x0b\x32\x0c.sysml.Value\x12\x1a\n\x04step\x18\x04 \x01(\x0b\x32\x0c.sysml.Value\"\xc0\x02\n\x0fRunSweepRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x11\n\tsymbol_id\x18\x02 \x01(\t\x12\x19\n\x11subject_symbol_id\x18\x03 \x01(\t\x12\x1f\n\targuments\x18\x04 \x03(\x0b\x32\x0c.sysml.Value\x12\x43\n\x0fnamed_arguments\x18\x05 \x03(\x0b\x32*.sysml.RunSweepRequest.NamedArgumentsEntry\x12!\n\x06ranges\x18\x06 \x03(\x0b\x32\x11.sysml.SweepRange\x12\x0f\n\x07samples\x18\x07 \x01(\x03\x12\x0c\n\x04seed\x18\x08 \x01(\x04\x1a\x43\n\x13NamedArgumentsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1b\n\x05value\x18\x02 \x01(\x0b\x32\x0c.sysml.Value:\x02\x38\x01\"\xc8\x01\n\x08SweepRow\x12!\n\x06inputs\x18\x01 \x03(\x0b\x32\x11.sysml.CalcOutput\x12\"\n\x07outputs\x18\x02 \x03(\x0b\x32\x11.sysml.CalcOutput\x12 \n\x08verdicts\x18\x03 \x03(\x0b\x32\x0e.sysml.Verdict\x12\x16\n\x0e\x65lapsed_micros\x18\x04 \x01(\x03\x12\r\n\x05\x65rror\x18\x05 \x01(\t\x12,\n\x0e\x66\x61ilure_reason\x18\x06 \x01(\x0e\x32\x14.sysml.FailureReason\"\xed\x01\n\x10RunSweepResponse\x12\x1d\n\x04rows\x18\x01 \x03(\x0b\x32\x0f.sysml.SweepRow\x12\x12\n\nparameters\x18\x02 \x03(\t\x12\x0f\n\x07sampled\x18\x03 \x01(\x08\x12\x0c\n\x04seed\x18\x04 \x01(\x04\x12\r\n\x05\x65rror\x18\x05 \x01(\t\x12&\n\x0b\x64iagnostics\x18\x06 \x03(\x0b\x32\x11.sysml.Diagnostic\x12,\n\x0e\x66\x61ilure_reason\x18\x07 \x01(\x0e\x32\x14.sysml.FailureReason\x12\"\n\tinstances\x18\x08 \x03(\x0b\x32\x0f.sysml.Instance\"n\n\x17RunDocumentQueryRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x10\n\x08query_id\x18\x02 \x01(\t\x12-\n\x08\x62indings\x18\x03 \x03(\x0b\x32\x1b.sysml.DocumentQueryBinding\"O\n\x14\x44ocumentQueryBinding\x12\x11\n\tparameter\x18\x01 \x01(\t\x12$\n\x06values\x18\x02 \x03(\x0b\x32\x14.sysml.DocumentValue\"\xd5\x01\n\rDocumentValue\x12\x14\n\nelement_id\x18\x01 \x01(\tH\x00\x12\x16\n\x0cstring_value\x18\x02 \x01(\tH\x00\x12\x13\n\tint_value\x18\x03 \x01(\x03H\x00\x12\x14\n\nreal_value\x18\x04 \x01(\x01H\x00\x12\x14\n\nbool_value\x18\x05 \x01(\x08H\x00\x12\x12\n\x08infinity\x18\x06 \x01(\x08H\x00\x12#\n\x08quantity\x18\x08 \x01(\x0b\x32\x0f.sysml.QuantityH\x00\x12\x14\n\x0c\x65lement_type\x18\x07 \x01(\tB\x06\n\x04kind\"#\n\x13\x44ocumentQueryColumn\x12\x0c\n\x04name\x18\x01 \x01(\t\"9\n\x11\x44ocumentQueryCell\x12$\n\x06values\x18\x01 \x03(\x0b\x32\x14.sysml.DocumentValue\"b\n\x10\x44ocumentQueryRow\x12%\n\x07\x65lement\x18\x01 \x01(\x0b\x32\x14.sysml.DocumentValue\x12\'\n\x05\x63\x65lls\x18\x02 \x03(\x0b\x32\x18.sysml.DocumentQueryCell\"n\n\x18RunDocumentQueryResponse\x12+\n\x07\x63olumns\x18\x01 \x03(\x0b\x32\x1a.sysml.DocumentQueryColumn\x12%\n\x04rows\x18\x02 \x03(\x0b\x32\x17.sysml.DocumentQueryRow\"@\n\x15RenderDocumentRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x13\n\x0b\x64ocument_id\x18\x02 \x01(\t\"*\n\x16RenderDocumentResponse\x12\x10\n\x08markdown\x18\x01 \x01(\t*\x93\x01\n\rFailureReason\x12\x1e\n\x1a\x46\x41ILURE_REASON_UNSPECIFIED\x10\x00\x12\x1d\n\x19\x46\x41ILURE_REASON_EVALUATION\x10\x01\x12\x1d\n\x19\x46\x41ILURE_REASON_WRONG_KIND\x10\x02\x12$\n FAILURE_REASON_AMBIGUOUS_SUBJECT\x10\x03*\x9d\x04\n\x0b\x45\x64itFailure\x12\x1c\n\x18\x45\x44IT_FAILURE_UNSPECIFIED\x10\x00\x12\x1e\n\x1a\x45\x44IT_FAILURE_NO_OPERATIONS\x10\x01\x12\x1f\n\x1b\x45\x44IT_FAILURE_UNKNOWN_TARGET\x10\x02\x12!\n\x1d\x45\x44IT_FAILURE_AMBIGUOUS_TARGET\x10\x03\x12\x1b\n\x17\x45\x44IT_FAILURE_NOT_VALUED\x10\x04\x12\x1e\n\x1a\x45\x44IT_FAILURE_INVALID_VALUE\x10\x05\x12\x1d\n\x19\x45\x44IT_FAILURE_INVALID_NAME\x10\x06\x12\x1a\n\x16\x45\x44IT_FAILURE_NOT_NAMED\x10\x07\x12\"\n\x1e\x45\x44IT_FAILURE_RENAME_REFERENCED\x10\x08\x12\"\n\x1e\x45\x44IT_FAILURE_OVERLAPPING_EDITS\x10\t\x12\x1f\n\x1b\x45\x44IT_FAILURE_RESULT_INVALID\x10\n\x12\x1e\n\x1a\x45\x44IT_FAILURE_OWNER_UNKNOWN\x10\x0b\x12$\n EDIT_FAILURE_OWNER_NOT_NAMESPACE\x10\x0c\x12\x1d\n\x19\x45\x44IT_FAILURE_ILLEGAL_KIND\x10\r\x12\"\n\x1e\x45\x44IT_FAILURE_MEMBER_NAME_TAKEN\x10\x0e\x12\"\n\x1e\x45\x44IT_FAILURE_DELETE_REFERENCED\x10\x0f*\x92\x01\n\x11PrimitiveOperator\x12\"\n\x1ePRIMITIVE_OPERATOR_UNSPECIFIED\x10\x00\x12\x1c\n\x18PRIMITIVE_OPERATOR_EQUAL\x10\x01\x12\x1e\n\x1aPRIMITIVE_OPERATOR_GREATER\x10\x02\x12\x1b\n\x17PRIMITIVE_OPERATOR_LESS\x10\x03*n\n\x11\x43ompositeOperator\x12\"\n\x1e\x43OMPOSITE_OPERATOR_UNSPECIFIED\x10\x00\x12\x1a\n\x16\x43OMPOSITE_OPERATOR_AND\x10\x01\x12\x19\n\x15\x43OMPOSITE_OPERATOR_OR\x10\x02\x32\xa4\x0b\n\x0cSysMLService\x12\x44\n\rGetServerInfo\x12\x18.sysml.ServerInfoRequest\x1a\x19.sysml.ServerInfoResponse\x12>\n\tParseFile\x12\x17.sysml.ParseFileRequest\x1a\x18.sysml.ParseFileResponse\x12G\n\x0cParseSources\x12\x1a.sysml.ParseSourcesRequest\x1a\x1b.sysml.ParseSourcesResponse\x12;\n\tGetSymbol\x12\x17.sysml.GetSymbolRequest\x1a\x15.sysml.SymbolResponse\x12G\n\x0eGetDiagnostics\x12\x19.sysml.DiagnosticsRequest\x1a\x1a.sysml.DiagnosticsResponse\x12;\n\x08\x45valuate\x12\x16.sysml.EvaluateRequest\x1a\x17.sysml.EvaluateResponse\x12\x44\n\x0bInstantiate\x12\x19.sysml.InstantiateRequest\x1a\x1a.sysml.InstantiateResponse\x12J\n\rExecuteAction\x12\x1b.sysml.ExecuteActionRequest\x1a\x1c.sysml.ExecuteActionResponse\x12G\n\x0c\x45xecuteState\x12\x1a.sysml.ExecuteStateRequest\x1a\x1b.sysml.ExecuteStateResponse\x12\x38\n\x07\x43onvert\x12\x15.sysml.ConvertRequest\x1a\x16.sysml.ConvertResponse\x12\x41\n\nApplyEdits\x12\x18.sysml.ApplyEditsRequest\x1a\x19.sysml.ApplyEditsResponse\x12S\n\x10VerifyConstraint\x12\x1e.sysml.VerifyConstraintRequest\x1a\x1f.sysml.VerifyConstraintResponse\x12V\n\x11VerifyRequirement\x12\x1f.sysml.VerifyRequirementRequest\x1a .sysml.VerifyRequirementResponse\x12Y\n\x12VerifySatisfaction\x12 .sysml.VerifySatisfactionRequest\x1a!.sysml.VerifySatisfactionResponse\x12G\n\x0c\x45valuateCalc\x12\x1a.sysml.EvaluateCalcRequest\x1a\x1b.sysml.EvaluateCalcResponse\x12\x44\n\x0bRunAnalysis\x12\x19.sysml.RunAnalysisRequest\x1a\x1a.sysml.RunAnalysisResponse\x12;\n\x08RunSweep\x12\x16.sysml.RunSweepRequest\x1a\x17.sysml.RunSweepResponse\x12\x32\n\x05Query\x12\x13.sysml.QueryRequest\x1a\x14.sysml.QueryResponse\x12S\n\x10RunDocumentQuery\x12\x1e.sysml.RunDocumentQueryRequest\x1a\x1f.sysml.RunDocumentQueryResponse\x12M\n\x0eRenderDocument\x12\x1c.sysml.RenderDocumentRequest\x1a\x1d.sysml.RenderDocumentResponseB*Z(github.com/Open-MBEE/OpenSysML/api/protob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0bsysml.proto\x12\x05sysml\"\xe2\x01\n\x07Verdict\x12\x0c\n\x04kind\x18\x01 \x01(\t\x12\x12\n\nelement_id\x18\x02 \x01(\t\x12\x0f\n\x07\x65lement\x18\x03 \x01(\t\x12\r\n\x05holds\x18\x04 \x01(\x08\x12\x11\n\tcondition\x18\x05 \x01(\t\x12\x13\n\x0binstance_id\x18\x06 \x01(\x03\x12\x18\n\x10instance_type_id\x18\x07 \x01(\t\x12\r\n\x05\x65rror\x18\x08 \x01(\t\x12,\n\x0e\x66\x61ilure_reason\x18\t \x01(\x0e\x32\x14.sysml.FailureReason\x12\x16\n\x0erequirement_id\x18\n \x01(\t\"[\n\x17VerifyConstraintRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x11\n\tsymbol_id\x18\x02 \x01(\t\x12\x19\n\x11subject_symbol_id\x18\x03 \x01(\t\"\x96\x01\n\x18VerifyConstraintResponse\x12\x1f\n\x07verdict\x18\x01 \x01(\x0b\x32\x0e.sysml.Verdict\x12\"\n\tinstances\x18\x02 \x03(\x0b\x32\x0f.sysml.Instance\x12\r\n\x05\x65rror\x18\x03 \x01(\t\x12&\n\x0b\x64iagnostics\x18\x04 \x03(\x0b\x32\x11.sysml.Diagnostic\"\\\n\x18VerifyRequirementRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x11\n\tsymbol_id\x18\x02 \x01(\t\x12\x19\n\x11subject_symbol_id\x18\x03 \x01(\t\"m\n\x13VerificationVerdict\x12\x0f\n\x07\x63\x61se_id\x18\x01 \x01(\t\x12\x0c\n\x04kind\x18\x02 \x01(\t\x12\x0e\n\x06\x64\x65tail\x18\x03 \x01(\t\x12\x0f\n\x07subcase\x18\x04 \x01(\x08\x12\x16\n\x0erequirement_id\x18\x05 \x01(\t\"\xd2\x01\n\x19VerifyRequirementResponse\x12\x1f\n\x07verdict\x18\x01 \x01(\x0b\x32\x0e.sysml.Verdict\x12\"\n\tinstances\x18\x02 \x03(\x0b\x32\x0f.sysml.Instance\x12\r\n\x05\x65rror\x18\x03 \x01(\t\x12&\n\x0b\x64iagnostics\x18\x04 \x03(\x0b\x32\x11.sysml.Diagnostic\x12\x39\n\x15verification_verdicts\x18\x05 \x03(\x0b\x32\x1a.sysml.VerificationVerdict\"B\n\x19VerifySatisfactionRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x11\n\tsymbol_id\x18\x02 \x01(\t\"\x82\x02\n\x1aVerifySatisfactionResponse\x12 \n\x08verdicts\x18\x01 \x03(\x0b\x32\x0e.sysml.Verdict\x12\"\n\tinstances\x18\x02 \x03(\x0b\x32\x0f.sysml.Instance\x12\r\n\x05\x65rror\x18\x03 \x01(\t\x12&\n\x0b\x64iagnostics\x18\x04 \x03(\x0b\x32\x11.sysml.Diagnostic\x12,\n\x0e\x66\x61ilure_reason\x18\x05 \x01(\x0e\x32\x14.sysml.FailureReason\x12\x39\n\x15verification_verdicts\x18\x06 \x03(\x0b\x32\x1a.sysml.VerificationVerdict\"]\n\x13\x45valuateCalcRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x11\n\tsymbol_id\x18\x02 \x01(\t\x12\x1f\n\targuments\x18\x03 \x03(\x0b\x32\x0c.sysml.Value\"\xbd\x01\n\x14\x45valuateCalcResponse\x12\x1c\n\x06result\x18\x01 \x01(\x0b\x32\x0c.sysml.Value\x12\"\n\x07outputs\x18\x02 \x03(\x0b\x32\x11.sysml.CalcOutput\x12\r\n\x05\x65rror\x18\x03 \x01(\t\x12&\n\x0b\x64iagnostics\x18\x04 \x03(\x0b\x32\x11.sysml.Diagnostic\x12,\n\x0e\x66\x61ilure_reason\x18\x05 \x01(\x0e\x32\x14.sysml.FailureReason\"7\n\nCalcOutput\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x1b\n\x05value\x18\x02 \x01(\x0b\x32\x0c.sysml.Value\"\x84\x02\n\x12RunAnalysisRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x11\n\tsymbol_id\x18\x02 \x01(\t\x12\x19\n\x11subject_symbol_id\x18\x03 \x01(\t\x12\x1f\n\targuments\x18\x04 \x03(\x0b\x32\x0c.sysml.Value\x12\x46\n\x0fnamed_arguments\x18\x05 \x03(\x0b\x32-.sysml.RunAnalysisRequest.NamedArgumentsEntry\x1a\x43\n\x13NamedArgumentsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1b\n\x05value\x18\x02 \x01(\x0b\x32\x0c.sysml.Value:\x02\x38\x01\"\x9f\x02\n\x13RunAnalysisResponse\x12\"\n\x07outputs\x18\x01 \x03(\x0b\x32\x11.sysml.CalcOutput\x12 \n\x08verdicts\x18\x02 \x03(\x0b\x32\x0e.sysml.Verdict\x12\"\n\tinstances\x18\x03 \x03(\x0b\x32\x0f.sysml.Instance\x12\r\n\x05\x65rror\x18\x04 \x01(\t\x12&\n\x0b\x64iagnostics\x18\x05 \x03(\x0b\x32\x11.sysml.Diagnostic\x12,\n\x0e\x66\x61ilure_reason\x18\x06 \x01(\x0e\x32\x14.sysml.FailureReason\x12\x39\n\x15verification_verdicts\x18\x07 \x03(\x0b\x32\x1a.sysml.VerificationVerdict\"\x8c\x01\n\x10ParseFileRequest\x12\x13\n\tfile_path\x18\x01 \x01(\tH\x00\x12\x11\n\x07\x63ontent\x18\x02 \x01(\tH\x00\x12\x18\n\x0c\x63ontent_hash\x18\x03 \x01(\tB\x02\x18\x01\x12\x10\n\x08language\x18\x04 \x01(\t\x12\x1a\n\x12strict_conformance\x18\x05 \x01(\x08\x42\x08\n\x06source\"b\n\x0eSourceDocument\x12\x13\n\tfile_path\x18\x01 \x01(\tH\x00\x12\x11\n\x07\x63ontent\x18\x02 \x01(\tH\x00\x12\x10\n\x08language\x18\x03 \x01(\t\x12\x0c\n\x04name\x18\x04 \x01(\tB\x08\n\x06source\"[\n\x13ParseSourcesRequest\x12(\n\tdocuments\x18\x01 \x03(\x0b\x32\x15.sysml.SourceDocument\x12\x1a\n\x12strict_conformance\x18\x02 \x01(\x08\"\x83\x01\n\x14ParseSourcesResponse\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12 \n\x05roots\x18\x02 \x03(\x0b\x32\x11.sysml.SymbolInfo\x12&\n\x0b\x64iagnostics\x18\x03 \x03(\x0b\x32\x11.sysml.Diagnostic\x12\r\n\x05\x65rror\x18\x04 \x01(\t\"\x7f\n\x11ParseFileResponse\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x1f\n\x04root\x18\x02 \x01(\x0b\x32\x11.sysml.SymbolInfo\x12&\n\x0b\x64iagnostics\x18\x03 \x03(\x0b\x32\x11.sysml.Diagnostic\x12\r\n\x05\x65rror\x18\x04 \x01(\t\"9\n\x10GetSymbolRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x11\n\tsymbol_id\x18\x02 \x01(\t\"B\n\x0eSymbolResponse\x12!\n\x06symbol\x18\x01 \x01(\x0b\x32\x11.sysml.SymbolInfo\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"(\n\x12\x44iagnosticsRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\"L\n\x13\x44iagnosticsResponse\x12&\n\x0b\x64iagnostics\x18\x01 \x03(\x0b\x32\x11.sysml.Diagnostic\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"o\n\x0f\x45valuateRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x12\n\nexpression\x18\x02 \x01(\t\x12\x19\n\x11\x63ontext_symbol_id\x18\x03 \x01(\t\x12\x19\n\x11subject_symbol_id\x18\x04 \x01(\t\"g\n\x10\x45valuateResponse\x12\x1c\n\x06result\x18\x01 \x01(\x0b\x32\x0c.sysml.Value\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12&\n\x0b\x64iagnostics\x18\x03 \x03(\x0b\x32\x11.sysml.Diagnostic\"\xc2\x01\n\x08Instance\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x16\n\x0etype_symbol_id\x18\x02 \x01(\t\x12:\n\x0e\x66\x65\x61ture_values\x18\x04 \x03(\x0b\x32\".sysml.Instance.FeatureValuesEntry\x1aI\n\x12\x46\x65\x61tureValuesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.sysml.FeatureValue:\x02\x38\x01J\x04\x08\x03\x10\x04R\x05slots\"\x84\x01\n\x0c\x46\x65\x61tureValue\x12\x14\n\x0c\x66\x65\x61ture_name\x18\x01 \x01(\t\x12\x1b\n\x05value\x18\x02 \x01(\x0b\x32\x0c.sysml.Value\x12\x1c\n\x06values\x18\x03 \x03(\x0b\x32\x0c.sysml.Value\x12\x14\n\x0cmaterialized\x18\x04 \x01(\x08\x12\r\n\x05\x65rror\x18\x05 \x01(\t\";\n\x12InstantiateRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x11\n\tsymbol_id\x18\x02 \x01(\t\"\x93\x01\n\x13InstantiateResponse\x12!\n\x08instance\x18\x01 \x01(\x0b\x32\x0f.sysml.Instance\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12&\n\x0b\x64iagnostics\x18\x03 \x03(\x0b\x32\x11.sysml.Diagnostic\x12\"\n\tinstances\x18\x04 \x03(\x0b\x32\x0f.sysml.Instance\"\xba\x01\n\x14\x45xecuteActionRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x18\n\x10\x61\x63tion_symbol_id\x18\x02 \x01(\t\x12\x37\n\x06inputs\x18\x03 \x03(\x0b\x32\'.sysml.ExecuteActionRequest.InputsEntry\x1a;\n\x0bInputsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1b\n\x05value\x18\x02 \x01(\x0b\x32\x0c.sysml.Value:\x02\x38\x01\"\xc8\x01\n\x15\x45xecuteActionResponse\x12:\n\x07outputs\x18\x01 \x03(\x0b\x32).sysml.ExecuteActionResponse.OutputsEntry\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12&\n\x0b\x64iagnostics\x18\x03 \x03(\x0b\x32\x11.sysml.Diagnostic\x1a<\n\x0cOutputsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1b\n\x05value\x18\x02 \x01(\x0b\x32\x0c.sysml.Value:\x02\x38\x01\"Z\n\x13\x45xecuteStateRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x1f\n\x17state_machine_symbol_id\x18\x02 \x01(\t\x12\x0e\n\x06\x65vents\x18\x03 \x03(\t\"\xee\x01\n\x14\x45xecuteStateResponse\x12\x16\n\x0estates_visited\x18\x01 \x03(\t\x12\x44\n\rfinal_context\x18\x02 \x03(\x0b\x32-.sysml.ExecuteStateResponse.FinalContextEntry\x12\r\n\x05\x65rror\x18\x03 \x01(\t\x12&\n\x0b\x64iagnostics\x18\x04 \x03(\x0b\x32\x11.sysml.Diagnostic\x1a\x41\n\x11\x46inalContextEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1b\n\x05value\x18\x02 \x01(\x0b\x32\x0c.sysml.Value:\x02\x38\x01\"\xa0\x01\n\x0e\x43onvertRequest\x12\x13\n\tfile_path\x18\x01 \x01(\tH\x00\x12\x11\n\x07\x63ontent\x18\x02 \x01(\tH\x00\x12\x14\n\nmodel_hash\x18\x06 \x01(\tH\x00\x12\x13\n\x0b\x66rom_format\x18\x03 \x01(\t\x12\x11\n\tto_format\x18\x04 \x01(\t\x12\x1e\n\x16tolerate_syntax_errors\x18\x05 \x01(\x08\x42\x08\n\x06source\"\xb4\x01\n\x0f\x43onvertResponse\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\x12\x13\n\x0b\x66rom_format\x18\x02 \x01(\t\x12\x11\n\tto_format\x18\x03 \x01(\t\x12\r\n\x05\x65rror\x18\x04 \x01(\t\x12&\n\x0b\x64iagnostics\x18\x05 \x03(\x0b\x32\x11.sysml.Diagnostic\x12\x14\n\x0c\x65xperimental\x18\x06 \x01(\x08\x12\x1b\n\x13\x65xperimental_notice\x18\x07 \x01(\t\"Q\n\x11\x41pplyEditsRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12(\n\noperations\x18\x02 \x03(\x0b\x32\x14.sysml.EditOperation\"\xbc\x01\n\rEditOperation\x12(\n\tset_value\x18\x01 \x01(\x0b\x32\x13.sysml.SetValueEditH\x00\x12#\n\x06rename\x18\x02 \x01(\x0b\x32\x11.sysml.RenameEditH\x00\x12*\n\nadd_member\x18\x03 \x01(\x0b\x32\x14.sysml.AddMemberEditH\x00\x12#\n\x06\x64\x65lete\x18\x04 \x01(\x0b\x32\x11.sysml.DeleteEditH\x00\x42\x0b\n\toperation\"\x82\x01\n\rAddMemberEdit\x12\r\n\x05owner\x18\x01 \x01(\t\x12\x0c\n\x04kind\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x0c\n\x04type\x18\x04 \x01(\t\x12\x14\n\x0cmultiplicity\x18\x05 \x01(\t\x12\r\n\x05value\x18\x06 \x01(\t\x12\x13\n\x0bspecializes\x18\x07 \x03(\t\"-\n\nDeleteEdit\x12\x0e\n\x06target\x18\x01 \x01(\t\x12\x0f\n\x07\x63\x61scade\x18\x02 \x01(\x08\"-\n\x0cSetValueEdit\x12\x0e\n\x06target\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\".\n\nRenameEdit\x12\x0e\n\x06target\x18\x01 \x01(\t\x12\x10\n\x08new_name\x18\x02 \x01(\t\"\xc2\x01\n\x12\x41pplyEditsResponse\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\x12#\n\x07\x61pplied\x18\x02 \x03(\x0b\x32\x12.sysml.AppliedEdit\x12\r\n\x05\x65rror\x18\x03 \x01(\t\x12#\n\x07\x66\x61ilure\x18\x04 \x01(\x0e\x32\x12.sysml.EditFailure\x12&\n\x0b\x64iagnostics\x18\x05 \x03(\x0b\x32\x11.sysml.Diagnostic\x12\x1a\n\x12referring_elements\x18\x06 \x03(\t\"z\n\x0b\x41ppliedEdit\x12\x17\n\x0foperation_index\x18\x01 \x01(\x05\x12\x0e\n\x06target\x18\x02 \x01(\t\x12\x0e\n\x06offset\x18\x03 \x01(\x05\x12\x0e\n\x06length\x18\x04 \x01(\x05\x12\x10\n\x08old_text\x18\x05 \x01(\t\x12\x10\n\x08new_text\x18\x06 \x01(\t\"\xfd\x02\n\nSymbolInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0c\n\x04kind\x18\x03 \x01(\t\x12\x31\n\x08metadata\x18\x04 \x03(\x0b\x32\x1f.sysml.SymbolInfo.MetadataEntry\x12\x11\n\tchild_ids\x18\x05 \x03(\t\x12(\n\nattributes\x18\x06 \x03(\x0b\x32\x14.sysml.AttributeInfo\x12\"\n\ttype_info\x18\x07 \x01(\x0b\x32\x0f.sysml.TypeInfo\x12-\n\x0cmultiplicity\x18\x08 \x01(\x0b\x32\x17.sysml.MultiplicityInfo\x12.\n\x0fspecializations\x18\t \x03(\x0b\x32\x15.sysml.Specialization\x12#\n\x1bwithheld_library_attributes\x18\n \x01(\x05\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"X\n\x0eSpecialization\x12\x0c\n\x04kind\x18\x01 \x01(\t\x12\x10\n\x08\x64\x65\x63lared\x18\x02 \x01(\t\x12\x11\n\ttarget_id\x18\x03 \x01(\t\x12\x13\n\x0btarget_kind\x18\x04 \x01(\t\"\x95\x01\n\x08TypeInfo\x12\x10\n\x08\x64\x65\x63lared\x18\x01 \x01(\t\x12\x13\n\x0bresolved_id\x18\x02 \x01(\t\x12\x15\n\rresolved_kind\x18\x03 \x01(\t\x12\x11\n\tprimitive\x18\x04 \x01(\t\x12\x18\n\x10primitive_source\x18\x05 \x01(\t\x12\x10\n\x08quantity\x18\x06 \x01(\x08\x12\x0c\n\x04unit\x18\x07 \x01(\t\"0\n\x10MultiplicityInfo\x12\r\n\x05lower\x18\x01 \x01(\t\x12\r\n\x05upper\x18\x02 \x01(\t\"V\n\rAttributeInfo\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04type\x18\x02 \x01(\t\x12\x1b\n\x05value\x18\x03 \x01(\x0b\x32\x0c.sysml.Value\x12\x0c\n\x04unit\x18\x04 \x01(\t\"\x9b\x04\n\x05Value\x12\x13\n\tint_value\x18\x01 \x01(\x03H\x00\x12\x14\n\nreal_value\x18\x02 \x01(\x01H\x00\x12\x14\n\nbool_value\x18\x03 \x01(\x08H\x00\x12\x16\n\x0cstring_value\x18\x04 \x01(\tH\x00\x12\x15\n\x0binstance_id\x18\x05 \x01(\x03H\x00\x12(\n\x08sequence\x18\x06 \x01(\x0b\x32\x14.sysml.ValueSequenceH\x00\x12\x0e\n\x04null\x18\x07 \x01(\tH\x00\x12#\n\x08quantity\x18\x08 \x01(\x0b\x32\x0f.sysml.QuantityH\x00\x12*\n\x0c\x65num_literal\x18\t \x01(\x0b\x32\x12.sysml.EnumLiteralH\x00\x12\x0f\n\x05unset\x18\n \x01(\x08H\x00\x12!\n\x07\x63omplex\x18\x0b \x01(\x0b\x32\x0e.sysml.ComplexH\x00\x12\x1d\n\x05\x61rray\x18\x0c \x01(\x0b\x32\x0c.sysml.ArrayH\x00\x12\x1f\n\x06vector\x18\r \x01(\x0b\x32\r.sysml.VectorH\x00\x12\x30\n\x0fvector_quantity\x18\x0e \x01(\x0b\x32\x15.sysml.VectorQuantityH\x00\x12\x30\n\x0fmeasurement_ref\x18\x0f \x01(\x0b\x32\x15.sysml.MeasurementRefH\x00\x12\x12\n\x08infinity\x18\x10 \x01(\x08H\x00\x12#\n\x08\x66unction\x18\x11 \x01(\x0b\x32\x0f.sysml.FunctionH\x00\x42\x06\n\x04kind\",\n\x08\x46unction\x12\x0f\n\x07\x63\x61lc_id\x18\x01 \x01(\t\x12\x0f\n\x07self_id\x18\x02 \x01(\x03\";\n\x05\x41rray\x12\x12\n\ndimensions\x18\x01 \x03(\x03\x12\x1e\n\x08\x65lements\x18\x02 \x03(\x0b\x32\x0c.sysml.Value\"*\n\x06Vector\x12 \n\ncomponents\x18\x01 \x03(\x0b\x32\x0c.sysml.Value\"5\n\x0eVectorQuantity\x12#\n\ncomponents\x18\x01 \x03(\x0b\x32\x0f.sysml.Quantity\"*\n\x07\x43omplex\x12\x0c\n\x04real\x18\x01 \x01(\x01\x12\x11\n\timaginary\x18\x02 \x01(\x01\"G\n\x0b\x45numLiteral\x12\x12\n\nliteral_id\x18\x01 \x01(\t\x12\x16\n\x0e\x65numeration_id\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\"/\n\rValueSequence\x12\x1e\n\x08\x65lements\x18\x01 \x03(\x0b\x32\x0c.sysml.Value\"|\n\x08Quantity\x12\x17\n\rint_magnitude\x18\x01 \x01(\x03H\x00\x12\x18\n\x0ereal_magnitude\x18\x02 \x01(\x01H\x00\x12\x0c\n\x04unit\x18\x03 \x01(\t\x12\"\n\tunit_term\x18\x04 \x01(\x0b\x32\x0f.sysml.UnitTermB\x0b\n\tmagnitude\"S\n\x0eMeasurementRef\x12\x0c\n\x04unit\x18\x01 \x01(\t\x12\"\n\tunit_term\x18\x02 \x01(\x0b\x32\x0f.sysml.UnitTerm\x12\x0f\n\x07unit_id\x18\x03 \x01(\t\"T\n\x08UnitTerm\x12\x11\n\tscale_num\x18\x01 \x01(\x01\x12\x11\n\tscale_den\x18\x02 \x01(\x01\x12\"\n\x07\x66\x61\x63tors\x18\x03 \x03(\x0b\x32\x11.sysml.UnitFactor\"/\n\nUnitFactor\x12\x0f\n\x07unit_id\x18\x01 \x01(\t\x12\x10\n\x08\x65xponent\x18\x02 \x01(\x01\"J\n\nDiagnostic\x12\x10\n\x08severity\x18\x01 \x01(\t\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x19\n\x04span\x18\x03 \x01(\x0b\x32\x0b.sysml.Span\"^\n\x04Span\x12\x0c\n\x04\x66ile\x18\x01 \x01(\t\x12\x12\n\nstart_line\x18\x02 \x01(\x05\x12\x11\n\tstart_col\x18\x03 \x01(\x05\x12\x10\n\x08\x65nd_line\x18\x04 \x01(\x05\x12\x0f\n\x07\x65nd_col\x18\x05 \x01(\x05\"\x13\n\x11ServerInfoRequest\";\n\x12ServerInfoResponse\x12\x0f\n\x07version\x18\x01 \x01(\t\x12\x14\n\x0c\x63\x61pabilities\x18\x02 \x03(\t\"S\n\x0cQueryRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x1b\n\x05query\x18\x02 \x01(\x0b\x32\x0c.sysml.Query\x12\x12\n\noslc_query\x18\x03 \x01(\t\"<\n\rQueryResponse\x12+\n\x08\x65lements\x18\x01 \x03(\x0b\x32\x19.sysml.QueryResultElement\"H\n\x05Query\x12\r\n\x05scope\x18\x01 \x03(\t\x12\x0e\n\x06select\x18\x02 \x03(\t\x12 \n\x05where\x18\x03 \x01(\x0b\x32\x11.sysml.Constraint\"|\n\nConstraint\x12/\n\tprimitive\x18\x01 \x01(\x0b\x32\x1a.sysml.PrimitiveConstraintH\x00\x12/\n\tcomposite\x18\x02 \x01(\x0b\x32\x1a.sysml.CompositeConstraintH\x00\x42\x0c\n\nconstraint\"s\n\x13PrimitiveConstraint\x12\x0f\n\x07inverse\x18\x01 \x01(\x08\x12\x10\n\x08property\x18\x02 \x01(\t\x12*\n\x08operator\x18\x03 \x01(\x0e\x32\x18.sysml.PrimitiveOperator\x12\r\n\x05value\x18\x04 \x03(\t\"h\n\x13\x43ompositeConstraint\x12*\n\x08operator\x18\x01 \x01(\x0e\x32\x18.sysml.CompositeOperator\x12%\n\nconstraint\x18\x02 \x03(\x0b\x32\x11.sysml.Constraint\"\xa0\x01\n\x12QueryResultElement\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04type\x18\x02 \x01(\t\x12=\n\nproperties\x18\x03 \x03(\x0b\x32).sysml.QueryResultElement.PropertiesEntry\x1a\x31\n\x0fPropertiesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"s\n\nSweepRange\x12\x11\n\tparameter\x18\x01 \x01(\t\x12\x1b\n\x05start\x18\x02 \x01(\x0b\x32\x0c.sysml.Value\x12\x19\n\x03\x65nd\x18\x03 \x01(\x0b\x32\x0c.sysml.Value\x12\x1a\n\x04step\x18\x04 \x01(\x0b\x32\x0c.sysml.Value\"\xc0\x02\n\x0fRunSweepRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x11\n\tsymbol_id\x18\x02 \x01(\t\x12\x19\n\x11subject_symbol_id\x18\x03 \x01(\t\x12\x1f\n\targuments\x18\x04 \x03(\x0b\x32\x0c.sysml.Value\x12\x43\n\x0fnamed_arguments\x18\x05 \x03(\x0b\x32*.sysml.RunSweepRequest.NamedArgumentsEntry\x12!\n\x06ranges\x18\x06 \x03(\x0b\x32\x11.sysml.SweepRange\x12\x0f\n\x07samples\x18\x07 \x01(\x03\x12\x0c\n\x04seed\x18\x08 \x01(\x04\x1a\x43\n\x13NamedArgumentsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1b\n\x05value\x18\x02 \x01(\x0b\x32\x0c.sysml.Value:\x02\x38\x01\"\xc8\x01\n\x08SweepRow\x12!\n\x06inputs\x18\x01 \x03(\x0b\x32\x11.sysml.CalcOutput\x12\"\n\x07outputs\x18\x02 \x03(\x0b\x32\x11.sysml.CalcOutput\x12 \n\x08verdicts\x18\x03 \x03(\x0b\x32\x0e.sysml.Verdict\x12\x16\n\x0e\x65lapsed_micros\x18\x04 \x01(\x03\x12\r\n\x05\x65rror\x18\x05 \x01(\t\x12,\n\x0e\x66\x61ilure_reason\x18\x06 \x01(\x0e\x32\x14.sysml.FailureReason\"\xed\x01\n\x10RunSweepResponse\x12\x1d\n\x04rows\x18\x01 \x03(\x0b\x32\x0f.sysml.SweepRow\x12\x12\n\nparameters\x18\x02 \x03(\t\x12\x0f\n\x07sampled\x18\x03 \x01(\x08\x12\x0c\n\x04seed\x18\x04 \x01(\x04\x12\r\n\x05\x65rror\x18\x05 \x01(\t\x12&\n\x0b\x64iagnostics\x18\x06 \x03(\x0b\x32\x11.sysml.Diagnostic\x12,\n\x0e\x66\x61ilure_reason\x18\x07 \x01(\x0e\x32\x14.sysml.FailureReason\x12\"\n\tinstances\x18\x08 \x03(\x0b\x32\x0f.sysml.Instance\"n\n\x17RunDocumentQueryRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x10\n\x08query_id\x18\x02 \x01(\t\x12-\n\x08\x62indings\x18\x03 \x03(\x0b\x32\x1b.sysml.DocumentQueryBinding\"O\n\x14\x44ocumentQueryBinding\x12\x11\n\tparameter\x18\x01 \x01(\t\x12$\n\x06values\x18\x02 \x03(\x0b\x32\x14.sysml.DocumentValue\"\xd5\x01\n\rDocumentValue\x12\x14\n\nelement_id\x18\x01 \x01(\tH\x00\x12\x16\n\x0cstring_value\x18\x02 \x01(\tH\x00\x12\x13\n\tint_value\x18\x03 \x01(\x03H\x00\x12\x14\n\nreal_value\x18\x04 \x01(\x01H\x00\x12\x14\n\nbool_value\x18\x05 \x01(\x08H\x00\x12\x12\n\x08infinity\x18\x06 \x01(\x08H\x00\x12#\n\x08quantity\x18\x08 \x01(\x0b\x32\x0f.sysml.QuantityH\x00\x12\x14\n\x0c\x65lement_type\x18\x07 \x01(\tB\x06\n\x04kind\"#\n\x13\x44ocumentQueryColumn\x12\x0c\n\x04name\x18\x01 \x01(\t\"9\n\x11\x44ocumentQueryCell\x12$\n\x06values\x18\x01 \x03(\x0b\x32\x14.sysml.DocumentValue\"b\n\x10\x44ocumentQueryRow\x12%\n\x07\x65lement\x18\x01 \x01(\x0b\x32\x14.sysml.DocumentValue\x12\'\n\x05\x63\x65lls\x18\x02 \x03(\x0b\x32\x18.sysml.DocumentQueryCell\"n\n\x18RunDocumentQueryResponse\x12+\n\x07\x63olumns\x18\x01 \x03(\x0b\x32\x1a.sysml.DocumentQueryColumn\x12%\n\x04rows\x18\x02 \x03(\x0b\x32\x17.sysml.DocumentQueryRow\"@\n\x15RenderDocumentRequest\x12\x12\n\nmodel_hash\x18\x01 \x01(\t\x12\x13\n\x0b\x64ocument_id\x18\x02 \x01(\t\"*\n\x16RenderDocumentResponse\x12\x10\n\x08markdown\x18\x01 \x01(\t*\x93\x01\n\rFailureReason\x12\x1e\n\x1a\x46\x41ILURE_REASON_UNSPECIFIED\x10\x00\x12\x1d\n\x19\x46\x41ILURE_REASON_EVALUATION\x10\x01\x12\x1d\n\x19\x46\x41ILURE_REASON_WRONG_KIND\x10\x02\x12$\n FAILURE_REASON_AMBIGUOUS_SUBJECT\x10\x03*\x9d\x04\n\x0b\x45\x64itFailure\x12\x1c\n\x18\x45\x44IT_FAILURE_UNSPECIFIED\x10\x00\x12\x1e\n\x1a\x45\x44IT_FAILURE_NO_OPERATIONS\x10\x01\x12\x1f\n\x1b\x45\x44IT_FAILURE_UNKNOWN_TARGET\x10\x02\x12!\n\x1d\x45\x44IT_FAILURE_AMBIGUOUS_TARGET\x10\x03\x12\x1b\n\x17\x45\x44IT_FAILURE_NOT_VALUED\x10\x04\x12\x1e\n\x1a\x45\x44IT_FAILURE_INVALID_VALUE\x10\x05\x12\x1d\n\x19\x45\x44IT_FAILURE_INVALID_NAME\x10\x06\x12\x1a\n\x16\x45\x44IT_FAILURE_NOT_NAMED\x10\x07\x12\"\n\x1e\x45\x44IT_FAILURE_RENAME_REFERENCED\x10\x08\x12\"\n\x1e\x45\x44IT_FAILURE_OVERLAPPING_EDITS\x10\t\x12\x1f\n\x1b\x45\x44IT_FAILURE_RESULT_INVALID\x10\n\x12\x1e\n\x1a\x45\x44IT_FAILURE_OWNER_UNKNOWN\x10\x0b\x12$\n EDIT_FAILURE_OWNER_NOT_NAMESPACE\x10\x0c\x12\x1d\n\x19\x45\x44IT_FAILURE_ILLEGAL_KIND\x10\r\x12\"\n\x1e\x45\x44IT_FAILURE_MEMBER_NAME_TAKEN\x10\x0e\x12\"\n\x1e\x45\x44IT_FAILURE_DELETE_REFERENCED\x10\x0f*\x92\x01\n\x11PrimitiveOperator\x12\"\n\x1ePRIMITIVE_OPERATOR_UNSPECIFIED\x10\x00\x12\x1c\n\x18PRIMITIVE_OPERATOR_EQUAL\x10\x01\x12\x1e\n\x1aPRIMITIVE_OPERATOR_GREATER\x10\x02\x12\x1b\n\x17PRIMITIVE_OPERATOR_LESS\x10\x03*n\n\x11\x43ompositeOperator\x12\"\n\x1e\x43OMPOSITE_OPERATOR_UNSPECIFIED\x10\x00\x12\x1a\n\x16\x43OMPOSITE_OPERATOR_AND\x10\x01\x12\x19\n\x15\x43OMPOSITE_OPERATOR_OR\x10\x02\x32\xa4\x0b\n\x0cSysMLService\x12\x44\n\rGetServerInfo\x12\x18.sysml.ServerInfoRequest\x1a\x19.sysml.ServerInfoResponse\x12>\n\tParseFile\x12\x17.sysml.ParseFileRequest\x1a\x18.sysml.ParseFileResponse\x12G\n\x0cParseSources\x12\x1a.sysml.ParseSourcesRequest\x1a\x1b.sysml.ParseSourcesResponse\x12;\n\tGetSymbol\x12\x17.sysml.GetSymbolRequest\x1a\x15.sysml.SymbolResponse\x12G\n\x0eGetDiagnostics\x12\x19.sysml.DiagnosticsRequest\x1a\x1a.sysml.DiagnosticsResponse\x12;\n\x08\x45valuate\x12\x16.sysml.EvaluateRequest\x1a\x17.sysml.EvaluateResponse\x12\x44\n\x0bInstantiate\x12\x19.sysml.InstantiateRequest\x1a\x1a.sysml.InstantiateResponse\x12J\n\rExecuteAction\x12\x1b.sysml.ExecuteActionRequest\x1a\x1c.sysml.ExecuteActionResponse\x12G\n\x0c\x45xecuteState\x12\x1a.sysml.ExecuteStateRequest\x1a\x1b.sysml.ExecuteStateResponse\x12\x38\n\x07\x43onvert\x12\x15.sysml.ConvertRequest\x1a\x16.sysml.ConvertResponse\x12\x41\n\nApplyEdits\x12\x18.sysml.ApplyEditsRequest\x1a\x19.sysml.ApplyEditsResponse\x12S\n\x10VerifyConstraint\x12\x1e.sysml.VerifyConstraintRequest\x1a\x1f.sysml.VerifyConstraintResponse\x12V\n\x11VerifyRequirement\x12\x1f.sysml.VerifyRequirementRequest\x1a .sysml.VerifyRequirementResponse\x12Y\n\x12VerifySatisfaction\x12 .sysml.VerifySatisfactionRequest\x1a!.sysml.VerifySatisfactionResponse\x12G\n\x0c\x45valuateCalc\x12\x1a.sysml.EvaluateCalcRequest\x1a\x1b.sysml.EvaluateCalcResponse\x12\x44\n\x0bRunAnalysis\x12\x19.sysml.RunAnalysisRequest\x1a\x1a.sysml.RunAnalysisResponse\x12;\n\x08RunSweep\x12\x16.sysml.RunSweepRequest\x1a\x17.sysml.RunSweepResponse\x12\x32\n\x05Query\x12\x13.sysml.QueryRequest\x1a\x14.sysml.QueryResponse\x12S\n\x10RunDocumentQuery\x12\x1e.sysml.RunDocumentQueryRequest\x1a\x1f.sysml.RunDocumentQueryResponse\x12M\n\x0eRenderDocument\x12\x1c.sysml.RenderDocumentRequest\x1a\x1d.sysml.RenderDocumentResponseB*Z(github.com/Open-MBEE/OpenSysML/api/protob\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -50,14 +50,14 @@ _globals['_QUERYRESULTELEMENT_PROPERTIESENTRY']._serialized_options = b'8\001' _globals['_RUNSWEEPREQUEST_NAMEDARGUMENTSENTRY']._loaded_options = None _globals['_RUNSWEEPREQUEST_NAMEDARGUMENTSENTRY']._serialized_options = b'8\001' - _globals['_FAILUREREASON']._serialized_start=10328 - _globals['_FAILUREREASON']._serialized_end=10475 - _globals['_EDITFAILURE']._serialized_start=10478 - _globals['_EDITFAILURE']._serialized_end=11019 - _globals['_PRIMITIVEOPERATOR']._serialized_start=11022 - _globals['_PRIMITIVEOPERATOR']._serialized_end=11168 - _globals['_COMPOSITEOPERATOR']._serialized_start=11170 - _globals['_COMPOSITEOPERATOR']._serialized_end=11280 + _globals['_FAILUREREASON']._serialized_start=10411 + _globals['_FAILUREREASON']._serialized_end=10558 + _globals['_EDITFAILURE']._serialized_start=10561 + _globals['_EDITFAILURE']._serialized_end=11102 + _globals['_PRIMITIVEOPERATOR']._serialized_start=11105 + _globals['_PRIMITIVEOPERATOR']._serialized_end=11251 + _globals['_COMPOSITEOPERATOR']._serialized_start=11253 + _globals['_COMPOSITEOPERATOR']._serialized_end=11363 _globals['_VERDICT']._serialized_start=23 _globals['_VERDICT']._serialized_end=249 _globals['_VERIFYCONSTRAINTREQUEST']._serialized_start=251 @@ -165,79 +165,81 @@ _globals['_ATTRIBUTEINFO']._serialized_start=6365 _globals['_ATTRIBUTEINFO']._serialized_end=6451 _globals['_VALUE']._serialized_start=6454 - _globals['_VALUE']._serialized_end=6956 - _globals['_ARRAY']._serialized_start=6958 - _globals['_ARRAY']._serialized_end=7017 - _globals['_VECTOR']._serialized_start=7019 - _globals['_VECTOR']._serialized_end=7061 - _globals['_VECTORQUANTITY']._serialized_start=7063 - _globals['_VECTORQUANTITY']._serialized_end=7116 - _globals['_COMPLEX']._serialized_start=7118 - _globals['_COMPLEX']._serialized_end=7160 - _globals['_ENUMLITERAL']._serialized_start=7162 - _globals['_ENUMLITERAL']._serialized_end=7233 - _globals['_VALUESEQUENCE']._serialized_start=7235 - _globals['_VALUESEQUENCE']._serialized_end=7282 - _globals['_QUANTITY']._serialized_start=7284 - _globals['_QUANTITY']._serialized_end=7408 - _globals['_MEASUREMENTREF']._serialized_start=7410 - _globals['_MEASUREMENTREF']._serialized_end=7493 - _globals['_UNITTERM']._serialized_start=7495 - _globals['_UNITTERM']._serialized_end=7579 - _globals['_UNITFACTOR']._serialized_start=7581 - _globals['_UNITFACTOR']._serialized_end=7628 - _globals['_DIAGNOSTIC']._serialized_start=7630 - _globals['_DIAGNOSTIC']._serialized_end=7704 - _globals['_SPAN']._serialized_start=7706 - _globals['_SPAN']._serialized_end=7800 - _globals['_SERVERINFOREQUEST']._serialized_start=7802 - _globals['_SERVERINFOREQUEST']._serialized_end=7821 - _globals['_SERVERINFORESPONSE']._serialized_start=7823 - _globals['_SERVERINFORESPONSE']._serialized_end=7882 - _globals['_QUERYREQUEST']._serialized_start=7884 - _globals['_QUERYREQUEST']._serialized_end=7967 - _globals['_QUERYRESPONSE']._serialized_start=7969 - _globals['_QUERYRESPONSE']._serialized_end=8029 - _globals['_QUERY']._serialized_start=8031 - _globals['_QUERY']._serialized_end=8103 - _globals['_CONSTRAINT']._serialized_start=8105 - _globals['_CONSTRAINT']._serialized_end=8229 - _globals['_PRIMITIVECONSTRAINT']._serialized_start=8231 - _globals['_PRIMITIVECONSTRAINT']._serialized_end=8346 - _globals['_COMPOSITECONSTRAINT']._serialized_start=8348 - _globals['_COMPOSITECONSTRAINT']._serialized_end=8452 - _globals['_QUERYRESULTELEMENT']._serialized_start=8455 - _globals['_QUERYRESULTELEMENT']._serialized_end=8615 - _globals['_QUERYRESULTELEMENT_PROPERTIESENTRY']._serialized_start=8566 - _globals['_QUERYRESULTELEMENT_PROPERTIESENTRY']._serialized_end=8615 - _globals['_SWEEPRANGE']._serialized_start=8617 - _globals['_SWEEPRANGE']._serialized_end=8732 - _globals['_RUNSWEEPREQUEST']._serialized_start=8735 - _globals['_RUNSWEEPREQUEST']._serialized_end=9055 + _globals['_VALUE']._serialized_end=6993 + _globals['_FUNCTION']._serialized_start=6995 + _globals['_FUNCTION']._serialized_end=7039 + _globals['_ARRAY']._serialized_start=7041 + _globals['_ARRAY']._serialized_end=7100 + _globals['_VECTOR']._serialized_start=7102 + _globals['_VECTOR']._serialized_end=7144 + _globals['_VECTORQUANTITY']._serialized_start=7146 + _globals['_VECTORQUANTITY']._serialized_end=7199 + _globals['_COMPLEX']._serialized_start=7201 + _globals['_COMPLEX']._serialized_end=7243 + _globals['_ENUMLITERAL']._serialized_start=7245 + _globals['_ENUMLITERAL']._serialized_end=7316 + _globals['_VALUESEQUENCE']._serialized_start=7318 + _globals['_VALUESEQUENCE']._serialized_end=7365 + _globals['_QUANTITY']._serialized_start=7367 + _globals['_QUANTITY']._serialized_end=7491 + _globals['_MEASUREMENTREF']._serialized_start=7493 + _globals['_MEASUREMENTREF']._serialized_end=7576 + _globals['_UNITTERM']._serialized_start=7578 + _globals['_UNITTERM']._serialized_end=7662 + _globals['_UNITFACTOR']._serialized_start=7664 + _globals['_UNITFACTOR']._serialized_end=7711 + _globals['_DIAGNOSTIC']._serialized_start=7713 + _globals['_DIAGNOSTIC']._serialized_end=7787 + _globals['_SPAN']._serialized_start=7789 + _globals['_SPAN']._serialized_end=7883 + _globals['_SERVERINFOREQUEST']._serialized_start=7885 + _globals['_SERVERINFOREQUEST']._serialized_end=7904 + _globals['_SERVERINFORESPONSE']._serialized_start=7906 + _globals['_SERVERINFORESPONSE']._serialized_end=7965 + _globals['_QUERYREQUEST']._serialized_start=7967 + _globals['_QUERYREQUEST']._serialized_end=8050 + _globals['_QUERYRESPONSE']._serialized_start=8052 + _globals['_QUERYRESPONSE']._serialized_end=8112 + _globals['_QUERY']._serialized_start=8114 + _globals['_QUERY']._serialized_end=8186 + _globals['_CONSTRAINT']._serialized_start=8188 + _globals['_CONSTRAINT']._serialized_end=8312 + _globals['_PRIMITIVECONSTRAINT']._serialized_start=8314 + _globals['_PRIMITIVECONSTRAINT']._serialized_end=8429 + _globals['_COMPOSITECONSTRAINT']._serialized_start=8431 + _globals['_COMPOSITECONSTRAINT']._serialized_end=8535 + _globals['_QUERYRESULTELEMENT']._serialized_start=8538 + _globals['_QUERYRESULTELEMENT']._serialized_end=8698 + _globals['_QUERYRESULTELEMENT_PROPERTIESENTRY']._serialized_start=8649 + _globals['_QUERYRESULTELEMENT_PROPERTIESENTRY']._serialized_end=8698 + _globals['_SWEEPRANGE']._serialized_start=8700 + _globals['_SWEEPRANGE']._serialized_end=8815 + _globals['_RUNSWEEPREQUEST']._serialized_start=8818 + _globals['_RUNSWEEPREQUEST']._serialized_end=9138 _globals['_RUNSWEEPREQUEST_NAMEDARGUMENTSENTRY']._serialized_start=1782 _globals['_RUNSWEEPREQUEST_NAMEDARGUMENTSENTRY']._serialized_end=1849 - _globals['_SWEEPROW']._serialized_start=9058 - _globals['_SWEEPROW']._serialized_end=9258 - _globals['_RUNSWEEPRESPONSE']._serialized_start=9261 - _globals['_RUNSWEEPRESPONSE']._serialized_end=9498 - _globals['_RUNDOCUMENTQUERYREQUEST']._serialized_start=9500 - _globals['_RUNDOCUMENTQUERYREQUEST']._serialized_end=9610 - _globals['_DOCUMENTQUERYBINDING']._serialized_start=9612 - _globals['_DOCUMENTQUERYBINDING']._serialized_end=9691 - _globals['_DOCUMENTVALUE']._serialized_start=9694 - _globals['_DOCUMENTVALUE']._serialized_end=9907 - _globals['_DOCUMENTQUERYCOLUMN']._serialized_start=9909 - _globals['_DOCUMENTQUERYCOLUMN']._serialized_end=9944 - _globals['_DOCUMENTQUERYCELL']._serialized_start=9946 - _globals['_DOCUMENTQUERYCELL']._serialized_end=10003 - _globals['_DOCUMENTQUERYROW']._serialized_start=10005 - _globals['_DOCUMENTQUERYROW']._serialized_end=10103 - _globals['_RUNDOCUMENTQUERYRESPONSE']._serialized_start=10105 - _globals['_RUNDOCUMENTQUERYRESPONSE']._serialized_end=10215 - _globals['_RENDERDOCUMENTREQUEST']._serialized_start=10217 - _globals['_RENDERDOCUMENTREQUEST']._serialized_end=10281 - _globals['_RENDERDOCUMENTRESPONSE']._serialized_start=10283 - _globals['_RENDERDOCUMENTRESPONSE']._serialized_end=10325 - _globals['_SYSMLSERVICE']._serialized_start=11283 - _globals['_SYSMLSERVICE']._serialized_end=12727 + _globals['_SWEEPROW']._serialized_start=9141 + _globals['_SWEEPROW']._serialized_end=9341 + _globals['_RUNSWEEPRESPONSE']._serialized_start=9344 + _globals['_RUNSWEEPRESPONSE']._serialized_end=9581 + _globals['_RUNDOCUMENTQUERYREQUEST']._serialized_start=9583 + _globals['_RUNDOCUMENTQUERYREQUEST']._serialized_end=9693 + _globals['_DOCUMENTQUERYBINDING']._serialized_start=9695 + _globals['_DOCUMENTQUERYBINDING']._serialized_end=9774 + _globals['_DOCUMENTVALUE']._serialized_start=9777 + _globals['_DOCUMENTVALUE']._serialized_end=9990 + _globals['_DOCUMENTQUERYCOLUMN']._serialized_start=9992 + _globals['_DOCUMENTQUERYCOLUMN']._serialized_end=10027 + _globals['_DOCUMENTQUERYCELL']._serialized_start=10029 + _globals['_DOCUMENTQUERYCELL']._serialized_end=10086 + _globals['_DOCUMENTQUERYROW']._serialized_start=10088 + _globals['_DOCUMENTQUERYROW']._serialized_end=10186 + _globals['_RUNDOCUMENTQUERYRESPONSE']._serialized_start=10188 + _globals['_RUNDOCUMENTQUERYRESPONSE']._serialized_end=10298 + _globals['_RENDERDOCUMENTREQUEST']._serialized_start=10300 + _globals['_RENDERDOCUMENTREQUEST']._serialized_end=10364 + _globals['_RENDERDOCUMENTRESPONSE']._serialized_start=10366 + _globals['_RENDERDOCUMENTRESPONSE']._serialized_end=10408 + _globals['_SYSMLSERVICE']._serialized_start=11366 + _globals['_SYSMLSERVICE']._serialized_end=12810 # @@protoc_insertion_point(module_scope) diff --git a/clients/python/opensysml/proto/sysml_pb2.pyi b/clients/python/opensysml/proto/sysml_pb2.pyi index c9b193c9c..a282991ae 100644 --- a/clients/python/opensysml/proto/sysml_pb2.pyi +++ b/clients/python/opensysml/proto/sysml_pb2.pyi @@ -686,7 +686,7 @@ class AttributeInfo(_message.Message): def __init__(self, name: _Optional[str] = ..., type: _Optional[str] = ..., value: _Optional[_Union[Value, _Mapping]] = ..., unit: _Optional[str] = ...) -> None: ... class Value(_message.Message): - __slots__ = ("int_value", "real_value", "bool_value", "string_value", "instance_id", "sequence", "null", "quantity", "enum_literal", "unset", "complex", "array", "vector", "vector_quantity", "measurement_ref", "infinity") + __slots__ = ("int_value", "real_value", "bool_value", "string_value", "instance_id", "sequence", "null", "quantity", "enum_literal", "unset", "complex", "array", "vector", "vector_quantity", "measurement_ref", "infinity", "function") INT_VALUE_FIELD_NUMBER: _ClassVar[int] REAL_VALUE_FIELD_NUMBER: _ClassVar[int] BOOL_VALUE_FIELD_NUMBER: _ClassVar[int] @@ -703,6 +703,7 @@ class Value(_message.Message): VECTOR_QUANTITY_FIELD_NUMBER: _ClassVar[int] MEASUREMENT_REF_FIELD_NUMBER: _ClassVar[int] INFINITY_FIELD_NUMBER: _ClassVar[int] + FUNCTION_FIELD_NUMBER: _ClassVar[int] int_value: int real_value: float bool_value: bool @@ -719,7 +720,16 @@ class Value(_message.Message): vector_quantity: VectorQuantity measurement_ref: MeasurementRef infinity: bool - def __init__(self, int_value: _Optional[int] = ..., real_value: _Optional[float] = ..., bool_value: _Optional[bool] = ..., string_value: _Optional[str] = ..., instance_id: _Optional[int] = ..., sequence: _Optional[_Union[ValueSequence, _Mapping]] = ..., null: _Optional[str] = ..., quantity: _Optional[_Union[Quantity, _Mapping]] = ..., enum_literal: _Optional[_Union[EnumLiteral, _Mapping]] = ..., unset: _Optional[bool] = ..., complex: _Optional[_Union[Complex, _Mapping]] = ..., array: _Optional[_Union[Array, _Mapping]] = ..., vector: _Optional[_Union[Vector, _Mapping]] = ..., vector_quantity: _Optional[_Union[VectorQuantity, _Mapping]] = ..., measurement_ref: _Optional[_Union[MeasurementRef, _Mapping]] = ..., infinity: _Optional[bool] = ...) -> None: ... + function: Function + def __init__(self, int_value: _Optional[int] = ..., real_value: _Optional[float] = ..., bool_value: _Optional[bool] = ..., string_value: _Optional[str] = ..., instance_id: _Optional[int] = ..., sequence: _Optional[_Union[ValueSequence, _Mapping]] = ..., null: _Optional[str] = ..., quantity: _Optional[_Union[Quantity, _Mapping]] = ..., enum_literal: _Optional[_Union[EnumLiteral, _Mapping]] = ..., unset: _Optional[bool] = ..., complex: _Optional[_Union[Complex, _Mapping]] = ..., array: _Optional[_Union[Array, _Mapping]] = ..., vector: _Optional[_Union[Vector, _Mapping]] = ..., vector_quantity: _Optional[_Union[VectorQuantity, _Mapping]] = ..., measurement_ref: _Optional[_Union[MeasurementRef, _Mapping]] = ..., infinity: _Optional[bool] = ..., function: _Optional[_Union[Function, _Mapping]] = ...) -> None: ... + +class Function(_message.Message): + __slots__ = ("calc_id", "self_id") + CALC_ID_FIELD_NUMBER: _ClassVar[int] + SELF_ID_FIELD_NUMBER: _ClassVar[int] + calc_id: str + self_id: int + def __init__(self, calc_id: _Optional[str] = ..., self_id: _Optional[int] = ...) -> None: ... class Array(_message.Message): __slots__ = ("dimensions", "elements") diff --git a/clients/python/opensysml/values.py b/clients/python/opensysml/values.py index 920e2ba46..2cca36b51 100644 --- a/clients/python/opensysml/values.py +++ b/clients/python/opensysml/values.py @@ -400,6 +400,54 @@ def __str__(self) -> str: return str(self.unit) +@dataclass(frozen=True) +class Function: + """A calc held as a value: a calc definition, or a calc usage with an input + no read could supply, as ``Sq`` in ``Fn(Sq, 3.0)`` or the ``f`` of + ``in calc f {...}``. + + It is the declaration it is a value of, which is its identity: two functions + are equal exactly when ``calc_id`` and ``self_id`` are. A function closing + over the bindings of the behavior body it is declared in has no wire form; + the service sends it as an unsupported null. + + Attributes: + calc_id (str): FQN of the calc declaration (``Analysis::Sq``) + self_id (int): ID of the object the calc's feature names resolve + against, for a calc usage read off a part (``holder.scale``); 0 for + a function closing over no object. An instance ID, valid only within + the response that sent it: the service rejects a function argument + whose ``self_id`` is not 0, since no later call holds that object. + """ + + calc_id: str + self_id: int = 0 + + @classmethod + def from_pb(cls, pb_fn) -> "Function": + """Build from a ``Function`` protobuf message. + + Raises: + UnsupportedValueError: If the message names no calc. + """ + if not pb_fn.calc_id: + raise UnsupportedValueError("function naming no calc") + return cls(pb_fn.calc_id, pb_fn.self_id) + + def to_pb(self) -> "sysml_pb2.Function": + """Encode as a ``Function`` message. + + Raises: + UnsupportedValueError: If the function names no calc. + """ + if not self.calc_id: + raise UnsupportedValueError("function naming no calc") + return sysml_pb2.Function(calc_id=self.calc_id, self_id=self.self_id) + + def __str__(self) -> str: + return self.calc_id + + def _is_number(value: object) -> bool: """Whether a value is an Integer or a Real as the wire keeps them apart: a bool is neither.""" return isinstance(value, (int, float)) and not isinstance(value, bool) @@ -703,8 +751,8 @@ def value_to_python(pb_value, resolve_instance=None): Returns: int, float, complex, bool, str, list, None, :data:`UNSET`, :data:`INFINITY`, a - :class:`Quantity`, a :class:`MeasurementRef`, an :class:`Array`, a - :class:`Vector`, a :class:`VectorQuantity`, an + :class:`Quantity`, a :class:`MeasurementRef`, a :class:`Function`, an + :class:`Array`, a :class:`Vector`, a :class:`VectorQuantity`, an :class:`~opensysml.enumeration.EnumLiteral`, or the resolved instance object. A Complex is one ``complex``, never two floats; a Vector is one :class:`Vector`, never a list of numbers. @@ -728,6 +776,8 @@ def value_to_python(pb_value, resolve_instance=None): return Quantity.from_pb(pb_value.quantity) if kind == 'measurement_ref': return MeasurementRef.from_pb(pb_value.measurement_ref) + if kind == 'function': + return Function.from_pb(pb_value.function) if kind == 'instance_id': if resolve_instance is None: return pb_value.instance_id diff --git a/clients/python/tests/test_function.py b/clients/python/tests/test_function.py new file mode 100644 index 000000000..3aa6ea781 --- /dev/null +++ b/clients/python/tests/test_function.py @@ -0,0 +1,246 @@ +"""Tests for a calc held as a value as a Python value. + +A calc definition, or a calc usage with an input no read could supply — ``Sq`` +in ``Fn(Sq, 3.0)``, the ``f`` of ``in calc f {...}`` — travels in an arm of its +own, ``Value.function``, so it must arrive as one :class:`Function` naming the +declaration it is a value of and the object it was read against: not as the +unsupported null a service without the capability sends. +""" + +from unittest.mock import Mock + +import pytest + +from opensysml.capabilities import ( + CAPABILITY_COMPLEX_VALUES, + CAPABILITY_FEATURE_VALUES, + CAPABILITY_FUNCTION_VALUES, + CAPABILITY_MEASUREMENT_REFS, + CAPABILITY_STRUCTURED_VALUES, + CAPABILITY_VERIFICATION, + MissingCapabilityError, +) +from opensysml.connection import Connection +from opensysml.errors import ExecutionError, UnsupportedValueError +from opensysml.proto import sysml_pb2 +from opensysml.values import Array, Function, value_to_python + +from tests.service_gate import skip_or_fail_without_service +from tests.test_measurement_ref import make_connection + + +def pb_fn(calc_id, self_id=0): + return sysml_pb2.Value(function=sysml_pb2.Function(calc_id=calc_id, self_id=self_id)) + + +CURRENT = ( + CAPABILITY_COMPLEX_VALUES, + CAPABILITY_STRUCTURED_VALUES, + CAPABILITY_MEASUREMENT_REFS, + CAPABILITY_FUNCTION_VALUES, + CAPABILITY_FEATURE_VALUES, + CAPABILITY_VERIFICATION, +) +OLD = tuple(c for c in CURRENT if c != CAPABILITY_FUNCTION_VALUES) + + +# --- Reading ------------------------------------------------------------- + + +def test_a_function_decodes_as_the_calc_it_names(): + sq = value_to_python(pb_fn("Demo::Sq")) + assert sq == Function("Demo::Sq") + assert sq.self_id == 0 + assert str(sq) == "Demo::Sq" + + +def test_a_function_read_off_an_object_keeps_the_object(): + scale = value_to_python(pb_fn("Demo::Scaler::scale", 7)) + assert scale == Function("Demo::Scaler::scale", 7) + # The object is part of the identity: another object's read is another value. + assert scale != Function("Demo::Scaler::scale", 8) + assert scale != Function("Demo::Scaler::scale") + + +def test_a_function_nested_in_a_sequence_or_array_decodes_in_place(): + seq = sysml_pb2.Value(sequence=sysml_pb2.ValueSequence( + elements=[pb_fn("Demo::Sq"), pb_fn("Demo::Cube")] + )) + assert value_to_python(seq) == [Function("Demo::Sq"), Function("Demo::Cube")] + arr = sysml_pb2.Value(array=sysml_pb2.Array( + dimensions=[1], elements=[pb_fn("Demo::Sq")] + )) + assert value_to_python(arr) == Array((1,), (Function("Demo::Sq"),)) + + +def test_a_function_naming_no_calc_is_reported(): + with pytest.raises(UnsupportedValueError, match="naming no calc"): + value_to_python(pb_fn("")) + with pytest.raises(UnsupportedValueError, match="naming no calc"): + value_to_python(pb_fn("", 3)) + + +def test_a_function_survives_the_wire_bytes(): + for value in (pb_fn("Demo::Sq"), pb_fn("Demo::Scaler::scale", 7)): + again = sysml_pb2.Value() + again.ParseFromString(value.SerializeToString()) + assert again == value + assert value_to_python(again) == value_to_python(value) + + +def test_a_service_without_the_capability_still_reports_unsupported(): + """An older service sends a null naming the calc, which stays an error.""" + null = sysml_pb2.Value(null="unsupported: function Demo::Sq") + with pytest.raises(UnsupportedValueError, match="function Demo::Sq"): + value_to_python(null) + + +# --- Sending ------------------------------------------------------------- + + +def test_a_function_is_sent_as_its_own_arm(): + """Round trip: what the client sends is what it reads back.""" + conn = make_connection(Mock(), CURRENT) + sq = Function("Demo::Sq") + + sent = conn._python_to_value(sq) + assert sent.WhichOneof("kind") == "function" + assert sent.function.calc_id == "Demo::Sq" + assert sent.function.self_id == 0 + assert value_to_python(sent) == sq + + scale = Function("Demo::Scaler::scale", 7) + sent = conn._python_to_value(scale) + assert sent.function.self_id == 7 + assert value_to_python(sent) == scale + + nested = conn._python_to_value([1, [sq]]) + assert value_to_python(nested) == [1, [sq]] + + +def test_a_function_naming_no_calc_is_refused_before_it_is_sent(): + conn = make_connection(Mock(), CURRENT) + with pytest.raises(UnsupportedValueError, match="naming no calc"): + conn._python_to_value(Function("")) + + +def test_a_function_is_not_sent_to_a_service_without_the_capability(): + """An older service would read the unknown arm as null, so nothing is sent.""" + stub = Mock() + conn = make_connection(stub, OLD) + sq = Function("Demo::Sq") + + for value in (sq, [1, [sq]], Array((1,), (sq,))): + with pytest.raises(MissingCapabilityError) as excinfo: + conn.execute_action("Demo::apply", "hash", inputs={"f": value}) + assert excinfo.value.capability == CAPABILITY_FUNCTION_VALUES + with pytest.raises(MissingCapabilityError) as excinfo: + conn.calc("Demo::fn", "hash", arguments=[value, 3.0]) + assert excinfo.value.capability == CAPABILITY_FUNCTION_VALUES + stub.ExecuteAction.assert_not_called() + stub.EvaluateCalc.assert_not_called() + + # A number still travels: it never needed the capability. + stub.EvaluateCalc.return_value = sysml_pb2.EvaluateCalcResponse( + result=sysml_pb2.Value(real_value=9.0) + ) + assert conn.calc("Demo::sq", "hash", arguments=[3.0]).value == 9.0 + stub.EvaluateCalc.assert_called_once() + + +def test_a_function_is_sent_to_a_service_with_the_capability(): + stub = Mock() + stub.EvaluateCalc.return_value = sysml_pb2.EvaluateCalcResponse( + result=sysml_pb2.Value(real_value=9.0) + ) + conn = make_connection(stub, CURRENT) + + got = conn.calc("Demo::fn", "hash", arguments=[Function("Demo::Sq"), 3.0]).value + assert got == 9.0 + request = stub.EvaluateCalc.call_args.args[0] + assert request.arguments[0].WhichOneof("kind") == "function" + assert request.arguments[0].function.calc_id == "Demo::Sq" + + +FUNCTION_MODEL = """ +package Demo { + private import ScalarValues::*; + + calc def Sq { in v : Real; return : Real = v * v; } + calc def Cube { in v : Real; return : Real = v * v * v; } + calc def Fn { in calc f { in v : Real; return : Real; } in a : Real; return : Real = f(a); } + calc fn : Fn; + calc def UseFn { return : Real = Fn(Sq, 3.0); } + calc useFn : UseFn; + + calc def Identity { in calc f { in v : Real; return : Real; } return r = f; } + attribute pick = Identity(Sq); + attribute picks = (Identity(Sq), Identity(Cube)); + + part def Scaler { + attribute k : Real = 2.0; + calc scale { in x : Real; return : Real = x * k; } + } + part holder : Scaler; + attribute scaler = holder.scale; +} +""" + + +@pytest.mark.integration +class TestFunctionsAgainstTheService: + """What a caller actually gets back from the real service for a real model.""" + + def setup_method(self): + import grpc + + try: + self.conn = Connection(auto_start=False) + self.conn._stub.GetDiagnostics(sysml_pb2.DiagnosticsRequest(model_hash="")) + except grpc.RpcError as exc: + if exc.code() != grpc.StatusCode.NOT_FOUND: + self.conn = None + skip_or_fail_without_service( + f"the sysml-grpc service on localhost:50051 answered {exc.code()}" + ) + except Exception as exc: + self.conn = None + skip_or_fail_without_service( + f"no sysml-grpc service could be reached on localhost:50051 ({exc})" + ) + self.model = self.conn.load_from_content(FUNCTION_MODEL) + + def teardown_method(self): + conn = self.__dict__.get("conn") + if conn is not None: + conn.close() + + def test_the_service_advertises_function_values(self): + assert self.conn.server_info().has(CAPABILITY_FUNCTION_VALUES) + + def test_a_calc_definition_reads_as_a_function(self): + sq = self.conn.eval("Demo::pick", self.model.hash) + assert sq == Function("Demo::Sq") + + assert self.conn.eval("Demo::picks", self.model.hash) == [ + Function("Demo::Sq"), Function("Demo::Cube"), + ] + + def test_a_calc_read_off_an_object_names_the_object(self): + scale = self.conn.eval("Demo::scaler", self.model.hash) + assert isinstance(scale, Function) + assert scale.calc_id == "Demo::Scaler::scale" + assert scale.self_id != 0 + + def test_a_function_sent_as_a_calc_argument_is_invoked(self): + assert self.conn.calc("Demo::useFn", self.model.hash).value == 9.0 + got = self.conn.calc( + "Demo::fn", self.model.hash, arguments=[Function("Demo::Cube"), 2.0] + ).value + assert got == 8.0 + + def test_a_function_naming_no_calc_of_the_model_is_refused(self): + with pytest.raises(ExecutionError): + self.conn.calc( + "Demo::fn", self.model.hash, arguments=[Function("Demo::Missing"), 2.0] + ) diff --git a/clients/python/tests/test_wire_compat.py b/clients/python/tests/test_wire_compat.py index 8dd3d963b..dbe063fbd 100644 --- a/clients/python/tests/test_wire_compat.py +++ b/clients/python/tests/test_wire_compat.py @@ -312,6 +312,29 @@ def test_a_measurement_reference_is_an_added_value_arm(): assert older.SerializeToString() == payload +def test_a_function_is_an_added_value_arm(): + """The function arm is new field 17.""" + fields = sysml_pb2.Value.DESCRIPTOR.fields_by_name + assert fields["function"].number == 17 + fn_fields = sysml_pb2.Function.DESCRIPTOR.fields_by_name + assert {name: f.number for name, f in fn_fields.items()} == { + "calc_id": 1, "self_id": 2, + } + + value = sysml_pb2.Value(function=sysml_pb2.Function( + calc_id="Demo::Scaler::scale", self_id=7, + )) + payload = value.SerializeToString() + again = sysml_pb2.Value() + again.ParseFromString(payload) + assert again == value + + # A client whose schema predates the arm keeps the bytes intact. + older = sysml_pb2.ServerInfoRequest() + older.ParseFromString(payload) + assert older.SerializeToString() == payload + + def test_apply_edits_is_an_added_rpc(): """The edit RPC is new, so it displaces nothing a client already calls.""" service = sysml_pb2.DESCRIPTOR.services_by_name["SysMLService"] diff --git a/clients/rust/README.md b/clients/rust/README.md index 195ae9507..ad438fe3e 100644 --- a/clients/rust/README.md +++ b/clients/rust/README.md @@ -144,10 +144,10 @@ for: Decoding a response is never gated on capabilities: if a service sends an enum, unset value, complex number, array, vector, vector quantity, measurement -reference, or feature-value arm, the client understands that answer. Consumers +reference, function, or feature-value arm, the client understands that answer. Consumers can inspect `Capabilities::has` or use `Capabilities::require` when they need to gate their own use of `enum_values`, `unset_value`, `complex_values`, -`structured_values`, `measurement_refs`, `feature_values`, or another advertised +`structured_values`, `measurement_refs`, `function_values`, `feature_values`, or another advertised operation. ## Conformance runner diff --git a/clients/rust/conformance/src/compare.rs b/clients/rust/conformance/src/compare.rs index 0014f3e01..ce40dd10c 100644 --- a/clients/rust/conformance/src/compare.rs +++ b/clients/rust/conformance/src/compare.rs @@ -260,6 +260,10 @@ fn lookup_parts<'a>(value: &'a Value, parts: &[&str]) -> Option<&'a Value> { } } +/// Fields other than `Instance.id` that carry a runtime instance id: a value's +/// reference to an object and a function's bound object. +pub const RUNTIME_ID_KEYS: [&str; 2] = ["instance_id", "self_id"]; + pub fn label_instance_ids(value: &mut Value) { let mut labels = HashMap::new(); label_ids(value, &mut labels); @@ -276,11 +280,15 @@ fn label_ids(value: &mut Value, labels: &mut HashMap) { label_id(child, labels); } } - if let Some(child) = object.get_mut("instance_id") { - label_id(child, labels); + for key in RUNTIME_ID_KEYS { + if let Some(child) = object.get_mut(key) { + label_id(child, labels); + } } for (key, child) in object.iter_mut() { - if !(is_instance && key == "id") && key != "instance_id" { + let labeled = + (is_instance && key == "id") || RUNTIME_ID_KEYS.contains(&key.as_str()); + if !labeled { label_ids(child, labels); } } @@ -354,13 +362,15 @@ mod tests { "instance": {"id": 9, "type_symbol_id": "T", "feature_values": {}}, "instances": [{"id": 9, "type_symbol_id": "T", "feature_values": {}}, {"id": 12, "type_symbol_id": "T", "feature_values": {}}], - "value": {"instance_id": 12} + "value": {"instance_id": 12}, + "function": {"calc_id": "T::f", "self_id": 9} }); label_instance_ids(&mut actual); assert_eq!(actual["instance"]["id"], "@1"); assert_eq!(actual["instances"][0]["id"], "@1"); assert_eq!(actual["instances"][1]["id"], "@2"); assert_eq!(actual["value"]["instance_id"], "@2"); + assert_eq!(actual["function"]["self_id"], "@1"); } #[test] diff --git a/clients/rust/conformance/src/normalize.rs b/clients/rust/conformance/src/normalize.rs index 437a43956..d19466a04 100644 --- a/clients/rust/conformance/src/normalize.rs +++ b/clients/rust/conformance/src/normalize.rs @@ -1,3 +1,4 @@ +use crate::compare::RUNTIME_ID_KEYS; use serde_json::Value; pub const MODEL_HASH: &str = "${model_hash}"; @@ -16,13 +17,18 @@ fn normalize_inner(value: &mut Value, model_hash: &str) { && object.contains_key("id"); let is_server_info = object.contains_key("capabilities") && object.contains_key("version"); + // A function bound to no object leaves self_id at its default, which + // the wire renders as absent rather than as an id to label. + if object.get("self_id").and_then(Value::as_i64) == Some(0) { + object.remove("self_id"); + } for (key, child) in object.iter_mut() { if is_server_info && key == "version" { *child = Value::String(VERSION.to_owned()); } else if is_instance && key == "id" { // Runtime ids are labelled in one pass after all other // normalization, where the response-wide map is shared. - } else if key == "instance_id" { + } else if RUNTIME_ID_KEYS.contains(&key.as_str()) { // See compare::label_instance_ids. } else { normalize_inner(child, model_hash); @@ -49,3 +55,23 @@ fn is_absolute_path(value: &str) -> bool { value.starts_with('/') || (value.len() > 2 && value.as_bytes()[1] == b':' && value.as_bytes()[2] == b'\\') } + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[test] + fn a_function_bound_to_no_object_has_no_self_id() { + let mut unbound = json!({"result": {"function": {"calc_id": "F::Sq", "self_id": 0}}}); + normalize(&mut unbound, ""); + assert_eq!( + unbound, + json!({"result": {"function": {"calc_id": "F::Sq"}}}) + ); + + let mut bound = json!({"result": {"function": {"calc_id": "F::S::f", "self_id": 7}}}); + normalize(&mut bound, ""); + assert_eq!(bound["result"]["function"]["self_id"], 7); + } +} diff --git a/clients/rust/conformance/sysml.descriptor.binpb b/clients/rust/conformance/sysml.descriptor.binpb index 4ff628720..bf4c1e2da 100644 Binary files a/clients/rust/conformance/sysml.descriptor.binpb and b/clients/rust/conformance/sysml.descriptor.binpb differ diff --git a/clients/rust/opensysml/src/domain.rs b/clients/rust/opensysml/src/domain.rs index 231ad2635..b1b7c5662 100644 --- a/clients/rust/opensysml/src/domain.rs +++ b/clients/rust/opensysml/src/domain.rs @@ -381,6 +381,24 @@ pub struct MeasurementRef { pub unit_id: Option, } +/// A calc held as a value: a calc definition, or a calc usage with an input no +/// read could supply, as `Sq` in `Fn(Sq, 3.0)` or the `f` of `in calc f {...}`. +/// +/// It is the declaration it is a value of, which is its identity: two functions +/// are equal exactly when both fields are. A function closing over the bindings +/// of the behavior body it is declared in has no wire form; the service sends +/// it as an unsupported [`Value::Null`], as does a service without +/// `function_values` for every function. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Function { + /// FQN of the calc declaration (`Analysis::Sq`). + pub calc_id: String, + /// ID of the object the calc's feature names resolve against, for a calc + /// usage read off a part (`holder.scale`); `None` for a function closing + /// over no object. + pub self_id: Option, +} + /// A runtime value returned by the service. #[derive(Clone, Debug, PartialEq)] pub enum Value { @@ -410,6 +428,8 @@ pub enum Value { VectorQuantity(VectorQuantity), /// A bare measurement unit. MeasurementRef(MeasurementRef), + /// A calc held as a value. + Function(Function), /// Explicit null value. Null, /// A materialized feature with no value. @@ -470,6 +490,15 @@ pub(crate) fn value_from_wire(value: wire::Value) -> Result { wire::value::Kind::MeasurementRef(v) => { Ok(Value::MeasurementRef(measurement_ref_from_wire(v)?)) } + wire::value::Kind::Function(v) => { + if v.calc_id.is_empty() { + return Err(Error::Decode("a function names no calc".to_owned())); + } + Ok(Value::Function(Function { + calc_id: v.calc_id, + self_id: (v.self_id != 0).then_some(v.self_id), + })) + } wire::value::Kind::EnumLiteral(v) => Ok(Value::EnumLiteral(EnumLiteral { literal_id: v.literal_id, enumeration_id: v.enumeration_id, @@ -507,6 +536,7 @@ fn kind_name(kind: &wire::value::Kind) -> &'static str { wire::value::Kind::Vector(_) => "vector", wire::value::Kind::VectorQuantity(_) => "vector_quantity", wire::value::Kind::MeasurementRef(_) => "measurement_ref", + wire::value::Kind::Function(_) => "function", } } @@ -1236,6 +1266,48 @@ mod tests { } } + fn function(calc_id: &str, self_id: i64) -> wire::Value { + wire::Value { + kind: Some(wire::value::Kind::Function(wire::Function { + calc_id: calc_id.to_owned(), + self_id, + })), + } + } + + #[test] + fn a_function_is_the_calc_it_names_read_against_an_object_or_none() { + assert_eq!( + value_from_wire(function("Demo::Sq", 0)).ok(), + Some(Value::Function(Function { + calc_id: "Demo::Sq".to_owned(), + self_id: None, + })) + ); + assert_eq!( + value_from_wire(function("Demo::Scaler::scale", 7)).ok(), + Some(Value::Function(Function { + calc_id: "Demo::Scaler::scale".to_owned(), + self_id: Some(7), + })) + ); + + // Naming no calc is malformed at any depth. + assert!(matches!( + value_from_wire(function("", 0)), + Err(Error::Decode(message)) if message.contains("names no calc") + )); + let nested = wire::Value { + kind: Some(wire::value::Kind::Sequence(wire::ValueSequence { + elements: vec![function("", 3)], + })), + }; + assert!(matches!( + value_from_wire(nested), + Err(Error::Decode(message)) if message.contains("names no calc") + )); + } + #[test] fn a_measurement_reference_keeps_its_unit_reduction_and_declaration() { let km = wire_term(1000.0, &[("SI::metre", 1.0)]); diff --git a/clients/rust/opensysml/src/lib.rs b/clients/rust/opensysml/src/lib.rs index 1ddb74ec6..f6dfa934e 100644 --- a/clients/rust/opensysml/src/lib.rs +++ b/clients/rust/opensysml/src/lib.rs @@ -16,8 +16,8 @@ pub mod wire { pub use connection::Connection; pub use domain::{ Array, Capabilities, Complex, Diagnostic, EnumLiteral, EvalOptions, Evaluation, FeatureValue, - Instance, Instantiation, Language, Magnitude, MeasurementRef, Model, ParseOptions, Quantity, - ServerInfo, Span, Symbol, UnitFactor, UnitTerm, Value, Vector, VectorQuantity, + Function, Instance, Instantiation, Language, Magnitude, MeasurementRef, Model, ParseOptions, + Quantity, ServerInfo, Span, Symbol, UnitFactor, UnitTerm, Value, Vector, VectorQuantity, }; pub use error::{Error, Status}; diff --git a/clients/rust/opensysml/src/proto/sysml/sysml.rs b/clients/rust/opensysml/src/proto/sysml/sysml.rs index a809e0542..89cf07971 100644 --- a/clients/rust/opensysml/src/proto/sysml/sysml.rs +++ b/clients/rust/opensysml/src/proto/sysml/sysml.rs @@ -825,7 +825,7 @@ pub struct AttributeInfo { /// Value represents a runtime-evaluable value #[derive(Clone, PartialEq, ::prost::Message)] pub struct Value { - #[prost(oneof="value::Kind", tags="1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16")] + #[prost(oneof="value::Kind", tags="1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17")] pub kind: ::core::option::Option, } /// Nested message and enum types in `Value`. @@ -877,8 +877,30 @@ pub mod value { /// as DocumentValue.infinity is. #[prost(bool, tag="16")] Infinity(bool), + /// a calc as a value, named by its declaration + #[prost(message, tag="17")] + Function(super::Function), } } +/// Function is a calc held as a value: a calc definition, or a calc usage with +/// an input no read could supply, as `Sq` in `Fn(Sq, 3.0)` or the `f` of +/// `in calc f {...}`. It crosses as the declaration it is a value of, which is +/// its identity: two functions are the same exactly when calc_id and self_id +/// are. A function closing over the bindings of the behavior body it is +/// declared in has no wire form and crosses as the null arm. +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct Function { + /// FQN of the calc declaration ("Analysis::Sq"). Its identity. + #[prost(string, tag="1")] + pub calc_id: ::prost::alloc::string::String, + /// ID of the object the calc's feature names resolve against, for a calc + /// usage read off a part (`holder.scale`); 0 for a function closing over no + /// object. An instance id, living only within the response that sent it: a + /// request function with a non-zero self_id is rejected, since no later call + /// holds that object. + #[prost(int64, tag="2")] + pub self_id: i64, +} /// Array is a Collections::Array: its elements flattened in row-major order /// under its dimensions, compared by content rather than by the object read. #[derive(Clone, PartialEq, ::prost::Message)] @@ -1095,6 +1117,11 @@ pub struct ServerInfoResponse { /// refused with UNIMPLEMENTED rather than read as another /// value. Separate from structured_values, which a client /// built before this arm existed may already claim. + /// "function_values" - a Value carries a calc held as a value as function, + /// named by its declaration, rather than reporting it as an + /// unsupported null, and one is accepted as an action input + /// or calc argument; without it, one is refused with + /// UNIMPLEMENTED rather than read as another value. /// "apply_edits" - the ApplyEdits RPC edits a parsed model's own source, /// preserving everything the edit did not touch. /// "document_query" - the RunDocumentQuery RPC runs a named document query diff --git a/clients/rust/opensysml/tests/client.rs b/clients/rust/opensysml/tests/client.rs index 0117b98ee..9f5a89221 100644 --- a/clients/rust/opensysml/tests/client.rs +++ b/clients/rust/opensysml/tests/client.rs @@ -7,7 +7,7 @@ use std::process::Command; use std::thread; use std::time::Duration; -use opensysml::{Complex, Connection, Error, EvalOptions, Magnitude, Value, Vector}; +use opensysml::{Complex, Connection, Error, EvalOptions, Function, Magnitude, Value, Vector}; fn service_or_skip() -> Option { match Connection::private() { @@ -331,6 +331,52 @@ fn a_bare_measurement_reference_arrives_with_its_reduction_and_declaration() { ); } +#[test] +fn a_calc_held_as_a_value_arrives_as_the_function_it_names() { + let Some(connection) = service_or_skip() else { + return; + }; + assert!(connection.capabilities().has("function_values")); + let model = match connection.parse_content( + "package Demo { + private import ScalarValues::*; + calc def Sq { in v : Real; return : Real = v * v; } + calc def Fn { in calc f { in v : Real; return : Real; } in a : Real; return : Real = f(a); } + calc def Identity { in calc f { in v : Real; return : Real; } return r = f; } + attribute pick = Identity(Sq); + attribute nine = Fn(Sq, 3.0); + part def Scaler { + attribute k : Real = 2.0; + calc scale { in x : Real; return : Real = x * k; } + } + part holder : Scaler; + attribute scaler = holder.scale; + }", + &Default::default(), + ) { + Ok(model) => model, + Err(error) => panic!("parse failed: {error}"), + }; + let eval = |expr: &str| match model.evaluate(expr, &EvalOptions::default()) { + Ok(evaluation) => evaluation.result, + Err(error) => panic!("evaluating {expr} failed: {error}"), + }; + + assert_eq!( + eval("Demo::pick"), + Value::Function(Function { + calc_id: "Demo::Sq".to_owned(), + self_id: None, + }) + ); + assert_eq!(eval("Demo::nine"), Value::Real(9.0)); + let Value::Function(scale) = eval("Demo::scaler") else { + panic!("Demo::scaler should be a function"); + }; + assert_eq!(scale.calc_id, "Demo::Scaler::scale"); + assert!(scale.self_id.is_some_and(|id| id > 0)); +} + #[test] fn blocking_calls_work_inside_a_runtime() { let Some(connection) = service_or_skip() else { diff --git a/cmd/conformance/normalize.go b/cmd/conformance/normalize.go index 882487086..c55688e37 100644 --- a/cmd/conformance/normalize.go +++ b/cmd/conformance/normalize.go @@ -24,6 +24,7 @@ var normalizedIDs = map[string]bool{ "sysml.Instance.id": true, "sysml.Value.instance_id": true, "sysml.Verdict.instance_id": true, + "sysml.Function.self_id": true, } // integer and unsigned are normalized integral values. They are distinct from diff --git a/cmd/conformance/normalize_test.go b/cmd/conformance/normalize_test.go index 0ed50e819..6d91919f6 100644 --- a/cmd/conformance/normalize_test.go +++ b/cmd/conformance/normalize_test.go @@ -45,6 +45,7 @@ func TestInstanceIDsAreLabelledInOrderOfAppearance(t *testing.T) { Id: 41, FeatureValues: map[string]*pb.FeatureValue{ "engine": {Value: &pb.Value{Kind: &pb.Value_InstanceId{InstanceId: 77}}}, + "scale": {Value: &pb.Value{Kind: &pb.Value_Function{Function: &pb.Function{CalcId: "T::scale", SelfId: 41}}}}, }, }, Instances: []*pb.Instance{{Id: 41}, {Id: 77}}, @@ -56,6 +57,9 @@ func TestInstanceIDsAreLabelledInOrderOfAppearance(t *testing.T) { if got, _ := lookup(tree, "instance.feature_values.engine.value.instance_id"); got != "@2" { t.Errorf("nested instance_id = %v, want @2", got) } + if got, _ := lookup(tree, "instance.feature_values.scale.value.function.self_id"); got != "@1" { + t.Errorf("function self_id = %v, want the owner's label @1", got) + } if got, _ := lookup(tree, "instances.1.id"); got != "@2" { t.Errorf("instances.1.id = %v, want the same label @2", got) } diff --git a/cmd/conformance/pkgclient.go b/cmd/conformance/pkgclient.go index 7193dec3a..c2d9cbbfa 100644 --- a/cmd/conformance/pkgclient.go +++ b/cmd/conformance/pkgclient.go @@ -806,6 +806,11 @@ func valueToProto(value opensysml.Value) *pb.Value { UnitTerm: unitTermToProto(v.Term), UnitId: v.UnitID, }}} + case opensysml.Function: + return &pb.Value{Kind: &pb.Value_Function{Function: &pb.Function{ + CalcId: v.CalcID, + SelfId: int64(v.Self), + }}} default: return nil } @@ -895,6 +900,11 @@ func valueFromProto(value *pb.Value) (opensysml.Value, bool) { Term: unitTermFromProto(kind.MeasurementRef.GetUnitTerm()), UnitID: kind.MeasurementRef.GetUnitId(), }, true + case *pb.Value_Function: + return opensysml.Function{ + CalcID: kind.Function.GetCalcId(), + Self: opensysml.InstanceID(kind.Function.GetSelfId()), + }, true default: return nil, true } diff --git a/conformance/README.md b/conformance/README.md index 61051c386..0a2d91e62 100644 --- a/conformance/README.md +++ b/conformance/README.md @@ -108,7 +108,7 @@ These cannot be compared literally, so the runner replaces them before comparing | `ServerInfoResponse.version` | `${version}` | A build string; the contract is capabilities, not versions. | | Any string equal to the model hash of the scenario's model | `${model_hash}` | Content-addressed and free to change with the parser. | | Any absolute path (`Span.file`, echoed request paths) | `${path}` | Names the machine the service ran on. A relative name is kept. | -| Runtime instance ids (`Instance.id`, `Value.instance_id`, `Verdict.instance_id`) | `@1`, `@2`, … | Assigned per call. Labelled in order of first appearance, so a scenario can still state that a feature value names the same object as an entry of `instances`. | +| Runtime instance ids (`Instance.id`, `Value.instance_id`, `Verdict.instance_id`, `Function.self_id`) | `@1`, `@2`, … | Assigned per call. Labelled in order of first appearance, so a scenario can still state that a feature value names the same object as an entry of `instances`. | ### Ignored values @@ -150,6 +150,7 @@ What a request asks for is fixed per capability: | `complex_values` | Response-population capability: encode complex numbers as unsupported nulls; no request asks for them. | | `structured_values` | Encode arrays, vectors and vector quantities as unsupported nulls; refuse a request carrying one, at any depth. | | `measurement_refs` | Encode bare measurement references as unsupported nulls; refuse a request carrying one, at any depth. | +| `function_values` | Encode functions (a calc read as a value) as unsupported nulls; refuse a request carrying one, at any depth. | The default service reports and supports every capability above. `make conformance` also starts a second service with `strict_conformance` and `oslc_query` withheld, verifies that its advertisement diff --git a/conformance/fixtures/function.sysml b/conformance/fixtures/function.sysml new file mode 100644 index 000000000..8c4a16a0a --- /dev/null +++ b/conformance/fixtures/function.sysml @@ -0,0 +1,16 @@ +package F { + private import ScalarValues::*; + calc def Sq { in v : Real; return : Real = v * v; } + calc def Cube { in v : Real; return : Real = v * v * v; } + calc def Apply { in calc f { in v : Real; return : Real; } in a : Real; return : Real = f(a); } + calc apply : Apply; + calc def Identity { in calc f { in v : Real; return : Real; } return r = f; } + attribute pick = Identity(Sq); + attribute nine = Apply(Sq, 3.0); + part def Scaler { + attribute k : Real = 2.0; + calc scale { in x : Real; return : Real = x * k; } + } + part holder : Scaler; + attribute scaler = holder.scale; +} diff --git a/conformance/scenarios/01-server-info.json b/conformance/scenarios/01-server-info.json index 100a31d1a..c12f510e5 100644 --- a/conformance/scenarios/01-server-info.json +++ b/conformance/scenarios/01-server-info.json @@ -26,6 +26,7 @@ "complex_values", "structured_values", "measurement_refs", + "function_values", "apply_edits", "strict_conformance", "feature_values", diff --git a/conformance/scenarios/04-evaluate.json b/conformance/scenarios/04-evaluate.json index 403f06ae6..556fac213 100644 --- a/conformance/scenarios/04-evaluate.json +++ b/conformance/scenarios/04-evaluate.json @@ -415,6 +415,77 @@ ] } }, + { + "id": "evaluate/a_calc_definition_read_as_a_value_is_a_function_naming_it", + "description": "A calc used as a value travels as a function naming the calc by qualified name and no object, so a client can hand it back as an argument of another calc.", + "rpc": "Evaluate", + "requires_capabilities": [ + "function_values" + ], + "model": { + "fixture": "function.sysml" + }, + "request": { + "model_hash": "${model_hash}", + "expression": "F::pick" + }, + "expect": { + "response": { + "result": { + "function": { + "calc_id": "F::Sq" + } + } + }, + "absent": [ + "result.function.self_id" + ] + } + }, + { + "id": "evaluate/a_calc_of_an_object_read_as_a_value_names_the_object_it_computes_over", + "description": "A calc usage owned by a part reads the part's features when invoked, so its function carries the object it is bound to beside the calc's name.", + "rpc": "Evaluate", + "requires_capabilities": [ + "function_values" + ], + "model": { + "fixture": "function.sysml" + }, + "request": { + "model_hash": "${model_hash}", + "expression": "F::scaler" + }, + "expect": { + "response": { + "result": { + "function": { + "calc_id": "F::Scaler::scale", + "self_id": "@1" + } + } + } + } + }, + { + "id": "evaluate/a_calc_invoked_through_a_calc_parameter_answers_its_result", + "description": "A calc passed to another calc's calc-typed parameter is invoked through that parameter, so Apply(Sq, 3.0) is 9.", + "rpc": "Evaluate", + "model": { + "fixture": "function.sysml" + }, + "request": { + "model_hash": "${model_hash}", + "expression": "F::nine" + }, + "expect": { + "response": { + "result": { + "real_value": 9.0 + } + } + } + }, { "id": "evaluate/an_unknown_model_is_not_found", "rpc": "Evaluate", diff --git a/conformance/scenarios/10-evaluate-calc.json b/conformance/scenarios/10-evaluate-calc.json index bfac6c388..23b680851 100644 --- a/conformance/scenarios/10-evaluate-calc.json +++ b/conformance/scenarios/10-evaluate-calc.json @@ -239,6 +239,118 @@ "result" ] } + }, + { + "id": "evaluate_calc/a_function_argument_is_the_calc_the_calc_invokes", + "description": "A function sent as an argument is bound to the calc it names and invoked through the calc-typed parameter, so applying Cube to 2 is 8.", + "rpc": "EvaluateCalc", + "requires_capabilities": [ + "function_values" + ], + "model": { + "fixture": "function.sysml" + }, + "request": { + "model_hash": "${model_hash}", + "symbol_id": "F::apply", + "arguments": [ + { + "function": { + "calc_id": "F::Cube" + } + }, + { + "real_value": 2.0 + } + ] + }, + "expect": { + "response": { + "result": { + "real_value": 8.0 + } + }, + "absent": [ + "error" + ] + }, + "expect_without_capability": { + "status": "UNIMPLEMENTED", + "status_message_contains": "function_values" + } + }, + { + "id": "evaluate_calc/a_function_argument_naming_no_calc_is_refused", + "description": "A function whose calc_id names nothing the model declares as a calc cannot be bound, so the answer reports the malformed argument in band rather than invoking anything.", + "rpc": "EvaluateCalc", + "requires_capabilities": [ + "function_values" + ], + "model": { + "fixture": "function.sysml" + }, + "request": { + "model_hash": "${model_hash}", + "symbol_id": "F::apply", + "arguments": [ + { + "function": { + "calc_id": "F::holder" + } + }, + { + "real_value": 2.0 + } + ] + }, + "expect": { + "contains": { + "error": "F::holder is not a calc" + }, + "response": { + "failure_reason": "FAILURE_REASON_EVALUATION" + }, + "absent": [ + "result" + ] + } + }, + { + "id": "evaluate_calc/a_function_argument_naming_no_object_is_refused", + "description": "Objects live only within the response that created them, so a function carrying a self_id, whatever object this call numbers the same, is refused in band.", + "rpc": "EvaluateCalc", + "requires_capabilities": [ + "function_values" + ], + "model": { + "fixture": "function.sysml" + }, + "request": { + "model_hash": "${model_hash}", + "symbol_id": "F::apply", + "arguments": [ + { + "function": { + "calc_id": "F::Scaler::scale", + "self_id": 3 + } + }, + { + "real_value": 2.0 + } + ] + }, + "expect": { + "contains": { + "error": "self_id 3 names no object of this call" + }, + "response": { + "failure_reason": "FAILURE_REASON_EVALUATION" + }, + "absent": [ + "result" + ] + } } ] } diff --git a/docs/internals/architecture.md b/docs/internals/architecture.md index cd71da7f4..e97ed269b 100644 --- a/docs/internals/architecture.md +++ b/docs/internals/architecture.md @@ -247,6 +247,7 @@ Full evaluator with **user-defined calc invocation**, **constraint evaluation**, - Feature access `x.y.z` resolved against instance feature values - KerML operator library (`->select`, `->collect`, `size`, string ops) - **Calc invocation:** Resolve calc symbol → extract params/return → bind args to parameters → evaluate return expression +- **Function values** (`function_value.go`, `ValFunction`): a calc definition, a calc usage with an unsupplied input or an `in calc` parameter read as a value is the calc's lowered `calcShape` plus the environment it was read in — declaring scope, the object it was read off, and, for a calc declared inside a behavior body, the frames through the innermost active run of that behavior (`EvalContext.enclosingRun`, by `frame.runs`) — never a caller's frames, and none when no such run is active. Invoking one (`f(a)` through a calc-typed parameter, or `SampledFunctions::Sample` applying its `calculation`) takes the calc invocation path (`invokeCalcShapeIn`), never a closure over statements; `ValExpr` remains the distinct kind for an expression body a collection operation evaluates per element - **Constraint evaluation:** Extract `assert`/`assume` members → evaluate boolean expressions → check satisfaction (with optional `not` negation) - **Requirement evaluation:** Extract `subject`/`assume`/`require`/`actor` members → validate bindings → evaluate conditions - **Scoped evaluation:** `EvalContext.scope` for name resolution, frame stack for parameter bindings diff --git a/docs/project/spec-compliance.md b/docs/project/spec-compliance.md index 76dcf61c1..6eb920c68 100644 --- a/docs/project/spec-compliance.md +++ b/docs/project/spec-compliance.md @@ -177,7 +177,7 @@ Each row documents one behavioral semantic feature: | Recursive calc (direct or mutual) evaluates to its result, bounded by the run's calc depth budget | `invoke_calc.go` `Context.enterCalc` (`ErrCalcRecursionLimit`), budget from `budget.go` `BudgetsFromEnv` (`OPENSYSML_MAX_CALC_DEPTH`, default 10000, ceiling 25000) | `calc_recursion_factorial.sysml`, `calc_recursion_fibonacci.sysml`, `calc_recursion_mutual.sysml`, `calc_recursion_descends_sequence.sysml`, `calc_recursion_beyond_nesting_bound.sysml`, `invoke_calc_recursion_test.go:TestRecursiveCalcSpendsTheDepthBudget`, `:TestRecursiveCalcDepthIsNotAFixedBound`, `:TestMutuallyRecursiveCalcsEvaluate` | ✅ Faithful | | A recursion that does not terminate spends a budget and is reported, never hanging or exhausting the stack | `invoke_calc.go` `Context.enterCalc` (`ErrCalcRecursionLimit`) and `context.go` step counter (`ErrStepLimitExceeded`); the ceiling on the depth budget keeps the report ahead of the goroutine stack limit | `robustness_test.go:testCalcDirectRecursion`, `:testCalcMutualRecursion`, `:testCalcRecursionSpendsStepBudget`, `:testCalcRecursionAtDepthCeiling`, `budget_test.go:TestBudgetFromValue` (above the ceiling) | ✅ Faithful | | Step budget bounds calc evaluation | `context.go` step counter (`ErrStepLimitExceeded`), budget from `budget.go` `BudgetsFromEnv` (`OPENSYSML_MAX_STEPS`, default 10000000) | `robustness_test.go:testStepBudgetExceeded`, `budget_test.go:TestBudgetFromValue` | ✅ Faithful | -| Ahead-of-time native compilation of a calc (`sysml -compile`, C or Go): the compiled program computes, prints and fails as the interpreter does, or the calc is refused with a typed error naming the construct | `codegen/compile.go` `Compiler.Compile` (`UnsupportedError`), `codegen/compile_seq.go` (sequences: shapes, literals, ranges, indexing, `for`, the sequence and control libraries), `codegen/emit_c.go` `EmitC` + `emit_c_seq.go`, `codegen/emit_go.go` `EmitGo` + `emit_go_seq.go`, `repl/compile.go` `Session.CompileCalc`; scope and measurements in `native-compilation.md` | `repl/compile_test.go:TestCompiledCalcsAgreeWithInterpreter` (scalar and `Seq::*` cases, value and failure agreement), `:TestCompileRefusesWhatItCannotCompile` | ⚠️ Approximate — scalars and homogeneous sequences of Integer/Real/Boolean with any multiplicity, the element budget included; records, enums, mixed Integer/Real sequences and strings are refused, and compiled code has no step budget | +| Ahead-of-time native compilation of a calc (`sysml -compile`, C or Go): the compiled program computes, prints and fails as the interpreter does, or the calc is refused with a typed error naming the construct | `codegen/compile.go` `Compiler.Compile` (`UnsupportedError`), `codegen/compile_seq.go` (sequences: shapes, literals, ranges, indexing, `for`, the sequence and control libraries), `codegen/emit_c.go` `EmitC` + `emit_c_seq.go`, `codegen/emit_go.go` `EmitGo` + `emit_go_seq.go`, `repl/compile.go` `Session.CompileCalc`; scope and measurements in `native-compilation.md` | `repl/compile_test.go:TestCompiledCalcsAgreeWithInterpreter` (scalar and `Seq::*` cases, value and failure agreement), `:TestCompileRefusesWhatItCannotCompile` | ⚠️ Approximate — scalars and homogeneous sequences of Integer/Real/Boolean with any multiplicity, the element budget included; records, enums, mixed Integer/Real sequences, strings and function values are refused (an `in calc` parameter is refused as `parameter f binds a function value`, and so is every calc invoking one, transitively), and compiled code has no step budget | | Statement body (SysML v2 7.19, `CalculationBodyItem` carries the items of an action body): local declarations, assignment (`assign x := e;`), `if`/`else`, `while`, `loop … until`, `for`, `return` | `parser/behavior.go` `parseCalcBody`/`atCalcStatement` → `lower/calc_body.go` `CalcBody` → `runtime/statements.go` `stmtEngine` driven by `invoke_calc.go` `runCalcBody` | `calc_statement_body.sysml` (golden AST), `calc_iterative_factorial.sysml`, `calc_conditional_branch.sysml`, `calc_for_over_sequence.sysml`, `calc_loop_until_body.sysml` | ✅ Faithful | | A kind-less `x = e;` or `x := e;` in a calculation body, a constraint body or a nested statement body (a `while`/`loop`/`for`/`if` body, a state's entry/do/exit block, a transition effect) declares a feature of that body (`CalculationBodyItem` → `ActionBodyItem` → `NonOccurrenceUsageMember` → `DefaultReferenceUsage` with a `FeatureValue`, `SysML.xtext:632`), as the pilot reads it; `AssignmentNode` (`:1535`) begins with the `assign` keyword, so no kind-less member is an assignment, and the trailing expression reads such a local by name | `parser/behavior.go` `atNamedCalcMember` (`parseCalcBody`, `atConstraintBodyDeclaration`), `parseActionMember`, `atCalcStatement` | `parse/calc_default_reference_usage.golden`, `parser/f61_keywordless_members_test.go:TestF61AssignmentStaysAssignment` (a body right after the name, `twice { doc /* */ }`, is the usage's `UsageBody`, not a body expression), `conformance/calc_body_default_reference_usage.sysml` | ✅ Faithful (the pinned validator accepts every fixture) | | `ReturnParameterMember` (`:1961`) is `'return' UsageElement`, so a result may specialize without a name or a typing, its `Identification` may open with a short name, its `FeatureDeclaration` may open with a multiplicity, and a body may follow the identification directly — while a value or a body alone declares nothing: `return :> ISQ::power = e;`, `return r :> ISQ::speed = e;`, `return :> T[*] = e;`, `return result : T = e;`, `return :> T = e;`, `return ;`, `return [*] = xs;`, `return [*] :> xs;`, `return r { doc /* */ }`; `return = e;` and `return { … }` are reported once | `parser/behavior.go` `atReturnedUsage`, `parseResultMember` (`parseIdentification`, `parseRelationships`, `parseMultiplicity`) | `parse/calc_default_reference_usage.golden`, `behavior_test.go:TestParseResultMember_AnonymousAndBodiedForms`, `negative_test.go:calc_return_subsets_no_target`, `:calc_return_named_subsets_no_target`, `:calc_return_short_name_unclosed`, `:calc_return_short_name_empty`, `:calc_return_value_only`, `:calc_return_body_only`, `:calc_return_multiplicity_unclosed` | ✅ Faithful (the pinned validator accepts every form) | @@ -225,6 +225,13 @@ Each row documents one behavioral semantic feature: | A `return` in a calc that also states an output supplies the invocation's value only: an output keeps the value its own binding computes, so a body returning something else does not change what reading that output answers. Only the result parameter (`return : Real = …`) takes the returned value directly | `runtime/calc_usage.go` `runCalcUsage` (memoizing a returned value under an output's name only for `calcOutput.IsResult`) | `calc_valued_output_with_return.sysml` conformance (`Apart`: `ca.a` is 6 while `Apart(5)` is 500; `Together`, `Both`) | ✅ Faithful | | `%calc` on a calc usage lists the outputs of one evaluation; a chain into a usage evaluates at the prompt | `repl/meta.go` `doCalc`/`calcUsageOutputs` | `repl/runtime_commands_test.go:TestCalcUsageOutputsAtThePrompt` | ✅ Faithful | | A calc usage's outputs are evaluation results, not feature values of an object | `runtime/calc_usage.go` (no instance materialization) | `calc_usage_instance_slots.sysml` (the features fed by the outputs are feature values; the usage itself is not), pilot-exec-diff `w6d:calc-usage` | ⚠️ Approximate (unrefereeable: the pinned artifact answers a `CalculationUsage` node rather than an output value. `%instances` and export show the features valued from outputs, not the usage's outputs themselves) | +| A calc definition, a calc usage with an unsupplied input, or an `in calc` parameter named where a value is expected is a **function value** (KerML 1.1 §7.4.4: a Function is a Behavior with a `result`, an Expression a Step typed by one, and a feature reference to either denotes it; §8.3.4.8 `Function::result`, `FeatureReferenceExpression`; SysML v2 §7.17: a calc def is a Function, a calc usage an Expression). The value is the calc's lowered invocation interface (`calcShape`) together with the environment it was read in — its declaring scope and the object it was read off — and nothing else: no statement closure is built, and the value is invoked through the same path a calc usage invocation takes (`invokeCalcShapeIn`). Reading a calc usage whose inputs are all bound evaluates it as before; a library function the runtime implements natively (`RealFunctions::sqrt`, `floor`) reads as a value carrying that implementation, while a library operation that binds its arguments unevaluated (`SequenceFunctions::size` and the other `->` operations) is refused as `ErrNotAFunction` | `runtime/value.go` `ValFunction`, `runtime/function_value.go` `functionValue`/`EvalContext.functionValueOf`/`Context.readsAsFunction`/`EvalContext.calcAsValue`, `invoke_calc.go` `calcShapeOf` (a natively implemented library function computes), `eval.go` `evalFeatureReference`, `describe.go` (`the function Sq`), `trace.go` `FormatTraceValue` (`calc(Sq)`), `repl/meta.go` | `function_value_read.sysml`, `function_value_probe.sysml` (`Fn(Sq, 3.0)` is `9.0`), `function_value_calc_usage.sysml`, `function_value_library.sysml`, `robustness_test.go:function_value_of_a_built_in`, `value_kinds_test.go:TestFunctionValueIdentity`, `:TestEveryValueKindIsDispatched`, `eval_no_value_test.go`, `repl/evalin_test.go` | ✅ Faithful | +| An `in calc` parameter of a calc or an action (SysML v2 §7.17, §8.3.16 `CalculationUsage` as a parameter) accepts a function value or null and nothing else, positionally or by name; the body invokes it as `f(a)`, through a chain (`p.f(a)`), nested (`f(f(a))`) and as an argument to another calc-typed parameter, binding the callee's inputs positionally and by name as a direct invocation does. A calc usage bound as an action input (`in f = sq;`) is the function value it reads as | `runtime/invoke_calc.go` `calcParameter.checkFunction`, `function_value.go` `EvalContext.invokeFunction`, `eval.go` `evalInvocation`/`evalFeatureChain`, `parser/behavior.go` `parameterKindKeywords` (`calc`) | `function_value_probe.sysml` + `function_value_probe.trace.golden` (`TestExecutionTrace`), `function_value_named_args.sysml`, `function_value_chain_call.sysml`, `function_value_action_parameter.sysml`, parser golden `action_calc_parameter.sysml`, `robustness_test.go:function_value_call_of_a_non_function` (`ErrNotAFunction`), `:function_value_bound_to_a_non_function` (`ErrNotAFunction`), `:function_value_arity_mismatch` (`ErrCalcArity`), `testCalcUnboundParameter` (`ErrUnboundParameter`) | ✅ Faithful | +| A call through a feature chain, `holder.scale(3.0)` (KerMLExpressions `InstantiatedTypeMember` → `OwnedFeatureChain`), is checked statically as a direct call is: the chain names the calc feature applied, its arguments are held to that feature's effective inputs positionally and by name (a wrong type, an unknown name, too many arguments, an unbound default-less input), a chain to a non-behavior is refused, and the call is typed by the calc's result, so binding it to an incompatible declared type is reported. At run time the chain in call position denotes the calc feature applied over the receiver's object whatever its inputs — one every input of which a default supplies, or one with no input, is applied (`holder.scaled()` is `10.0`) where the bare read `holder.scaled` computes its result — and any other member is read as written and must hold a function | `passes/typecheck_expr.go` `inferChainInvocation`, `passes/invocation.go` `ChainCallee`/`InvocationArgs`, `passes/typecheck_value.go` `invocationResultParameter`; `runtime/eval.go` `evalChainInvocation`/`chainCallee` | `passes/typecheck_expr_test.go:TestExprChainInvocationChecked`; `function_value_chain_call.sysml`, `function_value_chain_defaults.sysml` | ✅ Faithful | +| A calc read off an object (`twice.scale`, a calc usage owned by a part) is a function value over that object: invoked later it reads that object's features, so the same calc read off two parts is two values. A calc declared inside a behavior body (a calc's or an action's) closes over the bindings of the run it was read in — its enclosing parameters and locals — and a function returned from a calc (`return : Unary = scale;`) keeps them after the run ends. The closure reaches only the code written inside that body: a body the nested calc inherits from a calc declared elsewhere (`calc again : RecursiveStep;`) reads none of the enclosing run's bindings, so a case performing itself as a step nests its frames no deeper than its recursion. The run closed over is a run of the behavior the calc is declared in — the innermost active invocation of that calc (or of one specializing it) or performance of that action — not whatever calc happens to be evaluating: a nested calc applied from a calc between it and its owner reads the owner's `k`, not the caller's, and one applied (`Outer::inner(2.0)`) or read (`Fn(Outer::inner, 2.0)`) while no run of its owner is active closes over nothing, so its body's `k` is unresolved rather than a same-named parameter of the caller | `function_value.go` `functionValue.self`/`.enclosing`, `EvalContext.functionValueOf` (`enclosedByBehaviorBody`); `eval.go` `EvalContext.enclosingRun`; `frame.go` `frame.runs` (a calc frame by `calcShape.qualifiedBy`, a performance frame by its scope's owner, a snapshot by the action it was copied from: `frame.performs`/`frame.snapshot`); `calc_usage.go` `runOf`, `calcShape.bodyEnclosing`/`declaredWithin`; `invoke_calc.go` `Context.invokeCalcShapeIn` | `function_value_feature_closure.sysml` (`Fn(twice.scale, 5.0) + Fn(thrice.scale, 5.0)` is `25.0`), `function_value_body_closure.sysml` (`Outer`, `Maker`, `Shadowed`), `function_value_action_closure.sysml` (an action-local calc passed as a function applies another that reads the performance's input and local), `function_value_sampled_closure.sysml`, `action_nested_calc_reads_performance.sysml`; `robustness_test.go:function_value_inherited_body_outside_the_closure`, `:function_value_nested_calc_outside_its_run`; `analysis_robustness_test.go:recursion_through_a_step` | ✅ Faithful | +| Function values are equal, and key a set, by the calc together with the object it was read off (`Sq == Sq`, `twice.scale != thrice.scale`); a value closing over a behavior body's bindings is equal to every read of the same calc within the same run of that body and to none from another run (`inner == inner` within one calc body; `Maker(2.0) != Maker(2.0)` across two invocations). Adoption into another runtime rebinds an ordinary function value to the same calc of the adopting model, the object it was read off adopted with it; one closing over a body's bindings is refused as a typed error (the run those bindings belonged to has ended and cannot be reconstructed) | `value_equality.go` `valueEqual`/`valueKeyFunc` (`ValFunction` arm), `adopt.go` | `value_kinds_test.go:TestFunctionValueIdentity`, conformance `function_value_closure_equality`, `adopt_test.go:TestAdoptRebindsAFunctionValue` | ✅ Faithful | +| `SampledFunctions::Sample(f, domain)` samples a user calc passed as its `in calc calculation` argument: the library's own body runs, `domainValues->collect { in x; new SamplePair(x, calculation(x)) }` invoking the function value inside the collection body, and `Range` of the result reads the samples back | the library body under `invoke_calc.go` `invokeCalcShapeIn`, `function_value.go` `EvalContext.invokeFunction` (from a collection body's frame), `collections.go` | `function_value_sampled.sysml` (`Range(Sample(Sq, (1.0, 2.0, 3.0)))` is `[1.0, 4.0, 9.0]`), `function_value_sampled_closure.sysml` (a calc read off a part, sampled) | ✅ Faithful | +| `SampledFunctions::SamplePair` arithmetic and `SampledFunctions::interpolateLinear` on the library's own examples: reading a `SamplePair`'s `domainValue` or `rangeValue` yields the one-element sequence `[1.0]`, and `-`/`*` refuse a sequence operand (`type mismatch: operator '-' is not defined for a Real and a sequence`). The cause is the `[0..*]`-inherited member read not reducing a singleton sequence to the scalar it denotes, which is independent of function values (the same failure reproduces with no function value involved) and is not papered over here with a `SamplePair`-specific unwrap | `runtime/eval.go` `chainMemberValue`/`evalArithmetic`, `instance.go` (scalar-feature admission reduces a singleton only where the feature is declared scalar) | reproduced by `SampledFunctions::interpolateLinear` and by `s.samples#(1).domainValue - 1.0` | ❌ Not implemented (the singleton reduction of a `[0..*]`-inherited member read; the failure is a typed error, not a wrong answer) | | Boolean operators evaluated at runtime (`and`, `or`, `xor`, `implies`, short-circuiting where they can) | `eval.go` `evalLogical` | `calc_boolean_operators.sysml` | ✅ Faithful | | Identity (`===`, `!==`), null coalescing (`??`, lazy) and remainder (`%`) evaluated at runtime | `eval.go` `evalIdentity`/`evalNullCoalesce`/`evalArithmetic` | `calc_identity_operators.sysml`, `calc_null_coalesce.sysml`, `calc_modulo_operator.sysml` | ✅ Faithful | | Value classification evaluated at runtime (`istype`, `hastype`, and `x @ T` with a value subject and an ordinary type, the third `ClassificationTestOperator` of the KerML 1.0 ClassificationExpression grammar beside `hastype` and `istype`, which the reference evaluates as `istype`; SysML v2 8.4.4.2 ClassificationExpression) | `eval.go` `evalTypeClassification`, `valueHasType`, `directValueType`, and `evalOperator` routing `OpAt` there through `classifiesValue` — `x @ T` is a value test when a subject is written and T resolves to a type that is not a metadata definition or metaclass (`semantics.IsMetadataType`), and the metadata test of the next row otherwise — a value's *direct* type is derived from what the value already holds (a scalar constant's value kind, a string's `String`, an object's classifier, a selected variant, an enumeration literal's enumeration, a quantity's numeric value), so no type field was added to `runtime.Value`; `istype` is `semantics.Model.Classifies` — the operand's type conforming to the target or classifying it as a composed type does, the same relation a cast asks — and `hastype` is identity with it, and a sequence or set is classified elementwise | `eval_operator_test.go:TestTypeClassificationOperators` (the reference's 20-case truth table case by case), `:TestTypeClassificationFollowsSelectedVariant`, `conformance/w7d_type_classification.sysml` + `.expected.json` (the same table over the library scalar types), `robustness_test.go:type_classification_unresolved_type`, `:type_classification_undetermined_value_type`, `conformance/value_classification_at.sysml` + `.expected.json` (`n3 @ Integer`, `n3 @ Real`, `n3 @ String`, `seqInt @ Integer`, `car @ Vehicle` beside the same subjects under `hastype` and the metadata forms `belt @ Safety`, `belt @@ SysML::PartUsage`), pilot-exec-diff `w6d:istype-*`, `:hastype-*`, `at-*`, `real-at-integer`, `seq-at-integer`, `car-at-*` | ✅ Faithful — **externally refereed**: all 20 cases agree with the pinned reference, which they did not before (they were `ours-error`; the execution report moved from 31 to 51 agreeing of 94 cases), and the ten `@` cases agree too (65 agreeing of 104): `a : Integer = 3` answers `a @ Integer` and `a @ Real` `true`, `a @ String` `false`, and `car : Car` answers `car @ Vehicle` `true`, where before `a @ Integer` was reported as classifying no element. The referee settles what the direct type is: `nat3 : Natural = 3` answers `hastype Integer` `true`, so it is the value's type and not the declaring feature's, while `car : Car` answers `istype Vehicle` `true` and `hastype Vehicle` `false`. A mixed sequence is `false` and an empty one is `true`. A type operand that does not resolve is `ErrUnresolvedType` and a value whose direct type cannot be determined is `ErrUndeterminedValueType` — reported, never answered `false`. One local rule the referee does not cover: a feature declared `[0..1]` with no value classifies as the empty collection, because the evaluator otherwise reports it as valueless | @@ -381,7 +388,7 @@ by name is refused naming what is missing rather than approximated. | A relationship target that resolves outside the object names no feature of it, so `attribute totalmass :> ISQ::mass` specializes the library feature and contributes nothing to a same-named feature of the object; a target the object carries under its name — including one a restating declaration masks — is a feature of it, and an unqualified target the declaring scope cannot see is looked up among the object's members | `subsetting.go` `relatedFeatures`/`isFeatureOf` | `subsetting_test.go:TestSubsettingIgnoresALibraryFeatureOfTheSameName`, conformance `cubesat_mass_rollup` | ✅ Faithful | | An object carries the features the Systems, Domain and OpenSysML libraries declare for its kind — `Items::Item::voids`, `isSolid = isEmpty(voids)`, `shape :>> spaceBoundary`, `subitems`, `Parts::Part::ownedPorts`, `Requirements::RequirementCheck::subj`/`actors`/`stakeholders`/`assumptions`/`constraints`, the Geometry `ShapeItems` fields — with their defaults, derived expressions and multiplicity, masked by a model's own `:>>` like any inherited feature, while the Kernel Semantic Library frame every object restates (`Anything::self`, `Occurrence::portions`, `timeSlices`, `snapshots`, `startShot`, `endShot`, the transfer features) stays out of the shape. Which is which is the library *tier* the loader records for each library document (`Kernel Semantic`, `Kernel Data Type`, `Kernel Function`, `Systems`, `Domain`, `OpenSysML`), kept through the symbol cache, not a path the runtime inspects | `symbols/library_tier.go` `LibraryTier` (`Frame()`), `symbols/index.go` `MarkLibraryTier`/`LibraryTier`, `libs/loader.go` `TierOf`, `libs/snapshot.go` (format 3, carrying each library document's tier and text digest); `runtime/library_frame.go` `frameFeature` (a frame tier, a member a value-held type's value carries itself — `semantics/shape.go` `HeldByValue`, the library records staying objects; *Structured values* below — a directed or result parameter, or a Systems/Domain member that redefines or subsets a frame root), `runtime/shape.go` `buildFeatures`; `runtime/adopt.go` `writeShape` names a library type with the index's `LibraryIdentity` — a digest of every library document's name, tier and text — rather than expanding it, so the digest stays bounded and two contexts loaded over libraries that differ do not agree on it | `libs/library_tier_test.go` (tiers, identity through the snapshot, identity moved by an edited document), `symbols/library_tier_test.go`, `runtime/library_shape_test.go` (shape, classification, inheritance and masking, stable order, no extra objects, digest, typed error, subject/objective aliasing, redefinition through masked targets), `runtime/adopt_test.go` (`TestAdoptRefusesASameNamedLibraryTypeOfAnotherLibrary`, `TestShapeDigestExpandsALibraryOfUnknownText`), `runtime/connector_test.go:TestOptionalConnectorLinksNothingOfItsOwn`, conformance `instance_library_geometry_box`, `instance_library_item_part_features`, `instance_library_requirement_features`, `instance_library_requirement_subject_binding` (`subject v : Vehicle = car;` binds `v` and the inherited `subj`, through `shape.go` `extractDefaultValue` reading a `SubjectMember`'s binding), `repl/library_features_test.go` (`%features`, `%eval box.isSolid`, `box.voids`), `grpc/library_feature_values_test.go` | ⚠️ Approximate (the features materialize and evaluate — `box.isSolid` is `true`, `box.voids` and `box.shape` empty, `box.faces` six objects, `box.edges` their twenty-four edges, `rect.e1.length` the rectangle's own `length` — and a model inheriting nothing from these libraries keeps its shape digest. One residual: the Kernel frame roots a Systems member may restate (`Anything::self`, `Occurrence::timeSlices`, `incomingTransfers`, `outgoingTransfers`) are named in `library_frame.go` `frameRoots` rather than derived from the library, since nothing in the library marks them. The Geometry graph's own rules — a listed item classified by the collection it is listed in, a nested usage reading `Rectangle::length` from its enclosing object, `faces.edges` collected across the faces — are the five rows below. Unrefereeable: the pinned pilot artifact answers declaration nodes, not instance values, for these features) | | An optional composite feature fills to its lower bound like a collection: `part spare : Wheel[0..1]` holds no object of its own and reads as the empty sequence, holds the object a feature subsetting it holds, and an abstract feature holds only what subsets it (KerML 1.0 §7.3.3.1), so a library's `shape :>> spaceBoundary [0..1]` or `voids [0..*]` materializes no anonymous object; a required abstract feature whose contributions fall short of its lower bound is `ErrMultiplicityViolation`. A required feature holding nothing is still `ErrUninitializedFeatureValue` when read | `runtime/instance.go` `materializeFeatureValueIntrinsic`, `holdContributions`, `FeatureValue.ReadValue`; `runtime/eval.go` `selfFeatureValue`, `evalFeatureChain`, `emptyDeclaredFeature` | `runtime/optional_feature_test.go`, `runtime/library_shape_test.go:TestInheritedLibraryFeaturesMaterializeNoObjects`, conformance `instance_library_geometry_box` (`voids`, `shape`) | ⚠️ Approximate (a `[0..1]` part used to materialize one object; the lower bound is now the population as it already was for `[0..*]` and `[n..m]`. Unrefereeable: the pinned artifact answers the declaration node for a valueless optional part) | -| The values of a feature are classified by every type of that feature, and a feature value binds the feature to its value's, so an object written into a typed feature is classified by the feature's type rather than refused: `item e1 [1];` listed in `item :>> edges [4] : Line = (e1, e2, e3, e4)` makes the `e1` object a `Line` and it carries `Line`'s features (`vertices`, `length`) from then on, and its relationships too — the binding connectors, subsettings, connections and connectors a classifier declares hold for the object as its declared type's do, in type order and each once. A classifier redefining a feature the object already carries refines it: the object reads the redefinition's default, type and multiplicity, keeps what it held where the redefinition admits it (a written value, an object now classified by the narrower type) and is refused whole where it does not, while a usage's own redefinition (`item raw : Car { :>> doors default = 3; }`) is not refined by a classifier redefining what it redefines (KerML 1.0 §7.3.4.5 Redefinition). Two comparable classifiers declaring one name without redefinition (`A { attribute n : Real = 1.0; }`, `B :> A { attribute n : Real = 2.0; }`) read the declaration of the narrower type — it masks the wider type's as it does on an object created as that type (KerML 1.0 §7.3.2.1 Type Membership) — whichever classified the object first, so what the object holds under the name is settled by its types alone; a held value the governing declaration does not admit refuses the classification and the object stays as it was. A classifier's renamed redefinition of a carried feature (`grossMass :>> mass = …`) refines it the same way, and the walks over an object's feature values — `MaterializationErrors`, the nested objects a constraint's subject holds, `%features`, signal routing — cover the features every type of the object declares, the declared type's first then each classifier's in declaration order, each shared feature value once. A collection is classified whole: one object refused leaves every object of it as it was, the objects the classifications' behaviors made abandoned with them. The object a selected variant materialized is held as any object is: a typed feature holding a variation's value (`item tallied : Tallied = car.engine`) classifies that object by the feature, which keeps its identity as the variant's object. A classifier renaming a behavior the object already runs (`exhibit state fancyModes :>> modes`), or one whose behavior a running one renames, starts no second execution: the one behavior answers to every name redefinition gives it through any type of the object (KerML 1.0 §7.3.4.5), and a classifier's behavior writes the features the classifier itself declares, the performer being the object under every type of it. `istype` and `hastype` judge an object by every type of it too — the object a selected variant materialized included, and a type the object already conforms to is still recorded as a direct type when a feature of it holds the object, so `hastype` answers alike in either classification order — `hastype` when one is the stated type itself, `istype` when one conforms to it — and a name subject to them is read as any name is: a feature of the enclosing object reads that object's value, not a fresh occurrence of the declaration (KerML 1.0 §7.4.9 Classification Expressions). Object identity is kept — `rect.e1` and `rect.edges#(1)` are the one object — and a classification that cannot hold is still the write's typed error: an `Integer` into `edges : Line`, or an object of a type disjoint by specialization from the feature's, is `ErrTypeMismatch` (KerML 1.0 §7.3.4.3 Feature Typing, §7.4.11 Feature Values, §7.4.6.3 Binding Connector Declaration) | `runtime/classify.go` `Context.classify`, `canClassify`, `instanceConforms`, `comparableTypes` (comparable: neither type refuses the other along the specialization graph), `classifyHeld` (one journal over the value, every object of it, a selected variant's included), `refineFeatureValue` (a redefining or masking declaration of the classifier governs a carried value), `subsetting.go` `aliasRedefinedFeatureValuesOf` (a carried value refined by the redefinition renaming it), `outer_feature.go` `Context.FeaturesOfObject` (the features of an object under every type of it, read by `materialize.go`, `condition.go` `nestedObjects`, `signal.go`, `repl/meta.go`), `declaredBy`/`bindingsOf`/`connectionsOf`/`anonymousConnectorsOf`/`subsettingFeaturesOf`/`subsettedNamesOf` (relationships over every type of an object, read by `binding.go`, `subsetting.go`, `routing.go`, `connector.go`); `runtime/write_conformance.go` `valueConforms` (an object the feature's type can classify conforms, `Value.Object` naming a variant's object too); `runtime/classifier_behavior.go` `runsBound` (a member bound under a redefinition name runs once), `BehaviorNamed` (redefinition names over every type of the object), `namesPerformerFeature` (a body's name denotes a feature of the performer under any type of it); `runtime/eval.go` `directValueTypes` (an object's declared type then the type of each feature holding it), `valueHasType`; `runtime/instance.go` `Instance.classifiers`, `CompositeTypeOf` (an untyped occurrence usage names one object), `Context.declaresFeatures` (a redefining usage materializes the features the redefined usage's body declares); `runtime/holders.go` `holdingFeatures`/`mentionedFeatures`/`passedFeatureReferences`/`materializeHolders` (a feature whose objects another object-holding feature's value may answer as its own — listed, chosen by a condition or an index, an argument a calc's returns pass on, directly or through the locals its body declares, assigns or iterates with (`calc def choose { in x; attribute y = x; return y; }`) — any argument of a function whose body the model does not write — a body's result — is classified before its own features are read, whichever is read first; a chain's values, a condition tested, an operand computed from, an argument no return passes on and an attribute's data hold nothing; a holder that cannot materialize — too few values for its multiplicity, a type its object is refused by — holds nothing, and its error is reported when it is read, so the held feature reads alike in either order) | `runtime/classify_test.go:TestListedValueIsClassifiedByTheFeatureType`, `:TestIncomparableValueIsRefusedByTheFeatureType`, `:TestCalcLocalsPassArgumentsOnToTheReturn`, `:TestFailingHolderDoesNotFailTheFeatureItWouldHold`, `:TestRelationshipsComeFromEveryTypeOfTheObject`, `:TestRefusedClassificationUndoesWhatItsBehaviorsDid`, `:TestRefusedCollectionClassificationUndoesTheEarlierObjects`, `:TestRefusedCollectionClassificationAbandonsWhatItsBehaviorsMade`, `:TestComputedHoldingIsClassifiedWhicheverIsReadFirst`, `:TestOnlyFeaturesPassingObjectsOnHoldThem`, `:TestArgumentNotReturnedIsNotHeldByTheCall`, `:TestNarrowerClassifierRefinesTheCarriedFeatures`, `:TestSameNameFeaturesOfClassifiersReadTheNarrowerDeclaration`, `:TestObjectWalksCoverTheFeaturesClassifiersAdd`, `:TestClassifierBehaviorsWriteTheFeaturesTheClassifierAdds`, `:TestTypePredicatesSeeTheClassifiersOfAnObject`, `:TestTypePredicatesSeeTheTypesOfASelectedVariantObject`, `:TestHoldingByAWiderTypeRecordsItAsADirectType`, `:TestSelectedVariantObjectIsClassifiedByTheFeatureHoldingIt`, `runtime/classifier_behavior_test.go:TestBehaviorNamedFollowsRedefinitionByAClassifier`, conformance `instance_binding_classifies_value`, `instance_classifier_relationships` (an untyped `raw` made a `Site` sends through `Site`'s connection and reads its binding and subsetting) (model-owned `Segment`/`Loop`/`Square` mirror, `Broken` binding `3` into `edges : Segment` stays `type mismatch`), `instance_library_geometry_rectangle` (`rect.edges` four `Line`s, `rect.e1.vertices` two objects), `instance_library_geometry_triangle`, `instance_library_geometry_box` | ✅ Faithful (unrefereeable: the pinned artifact answers `rect.edges` with the four usage nodes `e1`…`e4` unevaluated and stack-overflows on `rect.e1.length`; the rule is the spec's, not the pilot's) | +| The values of a feature are classified by every type of that feature, and a feature value binds the feature to its value's, so an object written into a typed feature is classified by the feature's type rather than refused: `item e1 [1];` listed in `item :>> edges [4] : Line = (e1, e2, e3, e4)` makes the `e1` object a `Line` and it carries `Line`'s features (`vertices`, `length`) from then on, and its relationships too — the binding connectors, subsettings, connections and connectors a classifier declares hold for the object as its declared type's do, in type order and each once. A classifier redefining a feature the object already carries refines it: the object reads the redefinition's default, type and multiplicity, keeps what it held where the redefinition admits it (a written value, an object now classified by the narrower type) and is refused whole where it does not, while a usage's own redefinition (`item raw : Car { :>> doors default = 3; }`) is not refined by a classifier redefining what it redefines (KerML 1.0 §7.3.4.5 Redefinition). Two comparable classifiers declaring one name without redefinition (`A { attribute n : Real = 1.0; }`, `B :> A { attribute n : Real = 2.0; }`) read the declaration of the narrower type — it masks the wider type's as it does on an object created as that type (KerML 1.0 §7.3.2.1 Type Membership) — whichever classified the object first, so what the object holds under the name is settled by its types alone; a held value the governing declaration does not admit refuses the classification and the object stays as it was. A classifier's renamed redefinition of a carried feature (`grossMass :>> mass = …`) refines it the same way, and the walks over an object's feature values — `MaterializationErrors`, the nested objects a constraint's subject holds, `%features`, signal routing — cover the features every type of the object declares, the declared type's first then each classifier's in declaration order, each shared feature value once. A collection is classified whole: one object refused leaves every object of it as it was, the objects the classifications' behaviors made abandoned with them. The object a selected variant materialized is held as any object is: a typed feature holding a variation's value (`item tallied : Tallied = car.engine`) classifies that object by the feature, which keeps its identity as the variant's object. A classifier renaming a behavior the object already runs (`exhibit state fancyModes :>> modes`), or one whose behavior a running one renames, starts no second execution: the one behavior answers to every name redefinition gives it through any type of the object (KerML 1.0 §7.3.4.5), and a classifier's behavior writes the features the classifier itself declares, the performer being the object under every type of it. `istype` and `hastype` judge an object by every type of it too — the object a selected variant materialized included, and a type the object already conforms to is still recorded as a direct type when a feature of it holds the object, so `hastype` answers alike in either classification order — `hastype` when one is the stated type itself, `istype` when one conforms to it — and a name subject to them is read as any name is: a feature of the enclosing object reads that object's value, not a fresh occurrence of the declaration (KerML 1.0 §7.4.9 Classification Expressions). Object identity is kept — `rect.e1` and `rect.edges#(1)` are the one object — and a classification that cannot hold is still the write's typed error: an `Integer` into `edges : Line`, or an object of a type disjoint by specialization from the feature's, is `ErrTypeMismatch` (KerML 1.0 §7.3.4.3 Feature Typing, §7.4.11 Feature Values, §7.4.6.3 Binding Connector Declaration) | `runtime/classify.go` `Context.classify`, `canClassify`, `instanceConforms`, `comparableTypes` (comparable: neither type refuses the other along the specialization graph), `classifyHeld` (one journal over the value, every object of it, a selected variant's included), `refineFeatureValue` (a redefining or masking declaration of the classifier governs a carried value), `subsetting.go` `aliasRedefinedFeatureValuesOf` (a carried value refined by the redefinition renaming it), `outer_feature.go` `Context.FeaturesOfObject` (the features of an object under every type of it, read by `materialize.go`, `condition.go` `nestedObjects`, `signal.go`, `repl/meta.go`), `declaredBy`/`bindingsOf`/`connectionsOf`/`anonymousConnectorsOf`/`subsettingFeaturesOf`/`subsettedNamesOf` (relationships over every type of an object, read by `binding.go`, `subsetting.go`, `routing.go`, `connector.go`); `runtime/write_conformance.go` `valueConforms` (an object the feature's type can classify conforms, `Value.Object` naming a variant's object too); `runtime/classifier_behavior.go` `runsBound` (a member bound under a redefinition name runs once), `BehaviorNamed` (redefinition names over every type of the object), `namesPerformerFeature` (a body's name denotes a feature of the performer under any type of it); `runtime/eval.go` `directValueTypes` (an object's declared type then the type of each feature holding it), `valueHasType`; `runtime/instance.go` `Instance.classifiers`, `CompositeTypeOf` (an untyped occurrence usage names one object), `Context.declaresFeatures` (a redefining usage materializes the features the redefined usage's body declares); `runtime/holders.go` `holdingFeatures`/`mentionedFeatures`/`passedFeatureReferences`/`materializeHolders` (a feature whose objects another object-holding feature's value may answer as its own — listed, chosen by a condition or an index, an argument a calc's returns pass on, directly or through the locals its body declares, assigns or iterates with (`calc def choose { in x; attribute y = x; return y; }`), the calc a call through a feature chain applies (`picker.pickChosen(lead, trail)`) resolved from the chain, which is the callee rather than an argument (`returnedArguments`/`chainTarget`) — any argument of a function whose body the model does not write — a body's result — is classified before its own features are read, whichever is read first; a chain's values, a condition tested, an operand computed from, an argument no return passes on and an attribute's data hold nothing; a holder that cannot materialize — too few values for its multiplicity, a type its object is refused by — holds nothing, and its error is reported when it is read, so the held feature reads alike in either order) | `runtime/classify_test.go:TestListedValueIsClassifiedByTheFeatureType`, `:TestIncomparableValueIsRefusedByTheFeatureType`, `:TestCalcLocalsPassArgumentsOnToTheReturn`, `:TestFailingHolderDoesNotFailTheFeatureItWouldHold`, `:TestRelationshipsComeFromEveryTypeOfTheObject`, `:TestRefusedClassificationUndoesWhatItsBehaviorsDid`, `:TestRefusedCollectionClassificationUndoesTheEarlierObjects`, `:TestRefusedCollectionClassificationAbandonsWhatItsBehaviorsMade`, `:TestComputedHoldingIsClassifiedWhicheverIsReadFirst`, `:TestOnlyFeaturesPassingObjectsOnHoldThem`, `:TestArgumentNotReturnedIsNotHeldByTheCall`, `:TestChainCallReturnedArgumentsAreHeldByTheCall`, `:TestNarrowerClassifierRefinesTheCarriedFeatures`, `:TestSameNameFeaturesOfClassifiersReadTheNarrowerDeclaration`, `:TestObjectWalksCoverTheFeaturesClassifiersAdd`, `:TestClassifierBehaviorsWriteTheFeaturesTheClassifierAdds`, `:TestTypePredicatesSeeTheClassifiersOfAnObject`, `:TestTypePredicatesSeeTheTypesOfASelectedVariantObject`, `:TestHoldingByAWiderTypeRecordsItAsADirectType`, `:TestSelectedVariantObjectIsClassifiedByTheFeatureHoldingIt`, `runtime/classifier_behavior_test.go:TestBehaviorNamedFollowsRedefinitionByAClassifier`, conformance `instance_binding_classifies_value`, `function_value_chain_holder` (`lead`, returned by `picker.pickChosen`, is a `Tallied`; `trail` is not), `instance_classifier_relationships` (an untyped `raw` made a `Site` sends through `Site`'s connection and reads its binding and subsetting) (model-owned `Segment`/`Loop`/`Square` mirror, `Broken` binding `3` into `edges : Segment` stays `type mismatch`), `instance_library_geometry_rectangle` (`rect.edges` four `Line`s, `rect.e1.vertices` two objects), `instance_library_geometry_triangle`, `instance_library_geometry_box` | ✅ Faithful (unrefereeable: the pinned artifact answers `rect.edges` with the four usage nodes `e1`…`e4` unevaluated and stack-overflows on `rect.e1.length`; the rule is the spec's, not the pilot's) | | A qualified name naming a feature of an enclosing type, read from an expression nested in a usage of that type, is that feature of the enclosing *object*: `item :>> e1 { attribute :>> length = Rectangle::length; }` reads the rectangle's `length`, a level deeper `RectangularCuboid::length` inside `tf : Rectangle` reads the cuboid's, and `e3.length = e1.length` is the sibling feature of the same enclosing object. A qualified name that does not name a feature of an object on the evaluation's ownership chain still resolves as a declaration (a type, a calc, a library constant), and imports, aliases, visibility and a model's own shadowing are untouched, since the name is resolved first and only its referent is looked up among the enclosing objects (KerML 1.0 §7.4.9.3 Primary Expressions — a feature reference is evaluated relative to the object featuring the referenced feature; §7.2.5.2 Namespace Declaration, qualified names) | `runtime/outer_feature.go` `EvalContext.outerFeatureValue` (walks `self` and its owners, maps the resolved feature symbol to the effective feature name each object carries, reads its value), called from `runtime/eval.go` `evalNameGeneral` and `evalFeatureChain` (sibling chain heads) | `runtime/classify_test.go:TestQualifiedOuterFeatureReadsTheEnclosingObject`, conformance `instance_nested_usage_outer_feature` (model-owned `Frame::span`, sibling `bar.len`, `bar.len + Frame::gap`), `instance_library_geometry_rectangle` (`rect.e1.length` = `4 [m]`, `rect.e3.length` = `4 [m]`), `instance_library_geometry_box` (`box.tf.length` = `2 [m]`, `box.slf.length` = `1 [m]` — the side faces read `RectangularCuboid::height`) | ✅ Faithful | | A feature chain valued over a collection collects across the collection: `item :>> edges [24] = faces.edges` is every edge of every face, in face order, and a chain end that is itself a collection (`edges.vertices`) flattens the same way; a binding whose end path crosses a collection (`bind [0..*] base.edges = [0..*] be`) visits every object the path reaches. A collection valued this way is checked against the feature's multiplicity and type like any other value, and a required lower bound the named subsetting features fall short of is filled from an optional subsetting feature with spare upper bound before an anonymous object is made up for it — `CuboidOrTriangularPrism::srf : Quadrilateral [0..1]` is the sixth face of a `Box`, not an anonymous `Polygon` beside it (KerML 1.0 §7.3.4.6 Feature Chaining, §7.3.4.4 Subsetting, §7.4.12 Multiplicities) | `runtime/eval.go` `evalFeatureChain` (collection-valued heads), `runtime/binding.go` `resolveBindingLocation`, `bindingEndpointValue` (collection-valued paths), `runtime/subsetting.go` `subsettingContributions`, `fillsFromSubsetted`, `fillOptionalSubsetters`, `materializeSubsettedCollections` (guarded by `context.go` `readingSubsetted`, so mutually subsetting features stay `ErrCyclicFeatureValue`) | `runtime/classify_test.go:TestChainValueCollectsAcrossTheCollection`, `:TestOptionalSubsetterFillsTheCollection`, conformance `instance_chain_valued_subsetting` (model-owned `Panel.sides`, `corners = sides.corners`), `instance_library_geometry_box` (`box.edges` twenty-four, `box.faces` six), `instance_library_geometry_rectangle` (`rect.vertices` eight from `edges.vertices`), `robustness_test.go:mutually_subsetting_features` | ✅ Faithful (unrefereeable: the pinned artifact answers `box.edges` with the unevaluated `edges` usage node and `box.tf.length` with its `[` operator node) | | A binding connector's end multiplicities bound how many values of each end take part, so `bind [0..*] a.edges = [0..*] b` equates the two collections whole — two whole bindings of one collection agree or are `ErrBindingConflict` — while `bind [0..1] tf.edges = [0..1] tfe` links *one unspecified* value of each end: the model states that `tfe` is some edge of `tf`, not which, so reading `tfe` — and `tflv`, `tfe.length`, and `vertices`, subsetted by `tflv = tfe.vertices` — is the typed `ErrBindingEnd` naming the binding and both of its ends (`box.tfe is bound by … which makes some value of tfe a value of tf.edges without saying which value of either; the model does not state what tfe holds`) rather than a witness the runtime picked. Each partial binding is read on its own: the runtime does not solve the conjunction of several partial bindings of one feature, so a feature they would jointly pin down (`[1]` bound `[0..1]` to two collections sharing exactly one value) is reported the same way. A partial binding determines nothing by itself, so it never decides a feature bound whole as well: every binding of the feature is read, a whole binding — or a written value or default — answers whatever order the bindings are declared in, and the partial-binding error is reported only where no whole binding does. An end whose path crosses a collection (`bind [0..*] groups.items = [0..*] allItems`, `groups` multi-valued) reaches that feature on *every* object the collection holds, in the collection's order, and holds their values together: `allItems` is every group's items in group order, an end multiplicity counts those values as one sequence (`[4]` is met by two groups of two, `[5..*]` is `ErrMultiplicityViolation` over the four), and a step of the path holding a non-object, a feature the reached objects lack, a destroyed object or an unmaterialized one is `ErrBindingEnd`. The union determines none of its objects' parts: a group's `items` keeps what it holds on its own — a default, a write, or the objects it materialized — and one holding nothing of its own is the typed `ErrBindingEnd` naming the collection (`Group.shares is bound by … through every object groups holds`) rather than a partition the runtime picked, whichever end is read first. The end multiplicity is carried from the syntax to the runtime rather than re-read from the declaration, and `bind [m] a = [m] b` states `m` as the first end's multiplicity, as `binding [1] bind [m] a = [m] b` does — `bind` declares no connector for a multiplicity of its own (KerML 1.0 §7.4.6.2 Connector Declaration — end multiplicities; §7.4.6.3 Binding Connector Declaration; §7.3.4.6 Feature Chains — a chain's values are those of its last feature on every value of the preceding ones; SysML v2 §8.2.2.6 BindingConnectorAsUsage) | `ast/defusage.go` `ConnectorEnd.Multiplicity` (each of `Usage.ConnectorEnds`); `parser/defusage.go` `parseBindingEnds`, `parseConnectorEnd` (the `bind` shorthand's leading multiplicity is end 0's); `lower/binding.go` `lowerBinding`, `BindingEnd.Multiplicity`; `libs/record.go` (cache format 26); `runtime/binding.go` `resolveBindingSet`, `partialBinding`, `attemptBinding`, `resolveBindingLocations`, `bindingEndpoint.spread`, `bindingEndpointValue`, `readBindingEnds`, `heldOnItsOwn`, `UndeterminedBindingError.Across` | `parse/binding_indexed_ends.golden`, `parse/keyword_as_name.golden`, `robustness_test.go:binding_multiple_collection_contributors` (`partial` → `ErrBindingEnd`, `whole_unequal` → `ErrBindingConflict`, `whole_equal` succeeds, `whole_beside_partial` in either declaration order), `:binding_multiple_scalar_contributors`, `classify_test.go:TestBindingEndAcrossACollection` (the union in either read order, members undetermined by the union in either order, the union against its own value, end multiplicity over the union, a collection holding no object, a non-object step, a missing feature), `runtime/shape_items_limits_test.go` (`box.tfe`, `box.tfe.length`, `box.tflv`, `box.vertices`), conformance `instance_library_geometry_box` (`box.tfe`, `box.tfe.length`, `box.tflv`, `box.vertices` errors) | ⚠️ Approximate (the partial case is reported, not resolved: nothing in the spec, the library or the pinned artifact — which answers `box.vertices`, `box.tfe` and `box.tflv` with the unevaluated usage node — determines which edge each `[0..1]` binding links — the bindings of `tfe` on `tf.edges` and `ff.edges` pick one member each from disjoint collections, `MatesWith` holds for every choice and `size(edges)` counts `faces.edges` — so `box.vertices`, `box.tfe`…`box.urre` and `box.tflv`…`box.brrv` stay `ErrBindingEnd`. A rule choosing a witness would be an invention; the library's own `size(vertices) == size(edges)` is unsatisfiable beside `faces.vertices subsets vertices`, see [omg-issues.md](omg-issues.md). Solving a conjunction of partial bindings that does determine a value is not implemented) | @@ -678,6 +685,7 @@ by name is refused naming what is missing rather than approximated. | An Array, a Vector and a VectorQuantity cross the API boundary whole, in both directions: `Value.array` carries `dimensions` and the elements flattened in row-major order (each element a `Value`, so an array of quantities or of arrays nests), `Value.vector` carries the numeric components as `Value`s so an Integer and a Real component stay distinct, and `Value.vector_quantity` carries one `Quantity` per component — magnitude, unit as written and reduced unit term, so a composed unit (`m/s`) and per-component units survive. Every client this repository ships maps them to a native shape that checks its own invariants — Go `opensysml.Array`/`Vector`/`VectorQuantity`, Python `Array`/`Vector`/`VectorQuantity` dataclasses, Node `{ kind: "array" | "vector" | "vectorQuantity" }`, Java `Value.ArrayValue`/`VectorValue`/`VectorQuantityValue`, Rust `Value::Array`/`Vector`/`VectorQuantity` — and one sent as an action input or calc argument decodes to the same runtime value; a malformed one (dimensions the elements do not fill, a non-positive extent, a non-numeric vector component, an empty vector quantity, a unit without its reduction) is a typed error on whichever side sees it first, never a value with a different shape | `grpc/convert.go` `arrayToProto`/`protoToArray`, `vectorToProto`/`protoToVector`, `vectorQuantityToProto`/`protoToVectorQuantity`, `ErrArrayDimensionNotPositive`, `ErrArrayShapeMismatch`, `ErrVectorComponentNotNumeric`, `ErrVectorQuantityEmpty`; `capability_response.go` (`structured_values` arm); `api/proto/sysml.proto` `Array`, `Vector`, `VectorQuantity`; `client/opensysml/value.go`, `convert.go`, `client.go` (`structured_values` preflight); `clients/python/opensysml/values.py`, `connection.py`; `clients/node/src/core/values.ts`; `clients/java/.../Value.java`, `internal/Protos.java`; `clients/rust/opensysml/src/domain.rs` | `grpc/convert_structured_test.go`, `client/opensysml/structured_test.go`, `structured_internal_test.go`, `clients/python/tests/test_structured.py`, `clients/node/test/values.test.ts`, `client.test.ts`, `clients/java/.../ProtosTest.java`, `ApiIntegrationTest.java`, `clients/rust/opensysml/src/domain.rs` tests, `tests/client.rs`; `conformance/scenarios/04-evaluate.json`, `10-evaluate-calc.json` (`structured.sysml`) over gRPC, Connect protobuf and Connect JSON | ✅ Faithful (advertised as the `structured_values` capability; a service withholding it reports an unsupported null naming the value and refuses one sent to it with `UNIMPLEMENTED`, the clients refusing before the round trip; a client built before the arms existed reads each as an unknown `Value` arm) | | A bare measurement reference crosses the API boundary whole, in both directions: `Value.measurement_ref` carries the unit as written, its reduced unit term (required wherever the unit names one, as `Quantity` requires it) and the canonical id of the declaration a named unit is (`SI::metre`), which a composed unit (`m / s`, a `DerivedUnit`) omits. Every client this repository ships maps it to a typed reference — Go `opensysml.MeasurementRef`, Python `MeasurementRef` (over `Unit`), Node `{ kind: "measurementRef" }`, Java `Value.MeasurementRefValue`, Rust `Value::MeasurementRef` — and one sent as a calc argument decodes to the same `ValMeasurementRef`, so `ConvertQuantity(q, ref)` converts through it; a malformed one (no unit and no id, a named unit without its reduction, an id naming no unit declaration, a reduction disagreeing with the declaration's own) is a typed error on whichever side sees it first | `grpc/convert.go` `MeasurementRefToProto`/`ProtoToMeasurementRef`/`declaredMeasurementRef`, `ValueCarriesMeasurementRef`, `ErrMeasurementRefEmpty`, `ErrMeasurementRefNeedsIndex`; `capability_response.go` (`measurement_refs` arm); `api/proto/sysml.proto` `MeasurementRef`; `client/opensysml/value.go`, `convert.go`, `client.go` (`measurement_refs` preflight); `clients/python/opensysml/values.py`, `connection.py`; `clients/node/src/core/values.ts`; `clients/java/.../Value.java`, `internal/Protos.java`; `clients/rust/opensysml/src/domain.rs` | `grpc/convert_measurement_ref_test.go`, `client/opensysml/measurement_ref_test.go`, `clients/python/tests/test_measurement_ref.py`, `clients/node/test/values.test.ts`, `client.test.ts`, `clients/java/.../ProtosTest.java`, `ApiIntegrationTest.java`, `clients/rust/opensysml/src/domain.rs` tests, `tests/client.rs`; `conformance/scenarios/01-server-info.json`, `04-evaluate.json`, `10-evaluate-calc.json` (`measurement_ref.sysml`) over gRPC, Connect protobuf and Connect JSON | ✅ Faithful (advertised as the `measurement_refs` capability, separate from `structured_values` so a client built against those arms keeps reading a bare reference as `unsupported: measurement reference m`; a service withholding it reports that unsupported null and refuses one sent to it with `UNIMPLEMENTED`, the clients refusing before the round trip) | | A quantity crosses the API boundary in both directions: `Value.quantity` carries the magnitude with the kind it was written in (Integer or Real), the unit as written and the reduced unit term, so a quantity read from the service can be sent back as an input and evaluates against the unit it names — commensurability is decided over the reduction, so a unit named without one is refused client-side rather than compared by bare magnitude | `opensysml/values.py` `Quantity.to_pb`, `Unit.to_pb`/`Unit.reduced`, `connection.py` `_python_to_value`; service side `grpc/convert.go` `ProtoToQuantity`, `ProtoToValueIn` | `clients/python/tests/test_quantity.py`: `test_a_quantity_encodes_as_the_message_the_service_decodes`, `test_an_unreduced_unit_is_refused_before_it_is_sent`, and against a live service `TestQuantityAgainstTheService::test_a_quantity_sent_as_a_calc_argument_round_trips`, `::test_a_quantity_input_binds_into_an_action`, `::test_a_sent_quantity_is_commensurable_with_the_models_own_units` | ✅ Faithful | +| A function value crosses the API boundary as `Value.function`: `calc_id`, the qualified name of the calc it is a value of, and `self_id`, the id (within the answering response) of the object it was read off, 0 for none — identity being the pair. One sent as an argument (`EvaluateCalc`, `ExecuteAction`, `RunAnalysis`) is rebound to that calc of the named model and invoked through the calc-typed parameter it binds; an empty `calc_id`, one naming no calc, or any non-zero `self_id` is refused in band (`ErrFunctionUnbound`), never a null — objects live only within the call that created them, so a `self_id` is never matched to whichever object a later call numbered the same — and a function value nested in a sequence or array is found wherever it sits. A value closing over a behavior body's bindings cannot be named by calc and object, so it crosses as `unsupported: function ` (`ErrFunctionNeedsRuntime` on the way in). Every client this repository ships maps the arm to a typed value — Go `opensysml.Function`, Python `opensysml.Function`, Node `{ kind: "function" }`, Java `Value.FunctionValue`, Rust `Value::Function` — and refuses to send one to a service lacking the capability | `grpc/convert.go` `functionToProto`/`functionFromProto`/`ProtoToRuntimeValue`, `ValueCarriesFunction`, `ErrFunctionUnbound`, `ErrFunctionNeedsRuntime`; `capability_response.go` (`function_values` arm); `service.go` `CapabilityFunctionValues`; `api/proto/sysml.proto` `Function`; `client/opensysml/value.go`, `convert.go`, `client.go` (`function_values` preflight); `clients/python/opensysml/values.py`, `connection.py`; `clients/node/src/core/values.ts`; `clients/java/.../Value.java`, `internal/Protos.java`; `clients/rust/opensysml/src/domain.rs` | `grpc/convert_function_test.go:TestFunctionRoundTrip`, `:TestObjectBoundFunctionsDoNotCrossCalls`, `:TestMalformedFunctionsAreRejected`, `:TestFunctionCapability`, `:TestValueCarriesFunction`; `client/opensysml/function_test.go`; `clients/python/tests/test_function.py`; `clients/node/test/values.test.ts`, `client.test.ts`; `clients/java/.../ProtosTest.java`, `ApiIntegrationTest.java`; `clients/rust/opensysml/tests/client.rs`; `conformance/scenarios/01-server-info.json`, `04-evaluate.json`, `10-evaluate-calc.json` (`function.sysml`) over gRPC, Connect protobuf and Connect JSON | ✅ Faithful (advertised as the `function_values` capability; a service withholding it reports the unsupported null and refuses a function argument with `UNIMPLEMENTED`, the clients refusing before the round trip) | | An unqualified name resolves as a written reference does — the enclosing scope chain, inherited members, imports, then the global index — and the declaration it finds is evaluated in *its own* declaring scope, so the imports in force where a value was written answer the names that value uses | `runtime/eval.go` `evalFeatureReference` (scope arm) via `resolve/unqualified.go` `Resolver.LookupName`, `EvalContext.evalIn` | `action_body_package_member.sysml`, `action_body_declarer_scope.sysml`, `body_scope_test.go:TestBodyScopeImportSpellings`, `robustness_test.go:action_body_unresolved_feature` | ✅ Faithful | #### Scope of an expression in a behavior body @@ -2338,7 +2346,7 @@ operations are defined: set value, rename, add member, and delete. | RPC | Implementation | Status | Tests | |-----|---------------|--------|-------| -| GetServerInfo | service.go `Service.GetServerInfo`, `capabilityAvailability` | ✅ Faithful — reports the build version (informational; a source build reports `dev`) and this service instance's capabilities in append-only canonical order: `type_facts`, `convert`, `verification`, `query`, `oslc_query`, `enum_values`, `evaluate_subject`, `symbol_attributes`, `unset_value`, `feature_values`, `apply_edits`, `authoring`, `inline_language`, `strict_conformance`, `document_query`, `render_document`. The same availability object drives request refusal and response population, so a capability cannot be advertised while withheld or withheld while advertised. A service predating this RPC answers `UNIMPLEMENTED`, which the client reads as supporting no capability | service_test.go:TestGetServerInfo, capability_test.go:`TestCapabilityAvailabilityDrivesAdvertisementAndRefusal`, clients/python/tests/test_capabilities.py | +| GetServerInfo | service.go `Service.GetServerInfo`, `capabilityAvailability` | ✅ Faithful — reports the build version (informational; a source build reports `dev`) and this service instance's capabilities in append-only canonical order: `type_facts`, `convert`, `verification`, `query`, `oslc_query`, `enum_values`, `evaluate_subject`, `symbol_attributes`, `unset_value`, `feature_values`, `apply_edits`, `authoring`, `inline_language`, `strict_conformance`, `document_query`, `render_document`, `parse_sources`, `complex_values`, `structured_values`, `measurement_refs`, `function_values`. The same availability object drives request refusal and response population, so a capability cannot be advertised while withheld or withheld while advertised. A service predating this RPC answers `UNIMPLEMENTED`, which the client reads as supporting no capability | service_test.go:TestGetServerInfo, capability_test.go:`TestCapabilityAvailabilityDrivesAdvertisementAndRefusal`, clients/python/tests/test_capabilities.py | | ParseFile | service.go `Service.ParseFile` (parser + passes.Analyze + stdlib load) | ✅ Faithful — the cache is keyed by the file name and content the service read, so repeated parses of an unchanged source hit it whatever the request's (ignored) `content_hash` says, a hash disagreeing with its content cannot serve another model, and identical content read under two names keeps a record each, since their diagnostics name different files. An explicit inline language requires `inline_language`, and `strict_conformance=true` requires `strict_conformance`; an unavailable requested field is `UNIMPLEMENTED`, while an unset field retains the default parse | runtime_test.go:TestParseFile_*, service_test.go:`TestParseFileCachesByContentRead`, service_test.go:`TestParseFileCachesPerFileName`, capability_test.go, conformance parse cases | | ParseFile (standard library) | `grpc/libindex.go` `libraryBase`, `buildLibraryIndex`, `indexPrewarmFromEnv` (`OPENSYSML_GRPC_INDEX_POOL`, positive prewarms, 0 disables), `symbols/layer.go`, `symbols/index.go` `Freeze`/`NewOverlay`, `libs/shared.go` `SharedBase`/`NewModelIndex`, `grpc/service.go` `Service.Prewarm`/`Close`/`ParseFile` | ✅ Faithful — the library does not depend on the model and is immutable once loaded, so it is built once, frozen, and read by every model through an overlay holding that model's own document. What a model resolves against is unchanged (same source list, same expansion, same persist step, same `Index.Library` marking); a model writes only into its overlay, so no two cached models see each other's documents and eviction removes only the evicting model's state; a request arriving before the shared index exists builds it, so a result never depends on prewarming. A cold `ParseFile` on `examples/combined-behavioral-demo.sysml` measures ~0.5–0.9 ms against ~100–128 ms building the library per model, and 100 cached models cost ~1.1 MiB rather than ~1598 MiB | `grpc/libindex_test.go:TestSharedIndexMatchesFreshlyBuiltIndex` (identical diagnostics and identical qualified lookups over the whole index, shared vs built inline), `:TestParseFileServesEveryModelFromOneLibraryIndex`, `:TestParseFileTakesNoIndexOnACacheHit`, `:TestCachedModelsOwnTheirIndex`, `:TestLibraryBaseFallsBackToBuildingInline`, `:TestLibraryBaseCloseReleasesTheIndexAndStillServes`, `:TestIndexPrewarmFromEnv`, `:TestLibraryBaseBuildsOnceUnderConcurrentDemand`, `symbols/overlay_test.go:TestOverlayEqualsAnIndexBuiltWhole`, `:TestOverlayAnswersEveryLookupAsAnIndexBuiltWhole`, `:TestOverlayRemovalOfABaseDocumentLeavesTheBaseIntact`, `:TestOverlaySuppressesAnAmbiguatedBaseImport`, `:TestConcurrentOverlaysDoNotSeeEachOther`, `:TestFrozenIndexRejectsWrites`, `libs/shared_index_test.go:TestModelsOverASharedLibraryAgreeWithModelsOverTheirOwn`, `:TestLibraryMarkingReadsThroughTheSharedBase`, `:TestAModelOverTheSharedBaseCostsFarLessThanItsOwnIndex`, `robustness_test.go:parse_with_unavailable_standard_library`, `BenchmarkParseFileColdShared`/`ColdInline` | | Convert | export.go `Service.Convert`, conversion in `internal/core/export` | ✅ Faithful for what OpenSysML writes — SysML/KerML notation and RDF Turtle, from a loaded model named by its `model_hash`, a path the service opens, or inline content, with the format names `sysml -convert` takes and canonical names reported back. A `model_hash` converts the source that parse read, so a file edited since then does not change what is written, and a model evicted from the cache is `NOT_FOUND` rather than converted as something else; a `file_path` is read afresh, for a caller who does want the file as it stands. Notation to notation is source-preserving (comments and layout survive); a graph direction returns an equivalent model, not identical bytes, and drops comments, per docs/reference/rdf-mapping.md. A conversion that cannot be written faithfully returns `error` plus the diagnostics rather than partial output, and `tolerate_syntax_errors` is honored for notation to notation only, since a graph built from an unparsed declaration would lose it silently | export_test.go:TestConvert*, `TestConvertModelHashConvertsWhatWasParsed`, `TestConvertUncachedModelHashIsNotFound`, clients/python/tests/test_conversion.py | diff --git a/docs/reference/java-api.md b/docs/reference/java-api.md index 906fb3526..cf5a55d2a 100644 --- a/docs/reference/java-api.md +++ b/docs/reference/java-api.md @@ -104,6 +104,7 @@ else if (value instanceof Value.ArrayValue v) rendered = v.dimensions( else if (value instanceof Value.VectorValue v) rendered = v.components().toString(); // IntegerValue | RealValue else if (value instanceof Value.VectorQuantityValue v) rendered = v.components().toString(); // one Quantity each else if (value instanceof Value.MeasurementRefValue v) rendered = v.unit(); // a bare unit and its reduction +else if (value instanceof Value.FunctionValue v) rendered = v.calcId(); // a calc held as a value; selfId() when read off an object else if (value instanceof Value.EnumerationValue v) rendered = v.literal().name(); else if (value instanceof Value.InstanceReference v) rendered = "instance " + v.instanceId(); else if (value instanceof Value.Sequence v) rendered = v.elements().toString(); diff --git a/docs/reference/node-api.md b/docs/reference/node-api.md index b1e2e24e3..2135220b4 100644 --- a/docs/reference/node-api.md +++ b/docs/reference/node-api.md @@ -99,6 +99,7 @@ switch (value.kind) { case "array": value.dimensions; value.elements; // row-major SysMLValue[] case "vector": value.components; // Magnitude[]: int | real, kept apart case "vectorQuantity": value.components; // QuantityValue[], one unit each + case "function": value.calcId; value.selfId; // a calc held as a value; selfId when read off an object case "enum": value.value.name; // and its literal/enumeration ids case "instance": value.id; // an object in the same tree case "sequence": value.elements; // SysMLValue[] diff --git a/docs/reference/python-api.md b/docs/reference/python-api.md index 78abece52..9d31ea437 100644 --- a/docs/reference/python-api.md +++ b/docs/reference/python-api.md @@ -161,6 +161,7 @@ collection property. | `CartesianVectorValue` and the other numeric vectors | `opensysml.Vector`: a tuple of `int`/`float` components, kept apart | | `VectorQuantityValue` | `opensysml.VectorQuantity`: a tuple of `Quantity`, one unit per component | | `MeasurementUnit` and the other measurement references (`SI::m`, `m / s`, a quantity's `mRef`) | `opensysml.MeasurementRef`: the `Unit` with its reduction, and `unit_id` naming the declaration a named unit is (`SI::metre`), empty for a composed unit | +| a calc definition, calc usage or `in calc` parameter read as a value | `opensysml.Function`: `calc_id` naming the calc, `self_id` the object it was read off (0 for none) | | `Integer`, `Natural` | `int` | | `Boolean` | `bool` | | `String` | `str` | diff --git a/docs/reference/rust-api.md b/docs/reference/rust-api.md index c8023533f..fdbccf44d 100644 --- a/docs/reference/rust-api.md +++ b/docs/reference/rust-api.md @@ -108,6 +108,7 @@ match value { Value::Vector(v) => (), // v.components: Vec, Integer and Real apart Value::VectorQuantity(q) => (), // q.components(): one Quantity per component; q.unit() when shared Value::MeasurementRef(m) => (), // a bare unit: m.unit, m.unit_term, m.unit_id when it names a declaration + Value::Function(f) => (), // a calc held as a value: f.calc_id, f.self_id when read off an object Value::EnumLiteral(l) => (), // literal_id, enumeration_id, name Value::Null => (), // evaluated, no value Value::Unset => (), // a materialized feature with no value diff --git a/docs/reference/service-transports.md b/docs/reference/service-transports.md index 0a2b070c4..46a312f1b 100644 --- a/docs/reference/service-transports.md +++ b/docs/reference/service-transports.md @@ -52,11 +52,11 @@ capability's definition rather than something a client has to guess: | The capability describes | A request that needs it | What a client should do | |---|---|---| | what the service can be *asked*: `strict_conformance`, `inline_language`, `parse_sources`, `evaluate_subject`, `verification`, `convert`, `apply_edits`, `authoring`, `query`, `oslc_query`, `document_query`, `render_document` | is **refused** with `UNIMPLEMENTED`, naming the capability | check the advertised list first, and report the missing capability locally rather than spending a round trip | -| how a response is *populated*: `type_facts`, `symbol_attributes`, `feature_values`, `enum_values`, `unset_value`, `complex_values`, `structured_values`, `measurement_refs`, `verification_verdicts`, `infinity_value` | is answered with those fields **omitted** | check before reading the fields; an omitted field is not an error | +| how a response is *populated*: `type_facts`, `symbol_attributes`, `feature_values`, `enum_values`, `unset_value`, `complex_values`, `structured_values`, `measurement_refs`, `function_values`, `verification_verdicts`, `infinity_value` | is answered with those fields **omitted** | check before reading the fields; an omitted field is not an error | -`complex_values`, `structured_values`, `measurement_refs` and `infinity_value` sit in both -rows: a complex — or an array, vector or vector quantity, a bare measurement reference, or the -unbounded value `*` — in a response is +`complex_values`, `structured_values`, `measurement_refs`, `function_values` and `infinity_value` sit in both +rows: a complex — or an array, vector or vector quantity, a bare measurement reference, a calc held as a +value, or the unbounded value `*` — in a response is reported as an `unsupported` null without it, and one in an action input or calc argument is refused with `UNIMPLEMENTED` rather than read as another value — a service that predates the arm would read it as an unknown field, so every client checks the list before sending one diff --git a/docs/reference/wire-contract.md b/docs/reference/wire-contract.md index e83b4eae4..37fe8646d 100644 --- a/docs/reference/wire-contract.md +++ b/docs/reference/wire-contract.md @@ -192,10 +192,10 @@ Note that `not_found` is also the status for an unknown *symbol* on some methods which (`model not found:`, `symbol not found:`, `file not found:`), and a client that recovers by re-parsing must read it. -## `Value`: fifteen arms, exactly one present +## `Value`: sixteen arms, exactly one present Every value the engine returns — an expression result, a feature of an instance, an action -output, a state-machine context variable — is a `Value`, which is a proto `oneof` of fifteen +output, a state-machine context variable — is a `Value`, which is a proto `oneof` of sixteen arms. In JSON that is **an object with exactly one key**, and the key is the discriminator. A decoder therefore does not look for a `kind` field: it looks at which key is present. The arms, each captured from `Evaluate` against the model at the end of this section: @@ -218,10 +218,12 @@ arms, each captured from `Evaluate` against the model at the end of this section | `vectorQuantity` | object | `{"result":{"vectorQuantity":{"components":[{"realMagnitude":3,"unit":"m","unitTerm":{…}},…]}}}` | Vector of quantities; one `quantity` body per component | | `measurementRef` | object | `{"result":{"measurementRef":{"unit":"m","unitTerm":{…},"unitId":"SI::metre"}}}` | A measurement reference on its own: a unit, its reduction, and the declaration it names | | `infinity` | boolean | `{"result":{"infinity":true}}` | The unbounded value `*`, which is no number and no string | +| `function` | object | `{"result":{"function":{"calcId":"F::Sq"}}}` | A calc held as a value: the calc it names and, when it was read off an object, that object | The `array`, `vector` and `vectorQuantity` rows were captured against `conformance/fixtures/structured.sysml` (`S::grid`, `S::v`, `S::d`), `measurementRef` against -`conformance/fixtures/measurement_ref.sysml` (`M::u`); the rest against the model below, with requests of the form +`conformance/fixtures/measurement_ref.sysml` (`M::u`), `function` against +`conformance/fixtures/function.sysml` (`F::pick`); the rest against the model below, with requests of the form `{"modelHash":"59c4…a654","expression":"","contextSymbolId":"Rover"}` with `rover.count`, `1.0 / 3.0`, `rover.armed`, `"abc"`, `rover.wheel`, `rover.tags`, `null`, `rover.speed`, `Mode::idle`, `rover.serial` and `rover.z`, and the model was: @@ -287,6 +289,9 @@ decode(v): The arm is the value itself, so only true carries it: a caller sending infinity: false, directly or nested, is answered with an error rather than with the unbounded value + function → calc := v.function.calcId, require it non-empty, else an error; + self := v.function.selfId when present and not "0", an opaque reference + under the instanceId rule (this response only), else no object anything else → an error: a newer service than this decoder ``` @@ -455,6 +460,44 @@ $ … /Evaluate -d '{"modelHash":"5b0f…40d5","expression":"M::speed"}' - A `measurementRef` is not a `quantity` with magnitude one: `ConvertQuantity(q, ref)` takes one, `q * ref` does not. +**`function`.** A calc held as a value — a calc definition or usage named where a value is +expected, or bound to an `in calc` parameter — travels as the calc it names, not as what the +calc would compute: + +```console +$ … /Evaluate -d '{"modelHash":"e587…f81e","expression":"F::pick"}' +{"result":{"function":{"calcId":"F::Sq"}}} + +$ … /Evaluate -d '{"modelHash":"e587…f81e","expression":"F::scaler"}' +{"result":{"function":{"calcId":"F::Scaler::scale", "selfId":"1"}}} +``` + +- `calcId` is the fully qualified name of the calc declaration, and is the identity a client + keeps to send the same function back. It is never empty: a `function` with no `calcId` is + malformed, and a decoder refuses it rather than reading it as "no function". +- `selfId` is present when the calc is a usage owned by an object and was read off that object + (`holder.scale` above reads `holder.k` when invoked). It is an `instanceId` under that arm's + rules: a 64-bit integer sent as a string, valid within the response it arrived in, and + indexing that response's `instances` where the method returns them. Absent (or `"0"`, the + proto default) means the calc computes over no object. +- A function carrying a `selfId` cannot be sent back. Every call instantiates the model afresh + and numbers its objects from 1, so the object the function was read off does not exist in any + later call — and another call's object may well carry the same number. A request `function` + with a non-zero `selfId` is therefore refused in band, whatever the number, rather than bound + to whichever object that call numbered the same. To apply a calc over an object, name both + in one expression — `Evaluate` of `F::apply(F::holder.scale, 3.0)` reads the object and + applies the calc within the call that holds it. +- Two functions are the same function when their `calcId`s are equal and both name the same + object or neither names one; the engine's `==` says the same. +- A calc that closes over the bindings of a behavior body — one returned by another calc, or + read inside an action step — has no wire form: the frames it captured belong to a run that + has ended and cannot be reconstructed remotely. It is sent as the unsupported null + `{"null":"unsupported: function closing over a body's bindings"}`, under the `null` + arm's rule. +- The arm is gated by the `function_values` capability (see [Capabilities, and what an absent one does](service-transports.md#capabilities-and-what-an-absent-one-does)). + A service without it sends every function, at any depth, as + `{"null":"unsupported: function "}` and refuses a request that carries one. + ### What a client must not do - **Do not compare enum literals by `name`.** Compare `literalId`. @@ -473,6 +516,11 @@ $ … /Evaluate -d '{"modelHash":"5b0f…40d5","expression":"M::speed"}' - **Do not index an `array` before checking `len(elements) == product(dimensions)`.** - **Do not invent a `unitId` for a `measurementRef` that has none.** A composed unit names no declaration; send it back as it came, with its `unit` and `unitTerm` only. +- **Do not read a `function` as the value the calc computes, or invoke it locally.** It is a + reference: hand it back as an argument (`EvaluateCalc`) and let the service invoke it. +- **Do not send back a `function` that carries a `selfId`.** It is an `instanceId`, with that + arm's lifetime: no later call holds the object, and the service refuses the function rather + than guess. Only a function over no object (`selfId` absent or `"0"`) is an argument. ## Three places a failure can be @@ -831,9 +879,26 @@ $ … /EvaluateCalc -d '{"modelHash":"5b0f…40d5","symbolId":"M::toUnit","argum {"error":"calc argument could not be read: unit as written does not reduce to its unit_term: SI::metre reduces to metre, unit_term is 1000·metre", "failureReason":"FAILURE_REASON_EVALUATION"} ``` -A service without the `structured_values` capability refuses a structured argument, and one -without `measurement_refs` a `measurementRef` argument, with the `unimplemented` Connect -error instead, naming the capability; check `GetServerInfo` first. +A `function` argument binds an `in calc` parameter to the calc it names, resolved against the +model, over no object. A name that is empty, names nothing, or names something that is not a +calc is an in-body failure, at any depth; so is any non-zero `selfId`, since the object it +named lived only in the response that sent it and no call can hold it again: + +```console +$ … /EvaluateCalc -d '{"modelHash":"e587…f81e","symbolId":"F::apply","arguments":[{"function":{"calcId":"F::Sq"}},{"realValue":3.0}]}' +{"result":{"realValue":9}} + +$ … /EvaluateCalc -d '{"modelHash":"e587…f81e","symbolId":"F::apply","arguments":[{"function":{"calcId":"F::holder"}},{"realValue":2.0}]}' +{"error":"calc argument could not be read: function names no calc of this model: F::holder is not a calc", "failureReason":"FAILURE_REASON_EVALUATION"} + +$ … /EvaluateCalc -d '{"modelHash":"e587…f81e","symbolId":"F::apply","arguments":[{"function":{"calcId":"F::Scaler::scale","selfId":"3"}},{"realValue":2.0}]}' +{"error":"calc argument could not be read: function names no calc of this model: F::Scaler::scale: self_id 3 names no object of this call: an object lives only within the response that created it", "failureReason":"FAILURE_REASON_EVALUATION"} +``` + +A service without the `structured_values` capability refuses a structured argument, one +without `measurement_refs` a `measurementRef` argument, and one without `function_values` a +`function` argument, with the `unimplemented` Connect error instead, naming the capability; +check `GetServerInfo` first. A calc *usage* whose output features are evaluated from its own members (no `arguments`) answers them as `outputs`, a list of `{"name":…,"value":}` in declaration order, in diff --git a/internal/core/codegen/compile.go b/internal/core/codegen/compile.go index 3e11683c9..c84007b72 100644 --- a/internal/core/codegen/compile.go +++ b/internal/core/codegen/compile.go @@ -139,6 +139,9 @@ func (c *Compiler) compileCalc(sym *symbols.Symbol) (*Func, error) { if u.Value != nil { return nil, fc.unsupported(fmt.Sprintf("parameter %s has a default value", name)) } + if u.Kind == ast.UsageCalc { + return nil, fc.unsupported(fmt.Sprintf("parameter %s binds a function value", name)) + } b, err := fc.declaredBinding(sym.Scope, u, name) if err != nil { return nil, err diff --git a/internal/core/parser/behavior.go b/internal/core/parser/behavior.go index 98222c4b1..1746c91af 100644 --- a/internal/core/parser/behavior.go +++ b/internal/core/parser/behavior.go @@ -275,6 +275,7 @@ var parameterKindKeywords = map[string]ast.UsageKind{ "attribute": ast.UsageAttribute, "occurrence": ast.UsageOccurrence, "action": ast.UsageAction, + "calc": ast.UsageCalc, } // parameterKindKeyword reports whether parseDirectionParameter reads the token as diff --git a/internal/core/parser/testdata/parse/action_calc_parameter.golden b/internal/core/parser/testdata/parse/action_calc_parameter.golden new file mode 100644 index 000000000..2a3a3494f --- /dev/null +++ b/internal/core/parser/testdata/parse/action_calc_parameter.golden @@ -0,0 +1,44 @@ +(RootNamespace + (Membership visibility="default" + (Definition kind="calc" abstract=false variation=false name="Unary" + (Membership visibility="default" + (Usage kind="attribute" name="v" ref=false direction="in" composite=false derived=false ordered=false nonunique=false + (Relationship kind="typing" target=Real + (*ast.QualifiedName)))) + (Usage kind="attribute" name="" ref=false direction="out" composite=false derived=false ordered=false nonunique=false + (Relationship kind="typing" target=Real + (*ast.QualifiedName))))) + (Membership visibility="default" + (Definition kind="action" abstract=false variation=false name="Apply" + (Membership visibility="default" + (Usage kind="calc" name="f" ref=false direction="in" composite=false derived=false ordered=false nonunique=false + (Membership visibility="default" + (Usage kind="attribute" name="v" ref=false direction="in" composite=false derived=false ordered=false nonunique=false + (Relationship kind="typing" target=Real + (*ast.QualifiedName)))) + (Usage kind="attribute" name="" ref=false direction="out" composite=false derived=false ordered=false nonunique=false + (Relationship kind="typing" target=Real + (*ast.QualifiedName))))) + (Membership visibility="default" + (Usage kind="calc" name="g" ref=false direction="in" composite=false derived=false ordered=false nonunique=false + (Relationship kind="typing" target=Unary + (*ast.QualifiedName)))) + (Membership visibility="default" + (Usage kind="attribute" name="a" ref=false direction="in" composite=false derived=false ordered=false nonunique=false + (Relationship kind="typing" target=Real + (*ast.QualifiedName)))) + (Membership visibility="default" + (Usage kind="attribute" name="y" ref=false direction="out" composite=false derived=false ordered=false nonunique=false + (Relationship kind="typing" target=Real + (*ast.QualifiedName)))) + (Membership visibility="default" + (Usage kind="action" name="step" ref=false direction="none" composite=false derived=false ordered=false nonunique=false + (*ast.AssignmentActionNode))))) + (Membership visibility="default" + (Usage kind="action" name="apply" ref=false direction="none" composite=false derived=false ordered=false nonunique=false + (Relationship kind="typing" target=Apply + (*ast.QualifiedName)) + (Membership visibility="default" + (Usage kind="calc" name="h" ref=false direction="in" composite=false derived=false ordered=false nonunique=false + (Relationship kind="typing" target=Unary + (*ast.QualifiedName))))))) \ No newline at end of file diff --git a/internal/core/parser/testdata/parse/action_calc_parameter.sysml b/internal/core/parser/testdata/parse/action_calc_parameter.sysml new file mode 100644 index 000000000..57c411787 --- /dev/null +++ b/internal/core/parser/testdata/parse/action_calc_parameter.sysml @@ -0,0 +1,11 @@ +calc def Unary { in v : Real; return : Real; } +action def Apply { + in calc f { in v : Real; return : Real; } + in calc g : Unary; + in a : Real; + out y : Real; + action step { assign y := f(a) + g(a); } +} +action apply : Apply { + in calc h : Unary; +} diff --git a/internal/core/passes/invocation.go b/internal/core/passes/invocation.go index 4ee1e44c6..bedd3b68a 100644 --- a/internal/core/passes/invocation.go +++ b/internal/core/passes/invocation.go @@ -44,7 +44,7 @@ type argumentTypes struct { // argumentTypes types e's arguments once, so nested errors report once. func (ec *exprChecker) argumentTypes(scope *symbols.Scope, e *ast.InvocationExpr) argumentTypes { - args := invocationArgs(e) + args := InvocationArgs(e) types := argumentTypes{ positional: make([]semantics.Argument, len(args)), named: make([]semantics.Argument, len(e.NamedArgs)), @@ -58,14 +58,25 @@ func (ec *exprChecker) argumentTypes(scope *symbols.Scope, e *ast.InvocationExpr return types } -// invocationArgs returns e's positional arguments, the receiver first. -func invocationArgs(e *ast.InvocationExpr) []ast.Node { - if e.Operand == nil { +// InvocationArgs returns e's positional arguments, the receiver of `x->f(a)` +// first; the operand of a chain call `x.f(a)` is the calc applied, not an argument. +func InvocationArgs(e *ast.InvocationExpr) []ast.Node { + if e.Operand == nil || ChainCallee(e) != nil { return e.Args } return append([]ast.Node{e.Operand}, e.Args...) } +// ChainCallee is the feature chain a call `x.f(a)` applies (KerMLExpressions +// InstantiatedTypeMember → OwnedFeatureChain), nil for `T(a)` and `x->T(a)`. +func ChainCallee(e *ast.InvocationExpr) *ast.FeatureChainExpr { + if e.Type != nil { + return nil + } + chain, _ := e.Operand.(*ast.FeatureChainExpr) + return chain +} + // selectInvocation records the declaration e calls given the types of its arguments. func (ec *exprChecker) selectInvocation(scope *symbols.Scope, e *ast.InvocationExpr, argTypes argumentTypes, performs semantics.Performs) *semantics.InvocationSelection { return ec.model.SelectInvocation(scope, e, argTypes.arguments(), performs) diff --git a/internal/core/passes/typecheck_expr.go b/internal/core/passes/typecheck_expr.go index d354725eb..74746b58d 100644 --- a/internal/core/passes/typecheck_expr.go +++ b/internal/core/passes/typecheck_expr.go @@ -798,9 +798,12 @@ func (ec *exprChecker) inferInvocation(scope *symbols.Scope, e *ast.InvocationEx // inferNodeInvocation is inferInvocation for an invocation performed by node (nil for a bare // call). func (ec *exprChecker) inferNodeInvocation(scope *symbols.Scope, e *ast.InvocationExpr, node *symbols.Symbol) semantics.PrimType { - args := invocationArgs(e) + args := InvocationArgs(e) // Typed once and reused by checkArguments, so nested errors report once. argTypes := ec.argumentTypes(scope, e) + if chain := ChainCallee(e); chain != nil { + return ec.inferChainInvocation(scope, e, chain, args, argTypes, node) + } if e.Type == nil { for _, arg := range e.NamedArgs { ec.infer(scope, arg.Value) @@ -868,6 +871,37 @@ func (ec *exprChecker) inferNodeInvocation(scope *symbols.Scope, e *ast.Invocati return ec.model.PrimTypeOf(ec.model.ResultParameterOf(sym)) } +// inferChainInvocation is inferNodeInvocation for `x.f(a)`: the chain names the +// calc feature applied, whose effective inputs the arguments bind. +func (ec *exprChecker) inferChainInvocation(scope *symbols.Scope, e *ast.InvocationExpr, chain *ast.FeatureChainExpr, args []ast.Node, argTypes argumentTypes, node *symbols.Symbol) semantics.PrimType { + ec.infer(scope, chain) + sym, ok := ec.resolver.ResolveTarget(scope, chain) + if !ok || sym == nil { + return semantics.PrimUnknown + } + if !ec.isInvocationBehavior(sym, map[*symbols.Symbol]bool{}) { + if ec.isDefinitelyNonBehavior(sym) { + ec.diags = append(ec.diags, Diagnostic{ + Severity: SeverityError, + Span: chain.Span(), + Message: "Must invoke a behavior or a behavioral feature", + Code: "invocation-not-behavior", + Source: "type", + }) + } + return semantics.PrimUnknown + } + if isInvocationBehaviorKind(sym.Kind) && !isBehaviorKind(sym.Kind) { + return semantics.PrimUnknown + } + params, ok := ec.effectiveInParameters(sym, node) + if !ok { + return semantics.PrimUnknown + } + ec.checkArguments(scope, invocation{e, sym, args, argTypes, params}, nil) + return ec.model.PrimTypeOf(ec.model.ResultParameterOf(sym)) +} + // reporter reports a finding about an invocation's arguments. type reporter func(span source.Span, format string, args ...any) @@ -1091,7 +1125,7 @@ func (ec *exprChecker) checkNamedArguments(scope *symbols.Scope, call invocation e, sym, args, argTypes, params := call.e, call.sym, call.args, call.argTypes, call.params // A receiver binds by position, which named arguments leave unstated; runtime/eval.go // reports the same call. - if e.Operand != nil { + if e.Operand != nil && ChainCallee(e) == nil { report(e.Span(), "%s cannot be called with a receiver and named arguments", sym.Name) return } diff --git a/internal/core/passes/typecheck_expr_test.go b/internal/core/passes/typecheck_expr_test.go index 497444250..a3e04afe1 100644 --- a/internal/core/passes/typecheck_expr_test.go +++ b/internal/core/passes/typecheck_expr_test.go @@ -490,6 +490,38 @@ func TestExprInvocationReceiverWithNamedArguments(t *testing.T) { "add cannot be called with a receiver and named arguments") } +// calcHolder owns a calc feature reached through a feature chain, `holder.scale`. +const calcHolder = `part def Holder { + calc scale { in x : ScalarValues::Real; in k : ScalarValues::Real = 2.0; return : ScalarValues::Real = x * k; } +} +part holder : Holder; +` + +// A chain call `holder.scale(3.0)` applies the calc feature the chain names: its +// arguments are held to that calc's inputs and the call is typed by its result. +func TestExprChainInvocationChecked(t *testing.T) { + wantNoDiags(t, `package P { `+calcHolder+` attribute a : ScalarValues::Real = holder.scale(3.0); }`) + wantNoDiags(t, `package P { `+calcHolder+` attribute a : ScalarValues::Real = holder.scale(x = 3.0, k = 4.0); }`) + wantOneDiag(t, + `package P { `+calcHolder+` attribute a : ScalarValues::Real = holder.scale("three"); }`, + "argument 1 of scale expects Real, found String") + wantOneDiag(t, + `package P { `+calcHolder+` attribute a : ScalarValues::Real = holder.scale(x = 3.0, factor = 4.0); }`, + `scale has no parameter named "factor"`) + wantOneDiag(t, + `package P { `+calcHolder+` attribute a : ScalarValues::Real = holder.scale(1.0, 2.0, 3.0); }`, + "scale takes 2 argument(s), found 3") + wantOneWarning(t, + `package P { `+calcHolder+` attribute a : ScalarValues::Real = holder.scale(); }`, + CodeUnboundParameter, "scale leaves parameter x unbound, so the call cannot be evaluated") + wantOneDiag(t, + `package P { `+calcHolder+` attribute flag : ScalarValues::Boolean = holder.scale(3.0); }`, + "cannot bind Real value to a feature typed by Boolean") + wantOneDiag(t, + `package P { part def Box { attribute n : ScalarValues::Real; } part box : Box; attribute a : ScalarValues::Real = box.n(3.0); }`, + "Must invoke a behavior or a behavioral feature") +} + func TestExprInvocationNamedArgumentsOK(t *testing.T) { wantNoDiags(t, `package P { `+calcAdd+` calc c { add(a = 1, b = 2) } }`) wantNoDiags(t, `package P { `+calcAdd+` calc c { add(b = 2, a = 1) } }`) diff --git a/internal/core/passes/typecheck_value.go b/internal/core/passes/typecheck_value.go index 58303a539..5bd2852be 100644 --- a/internal/core/passes/typecheck_value.go +++ b/internal/core/passes/typecheck_value.go @@ -327,10 +327,15 @@ func (ec *exprChecker) invocationResultTypeSymbol(scope *symbols.Scope, value as // invocation names; nil for any other value or an unresolved invocation. func (ec *exprChecker) invocationResultParameter(scope *symbols.Scope, value ast.Node) *symbols.Symbol { inv, ok := value.(*ast.InvocationExpr) - if !ok || inv.Type == nil { + if !ok { return nil } - sym := SelectInvocation(ec.resolver, ec.model, scope, inv, ec.performs(inv)).Selected + var sym *symbols.Symbol + if chain := ChainCallee(inv); chain != nil { + sym, _ = ec.resolver.ResolveTarget(scope, chain) + } else if inv.Type != nil { + sym = SelectInvocation(ec.resolver, ec.model, scope, inv, ec.performs(inv)).Selected + } if sym == nil || !ec.isInvocationBehavior(sym, map[*symbols.Symbol]bool{}) { return nil } diff --git a/internal/core/runtime/action_frame.go b/internal/core/runtime/action_frame.go index f2be50d27..b38d89462 100644 --- a/internal/core/runtime/action_frame.go +++ b/internal/core/runtime/action_frame.go @@ -72,6 +72,8 @@ type actionFrame struct { result string // began is the activation the performance began in, which orders performances. began int64 + // run is the identity of this performance among the context's runs (Context.newRun). + run int64 // performs is the flow of the action a typed or invoked node performed, whose // subactions the node's performance adopted as its own. nil otherwise. performs *lower.ActionGraph @@ -131,6 +133,7 @@ func (e *ActionExecutor) newRootFrame() *actionFrame { data: make(map[string]Value), features: make(map[string]ast.FeatureDirection), subactions: make(map[ast.Node]*actionFrame), + run: e.ctx.newRun(), } for _, attr := range e.graph.Attributes { root.features[attr.Name] = ast.DirNone @@ -212,6 +215,7 @@ func (e *performances) beginPerformance( connections: parent.connections, data: make(map[string]Value), features: make(map[string]ast.FeatureDirection), + run: e.ctx.newRun(), } if perf.scope == nil { perf.scope = parent.scope @@ -1119,5 +1123,5 @@ func checkInputsBound(inv actionInvocation, params []actionParameter, inputs map // performanceFrame is the frame an evaluation reads a performance's values // through, which also answers for the nodes of its flow. func performanceFrame(f *actionFrame) frame { - return frame{vars: f.data, aliases: f.aliases, perf: f} + return frame{vars: f.data, aliases: f.aliases, perf: f, run: f.run} } diff --git a/internal/core/runtime/adopt.go b/internal/core/runtime/adopt.go index a2625642c..c6a1d107d 100644 --- a/internal/core/runtime/adopt.go +++ b/internal/core/runtime/adopt.go @@ -95,6 +95,9 @@ func (ctx *Context) recordShapes(obj *Instance, shapes *Shapes, seen map[int64]b if v.Kind == ValVariant { ctx.recordShape(v.Variant(), shapes) } + if self := v.FunctionSelf(); self != nil { + ctx.recordShapes(self, shapes, seen) + } if id, ok := carriedObject(v); ok { if held, found := ctx.instances[id]; found { ctx.recordShapes(held, shapes, seen) @@ -630,6 +633,13 @@ func (a *adoption) planValue(owner string, val Value) error { err = &AdoptError{Type: owner, Reason: "it holds an expression that was never evaluated"} return } + // A function value denotes its calc by name, so it is rebound as a variant is; + // the object it closes over is carried with it. + if v.Kind == ValFunction { + if err = a.planFunction(owner, v); err != nil { + return + } + } if v.Kind == ValVariant { if _, rebindErr := a.rebind(v.Variant(), "a variant it selected"); rebindErr != nil { err = rebindErr @@ -657,6 +667,25 @@ func (a *adoption) planValue(owner string, val Value) error { return err } +// planFunction rebinds the calc a function value is of to its declaration here, +// refusing one that is no longer a calc that can be invoked. +func (a *adoption) planFunction(owner string, v Value) error { + if v.FunctionClosesOverBody() { + return &AdoptError{Type: owner, Reason: "the function " + v.FunctionName() + " it holds closes over the bindings of a run that has ended"} + } + found, err := a.rebind(v.Function(), "the function "+v.FunctionName()+" it holds") + if err != nil { + return err + } + if _, err := a.ctx.calcShapeOf(found); err != nil { + return &AdoptError{Type: owner, Reason: "the function " + v.FunctionName() + " it holds cannot be invoked here: " + err.Error()} + } + if self := v.FunctionSelf(); self != nil { + return a.planHeld(owner, self.ID) + } + return nil +} + // unitsOf is the measurement units a quantity, reference or empty quantity // sequence names. func unitsOf(v Value) []Unit { @@ -1023,6 +1052,18 @@ func (a *adoption) rewrite(val Value) Value { return NewVariantValue(found, val.Instance) } return val + case ValFunction: + found, ok := a.rebound[val.Function()] + if !ok { + return val + } + shape, err := a.ctx.calcShapeOf(found) + if err != nil { + return val + } + fn := &functionValue{shape: shape, scope: found.OwnerScope, self: val.FunctionSelf()} + fn.library, _ = a.ctx.libraryFunctionFor(found) + return Value{Kind: ValFunction, ref: fn} case ValSequence: if val.Sequence() == nil { return val diff --git a/internal/core/runtime/adopt_test.go b/internal/core/runtime/adopt_test.go index 5ce432e70..19d6d6d9c 100644 --- a/internal/core/runtime/adopt_test.go +++ b/internal/core/runtime/adopt_test.go @@ -1626,3 +1626,70 @@ func TestAdoptRebindsTheFramesAWrittenValueNames(t *testing.T) { t.Errorf("Adopt refused for %q, want the missing frame named", err) } } + +const adoptFunctionSrc = `package Demo { + private import ScalarValues::*; + calc def Sq { in v : Real; return : Real = v * v; } + calc def Fn { in calc f { in v : Real; return : Real; } in a : Real; return : Real = f(a); } + part def Scaler { + attribute k : Real = 2.0; + calc scale { in x : Real; return : Real = x * k; } + } + part def Holder { attribute fn; attribute scaled; part scaler : Scaler; } + part holder : Holder; +}` + +// A function value a run wrote is carried over as a function of the calc the +// re-analysis declares, so applying it runs the calc as it is declared now; one +// closing over an object keeps that object with it. +func TestAdoptRebindsAFunctionValue(t *testing.T) { + prev := libraryContextOver(t, adoptFunctionSrc) + scope := lookupOne(t, prev.resolver.Index(), "Demo").Scope + holder, err := prev.Instantiate(lookupOne(t, prev.resolver.Index(), "Demo::holder")) + if err != nil { + t.Fatalf("Instantiate: %v", err) + } + for feature, expr := range map[string]string{"fn": "Sq", "scaled": "holder.scaler.scale"} { + val, err := evalIn(t, prev, scope, expr) + if err != nil || val.Kind != ValFunction { + t.Fatalf("%s = %s, %v; want a function", expr, FormatValue(val), err) + } + if err := holder.SetFeatureValue(prev, feature, val); err != nil { + t.Fatalf("write %s: %v", feature, err) + } + } + shapes := prev.ShapesOf(holder) + + ctx := libraryContextOver(t, strings.Replace(adoptFunctionSrc, "v * v", "v * v * v", 1)) + if _, err := ctx.Adopt(prev, shapes, holder); err != nil { + t.Fatalf("Adopt: %v", err) + } + fn, err := holder.GetFeatureValue(ctx, "fn") + if err != nil { + t.Fatalf("GetFeatureValue(fn): %v", err) + } + if want := lookupOne(t, ctx.resolver.Index(), "Demo::Sq"); fn.Value.Function() != want { + t.Errorf("fn is of %p, want the Sq declared by the re-analysis %p", fn.Value.Function(), want) + } + newScope := lookupOne(t, ctx.resolver.Index(), "Demo").Scope + for expr, want := range map[string]string{ + "holder.fn": "Demo::Sq", + "holder.fn == Sq": "true", + "Fn(holder.fn, 2.0)": "8.0", + "Fn(holder.scaled, 5.0)": "10.0", + "holder.scaled == holder.scaler.scale": "true", + } { + got, err := evalIn(t, ctx, newScope, expr) + if err != nil || FormatValue(got) != want { + t.Errorf("%s after the carry-over = %s, %v; want %s", expr, FormatValue(got), err, want) + } + } + + gone := libraryContextOver(t, strings.Replace(adoptFunctionSrc, "calc def Sq { in v : Real; return : Real = v * v; }", "", 1)) + var adoptErr *AdoptError + if _, err := gone.Adopt(prev, shapes, holder); !errors.As(err, &adoptErr) { + t.Fatalf("Adopt into a re-analysis without the calc: %v, want an AdoptError", err) + } else if !strings.Contains(err.Error(), "the function Demo::Sq it holds is no longer declared") { + t.Errorf("Adopt refused for %q, want the missing calc named", err) + } +} diff --git a/internal/core/runtime/analysis_run.go b/internal/core/runtime/analysis_run.go index 387e34870..ee3179b10 100644 --- a/internal/core/runtime/analysis_run.go +++ b/internal/core/runtime/analysis_run.go @@ -549,7 +549,7 @@ func (ctx *Context) analysisVerdict(kind, name string, check conditionCheck, con // bindingsFrame is the run's bindings as a frame the case owns, so a condition reads // its features by qualified name (`MassCase::result`) and its steps' pins (`step.out`). func (run *calcRun) bindingsFrame(ctx *Context) frame { - return frame{vars: run.bindings(ctx), perf: run.perf, owner: run.shape} + return frame{vars: run.bindings(ctx), perf: run.perf, owner: run.shape, run: run.env.run} } // bindings are the values a run bound, by name: its parameters and locals, and diff --git a/internal/core/runtime/calc_statements.go b/internal/core/runtime/calc_statements.go index e1e825176..d407c7185 100644 --- a/internal/core/runtime/calc_statements.go +++ b/internal/core/runtime/calc_statements.go @@ -36,6 +36,7 @@ func (h *calcStmtHost) attachPerformances(engine *stmtEngine) { nodes: h.shape.Nodes, label: h.shape.Label, outer: append(append([]frame{}, engine.env.enclosing...), engine.env.data), + run: h.ctx.newRun(), } h.flow = &ActionExecutor{ performances: performances{ctx: h.ctx, self: h.self, root: root, owner: h}, diff --git a/internal/core/runtime/calc_usage.go b/internal/core/runtime/calc_usage.go index 7c8633129..ddb7ce350 100644 --- a/internal/core/runtime/calc_usage.go +++ b/internal/core/runtime/calc_usage.go @@ -273,11 +273,19 @@ func (shape *calcShape) memberName(ctx *Context, sym *symbols.Symbol) (string, b // qualifiedBy reports whether a name qualified by qualifier (`MassCase::result`, // `Cases::Case::result`) denotes this calc's run: the calc itself or one it specializes. func (shape *calcShape) qualifiedBy(ctx *Context, qualifier *symbols.Symbol) bool { - if qualifier == shape.Sym { + return ctx.isOrSpecializes(shape.Sym, qualifier) +} + +// isOrSpecializes reports sym being general, or inheriting its members from it. +func (ctx *Context) isOrSpecializes(sym, general *symbols.Symbol) bool { + if sym == nil || general == nil { + return false + } + if sym == general { return true } - for _, general := range ctx.model.MemberSources(shape.Sym) { - if general == qualifier { + for _, source := range ctx.model.MemberSources(sym) { + if source == general { return true } } @@ -633,7 +641,7 @@ func (ctx *Context) bindCalcUsage(shape *calcShape, reader *EvalContext, args ca ec.trace.RecordCalculationEnter(shape.Kind, shape.Name) } - env := frame{vars: make(map[string]Value, len(shape.Params)), aliases: shape.Aliases, owner: shape} + env := frame{vars: make(map[string]Value, len(shape.Params)), aliases: shape.Aliases, owner: shape, run: ctx.newRun()} ec.pushFrame(env) // A usage declared in a behavior's body is written in that body, so its own @@ -661,8 +669,69 @@ func (ctx *Context) bindCalcUsage(shape *calcShape, reader *EvalContext, args ca // members or in a body-local block of it, which declares no owner of its own — // rather than in a part or a package, whose members hold no running values. func enclosedByBehaviorBody(sym *symbols.Symbol) bool { - owner := enclosingBehavior(sym) - return isCalcSymbol(owner) || isActionSymbol(owner) || isStateSymbol(owner) + return holdsRunningValues(enclosingBehavior(sym)) +} + +// holdsRunningValues reports a behavior whose runs bind values: a calc, an action +// or a state machine. +func holdsRunningValues(sym *symbols.Symbol) bool { + return isCalcSymbol(sym) || isActionSymbol(sym) || isStateSymbol(sym) +} + +// bodyEnclosing is the part of enclosing, the bindings of the behavior body the +// calc is declared in, its body reads: all of it for a body written inside that +// behavior, none for a body inherited from a calc declared elsewhere. +func (shape *calcShape) bodyEnclosing(enclosing []frame) []frame { + if len(enclosing) == 0 || !declaredWithin(shape.BodyOwner, enclosingBehavior(shape.Sym)) { + return nil + } + return enclosing +} + +// runOf is the environment of the innermost run of behavior among frames: the +// frames through the one holding that run; nil when none of them does. +func runOf(ctx *Context, frames []frame, behavior *symbols.Symbol) []frame { + if !holdsRunningValues(behavior) { + return nil + } + for i := len(frames) - 1; i >= 0; i-- { + if frames[i].runs(ctx, behavior) { + return frames[:i+1] + } + } + return nil +} + +// closesOverBody reports the calc reading the bindings of the behavior body it is +// declared in: through a body written there, or a default it declares there. +func (shape *calcShape) closesOverBody() bool { + if !enclosedByBehaviorBody(shape.Sym) { + return false + } + behavior := enclosingBehavior(shape.Sym) + if declaredWithin(shape.BodyOwner, behavior) { + return true + } + for i := range shape.Params { + if param := &shape.Params[i]; param.Default != nil && declaredWithin(param.Owner, behavior) { + return true + } + } + return false +} + +// declaredWithin reports sym declared in the body of behavior, directly or in a +// behavior nested in it. +func declaredWithin(sym, behavior *symbols.Symbol) bool { + if behavior == nil { + return false + } + for owner := enclosingBehavior(sym); owner != nil; owner = enclosingBehavior(owner) { + if owner == behavior { + return true + } + } + return false } // checkCalcTyping rejects a calc usage typed by something that is not a calc: it @@ -688,7 +757,13 @@ func (ctx *Context) runCalcUsage( shape *calcShape, ec, nested *EvalContext, env frame, reader *EvalContext, ) (*calcRun, error) { host := &calcStmtHost{ctx: ctx, shape: shape, self: reader.self} - engine := newStmtEngineIn(ctx, host, env, nil) + // A usage nested in a behavior body computes over that body's bindings, as + // an invocation of it does. + var enclosing []frame + if nested != nil { + enclosing = shape.bodyEnclosing(nested.enclosingRun(shape)) + } + engine := newStmtEngineIn(ctx, host, env, enclosing) host.attachPerformances(engine) result, returned, err := runCalcSteps(engine, host, shape) if err != nil { @@ -991,6 +1066,9 @@ func (ec *EvalContext) occurrenceOperand(operand ast.Node) (*symbols.Symbol, boo // against that object so its inputs read the object's feature values. Naming the usage // itself names no value: its outputs are what it computes. func (ec *EvalContext) calcUsageMemberValue(sym *symbols.Symbol, self *Instance, parts []ast.NameSegment) (Value, error) { + if len(parts) == 0 && ec.ctx.readsAsFunction(sym) { + return NewEvalContextIn(ec.ctx, sym.OwnerScope, self).functionValueOf(sym) + } if len(parts) == 0 && ec.ctx.returnsResult(sym) { parts = resultSegments } diff --git a/internal/core/runtime/classify_test.go b/internal/core/runtime/classify_test.go index 4c815c665..4b93e3b7a 100644 --- a/internal/core/runtime/classify_test.go +++ b/internal/core/runtime/classify_test.go @@ -221,6 +221,50 @@ func TestArgumentNotReturnedIsNotHeldByTheCall(t *testing.T) { } } +// A call through a feature chain, `picker.pickChosen(lead, trail)`, applies the calc the +// chain denotes: the feature it values holds the arguments that calc's returns pass on, +// and the chain itself is the callee, not an argument. +func TestChainCallReturnedArgumentsAreHeldByTheCall(t *testing.T) { + ctx, idx := libraryShapeContext(t, `package test { + private import ScalarValues::*; + item def Tallied { attribute tally : Integer = 7; } + item def Picker { + calc pickChosen { in chosen; in other; return : Anything = chosen; } + calc pickOther { in chosen; in other; return : Anything = other; } + } + item def Rack { + item picker : Picker; + item lead [1]; + item trail [1]; + item spare [1]; + item tallied : Tallied [1] = picker.pickChosen(lead, trail); + item named : Tallied [1] = picker.pickOther(other = spare, chosen = trail); + } + item rack : Rack; + }`) + rack := instantiateQualified(t, ctx, idx, "test::rack") + tallied := idx.LookupQualified("test::Tallied")[0] + + trail := readInstance(t, ctx, rack, "trail") + if ctx.instanceConforms(trail, tallied) { + t.Fatal("trail, which neither call returns, is a Tallied") + } + if rack.FeatureValues["tallied"].Materialized || rack.FeatureValues["named"].Materialized { + t.Fatal("reading trail computed a call that does not hold it") + } + lead := readInstance(t, ctx, rack, "lead") + if !ctx.instanceConforms(lead, tallied) { + t.Fatalf("lead, read first, is classified by %v, want Tallied", lead.classifiers) + } + spare := readInstance(t, ctx, rack, "spare") + if !ctx.instanceConforms(spare, tallied) { + t.Fatalf("spare, read first, is classified by %v, want Tallied", spare.classifiers) + } + if picker := readInstance(t, ctx, rack, "picker"); ctx.instanceConforms(picker, tallied) { + t.Fatal("picker, the callee's holder, is a Tallied") + } +} + // A holder that cannot materialize holds nothing: the held feature reads alike in either // order, and the holder's own error is reported when the holder is read. func TestFailingHolderDoesNotFailTheFeatureItWouldHold(t *testing.T) { diff --git a/internal/core/runtime/compile.go b/internal/core/runtime/compile.go index 3393f9c8c..2c07d05f9 100644 --- a/internal/core/runtime/compile.go +++ b/internal/core/runtime/compile.go @@ -248,6 +248,9 @@ func (c *calcCompiler) compile(cell *compiledCalc) error { cell.params = make([]compiledParam, len(shape.Params)) for i := range shape.Params { param := &shape.Params[i] + if param.IsCalc { + return ineligible(fmt.Sprintf("parameter %q binds a function value", param.Name)) + } check, ok := c.scalarCheckFor(¶m.Decl) if !ok { return ineligible(fmt.Sprintf("parameter %q declares a type outside the scalar lattice", param.Name)) @@ -455,6 +458,9 @@ func (c *calcCompiler) compileName(qn *ast.QualifiedName, scope *symbols.Scope, if !ok || sym == nil { return nil, ineligible(fmt.Sprintf("name %q is not bound in the frame", name)) } + if c.ctx.readsAsFunction(sym) { + return nil, ineligible(fmt.Sprintf("name %q is a function value", name)) + } node, err := c.libraryConstant(sym, name) if err != nil { return nil, err diff --git a/internal/core/runtime/conformance_test.go b/internal/core/runtime/conformance_test.go index 3ba2aebc8..dc37c9627 100644 --- a/internal/core/runtime/conformance_test.go +++ b/internal/core/runtime/conformance_test.go @@ -1564,6 +1564,8 @@ func expectedToRuntimeValue(t *testing.T, ev ExpectedValue) Value { t.Fatalf("a measurement reference names a unit the model declares, so it cannot be built from a case value") case "CoordinateFrame", "CoordinateTransformation": t.Fatalf("a %s is declared by the model, so it cannot be built from a case value", ev.Type) + case "Function": + t.Fatalf("a function is a calc the model declares, so it cannot be built from a case value") case "Complex": v, ok := ev.Value.(float64) if !ok || ev.Im == nil { @@ -1755,6 +1757,14 @@ func validateValue(t reporter, ctx *Context, name string, expected ExpectedValue if got := actual.MeasurementRef().Unit.String(); got != expected.Unit { t.Errorf("%s: unit = %q, want %q", name, got, expected.Unit) } + case "Function": + if actual.Kind != ValFunction || actual.Function() == nil { + t.Errorf("%s: type = %v, want Function", name, actual.Kind) + return + } + if want := expected.Value.(string); actual.FunctionName() != want { + t.Errorf("%s: function = %q, want %q", name, actual.FunctionName(), want) + } case "CoordinateFrame": actual = denotedObjectValue(t, ctx, name, actual) if actual.Kind != ValCoordinateFrame || actual.CoordinateFrame() == nil { diff --git a/internal/core/runtime/context.go b/internal/core/runtime/context.go index 9000270c6..9a4bd1864 100644 --- a/internal/core/runtime/context.go +++ b/internal/core/runtime/context.go @@ -104,6 +104,9 @@ type Context struct { // activations numbers the body activations begun in this context: a calc // invocation, a block entry, a loop iteration, a body application. activations int64 + // runs numbers the behavior runs begun in this context — calc invocations, calc + // usage evaluations, action performances — which functions closing over one carry. + runs int64 // occurrences holds the object each usage carrying no value of its own // denotes, so a feature chain through a part reads one occurrence of it. @@ -667,6 +670,13 @@ func (ctx *Context) newActivation() int64 { return ctx.activations } +// newRun begins one behavior run: the identity a function closing over it carries, +// which no other run of the same behavior shares. +func (ctx *Context) newRun() int64 { + ctx.runs++ + return ctx.runs +} + // endActivation forgets what an activation computed, once it has ended, and the // activations of the calc usage evaluations it held. func (ctx *Context) endActivation(activation int64) { diff --git a/internal/core/runtime/describe.go b/internal/core/runtime/describe.go index fa1792937..c55886dc6 100644 --- a/internal/core/runtime/describe.go +++ b/internal/core/runtime/describe.go @@ -68,6 +68,8 @@ func describeOperand(val Value) string { return "a coordinate frame" case ValCoordinateTransformation: return "a coordinate transformation" + case ValFunction: + return "the function " + val.FunctionName() } return "a value" } diff --git a/internal/core/runtime/errors.go b/internal/core/runtime/errors.go index 559c291e5..3a3ed691e 100644 --- a/internal/core/runtime/errors.go +++ b/internal/core/runtime/errors.go @@ -51,6 +51,10 @@ var ( // not a calc definition or usage. ErrNotACalc = errors.New("not a calc") + // ErrNotAFunction is returned when a value that is no function is called, + // or is bound where a calc-typed feature needs one. + ErrNotAFunction = errors.New("not a function") + // ErrNotAConstraint is returned when a symbol asked to be evaluated as a // constraint declares something else. It is a usage error about the request, // not a verdict about the model, so callers can tell the two apart. diff --git a/internal/core/runtime/eval.go b/internal/core/runtime/eval.go index 605e01c2e..f0f36f869 100644 --- a/internal/core/runtime/eval.go +++ b/internal/core/runtime/eval.go @@ -134,15 +134,23 @@ func (ec *EvalContext) nestedEnv(scope *symbols.Scope) *EvalContext { // are copied since an invocation's frame storage is reused once it returns, and // the calc evaluation whose outputs it may name is detached from that storage. func (ec *EvalContext) closure() *EvalContext { - frames := make([]frame, len(ec.frames)) - for i, f := range ec.frames { - frames[i] = f.snapshot() - } - out := ec.over(ec.scope, frames) + out := ec.over(ec.scope, snapshotFrames(ec.frames)) out.calcRun = ec.calcRun.detached() return out } +// snapshotFrames copies the bindings of frames, which their runs may reuse or drop. +func snapshotFrames(frames []frame) []frame { + if len(frames) == 0 { + return nil + } + out := make([]frame, len(frames)) + for i, f := range frames { + out[i] = f.snapshot() + } + return out +} + // over is this environment resolving names in scope over frames of its own. func (ec *EvalContext) over(scope *symbols.Scope, frames []frame) *EvalContext { return &EvalContext{ @@ -340,6 +348,10 @@ func (ctx *Context) EvalDeclaredValue(sym *symbols.Symbol) (Value, error) { if val, ok, err := ctx.declaredArrayValue(sym); ok { return val, err } + // A calc definition, or a calc usage awaiting arguments, is a function. + if val, ok, err := ctx.FunctionValue(sym); ok { + return val, err + } // A calc usage returning one unnamed result is read as that result. if isCalcUsageSymbol(sym) && ctx.returnsResult(sym) { return NewEvalContext(ctx, sym.OwnerScope).evalCalcUsageMembers(sym, resultSegments) @@ -627,6 +639,10 @@ func (ec *EvalContext) evalNameGeneral(qn *ast.QualifiedName) (Value, error) { if val, ok, err := ec.occurrenceReference(sym); ok { return val, err } + // A calc definition, or a calc usage awaiting arguments, is a function. + if val, ok, err := ec.calcAsValue(sym); ok { + return val, err + } // A calc usage returning one unnamed result is read as that result. if isCalcUsageSymbol(sym) && ec.ctx.returnsResult(sym) { return ec.evalCalcUsageMembers(sym, resultSegments) @@ -719,6 +735,11 @@ func (ec *EvalContext) evalNameGeneral(qn *ast.QualifiedName) (Value, error) { return val, err } + // A calc definition, or a calc usage awaiting arguments, is a function. + if val, ok, err := ec.calcAsValue(currentSym); ok { + return val, err + } + // Evaluate the final symbol's declaration if decl, ok := currentSym.Decl.(*ast.Usage); ok { // An enumerated value is a value of its enumeration, whether or not it @@ -1504,6 +1525,12 @@ func (ctx *Context) directValueType(scope *symbols.Scope, value Value) (*symbols ErrUndeterminedValueType, value.Literal().Name) } return enum, nil + case ValFunction: + // A function is of the calc it is a value of: a usage's type is that usage. + if value.Function() == nil { + return nil, fmt.Errorf("%w: function", ErrUndeterminedValueType) + } + return value.Function(), nil case ValQuantity: if value.Quantity() == nil { return nil, fmt.Errorf("%w: quantity", ErrUndeterminedValueType) @@ -2372,6 +2399,10 @@ func (ec *EvalContext) unresolvedInvocation(qn *ast.QualifiedName, written strin // evalInvocation evaluates a function/calc invocation. func (ec *EvalContext) evalInvocation(n *ast.InvocationExpr) (Value, error) { + // `holder.f(a)`: the chain names the function applied, not the callee's type. + if chain := passes.ChainCallee(n); chain != nil { + return ec.evalChainInvocation(n, chain) + } target := ec.invocationTarget(n) qualName := target.qualName if len(target.ambiguous) > 0 { @@ -2389,78 +2420,159 @@ func (ec *EvalContext) evalInvocation(n *ast.InvocationExpr) (Value, error) { // Eval args in source order. An operand is the first argument of the // invocation it is written before: `seq->size()` invokes size with seq, which - // is how the semantics layer reads the same expression (passes/ - // typecheck_expr.go), so the two agree on which parameter an argument binds. - exprs := n.Args - if n.Operand != nil { - exprs = append([]ast.Node{n.Operand}, n.Args...) + // is how the semantics layer reads the same expression, so the two agree on + // which parameter an argument binds. + exprs := passes.InvocationArgs(n) + // A calc-typed feature bound to a function value here — a parameter given a + // calc as its argument — applies that value, not the feature's own declaration. + if fn, ok, err := ec.boundFunction(target.calc, n.Type); ok { + if err != nil { + return Value{}, err + } + // Named arguments bind parameters of the calc applied, not of the feature named. + applied := *target + if len(n.NamedArgs) > 0 { + applied.names, applied.unbound = ec.ctx.boundParameterNames(ec.scope, fn.Function(), n.NamedArgs) + } + callArgs, err := ec.evalInvocationArgs(qualName, exprs, n.NamedArgs, &applied) + if err != nil { + return Value{}, err + } + return ec.invokeFunction(qualName, fn, callArgs) } // A calc bound by position alone consumes its arguments within the call, so // they live on the context's argument stack rather than in a slice of their own. if target.shape != nil && len(n.NamedArgs) == 0 { - return ec.invokeCalcShapeStacked(target.shape, exprs) + return ec.invokeCalcShapeStacked(target.shape, exprs, ec.enclosingRun(target.shape)) } // A built-in binds its arguments by its declared signature. if target.builtin != nil { return ec.invokeBuiltin(target.builtinName, target.builtin, exprs, n.NamedArgs, target.names, target.unbound) } + callArgs, err := ec.evalInvocationArgs(qualName, exprs, n.NamedArgs, target) + if err != nil { + return Value{}, err + } + + // An argument that fails is reported before the target is judged. A name + // that resolves to nothing denotes nothing, not the library function of + // that name: the validator reports the same expression unresolved. + if target.calc == nil && target.library == nil { + return Value{}, ec.unresolvedInvocation(n.Type, qualName) + } + // Every invocation goes through the one calc path, so an expression and a + // direct InvokeCalc bind parameters and trace identically. + if target.library != nil { + return target.library.invoke(ec.ctx, callArgs) + } + if target.shape == nil { + return ec.ctx.invokeCalcWithSelf(target.calc, callArgs, ec.scope, ec.self) + } + return ec.ctx.invokeCalcShapeIn(target.shape, callArgs, ec.scope, ec.self, ec.enclosingRun(target.shape)) +} + +// enclosingRun is the environment a nested calc closes over here: the frames through +// the innermost run of the behavior it is declared in, none when no such run is active. +func (ec *EvalContext) enclosingRun(shape *calcShape) []frame { + return runOf(ec.ctx, ec.frames, enclosingBehavior(shape.Sym)) +} + +// evalChainInvocation applies the function value a feature chain denotes to the +// arguments written after it (KerMLExpressions InstantiatedTypeMember → OwnedFeatureChain). +func (ec *EvalContext) evalChainInvocation(n *ast.InvocationExpr, chain *ast.FeatureChainExpr) (Value, error) { + callee := chainText(chain) + fn, err := ec.chainCallee(chain) + if err != nil { + return Value{}, err + } + if fn.Kind != ValFunction { + return Value{}, fmt.Errorf("%w: %s is %s, not a function", ErrNotAFunction, callee, describeValue(fn)) + } + target := &invocationTarget{qualName: callee, calc: fn.Function()} + if len(n.NamedArgs) > 0 { + target.names, target.unbound = ec.ctx.boundParameterNames(ec.scope, fn.Function(), n.NamedArgs) + } + callArgs, err := ec.evalInvocationArgs(callee, n.Args, n.NamedArgs, target) + if err != nil { + return Value{}, err + } + return ec.invokeFunction(callee, fn, callArgs) +} + +// chainCallee is what a feature chain denotes in call position: a calc of the receiver's +// object is the function applied over it even where a bare read would compute its result. +func (ec *EvalContext) chainCallee(chain *ast.FeatureChainExpr) (Value, error) { + if chain.Member == nil || len(chain.Member.Parts) != 1 { + return ec.Eval(chain) + } + receiver, err := ec.Eval(chain.Operand) + if err != nil { + return Value{}, err + } + name := chain.Member.Parts[0].Text + if id, isObject := receiver.Object(); isObject { + if inst, ok := ec.ctx.instances[id]; ok { + if _, held := inst.FeatureValues[name]; !held { + if sym, found := ec.ctx.model.LookupMember(inst.Type, name); found && isCalcUsageSymbol(sym) { + return NewEvalContextIn(ec.ctx, sym.OwnerScope, inst).functionValueOf(sym) + } + } + } + } + return ec.chainMemberValue(receiver, chain.Member.Parts, "") +} + +// chainText spells a feature chain as written, `holder.scale`. +func chainText(n ast.Node) string { + switch c := n.(type) { + case *ast.FeatureChainExpr: + return chainText(c.Operand) + "." + qualifiedNameToString(c.Member) + case *ast.FeatureReference: + return qualifiedNameToString(c.Name) + } + return TraceLabel(n) +} + +// evalInvocationArgs evaluates an invocation's arguments in source order into the +// calc arguments they bind: positional, or named against target's parameter names. +// The notation keeps the two forms mutually exclusive. +func (ec *EvalContext) evalInvocationArgs(qualName string, exprs []ast.Node, namedArgs []ast.NamedArg, target *invocationTarget) (calcArgs, error) { args := make([]Value, len(exprs)) for i, arg := range exprs { val, err := ec.Eval(arg) if err != nil { - return Value{}, err + return calcArgs{}, err } args[i] = val } - - var named map[string]Value - if len(n.NamedArgs) > 0 { - named = make(map[string]Value, len(n.NamedArgs)) + if len(namedArgs) == 0 { + return calcArgs{positional: args}, nil } - for i, arg := range n.NamedArgs { + named := make(map[string]Value, len(namedArgs)) + for i, arg := range namedArgs { name := target.names[i] if name == "" { - return Value{}, fmt.Errorf("unnamed argument in invocation of %s", qualName) + return calcArgs{}, fmt.Errorf("unnamed argument in invocation of %s", qualName) } if err := target.unbound[i]; err != nil { - return Value{}, err + return calcArgs{}, err } if _, dup := named[name]; dup { - return Value{}, fmt.Errorf("%w: %s binds parameter %q twice", ErrCalcArity, qualName, name) + return calcArgs{}, fmt.Errorf("%w: %s binds parameter %q twice", ErrCalcArity, qualName, name) } val, err := ec.Eval(arg.Value) if err != nil { - return Value{}, err + return calcArgs{}, err } named[name] = val } - - // An argument that fails is reported before the target is judged. A name - // that resolves to nothing denotes nothing, not the library function of - // that name: the validator reports the same expression unresolved. - if target.calc == nil && target.library == nil { - return Value{}, ec.unresolvedInvocation(n.Type, qualName) - } - // Every invocation goes through the one calc path, so an expression and a - // direct InvokeCalc bind parameters and trace identically. The notation keeps - // the argument forms mutually exclusive. - callArgs := calcArgs{positional: args} - if len(named) > 0 { - callArgs = calcArgs{named: named} - } - if target.library != nil { - return target.library.invoke(ec.ctx, callArgs) - } - if target.shape == nil { - return ec.ctx.invokeCalcWithSelf(target.calc, callArgs, ec.scope, ec.self) - } - return ec.ctx.invokeCalcShape(target.shape, callArgs, ec.scope, ec.self) + return calcArgs{named: named}, nil } // invokeCalcShapeStacked evaluates exprs onto the context's argument stack and // invokes shape with them, popping them however the invocation ends. -func (ec *EvalContext) invokeCalcShapeStacked(shape *calcShape, exprs []ast.Node) (Value, error) { +func (ec *EvalContext) invokeCalcShapeStacked(shape *calcShape, exprs []ast.Node, enclosing []frame) (Value, error) { ctx := ec.ctx base := len(ctx.argStack) for _, arg := range exprs { @@ -2473,7 +2585,7 @@ func (ec *EvalContext) invokeCalcShapeStacked(shape *calcShape, exprs []ast.Node } top := len(ctx.argStack) args := ctx.argStack[base:top:top] - result, err := ctx.invokeCalcShape(shape, calcArgs{positional: args}, ec.scope, ec.self) + result, err := ctx.invokeCalcShapeIn(shape, calcArgs{positional: args}, ec.scope, ec.self, enclosing) ctx.popArgs(base) return result, err } @@ -2551,6 +2663,11 @@ func valueEqual(a, b Value) bool { return a.CoordinateFrame().equal(b.CoordinateFrame()) case ValCoordinateTransformation: return a.CoordinateTransformation().equal(b.CoordinateTransformation()) + case ValFunction: + // A function is the calc it is a value of, read against the same object and, for + // one closing over a body's bindings, within the same run of that body. + return a.Function() == b.Function() && a.FunctionSelf() == b.FunctionSelf() && + a.functionRun() == b.functionRun() default: return false } diff --git a/internal/core/runtime/eval_no_value_test.go b/internal/core/runtime/eval_no_value_test.go index ecd48901c..09ea6abbe 100644 --- a/internal/core/runtime/eval_no_value_test.go +++ b/internal/core/runtime/eval_no_value_test.go @@ -85,8 +85,8 @@ func TestChainOverValuelessOperandResolvesItsMembers(t *testing.T) { } // TestTypeDeclarationIsNotAValuelessFeature: a KerML type the parser records as -// a usage — a class, struct, behavior, datatype or function — is a type like a -// definition, not a feature that lacks a value. +// a usage — a class, struct, behavior or datatype — is a type like a definition, +// not a feature that lacks a value; a function, being a calc, is a function value. func TestTypeDeclarationIsNotAValuelessFeature(t *testing.T) { model, resolver, root := parseAndBuildModel(t, ` package test { @@ -104,7 +104,7 @@ package test { pkg, _ := root.LookupLocal("test") scope := pkg.Scope - for _, name := range []string{"Vehicle", "Frame", "Drive", "Mass", "Twice", "Car"} { + for _, name := range []string{"Vehicle", "Frame", "Drive", "Mass", "Car"} { _, err := ctx.EvalWithScope(parseExpr(t, name), scope) if err == nil || !strings.Contains(err.Error(), "cannot evaluate definition "+name) { t.Errorf("%s: err = %v; want cannot evaluate definition", name, err) @@ -115,6 +115,10 @@ package test { } } + if val, err := ctx.EvalWithScope(parseExpr(t, "Twice"), scope); err != nil || val.Kind != ValFunction || val.FunctionName() != "test::Twice" { + t.Errorf("Twice = %s, %v; want the function test::Twice", FormatValue(val), err) + } + car, _ := scope.LookupLocal("car") var noValue *NoValueError if _, err := ctx.EvalWithScope(parseExpr(t, "unsetMass"), car.Scope); !errors.As(err, &noValue) { diff --git a/internal/core/runtime/frame.go b/internal/core/runtime/frame.go index f011bb2b5..ef89eea14 100644 --- a/internal/core/runtime/frame.go +++ b/internal/core/runtime/frame.go @@ -1,5 +1,7 @@ package runtime +import "github.com/Open-MBEE/OpenSysML/internal/core/symbols" + // frame is one level of local bindings an evaluation reads: a calc invocation's // parameter slots, a map of named values, or both. type frame struct { @@ -14,6 +16,12 @@ type frame struct { // owner is the calc whose parameters, locals and outputs the frame binds, so a // qualified name of one of its members (`MassCase::result`) reads the binding. owner *calcShape + // performed is the action whose performance a snapshot copied its bindings from, + // so the copy still answers for a run of that action without the live perf. + performed *symbols.Symbol + // run numbers the behavior run the frame binds (Context.newRun), 0 for bindings + // that are no run's; a function closing over the run is identified by it. + run int64 } // canonical is the name aliases bind name under: its redefinition's, else its own. @@ -53,10 +61,31 @@ func ownedFrame(owner *calcShape, vars map[string]Value) frame { return frame{vars: vars, owner: owner} } +// runs reports whether the frame holds a run of behavior: an invocation or usage of +// that calc or one specializing it, or a performance of that action or one typed by it. +func (f frame) runs(ctx *Context, behavior *symbols.Symbol) bool { + if f.owner != nil { + return f.owner.qualifiedBy(ctx, behavior) + } + if performed := f.performs(); performed != nil { + return ctx.isOrSpecializes(performed, behavior) + } + return false +} + +// performs is the action the frame holds a performance of: the live one's, or +// the one a snapshot copied; nil for a frame of a calc run or of plain bindings. +func (f frame) performs() *symbols.Symbol { + if f.perf != nil && f.perf.scope != nil { + return f.perf.scope.Owner() + } + return f.performed +} + // withVars is the frame holding vars in place of its own, still answering for // the same run and performance. func (f frame) withVars(vars map[string]Value) frame { - return frame{vars: vars, aliases: f.aliases, perf: f.perf, owner: f.owner} + return frame{vars: vars, aliases: f.aliases, perf: f.perf, owner: f.owner, performed: f.performed, run: f.run} } // lookup finds name in the frame: a slot binding it, else the map. @@ -105,12 +134,21 @@ func (f frame) each(fn func(name string, value Value)) { } } -// snapshot copies the frame's bindings into storage of its own, unchanged by -// whatever later reuses the frame's. +// snapshot copies the frame's bindings, and the aliases they are read through, +// into storage of its own, unchanged by whatever later reuses the frame's. The +// copy still answers for the run it was taken from, though not for its flow's nodes. func (f frame) snapshot() frame { vars := make(map[string]Value, f.width()) f.each(func(name string, value Value) { vars[name] = value }) - return ownedFrame(f.owner, vars) + out := ownedFrame(f.owner, vars) + out.performed, out.run = f.performs(), f.run + if len(f.aliases) > 0 { + out.aliases = make(map[string]string, len(f.aliases)) + for name, alias := range f.aliases { + out.aliases[name] = alias + } + } + return out } // width is the number of names the frame binds. diff --git a/internal/core/runtime/function_value.go b/internal/core/runtime/function_value.go new file mode 100644 index 000000000..42a7061a3 --- /dev/null +++ b/internal/core/runtime/function_value.go @@ -0,0 +1,210 @@ +package runtime + +import ( + "fmt" + + "github.com/Open-MBEE/OpenSysML/internal/core/ast" + "github.com/Open-MBEE/OpenSysML/internal/core/symbols" +) + +// functionValue is a calc read as a value: its lowered invocation interface and +// the environment it was read in, which an invocation of the value runs it against. +// A library calc the runtime implements natively carries that implementation instead. +type functionValue struct { + shape *calcShape + library *libraryFunction + scope *symbols.Scope // names the calc's defaults and body resolve against + self *Instance // object the calc's feature names resolve against, nil for none + // enclosing are the bindings of the behavior body the calc is declared in, as + // they stood when it was read; nil for a calc reading none of them. + enclosing []frame +} + +// function is the payload of a ValFunction; nil for every other kind. +func (v Value) function() *functionValue { + if v.Kind != ValFunction { + return nil + } + fn, _ := v.ref.(*functionValue) + return fn +} + +// Function is the calc a ValFunction is a value of — the definition or usage it +// was read from; nil for every other kind. +func (v Value) Function() *symbols.Symbol { + if fn := v.function(); fn != nil && fn.shape != nil { + return fn.shape.Sym + } + return nil +} + +// FunctionName is the qualified name of the calc a ValFunction is a value of, as +// the wire and diagnostics spell it. +func (v Value) FunctionName() string { + if fn := v.function(); fn != nil && fn.shape != nil { + return fn.shape.Name + } + return "" +} + +// FunctionSelf is the object a ValFunction's feature names resolve against, nil +// for a function closing over none. +func (v Value) FunctionSelf() *Instance { + if fn := v.function(); fn != nil { + return fn.self + } + return nil +} + +// FunctionClosesOverBody reports a ValFunction closing over the bindings of the +// behavior body its calc is declared in, which nothing outside that run can rebuild. +func (v Value) FunctionClosesOverBody() bool { + fn := v.function() + return fn != nil && len(fn.enclosing) > 0 +} + +// functionRun identifies the behavior run a ValFunction closes over (frame.run), so +// every read of the calc within that run is one function; 0 for one closing over none. +func (v Value) functionRun() int64 { + fn := v.function() + if fn == nil || len(fn.enclosing) == 0 { + return 0 + } + return fn.enclosing[len(fn.enclosing)-1].run +} + +// functionValueOf is the value of the calc sym denotes in this environment: its +// lowered shape closed over the scope and object the read resolves against. A +// library calc applied natively is the value of that implementation; one bound +// by an unevaluated argument (a built-in) has no value to pass on. +func (ec *EvalContext) functionValueOf(sym *symbols.Symbol) (Value, error) { + if _, builtin := ec.ctx.builtinFor(sym); builtin { + return Value{}, fmt.Errorf("%w: %s binds its arguments unevaluated and cannot be read as a value", + ErrNotAFunction, ec.ctx.qualifiedSymbolName(sym)) + } + shape, err := ec.ctx.calcShapeOf(sym) + if err != nil { + return Value{}, err + } + fn := &functionValue{shape: shape, scope: ec.scope, self: ec.self} + fn.library, _ = ec.ctx.libraryFunctionFor(sym) + if shape.closesOverBody() { + fn.enclosing = snapshotFrames(ec.enclosingRun(shape)) + } + return Value{Kind: ValFunction, ref: fn}, nil +} + +// isCalcDefSymbol reports a symbol declaring a calc definition or KerML function. +func isCalcDefSymbol(sym *symbols.Symbol) bool { + return sym != nil && sym.Kind == symbols.SymbolCalcDef +} + +// readsAsFunction reports a calc a bare read of its name denotes as a function: a +// calc definition, or a calc usage with an input no read could supply, which +// therefore computes no result to read. +func (ctx *Context) readsAsFunction(sym *symbols.Symbol) bool { + if isCalcDefSymbol(sym) { + return true + } + if !isCalcUsageSymbol(sym) { + return false + } + shape, err := ctx.calcShapeOf(sym) + return err == nil && shape.hasUnsuppliedInput() +} + +// hasUnsuppliedInput reports an input parameter no read of the calc could bind: +// neither an argument, a default nor an omitted optional supplies it. +func (shape *calcShape) hasUnsuppliedInput() bool { + for i := range shape.Params { + param := &shape.Params[i] + if param.Default == nil && !param.IsSubject && !param.optional() { + return true + } + } + return false +} + +// FunctionValue is the function a read of the declaration sym denotes — a calc +// definition, or a calc usage with an input no read could supply — closed over +// its own scope and no object; false when sym is no calc read as one. +func (ctx *Context) FunctionValue(sym *symbols.Symbol) (Value, bool, error) { + if !ctx.readsAsFunction(sym) { + return Value{}, false, nil + } + val, err := NewEvalContextIn(ctx, sym.OwnerScope, nil).functionValueOf(sym) + return val, true, err +} + +// calcAsValue is the function value a bare read of sym denotes, false when sym +// is no calc read as one. +func (ec *EvalContext) calcAsValue(sym *symbols.Symbol) (Value, bool, error) { + if !ec.ctx.readsAsFunction(sym) { + return Value{}, false, nil + } + val, err := ec.functionValueOf(sym) + return val, true, err +} + +// boundFunction is the value the calc-typed feature callee holds in this +// environment — a parameter bound by argument, or a feature of the bound object — +// which an invocation of callee applies; false when nothing here binds it. +func (ec *EvalContext) boundFunction(callee *symbols.Symbol, qn *ast.QualifiedName) (Value, bool, error) { + if qn == nil || len(qn.Parts) == 0 || !isCalcUsageSymbol(callee) { + return Value{}, false, nil + } + if len(qn.Parts) > 1 { + return ec.qualifiedBoundFunction(callee, qn) + } + if qn.Global { + return Value{}, false, nil + } + name := qn.Parts[0].Text + if val, ok := ec.Lookup(name); ok { + return val, true, nil + } + if ec.self != nil && ec.selfFeatureInScope(name) { + val, ok, err := ec.selfFeatureValue(name) + if err != nil { + return Value{}, true, err + } + if ok && val.Kind == ValFunction { + return val, true, nil + } + } + return Value{}, false, nil +} + +// qualifiedBoundFunction reads what the innermost run of the qualifying calc +// (`Apply::f(2.0)`), or of one specializing it, bound the calc-typed callee to. +func (ec *EvalContext) qualifiedBoundFunction(callee *symbols.Symbol, qn *ast.QualifiedName) (Value, bool, error) { + qualifier, ok := ec.ctx.resolver.ReadQualified(ec.scope, qn).Part(len(qn.Parts) - 2) + if !ok { + return Value{}, false, nil + } + val, ok := ec.frameFeatureValue(qualifier, callee) + return val, ok, nil +} + +// checkFunction refuses a value bound to a calc usage parameter that is no +// function; an omitted optional parameter holds null. +func (param *calcParameter) checkFunction(value *Value, what func() string) error { + if !param.IsCalc || value.Kind == ValFunction || value.Kind == ValNull { + return nil + } + return fmt.Errorf("%s: %w: %s is %s, not a function", + what(), ErrNotAFunction, FormatValue(*value), describeValue(*value)) +} + +// invokeFunction applies a value called as a function to args through the calc +// invocation path: a value that is no function is refused with a typed error. +func (ec *EvalContext) invokeFunction(callee string, val Value, args calcArgs) (Value, error) { + fn := val.function() + if fn == nil || fn.shape == nil { + return Value{}, fmt.Errorf("%w: %s is %s, not a function", ErrNotAFunction, callee, describeValue(val)) + } + if fn.library != nil { + return fn.library.invoke(ec.ctx, args) + } + return ec.ctx.invokeCalcShapeIn(fn.shape, args, fn.scope, fn.self, fn.enclosing) +} diff --git a/internal/core/runtime/holders.go b/internal/core/runtime/holders.go index 0c93afefb..02feac1f2 100644 --- a/internal/core/runtime/holders.go +++ b/internal/core/runtime/holders.go @@ -5,6 +5,7 @@ import ( "github.com/Open-MBEE/OpenSysML/internal/core/ast" "github.com/Open-MBEE/OpenSysML/internal/core/lower" + "github.com/Open-MBEE/OpenSysML/internal/core/passes" "github.com/Open-MBEE/OpenSysML/internal/core/symbols" ) @@ -214,11 +215,13 @@ func (ctx *Context) bodyReferences(scope *symbols.Scope, body *ast.BodyExpr, ope // to parameters a calc's returns pass on; every one for a function whose body is not // written in the model; none for a call that denotes nothing or computes no result. func (ctx *Context) returnedArguments(scope *symbols.Scope, call *ast.InvocationExpr) []ast.Node { - target := NewEvalContext(ctx, scope).invocationTarget(call) - positional := call.Args - if call.Operand != nil { - positional = append([]ast.Node{call.Operand}, call.Args...) + var target *invocationTarget + if chain := passes.ChainCallee(call); chain != nil { + target = ctx.chainTarget(scope, chain, call.NamedArgs) + } else { + target = NewEvalContext(ctx, scope).invocationTarget(call) } + positional := passes.InvocationArgs(call) var args []ast.Node switch { case target.shape != nil: @@ -242,6 +245,21 @@ func (ctx *Context) returnedArguments(scope *symbols.Scope, call *ast.Invocation return args } +// chainTarget is how a call `x.f(a)` is applied as far as the model states it: by the +// shape of the calc feature the chain denotes, which the named arguments bind parameters of. +func (ctx *Context) chainTarget(scope *symbols.Scope, chain *ast.FeatureChainExpr, named []ast.NamedArg) *invocationTarget { + target := &invocationTarget{qualName: chainText(chain)} + if sym, ok := ctx.resolver.ResolveTarget(scope, chain); ok && sym != nil { + if shape, err := ctx.calcShapeOf(sym); err == nil { + target.calc, target.shape = sym, shape + } + } + if len(named) > 0 { + target.names, target.unbound = ctx.boundParameterNames(scope, target.calc, named) + } + return target +} + // returnedAnalysis is the parameters a calc shape's returns pass on, as far as known. // Shapes calling each other are analysed together: each is provisional until the first // of them entered — the root of the cycle — is stable, when all of them are final. diff --git a/internal/core/runtime/invoke_calc.go b/internal/core/runtime/invoke_calc.go index 9b6455796..f31583d11 100644 --- a/internal/core/runtime/invoke_calc.go +++ b/internal/core/runtime/invoke_calc.go @@ -19,6 +19,8 @@ type calcParameter struct { Decl calcMemberDecl // the declaration, closest to the invoked calc, a bound value answers to // IsSubject marks a case's subject parameter, the object the case is about. IsSubject bool + // IsCalc marks a calc usage parameter (`in calc f`), which a function value binds. + IsCalc bool } // calcMemberDecl is the type and multiplicity a calc's parameter or output @@ -225,9 +227,11 @@ func (ctx *Context) calcShapeOf(sym *symbols.Symbol) (*calcShape, error) { shape.Bindings = calcBindings(chain) shape.ResultExpr = resultBindingExpr(shape.Bindings) // A calc computes nothing unless it returns or binds an output; a case also - // computes through its steps, or answers with its verdicts alone. + // computes through its steps, or answers with its verdicts alone, and a + // library function the runtime implements natively computes through that. performs := shape.isCase() && (len(shape.Nodes) > 0 || ctx.analysisChecks(sym)) - computes := lower.Returns(shape.Body) || len(shape.BodyOutputs) > 0 || shape.ResultExpr != nil || shape.hasInitialOutput() || performs + _, native := ctx.libraryFunctionFor(sym) + computes := lower.Returns(shape.Body) || len(shape.BodyOutputs) > 0 || shape.ResultExpr != nil || shape.hasInitialOutput() || performs || native if !computes { if len(shape.Outputs) > 0 && shape.resultOutput() == nil { return nil, fmt.Errorf("%w: %s binds none of its outputs (%s)", @@ -303,7 +307,10 @@ func (ctx *Context) calcParameters(chain []*symbols.Symbol, aliases *map[string] continue } sym := memberSymbol(declScope(link), usage) - param := calcParameter{Name: name, Default: usage.Value, Owner: link, Decl: ctx.calcMemberDeclOf(link, sym, name)} + param := calcParameter{ + Name: name, Default: usage.Value, Owner: link, + Decl: ctx.calcMemberDeclOf(link, sym, name), IsCalc: isCalcUsageSymbol(sym), + } if at, seen := ctx.redeclaredIndex(index, sym, name); seen { // A redeclaration binding no value keeps the inherited default, // which is written in the scope of the calc that stated it. @@ -561,6 +568,7 @@ type invocationFrame struct { bindings map[string]Value aliases map[string]string owner *calcShape // the calc invoked, whose members the locals bind + run int64 // the run this invocation is (Context.newRun) host calcStmtHost env stmtEnv engine stmtEngine @@ -568,7 +576,7 @@ type invocationFrame struct { // locals is the frame the invocation's parameters and body locals are bound in. func (f *invocationFrame) locals() frame { - return frame{slots: &f.slots, vars: f.bindings, aliases: f.aliases, owner: f.owner} + return frame{slots: &f.slots, vars: f.bindings, aliases: f.aliases, owner: f.owner, run: f.run} } // maxFreeInvocationFrames bounds the frames kept, so one deep recursion does not @@ -616,14 +624,21 @@ func (ctx *Context) releaseInvocationFrame(frame *invocationFrame) { // in the calc's own environment: defaults and the result expression see the // parameters and the calc's lexical scope, never the caller's frames. func (ctx *Context) invokeCalcShape(shape *calcShape, args calcArgs, callerScope *symbols.Scope, self *Instance) (Value, error) { + return ctx.invokeCalcShapeIn(shape, args, callerScope, self, nil) +} + +// invokeCalcShapeIn is invokeCalcShape for a calc declared in a behavior body: +// enclosing holds that body's bindings, outermost first, which the calc's own shadow. +func (ctx *Context) invokeCalcShapeIn(shape *calcShape, args calcArgs, callerScope *symbols.Scope, self *Instance, enclosing []frame) (Value, error) { if err := shape.checkArgs(args); err != nil { return Value{}, err } // A pure body runs compiled unless the run is traced, which records every - // sub-expression, an argument is not a scalar, or a bound object may answer - // a library constant the body reads before the library does. - if ctx.compileCalcs && ctx.trace == nil { + // sub-expression, an argument is not a scalar, a bound object may answer + // a library constant the body reads before the library does, or the body + // reads the bindings enclosing it. + if ctx.compileCalcs && ctx.trace == nil && len(enclosing) == 0 { if compiled := ctx.compiledCalcOf(shape); compiled != nil && (self == nil || !compiled.readsLibrary) { if result, ran, err := compiled.invokeBoxed(ctx, args); ran { return result, err @@ -645,14 +660,14 @@ func (ctx *Context) invokeCalcShape(shape *calcShape, args calcArgs, callerScope defer ctx.endActivation(activation) frame.slots.reset(shape.ParamNames) - frame.aliases, frame.owner = shape.Aliases, shape + frame.aliases, frame.owner, frame.run = shape.Aliases, shape, ctx.newRun() locals := frame.locals() ec := &frame.ec *ec = EvalContext{ ctx: ctx, scope: ctx.calcScope(shape.BodyOwner, shape.Sym, callerScope), self: self, - frames: append(frame.frames[:0], locals), + frames: append(append(frame.frames[:0], enclosing...), locals), trace: ctx.trace, activation: activation, } @@ -668,7 +683,7 @@ func (ctx *Context) invokeCalcShape(shape *calcShape, args calcArgs, callerScope return Value{}, err } - result, err := ctx.runCalcBody(shape, frame, callerScope, self, activation) + result, err := ctx.runCalcBody(shape, frame, callerScope, self, activation, enclosing) if ec.trace != nil { if err != nil { ec.trace.RecordCalculationExitError(shape.Kind, shape.Name, err) @@ -735,9 +750,13 @@ func (ctx *Context) bindCalcParameters( } // The parameter holds the value bound to it, so that value answers to the // parameter's declaration as a written one does. - if err := param.Decl.check(ctx, &value, func() string { + what := func() string { return fmt.Sprintf("%s: %s for parameter %q", shape.Label, source, param.Name) - }); err != nil { + } + if err := param.Decl.check(ctx, &value, what); err != nil { + return err + } + if err := param.checkFunction(&value, what); err != nil { return err } bindings.bindParam(i, param.Name, value) @@ -753,9 +772,9 @@ func (ctx *Context) bindCalcParameters( // the invocation yields: what the body returned, or, for a body that returns // nothing, the calc's designated output feature, evaluated in the invocation's // activation, which the caller ends after it. -func (ctx *Context) runCalcBody(shape *calcShape, frame *invocationFrame, callerScope *symbols.Scope, self *Instance, activation int64) (Value, error) { +func (ctx *Context) runCalcBody(shape *calcShape, frame *invocationFrame, callerScope *symbols.Scope, self *Instance, activation int64, enclosing []frame) (Value, error) { frame.host = calcStmtHost{ctx: ctx, shape: shape, self: self} - frame.env = stmtEnv{data: frame.locals()} + frame.env = stmtEnv{data: frame.locals(), enclosing: shape.bodyEnclosing(enclosing)} frame.engine = stmtEngine{ctx: ctx, host: &frame.host, env: &frame.env, activation: activation, frameBuf: frame.engine.frameBuf} frame.host.attachPerformances(&frame.engine) result, returned, err := runCalcSteps(&frame.engine, &frame.host, shape) @@ -774,6 +793,9 @@ func (ctx *Context) runCalcBody(shape *calcShape, frame *invocationFrame, caller // through the same run bookkeeping a calc usage's outputs use. run := newCalcRun(shape, callerScope, self, frame.locals()) run.activation, run.perf = activation, frame.host.performance() + if len(enclosing) > 0 { + run.outer = &EvalContext{ctx: ctx, scope: callerScope, self: self, frames: enclosing, trace: ctx.trace, activation: activation} + } // The invocation already holds this evaluation's nesting feature value. run.onStack = true return run.value(ctx, out) @@ -975,7 +997,7 @@ func (ctx *Context) resolveLibraryPerformance(sym *symbols.Symbol) *libraryPerfo // written, declared and defaulted by the nearest of its redefinitions the model // states — with the position of the library input it redefines, -1 for none. func (ctx *Context) effectiveParameter(sym *symbols.Symbol, libInputs []*symbols.Symbol) (calcParameter, int) { - param := calcParameter{Name: sym.Name} + param := calcParameter{Name: sym.Name, IsCalc: isCalcUsageSymbol(sym)} if effective, _ := ast.EffectiveName(sym.Decl.(*ast.Usage)); effective != "" { param.Name = effective } diff --git a/internal/core/runtime/robustness_test.go b/internal/core/runtime/robustness_test.go index afa150c65..51e6318ab 100644 --- a/internal/core/runtime/robustness_test.go +++ b/internal/core/runtime/robustness_test.go @@ -368,6 +368,16 @@ func TestRuntimeRobustness(t *testing.T) { t.Run("action_local_write_of_a_wrong_typed_value", testActionLocalWriteOfAWrongTypedValue) t.Run("action_output_write_of_a_wrong_typed_value", testActionOutputWriteOfAWrongTypedValue) t.Run("performance_occurrence_write_of_a_wrong_typed_value", testPerformanceOccurrenceWriteOfAWrongTypedValue) + t.Run("function_value_call_of_a_non_function", testFunctionValueCallOfANonFunction) + t.Run("function_value_bound_to_a_non_function", testFunctionValueBoundToANonFunction) + t.Run("function_value_arity_mismatch", testFunctionValueArityMismatch) + t.Run("function_value_unknown_named_argument", testFunctionValueUnknownNamedArgument) + t.Run("function_value_unbound_calc_parameter", testFunctionValueUnboundCalcParameter) + t.Run("function_value_of_a_wrong_typed_calc", testFunctionValueOfAWrongTypedCalc) + t.Run("function_value_of_a_built_in", testFunctionValueOfABuiltIn) + t.Run("function_value_applied_to_itself_forever", testFunctionValueAppliedToItselfForever) + t.Run("function_value_inherited_body_outside_the_closure", testFunctionValueInheritedBodyOutsideTheClosure) + t.Run("function_value_nested_calc_outside_its_run", testFunctionValueNestedCalcOutsideItsRun) t.Run("verification_body_that_cannot_run", testVerificationBodyThatCannotRun) t.Run("verification_body_step_that_fails", testVerificationBodyStepThatFails) t.Run("verification_subcase_that_cannot_run", testVerificationSubcaseThatCannotRun) @@ -11458,6 +11468,175 @@ func testNodeFlowIntoAPinTheTargetDoesNotDeclare(t *testing.T) { } } +// functionValueFixture declares Fn, which applies its calc-typed parameter f to a. +const functionValueFixture = ` + private import ScalarValues::*; + calc def Sq { in v : Real; return : Real = v * v; } + calc def Add { in x : Real; in y : Real; return : Real = x + y; } + calc def Fn { in calc f { in v : Real; return : Real; } in a : Real; return : Real = f(a); } +` + +// invokeCalcExpecting evaluates the calc call expr against src with the standard library, +// on its own goroutine so a body that never terminates fails the case instead of stalling it. +func invokeCalcExpecting(t *testing.T, src, expr string) error { + t.Helper() + idx, _, ctx := buildRuntimeWithLibraries(t, "", parseAndBuild(t, src)) + ctx.maxSteps = 100000 + scope := idx.DocumentRoot("") + + done := make(chan error, 1) + go func() { + node := parser.New(source.New("", []byte(expr))).ParseExpression() + result, err := ctx.EvalWithScopeOn(node, scope, nil) + if err == nil { + err = fmt.Errorf("%s = %s, expected it to fail", expr, FormatTraceValue(result)) + } + done <- err + }() + select { + case err := <-done: + return err + case <-time.After(10 * time.Second): + t.Fatalf("%s did not terminate", expr) + return nil + } +} + +// testFunctionValueCallOfANonFunction: a scalar passed where a calc-typed parameter +// is declared is refused when it is bound, before the body calls it. +func testFunctionValueCallOfANonFunction(t *testing.T) { + err := invokeCalcExpecting(t, `package test {`+functionValueFixture+`}`, "test::Fn(3.0, 3.0)") + if !errors.Is(err, ErrNotAFunction) || !strings.Contains(err.Error(), `parameter "f"`) { + t.Fatalf("error = %v, want ErrNotAFunction naming f", err) + } +} + +// testFunctionValueBoundToANonFunction: a named argument binding a calc-typed +// parameter to an object is refused the same way. +func testFunctionValueBoundToANonFunction(t *testing.T) { + src := `package test {` + functionValueFixture + ` + part def Box; + part box : Box; + }` + err := invokeCalcExpecting(t, src, "test::Fn(a = 3.0, f = test::box)") + if !errors.Is(err, ErrNotAFunction) { + t.Fatalf("error = %v, want ErrNotAFunction", err) + } +} + +// testFunctionValueArityMismatch: applying a function value to more arguments +// than its calc declares is a calc arity error naming the calc the value is of. +func testFunctionValueArityMismatch(t *testing.T) { + src := `package test {` + functionValueFixture + ` + calc def Two { in calc f { in v : Real; return : Real; } return : Real = f(1.0, 2.0); } + }` + err := invokeCalcExpecting(t, src, "test::Two(test::Sq)") + if !errors.Is(err, ErrCalcArity) || !strings.Contains(err.Error(), "test::Sq") { + t.Fatalf("error = %v, want ErrCalcArity for test::Sq", err) + } +} + +// testFunctionValueUnknownNamedArgument: a named argument the applied calc does +// not declare is reported against that calc, not the parameter it was passed through. +func testFunctionValueUnknownNamedArgument(t *testing.T) { + src := `package test {` + functionValueFixture + ` + calc def Named { in calc f { in v : Real; return : Real; } return : Real = f(w = 1.0); } + }` + err := invokeCalcExpecting(t, src, "test::Named(test::Sq)") + if !errors.Is(err, ErrUnknownParameter) || !strings.Contains(err.Error(), "test::Sq") { + t.Fatalf("error = %v, want ErrUnknownParameter for test::Sq", err) + } +} + +// testFunctionValueUnboundCalcParameter: a calc-typed parameter no argument binds +// is reported as unbound when the calc is invoked, and applying a function value +// to fewer arguments than its calc needs is reported the same way. +func testFunctionValueUnboundCalcParameter(t *testing.T) { + src := `package test {` + functionValueFixture + ` + calc def Partial { in calc f { in x : Real; in y : Real; return : Real; } return : Real = f(1.0); } + }` + err := invokeCalcExpecting(t, src, "test::Fn(a = 3.0)") + if !errors.Is(err, ErrUnboundParameter) || !strings.Contains(err.Error(), `parameter "f"`) { + t.Fatalf("error = %v, want ErrUnboundParameter naming f", err) + } + err = invokeCalcExpecting(t, src, "test::Partial(test::Add)") + if !errors.Is(err, ErrUnboundParameter) || !strings.Contains(err.Error(), `parameter "y"`) { + t.Fatalf("error = %v, want ErrUnboundParameter naming y", err) + } +} + +// testFunctionValueOfAWrongTypedCalc: a calc-typed parameter typed by a calc def +// refuses a function value of an unrelated calc. +func testFunctionValueOfAWrongTypedCalc(t *testing.T) { + src := `package test {` + functionValueFixture + ` + calc def Typed { in calc f : Sq; return : Real = f(2.0); } + }` + err := invokeCalcExpecting(t, src, "test::Typed(test::Add)") + if !errors.Is(err, ErrTypeMismatch) { + t.Fatalf("error = %v, want ErrTypeMismatch", err) + } +} + +// testFunctionValueOfABuiltIn: a library function the runtime binds unevaluated +// has no value to pass on, and says so. +func testFunctionValueOfABuiltIn(t *testing.T) { + src := `package test {` + functionValueFixture + ` + private import ControlFunctions::*; + calc def PassIf { return : Real = Fn(ControlFunctions::'if', 3.0); } + }` + err := invokeCalcExpecting(t, src, "test::PassIf()") + if !errors.Is(err, ErrNotAFunction) { + t.Fatalf("error = %v, want ErrNotAFunction", err) + } +} + +// testFunctionValueAppliedToItselfForever: a calc passing itself as a function +// value to itself without end spends the recursion budget rather than hanging. +func testFunctionValueAppliedToItselfForever(t *testing.T) { + src := `package test {` + functionValueFixture + ` + calc def Loop { in calc f { in v : Real; return : Real; } in v : Real; return : Real = Loop(f, f(v)); } + }` + err := invokeCalcExpecting(t, src, "test::Loop(test::Sq, 1.0)") + if !errors.Is(err, ErrCalcRecursionLimit) && !errors.Is(err, ErrStepLimitExceeded) { + t.Fatalf("error = %v, want the recursion or step budget spent", err) + } +} + +// testFunctionValueInheritedBodyOutsideTheClosure: a usage nested in a calc body +// closes over that body only for the code written there; the body it inherits from +// a calc declared outside reads no binding of the enclosing run, however it is applied. +func testFunctionValueInheritedBodyOutsideTheClosure(t *testing.T) { + src := `package test {` + functionValueFixture + ` + calc def Leaky { in v : Real; return : Real = v * k; } + calc def Bare { in k : Real; calc inner : Leaky { in v = 2.0; } return : Real = inner; } + calc def Called { in k : Real; calc inner : Leaky; return : Real = inner(2.0); } + calc def Passed { in k : Real; calc inner : Leaky; return : Real = Fn(inner, 2.0); } + }` + for _, expr := range []string{"test::Bare(3.0)", "test::Called(3.0)", "test::Passed(3.0)"} { + err := invokeCalcExpecting(t, src, expr) + if !errors.Is(err, ErrNoValue) && !errors.Is(err, ErrUnresolvedReference) { + t.Fatalf("%s: error = %v, want k unresolved in Leaky's body", expr, err) + } + } +} + +// testFunctionValueNestedCalcOutsideItsRun: a calc nested in another calc's body +// closes over a run of that calc alone; applied from a calc that binds the same +// parameter name while no such run is active, it reads no binding of the caller's. +func testFunctionValueNestedCalcOutsideItsRun(t *testing.T) { + src := `package test {` + functionValueFixture + ` + calc def Outer { in k : Real; calc inner { in v : Real; return : Real = v * k; } return : Real = inner(1.0); } + calc def Called { in k : Real; return : Real = Outer::inner(2.0); } + calc def Passed { in k : Real; return : Real = Fn(Outer::inner, 2.0); } + }` + for _, expr := range []string{"test::Called(3.0)", "test::Passed(3.0)"} { + err := invokeCalcExpecting(t, src, expr) + if !errors.Is(err, ErrNoValue) && !errors.Is(err, ErrUnresolvedReference) { + t.Fatalf("%s: error = %v, want k unresolved in inner's body", expr, err) + } + } +} + // verificationRobustnessModel states a case whose subject nothing binds, one // whose body reads a feature holding no value, and a part that is no case. const verificationRobustnessModel = ` diff --git a/internal/core/runtime/state_statements.go b/internal/core/runtime/state_statements.go index 1509ffd99..6c8ddc310 100644 --- a/internal/core/runtime/state_statements.go +++ b/internal/core/runtime/state_statements.go @@ -47,6 +47,7 @@ func (h *stateStmtHost) rootFrame(attrs []map[string]Value) *actionFrame { nodes: h.behavior.Nodes, label: h.describe(), outer: []frame{mapFrame(h.exec.stateData)}, + run: h.exec.ctx.newRun(), } if root.scope == nil { root.scope = h.exec.stateMachine.Scope diff --git a/internal/core/runtime/testdata/conformance/README.md b/internal/core/runtime/testdata/conformance/README.md index 5c2bb7527..19685e924 100644 --- a/internal/core/runtime/testdata/conformance/README.md +++ b/internal/core/runtime/testdata/conformance/README.md @@ -331,6 +331,8 @@ Supported types: string (`{"type": "Variant", "value": "cutIdeal"}`) - `EnumLiteral`: the enumeration literal a value is, written as the enumeration declaring it qualifies it (`{"type": "EnumLiteral", "value": "Color::red"}`) +- `Function`: the qualified name of the calc a function value is a value of + (`{"type": "Function", "value": "test::Sq"}`) In place of a value, `error` states the text producing that value must fail with, for a slot or result whose contract is a diagnostic (`{"error": "not a diff --git a/internal/core/runtime/testdata/conformance/action_nested_calc_reads_performance.expected.json b/internal/core/runtime/testdata/conformance/action_nested_calc_reads_performance.expected.json new file mode 100644 index 000000000..adf1d9624 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/action_nested_calc_reads_performance.expected.json @@ -0,0 +1,9 @@ +{ + "type": "action", + "evaluate": "test::run", + "libraries": true, + "outputs": { + "total": {"type": "Real", "value": 60.0}, + "s.r": {"type": "Real", "value": 60.0} + } +} diff --git a/internal/core/runtime/testdata/conformance/action_nested_calc_reads_performance.sysml b/internal/core/runtime/testdata/conformance/action_nested_calc_reads_performance.sysml new file mode 100644 index 000000000..8f3df7d67 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/action_nested_calc_reads_performance.sysml @@ -0,0 +1,33 @@ +// A calc declared in an action's body reads the performance of that action it is +// applied in — the one typed by the action def, or the subaction's own — whether +// invoked directly or passed on as a function value. +package test { + private import ScalarValues::*; + + calc def Fn { in calc f { in x : Real; return : Real; } in a : Real; return : Real = f(a); } + + action def Scale { + in k : Real; + out r : Real; + calc byK { in x : Real; return : Real = x * k; } + + first start; + then action direct { assign r := byK(3.0); } + then action nested { + attribute m : Real = 2.0; + calc byM { in x : Real; return : Real = x * m * k; } + assign r := r + byM(1.0); + } + then action passed { assign r := r + Fn(byK, 1.0); } + then done; + } + + action run { + out attribute total : Real = 0.0; + + first start; + then action s : Scale { in k = 10.0; } + then action tally { assign total := s.r; } + then done; + } +} diff --git a/internal/core/runtime/testdata/conformance/function_value_action_closure.expected.json b/internal/core/runtime/testdata/conformance/function_value_action_closure.expected.json new file mode 100644 index 000000000..4dbb7205a --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_action_closure.expected.json @@ -0,0 +1,9 @@ +{ + "type": "action", + "evaluate": "test::outer", + "libraries": true, + "outputs": { + "result": {"type": "Real", "value": 150.0}, + "scale.y": {"type": "Real", "value": 150.0} + } +} diff --git a/internal/core/runtime/testdata/conformance/function_value_action_closure.sysml b/internal/core/runtime/testdata/conformance/function_value_action_closure.sysml new file mode 100644 index 000000000..e47e20e1e --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_action_closure.sysml @@ -0,0 +1,30 @@ +package test { + private import ScalarValues::*; + + calc def Unary { in x : Real; return : Real; } + calc def Fn { in calc f { in x : Real; return : Real; } in a : Real; return : Real = f(a); } + + // A calc declared in an action body closes over the performance it is read in; + // passed on as a function, it still reaches that performance's inputs and locals, + // and so does a second body-local calc it applies. + action def Scaler { + in k : Real; + out y : Real; + attribute offset : Real = 100.0; + + calc byK { in x : Real; return : Real = x * k; } + calc shifted { in x : Real; return : Real = byK(x) + offset; } + + first step; + action step { assign y := Fn(shifted, 3.0) + Fn(byK, 2.0); } + } + + action outer { + out attribute result : Real; + + first start; + then action scale : Scaler { in k = 10.0; } + then action fin { assign result := scale.y; } + then done; + } +} diff --git a/internal/core/runtime/testdata/conformance/function_value_action_parameter.expected.json b/internal/core/runtime/testdata/conformance/function_value_action_parameter.expected.json new file mode 100644 index 000000000..f1bd8cb03 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_action_parameter.expected.json @@ -0,0 +1,10 @@ +{ + "type": "action", + "evaluate": "test::outer", + "libraries": true, + "outputs": { + "result": {"type": "Real", "value": 12.0}, + "apply.a": {"type": "Real", "value": 2.0}, + "apply.y": {"type": "Real", "value": 12.0} + } +} diff --git a/internal/core/runtime/testdata/conformance/function_value_action_parameter.sysml b/internal/core/runtime/testdata/conformance/function_value_action_parameter.sysml new file mode 100644 index 000000000..ecaf223ff --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_action_parameter.sysml @@ -0,0 +1,34 @@ +package test { + private import ScalarValues::*; + + calc def Unary { in v : Real; return : Real; } + calc def Sq :> Unary { in :>> v; return : Real = v * v; } + calc def Cube :> Unary { in :>> v; return : Real = v * v * v; } + calc sq : Sq; + calc cube : Cube; + + // An action's `in calc` parameters hold the functions bound to them, and a + // body applies them as it applies any calc. + action def Apply { + in calc f { in v : Real; return : Real; } + in calc g : Unary; + in a : Real; + out y : Real; + + first step; + action step { assign y := f(a) + g(a); } + } + + action outer { + out attribute result : Real; + + first start; + then action apply : Apply { + in f = sq; + in g = cube; + in a = 2.0; + } + then action fin { assign result := apply.y; } + then done; + } +} diff --git a/internal/core/runtime/testdata/conformance/function_value_body_closure.expected.json b/internal/core/runtime/testdata/conformance/function_value_body_closure.expected.json new file mode 100644 index 000000000..69c7bf64f --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_body_closure.expected.json @@ -0,0 +1,6 @@ +{ + "type": "calc", + "evaluate": "test::UseOuter", + "libraries": true, + "result": {"type": "Real", "value": 233.0} +} diff --git a/internal/core/runtime/testdata/conformance/function_value_body_closure.sysml b/internal/core/runtime/testdata/conformance/function_value_body_closure.sysml new file mode 100644 index 000000000..7c4cc1080 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_body_closure.sysml @@ -0,0 +1,44 @@ +package test { + private import ScalarValues::*; + calc def Unary { in x : Real; return : Real; } + calc def Fn { in calc f { in x : Real; return : Real; } in a : Real; return : Real = f(a); } + calc def Outer { + in k : Real; + calc inner { in x : Real; return : Real = x * k; } + return : Real = Fn(inner, 2.0) + inner(3.0); + } + // A nested usage read bare computes over the enclosing run's bindings. + calc def Bare { + in k : Real; + calc inner { in x : Real = 2.0; return : Real = x * k; } + return : Real = inner; + } + // A nested calc returned as a function keeps the bindings of the run that returned it. + calc def Maker { + in k : Real; + calc scale :> Unary { in :>> x; return : Real = x * k; } + return : Unary = scale; + } + // A nested usage inheriting its body still closes over a default it declares in the run. + calc def Mul { in a : Real; in b : Real; return : Real = a * b; } + calc def Fixer { in k : Real; calc byK : Mul { in :>> b = k; } return : Mul = byK; } + calc def ApplyOne { in calc f { in a : Real; return : Real; } in a : Real; return : Real = f(a); } + // A nested calc reads the run of the calc it is declared in, not the caller + // between them, even where that caller binds the same name. + calc def Shadowed { + in k : Real; + calc inner { in x : Real; return : Real = x * k; } + calc wrap { in k : Real; return : Real = inner(1.0) + Fn(inner, 1.0) + k; } + return : Real = wrap(100.0); + } + // A specialization renaming the enclosing parameter still answers the nested + // calc's read of the inherited name, whether it is returned or passed on. + calc def Tripler :> Maker { in m :>> k = 3.0; } + calc def PassOn { + in k : Real; + calc inner { in x : Real; return : Real = x * k; } + return : Real = Fn(inner, 5.0); + } + calc def PassOnRenamed :> PassOn { in m :>> k; } + calc def UseOuter { return : Real = Outer(3.0) + Outer(4.0) + Bare(5.0) + Fn(Maker(10.0), 2.0) + ApplyOne(Fixer(10.0), 4.0) + Shadowed(3.0) + Fn(Tripler(), 4.0) + PassOnRenamed(2.0); } +} diff --git a/internal/core/runtime/testdata/conformance/function_value_calc_usage.expected.json b/internal/core/runtime/testdata/conformance/function_value_calc_usage.expected.json new file mode 100644 index 000000000..367d7a89f --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_calc_usage.expected.json @@ -0,0 +1,6 @@ +{ + "type": "calc", + "evaluate": "test::UseCube", + "libraries": true, + "result": {"type": "Real", "value": 8.0} +} diff --git a/internal/core/runtime/testdata/conformance/function_value_calc_usage.sysml b/internal/core/runtime/testdata/conformance/function_value_calc_usage.sysml new file mode 100644 index 000000000..e15160c84 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_calc_usage.sysml @@ -0,0 +1,6 @@ +package test { + private import ScalarValues::*; + calc cube { in v : Real; return : Real = v * v * v; } + calc def Fn { in calc f { in v : Real; return : Real; } in a : Real; return : Real = f(a); } + calc def UseCube { return : Real = Fn(cube, 2.0); } +} diff --git a/internal/core/runtime/testdata/conformance/function_value_chain_call.expected.json b/internal/core/runtime/testdata/conformance/function_value_chain_call.expected.json new file mode 100644 index 000000000..0e1ad5ed3 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_chain_call.expected.json @@ -0,0 +1,6 @@ +{ + "type": "calc", + "evaluate": "test::UseChain", + "libraries": true, + "result": {"type": "Real", "value": 14.0} +} diff --git a/internal/core/runtime/testdata/conformance/function_value_chain_call.sysml b/internal/core/runtime/testdata/conformance/function_value_chain_call.sysml new file mode 100644 index 000000000..327e5eddf --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_chain_call.sysml @@ -0,0 +1,9 @@ +package test { + private import ScalarValues::*; + part def Holder { + attribute k : Real = 2.0; + calc scale { in x : Real; return : Real = x * k; } + } + part holder : Holder; + calc def UseChain { return : Real = holder.scale(3.0) + holder.scale(x = 4.0); } +} diff --git a/internal/core/runtime/testdata/conformance/function_value_chain_defaults.expected.json b/internal/core/runtime/testdata/conformance/function_value_chain_defaults.expected.json new file mode 100644 index 000000000..e785fff20 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_chain_defaults.expected.json @@ -0,0 +1,10 @@ +{ + "type": "instance", + "instantiate": "test::Reads", + "libraries": true, + "slots": { + "bare": {"type": "Real", "value": 10.0}, + "called": {"type": "Real", "value": 10.0}, + "total": {"type": "Real", "value": 48.0} + } +} diff --git a/internal/core/runtime/testdata/conformance/function_value_chain_defaults.sysml b/internal/core/runtime/testdata/conformance/function_value_chain_defaults.sysml new file mode 100644 index 000000000..23201f4b2 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_chain_defaults.sysml @@ -0,0 +1,28 @@ +package test { + private import ScalarValues::*; + + part def Holder { + attribute k : Real = 2.0; + calc scaled { in x : Real = 5.0; return : Real = x * k; } + calc squared { return : Real = k * k; } + } + part def Rig { + part holder : Holder; + part other : Holder { attribute :>> k = 3.0; } + } + part rig : Rig; + + // A calc every input of which a default supplies, or that has no input, is + // still applied when written in call position: `holder.scaled()` is 10.0, not + // the bare read `holder.scaled`. + calc def UseDefaults { + return : Real = rig.holder.scaled() + rig.holder.scaled(4.0) + rig.holder.scaled(x = 1.0) + + rig.holder.squared() + rig.other.squared() + rig.other.scaled(); + } + // 10 + 8 + 2 + 4 + 9 + 15 + part def Reads { + attribute bare : Real = rig.holder.scaled; + attribute called : Real = rig.holder.scaled(); + attribute total : Real = UseDefaults(); + } +} diff --git a/internal/core/runtime/testdata/conformance/function_value_chain_holder.expected.json b/internal/core/runtime/testdata/conformance/function_value_chain_holder.expected.json new file mode 100644 index 000000000..6eeaf2b12 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_chain_holder.expected.json @@ -0,0 +1,15 @@ +{ + "type": "instance", + "instantiate": "test::Rack", + "libraries": true, + "slots": { + "lead.tally": {"type": "Integer", "value": 7}, + "trail.tally": {"error": "object has no such feature"} + }, + "identical": [ + ["tallied", "lead"] + ], + "distinct": [ + ["tallied", "trail"] + ] +} diff --git a/internal/core/runtime/testdata/conformance/function_value_chain_holder.sysml b/internal/core/runtime/testdata/conformance/function_value_chain_holder.sysml new file mode 100644 index 000000000..6bfaa7b60 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_chain_holder.sysml @@ -0,0 +1,18 @@ +// A feature valued by a call through a feature chain holds the arguments the calc +// applied passes on: `lead`, returned by `picker.pickChosen`, is a Tallied and +// carries its features; `trail`, which the calc never returns, is not. +package test { + private import ScalarValues::*; + + item def Tallied { attribute tally : Integer = 7; } + item def Picker { + calc pickChosen { in chosen; in other; return : Anything = chosen; } + } + item def Rack { + item picker : Picker; + item lead [1]; + item trail [1]; + item tallied : Tallied [1] = picker.pickChosen(lead, trail); + } + item rack : Rack; +} diff --git a/internal/core/runtime/testdata/conformance/function_value_closure_equality.expected.json b/internal/core/runtime/testdata/conformance/function_value_closure_equality.expected.json new file mode 100644 index 000000000..2dacf9c50 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_closure_equality.expected.json @@ -0,0 +1,11 @@ +{ + "type": "action", + "evaluate": "test::rebinding", + "libraries": true, + "outputs": { + "sameRun": {"type": "Boolean", "value": true}, + "acrossRuns": {"type": "Boolean", "value": true}, + "before": {"type": "Boolean", "value": true}, + "after": {"type": "Boolean", "value": true} + } +} diff --git a/internal/core/runtime/testdata/conformance/function_value_closure_equality.sysml b/internal/core/runtime/testdata/conformance/function_value_closure_equality.sysml new file mode 100644 index 000000000..aed565cab --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_closure_equality.sysml @@ -0,0 +1,51 @@ +package test { + private import ScalarValues::*; + private import SequenceFunctions::*; + + calc def Unary { in x : Real; return : Real; } + calc def Same { + in calc f { in x : Real; return : Real; } + in calc g { in x : Real; return : Real; } + return : Boolean = f == g; + } + calc def Maker { + in k : Real; + calc scale :> Unary { in :>> x; return : Real = x * k; } + return : Unary = scale; + } + // A nested calc read twice in one run is one function: the two reads are equal, + // and a sequence of both holds one distinct value. + calc def SameRun { + in k : Real; + calc inner { in x : Real; return : Real = x * k; } + return : Boolean = Same(inner, inner) and inner == inner + and includes((inner, inner), inner) and isEmpty(excluding((inner, inner), inner)) + and size(intersection((inner, inner), inner)) == 2; + } + // Functions returned by two runs are distinct even when the runs bind the + // closed-over name alike: each run's function is its own. + calc def AcrossRuns { + in calc once :> Unary = Maker(2.0); + return : Boolean = Maker(2.0) != Maker(2.0) and Maker(2.0) != Maker(3.0) + and once == once and excludes((Maker(2.0), Maker(3.0)), once) + and size(excluding((once, Maker(2.0), once), once)) == 1; + } + // Within one performance, a read taken before the body rebinds a closed-over + // name and a read taken after are the same function. + action def Rebinding { + out sameRun : Boolean = SameRun(3.0); + out acrossRuns : Boolean = AcrossRuns(); + out before : Boolean; + out after : Boolean; + attribute k : Real = 2.0; + calc byK { in x : Real; return : Real = x * k; } + attribute held = byK; + + first start; + then action same { assign before := held == byK; } + then action rebind { assign k := 3.0; } + then action changed { assign after := held == byK; } + then done; + } + action rebinding : Rebinding; +} diff --git a/internal/core/runtime/testdata/conformance/function_value_feature_closure.expected.json b/internal/core/runtime/testdata/conformance/function_value_feature_closure.expected.json new file mode 100644 index 000000000..4ee42cb57 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_feature_closure.expected.json @@ -0,0 +1,6 @@ +{ + "type": "calc", + "evaluate": "test::UseScalers", + "libraries": true, + "result": {"type": "Real", "value": 25.0} +} diff --git a/internal/core/runtime/testdata/conformance/function_value_feature_closure.sysml b/internal/core/runtime/testdata/conformance/function_value_feature_closure.sysml new file mode 100644 index 000000000..f90782adc --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_feature_closure.sysml @@ -0,0 +1,11 @@ +package test { + private import ScalarValues::*; + part def Scaler { + attribute k : Real; + calc scale { in x : Real; return : Real = x * k; } + } + part twice : Scaler { attribute :>> k = 2.0; } + part thrice : Scaler { attribute :>> k = 3.0; } + calc def Fn { in calc f { in x : Real; return : Real; } in a : Real; return : Real = f(a); } + calc def UseScalers { return : Real = Fn(twice.scale, 5.0) + Fn(thrice.scale, 5.0); } +} diff --git a/internal/core/runtime/testdata/conformance/function_value_library.expected.json b/internal/core/runtime/testdata/conformance/function_value_library.expected.json new file mode 100644 index 000000000..f928f7ba1 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_library.expected.json @@ -0,0 +1,10 @@ +{ + "type": "instance", + "instantiate": "test::Holder", + "libraries": true, + "slots": { + "root": {"type": "Real", "value": 4.0}, + "floored": {"type": "Integer", "value": 2}, + "fn": {"type": "Function", "value": "RealFunctions::sqrt"} + } +} diff --git a/internal/core/runtime/testdata/conformance/function_value_library.sysml b/internal/core/runtime/testdata/conformance/function_value_library.sysml new file mode 100644 index 000000000..fe1429ffc --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_library.sysml @@ -0,0 +1,12 @@ +package test { + private import ScalarValues::*; + private import RealFunctions::*; + calc def Fn { in calc f { in v : Real; return : Real; } in a : Real; return : Real = f(a); } + calc def FnI { in calc f { in v : Real; return : Integer; } in a : Real; return : Integer = f(a); } + calc def Identity { in calc f { in v : Real; return : Real; } return r = f; } + part def Holder { + attribute root : Real = Fn(sqrt, 16.0); + attribute floored : Integer = FnI(floor, 2.75); + attribute fn = Identity(sqrt); + } +} diff --git a/internal/core/runtime/testdata/conformance/function_value_named_args.expected.json b/internal/core/runtime/testdata/conformance/function_value_named_args.expected.json new file mode 100644 index 000000000..eb50d6d28 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_named_args.expected.json @@ -0,0 +1,6 @@ +{ + "type": "calc", + "evaluate": "test::UseApply", + "libraries": true, + "result": {"type": "Real", "value": 9.0} +} diff --git a/internal/core/runtime/testdata/conformance/function_value_named_args.sysml b/internal/core/runtime/testdata/conformance/function_value_named_args.sysml new file mode 100644 index 000000000..7d4014eda --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_named_args.sysml @@ -0,0 +1,10 @@ +package test { + private import ScalarValues::*; + calc def Pow { in base : Real; in exp : Real; return : Real = base ** exp; } + calc def Apply { + in calc f { in base : Real; in exp : Real; return : Real; } + in x : Real; + return : Real = f(exp = 2.0, base = x); + } + calc def UseApply { return : Real = Apply(x = 3.0, f = Pow); } +} diff --git a/internal/core/runtime/testdata/conformance/function_value_probe.expected.json b/internal/core/runtime/testdata/conformance/function_value_probe.expected.json new file mode 100644 index 000000000..f0e3942d0 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_probe.expected.json @@ -0,0 +1,7 @@ +{ + "type": "calc", + "evaluate": "test::UseFn", + "libraries": true, + "result": {"type": "Real", "value": 9.0}, + "trace": true +} diff --git a/internal/core/runtime/testdata/conformance/function_value_probe.sysml b/internal/core/runtime/testdata/conformance/function_value_probe.sysml new file mode 100644 index 000000000..afb2dc484 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_probe.sysml @@ -0,0 +1,6 @@ +package test { + private import ScalarValues::*; + calc def Sq { in v : Real; return : Real = v * v; } + calc def Fn { in calc f { in v : Real; return : Real; } in a : Real; return : Real = f(a); } + calc def UseFn { return : Real = Fn(Sq, 3.0); } +} diff --git a/internal/core/runtime/testdata/conformance/function_value_probe.trace.golden b/internal/core/runtime/testdata/conformance/function_value_probe.trace.golden new file mode 100644 index 000000000..256616302 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_probe.trace.golden @@ -0,0 +1,20 @@ +enter calc test::UseFn + stmt return + eval feature Sq -> calc(test::Sq) + eval literal 3.0 -> 3.0 + enter calc test::Fn + bind f = calc(test::Sq) [argument] + bind a = 3.0 [argument] + stmt return + eval feature a -> 3.0 + enter calc test::Sq + bind v = 3.0 [argument] + stmt return + eval feature v -> 3.0 + eval feature v -> 3.0 + eval operator * -> 9.0 + exit calc test::Sq -> 9.0 + eval invoke f -> 9.0 + exit calc test::Fn -> 9.0 + eval invoke Fn -> 9.0 +exit calc test::UseFn -> 9.0 diff --git a/internal/core/runtime/testdata/conformance/function_value_qualified_call.expected.json b/internal/core/runtime/testdata/conformance/function_value_qualified_call.expected.json new file mode 100644 index 000000000..de9618d27 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_qualified_call.expected.json @@ -0,0 +1,6 @@ +{ + "type": "calc", + "evaluate": "test::UseQualified", + "libraries": true, + "result": {"type": "Real", "value": 26.0} +} diff --git a/internal/core/runtime/testdata/conformance/function_value_qualified_call.sysml b/internal/core/runtime/testdata/conformance/function_value_qualified_call.sysml new file mode 100644 index 000000000..0ad6e2b91 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_qualified_call.sysml @@ -0,0 +1,17 @@ +package test { + private import ScalarValues::*; + calc def Sq { in v : Real; return : Real = v * v; } + calc def Cube { in v : Real; return : Real = v * v * v; } + // A calc-typed parameter called by its qualified name applies what the run bound it to. + calc def Apply { + in calc f { in v : Real; return : Real; } + in a : Real; + return : Real = Apply::f(a) + test::Apply::f(1.0); + } + // So does a name qualified by the calc the run specializes, and by the redeclaration. + calc def Twice :> Apply { + in calc g :>> f; + return : Real = Apply::f(a) + Twice::g(a); + } + calc def UseQualified { return : Real = Apply(Sq, 3.0) + Twice(Cube, 2.0); } +} diff --git a/internal/core/runtime/testdata/conformance/function_value_read.expected.json b/internal/core/runtime/testdata/conformance/function_value_read.expected.json new file mode 100644 index 000000000..ef1594488 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_read.expected.json @@ -0,0 +1,10 @@ +{ + "type": "instance", + "instantiate": "test::Holder", + "libraries": true, + "slots": { + "fn": {"type": "Function", "value": "test::Sq"}, + "same": {"type": "Boolean", "value": true}, + "other": {"type": "Boolean", "value": false} + } +} diff --git a/internal/core/runtime/testdata/conformance/function_value_read.sysml b/internal/core/runtime/testdata/conformance/function_value_read.sysml new file mode 100644 index 000000000..a7758eae3 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_read.sysml @@ -0,0 +1,16 @@ +package test { + private import ScalarValues::*; + calc def Sq { in v : Real; return : Real = v * v; } + calc def Cube { in v : Real; return : Real = v * v * v; } + calc def Identity { in calc f { in v : Real; return : Real; } return r = f; } + calc def Same { + in calc f { in v : Real; return : Real; } + in calc g { in v : Real; return : Real; } + return : Boolean = f == g; + } + part def Holder { + attribute fn = Identity(Sq); + attribute same : Boolean = Same(Sq, Sq); + attribute other : Boolean = Same(Sq, Cube); + } +} diff --git a/internal/core/runtime/testdata/conformance/function_value_sampled.expected.json b/internal/core/runtime/testdata/conformance/function_value_sampled.expected.json new file mode 100644 index 000000000..644311e5d --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_sampled.expected.json @@ -0,0 +1,6 @@ +{ + "type": "calc", + "evaluate": "test::SquaresOf", + "libraries": true, + "result": {"type": "Sequence", "elements": [{"type": "Real", "value": 1.0}, {"type": "Real", "value": 4.0}, {"type": "Real", "value": 9.0}]} +} diff --git a/internal/core/runtime/testdata/conformance/function_value_sampled.sysml b/internal/core/runtime/testdata/conformance/function_value_sampled.sysml new file mode 100644 index 000000000..8ebe45ac7 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_sampled.sysml @@ -0,0 +1,9 @@ +package test { + private import ScalarValues::*; + private import SampledFunctions::*; + calc def Sq { in v : Real; return : Real = v * v; } + calc def SquaresOf { + attribute sampled : SampledFunction = Sample(Sq, (1.0, 2.0, 3.0)); + return : Real[0..*] = Range(sampled); + } +} diff --git a/internal/core/runtime/testdata/conformance/function_value_sampled_closure.expected.json b/internal/core/runtime/testdata/conformance/function_value_sampled_closure.expected.json new file mode 100644 index 000000000..340515046 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_sampled_closure.expected.json @@ -0,0 +1,6 @@ +{ + "type": "calc", + "evaluate": "test::ScaledDomain", + "libraries": true, + "result": {"type": "Sequence", "elements": [{"type": "Real", "value": 1.0}, {"type": "Real", "value": 2.0}, {"type": "Real", "value": 10.0}, {"type": "Real", "value": 20.0}]} +} diff --git a/internal/core/runtime/testdata/conformance/function_value_sampled_closure.sysml b/internal/core/runtime/testdata/conformance/function_value_sampled_closure.sysml new file mode 100644 index 000000000..44d85cbe5 --- /dev/null +++ b/internal/core/runtime/testdata/conformance/function_value_sampled_closure.sysml @@ -0,0 +1,13 @@ +package test { + private import ScalarValues::*; + private import SampledFunctions::*; + part def Scaler { + attribute k : Real = 10.0; + calc scale { in x : Real; return : Real = x * k; } + } + part scaler : Scaler; + calc def ScaledDomain { + attribute sampled : SampledFunction = Sample(calculation = scaler.scale, domainValues = (1.0, 2.0)); + return : Real[0..*] = (Domain(sampled), Range(sampled)); + } +} diff --git a/internal/core/runtime/trace.go b/internal/core/runtime/trace.go index cf3d80188..fbb8f1065 100644 --- a/internal/core/runtime/trace.go +++ b/internal/core/runtime/trace.go @@ -405,6 +405,8 @@ func FormatTraceValue(v Value) string { return v.CoordinateTransformation().String() case ValExpr: return fmt.Sprintf("expr(%s)", TraceLabel(v.Expr())) + case ValFunction: + return fmt.Sprintf("calc(%s)", v.FunctionName()) default: return v.Kind.String() } diff --git a/internal/core/runtime/value.go b/internal/core/runtime/value.go index ccf3208c0..5ab8093a9 100644 --- a/internal/core/runtime/value.go +++ b/internal/core/runtime/value.go @@ -34,6 +34,7 @@ const ( ValTensorQuantity // a TensorQuantityValue: an array of numbers with a measurement unit per component ValCoordinateFrame // a VectorMeasurementReference: a frame's axes, or a measurement scale's one ValCoordinateTransformation // a CoordinateTransformation: a placement of one frame in another + ValFunction // a calc as a value: its lowered shape closed over the environment it was read in // valueKindCount bounds the kinds; TestEveryValueKindIsDispatched walks them. valueKindCount @@ -102,6 +103,8 @@ func FormatValue(v Value) string { return v.CoordinateTransformation().String() case ValExpr: return "" + case ValFunction: + return v.FunctionName() default: return unknownText } @@ -165,6 +168,8 @@ func (k ValueKind) String() string { return "coordinate frame" case ValCoordinateTransformation: return "coordinate transformation" + case ValFunction: + return "function" default: return "invalid" } @@ -179,8 +184,9 @@ type Value struct { Instance int64 // ValInstance: instance ID; ValVariant: materialized object, 0 for none // ref holds the kind-specific payload of the remaining kinds: a string // (ValString), *Sequence, *Set, *exprValue (ValExpr), *Quantity, a complex128 - // (ValComplex), *Array, *Vector, *VectorQuantity, *MeasurementRef, *TensorQuantity, or the - // *symbols.Symbol of a variant (ValVariant) or enumeration literal (ValEnumLiteral). + // (ValComplex), *Array, *Vector, *VectorQuantity, *MeasurementRef, *TensorQuantity, + // *functionValue (ValFunction), or the *symbols.Symbol of a variant (ValVariant) or + // enumeration literal (ValEnumLiteral). ref any } diff --git a/internal/core/runtime/value_equality.go b/internal/core/runtime/value_equality.go index 217a1181e..645ed1961 100644 --- a/internal/core/runtime/value_equality.go +++ b/internal/core/runtime/value_equality.go @@ -20,6 +20,8 @@ type valueKey struct { colHash uint64 variant *symbols.Symbol literal *symbols.Symbol + calc *symbols.Symbol + run int64 // the body run a function closes over (Value.functionRun) } // valueKeyFunc extracts a comparable key from a Value. Values valueEqual holds @@ -77,6 +79,11 @@ func valueKeyFunc(v Value) valueKey { key.strVal = v.CoordinateFrame().key() case ValCoordinateTransformation: key.strVal = v.CoordinateTransformation().key() + case ValFunction: + key.calc, key.run = v.Function(), v.functionRun() + if self := v.FunctionSelf(); self != nil { + key.instID = self.ID + } } return key } diff --git a/internal/core/runtime/value_kinds_test.go b/internal/core/runtime/value_kinds_test.go index 2e3ec946c..2aa3deaef 100644 --- a/internal/core/runtime/value_kinds_test.go +++ b/internal/core/runtime/value_kinds_test.go @@ -34,6 +34,9 @@ func kindSamples() map[ValueKind][2]Value { return &CoordinateFrame{Dimensions: []int64{int64(len(axes))}, Axes: axes, Text: text} } spatial, temporal := frameOf("spatial", metre, metre), frameOf("temporal", second, second) + functionOf := func(name string, sym *symbols.Symbol) Value { + return Value{Kind: ValFunction, ref: &functionValue{shape: &calcShape{Sym: sym, Name: name}}} + } placementOf := func(source, target *CoordinateFrame, origin int64) *CoordinateTransformation { return &CoordinateTransformation{Source: source, Target: target, Placement: &FramePlacement{ Origin: NewVectorQuantityValue([]semantics.Value{integerValue(origin).Const, integerValue(0).Const}, source.Axes), @@ -62,6 +65,7 @@ func kindSamples() map[ValueKind][2]Value { NewTensorQuantityValue([]int64{2, 2}, []semantics.Value{integerValue(1).Const, integerValue(2).Const, integerValue(3).Const, integerValue(4).Const}, []Unit{second, second, second, second}), }, ValMeasurementRef: {NewMeasurementRefValue(metre), NewMeasurementRefValue(second)}, + ValFunction: {functionOf("a", symA), functionOf("b", symB)}, ValCoordinateFrame: {NewCoordinateFrameValue(spatial), NewCoordinateFrameValue(temporal)}, ValCoordinateTransformation: { NewCoordinateTransformationValue(placementOf(spatial, temporal, 1)), @@ -70,6 +74,34 @@ func kindSamples() map[ValueKind][2]Value { } } +// A function read against the same object is one value however often it is +// read; one closing over a body's bindings is one value with every read within +// the same run of the body, and another with a read in a different run, since +// two runs bind the names it closes over differently. +func TestFunctionValueIdentity(t *testing.T) { + sym := &symbols.Symbol{Name: "inner"} + shape := &calcShape{Sym: sym, Name: "inner"} + self := &Instance{ID: 7} + bare := func() Value { return Value{Kind: ValFunction, ref: &functionValue{shape: shape, self: self}} } + closing := func(run int64) Value { + enclosing := []frame{{vars: map[string]Value{"k": integerValue(1)}, run: run}} + return Value{Kind: ValFunction, ref: &functionValue{shape: shape, self: self, enclosing: enclosing}} + } + if a, b := bare(), bare(); !valueEqual(a, b) || valueKeyFunc(a) != valueKeyFunc(b) { + t.Errorf("two reads of %s against one object are not one value", FormatValue(a)) + } + c := closing(1) + if again := closing(1); !valueEqual(c, again) || valueKeyFunc(c) != valueKeyFunc(again) { + t.Errorf("two reads of %s within one run are not one value", FormatValue(c)) + } + if d := closing(2); valueEqual(c, d) || valueKeyFunc(c) == valueKeyFunc(d) { + t.Errorf("reads of %s in two runs compare equal", FormatValue(c)) + } + if b := bare(); valueEqual(b, c) || valueEqual(c, b) || valueKeyFunc(b) == valueKeyFunc(c) { + t.Errorf("a body-closing read of %s equals a bare one", FormatValue(c)) + } +} + // TestEveryValueKindIsDispatched walks every ValueKind through the surfaces that // switch on it — its name, its renderings, its description, equality and set // keying — so a new kind cannot fall through to a fallback arm unnoticed. diff --git a/internal/grpc/analysis.go b/internal/grpc/analysis.go index b1ae83f8f..de57c4545 100644 --- a/internal/grpc/analysis.go +++ b/internal/grpc/analysis.go @@ -128,7 +128,7 @@ func (v *verifyContext) analysisArgument(arg *pb.Value) (runtime.Value, *pb.RunA if err := v.service.requireValueCapabilities(arg); err != nil { return runtime.Value{}, nil, err } - val, err := ProtoToValueIn(arg, v.cached.Index, v.sem) + val, err := ProtoToRuntimeValue(v.runtime, arg, v.cached.Index, v.sem) if err != nil { return runtime.Value{}, &pb.RunAnalysisResponse{ Error: fmt.Sprintf("analysis argument could not be read: %v", err), diff --git a/internal/grpc/capability_response.go b/internal/grpc/capability_response.go index 50346189f..de9a0f0ab 100644 --- a/internal/grpc/capability_response.go +++ b/internal/grpc/capability_response.go @@ -93,6 +93,10 @@ func (s *Service) filterValueCapabilities(value *pb.Value) { shown := displayValue(value) value.Kind = &pb.Value_Null{Null: "unsupported: " + shown.Kind.String() + " " + runtime.FormatValue(shown)} } + case *pb.Value_Function: + if !s.capabilities.has(CapabilityFunctionValues) { + value.Kind = &pb.Value_Null{Null: "unsupported: " + runtime.ValFunction.String() + " " + kind.Function.GetCalcId()} + } case *pb.Value_Infinity: if !s.capabilities.has(CapabilityInfinityValue) { value.Kind = &pb.Value_Null{Null: "unsupported: unbounded value *"} diff --git a/internal/grpc/convert.go b/internal/grpc/convert.go index 26a14215e..62f3ee888 100644 --- a/internal/grpc/convert.go +++ b/internal/grpc/convert.go @@ -322,6 +322,11 @@ func ValueToProtoIn(rt *runtime.Context, val runtime.Value, idx *symbols.Index) return &pb.Value{Kind: &pb.Value_VectorQuantity{VectorQuantity: pvq}} case runtime.ValMeasurementRef: return &pb.Value{Kind: &pb.Value_MeasurementRef{MeasurementRef: MeasurementRefToProto(val.MeasurementRef())}} + case runtime.ValFunction: + if val.FunctionClosesOverBody() { + return &pb.Value{Kind: &pb.Value_Null{Null: "unsupported: function " + val.FunctionName() + " closing over a body's bindings"}} + } + return &pb.Value{Kind: &pb.Value_Function{Function: functionToProto(val, idx)}} case runtime.ValTensorQuantity: // No wire arm carries dimensions above one with a unit per component. return &pb.Value{Kind: &pb.Value_Null{Null: "unsupported: " + val.Kind.String() + " " + runtime.FormatValue(val)}} @@ -333,6 +338,19 @@ func ValueToProtoIn(rt *runtime.Context, val runtime.Value, idx *symbols.Index) } } +// functionToProto names a function by the calc declaration it is a value of and +// the object it closes over, if any. +func functionToProto(val runtime.Value, idx *symbols.Index) *pb.Function { + fn := &pb.Function{CalcId: val.FunctionName()} + if idx != nil && val.Function() != nil { + fn.CalcId = idx.GetFQN(val.Function()) + } + if self := val.FunctionSelf(); self != nil { + fn.SelfId = self.ID + } + return fn +} + // enumLiteralToProto names a literal by the declaration it is, which is its // identity, and by the enumeration declaring it. Nil for an unresolved literal. func enumLiteralToProto(val runtime.Value, idx *symbols.Index) *pb.EnumLiteral { @@ -501,6 +519,14 @@ var ( // ErrUnitIDMismatch reports a unit_id whose declaration the unit text sent // with it does not spell, so the two name different units. ErrUnitIDMismatch = errors.New("unit as written does not spell unit_id") + + // ErrFunctionNeedsRuntime reports a Function read with no runtime to bind + // its calc in: only a runtime can turn a calc's name into a callable value. + ErrFunctionNeedsRuntime = errors.New("function needs a runtime to bind its calc") + + // ErrFunctionUnbound reports a Function naming no calc of the model read as + // a function, or an object the runtime does not hold. + ErrFunctionUnbound = errors.New("function names no calc of this model") ) // ValueCarriesMeasurementRef reports whether a value, or any value nested in @@ -512,6 +538,15 @@ func ValueCarriesMeasurementRef(pv *pb.Value) bool { }) } +// ValueCarriesFunction reports whether a value, or any value nested in it, is a +// Function: the kind function_values governs. +func ValueCarriesFunction(pv *pb.Value) bool { + return valueCarries(pv, func(v *pb.Value) bool { + _, ok := v.GetKind().(*pb.Value_Function) + return ok + }) +} + // ValueCarriesInfinity reports whether a value, or any value nested in it, is // the unbounded value: the kind infinity_value governs. func ValueCarriesInfinity(pv *pb.Value) bool { @@ -571,8 +606,16 @@ func nestedValues(pv *pb.Value) []*pb.Value { // ProtoToValueIn converts a protobuf Value to a runtime.Value in the model idx // and sem describe, resolving a quantity's base units against them. Inverse of -// ValueToProto. +// ValueToProto. A function, which only a runtime can bind, is refused: read +// one with ProtoToRuntimeValue. func ProtoToValueIn(pv *pb.Value, idx *symbols.Index, sem *semantics.Model) (runtime.Value, error) { + return ProtoToRuntimeValue(nil, pv, idx, sem) +} + +// ProtoToRuntimeValue is ProtoToValueIn for a value bound for the runtime rt, +// which is what a function's calc is bound in and its object looked up in; +// rt may be nil for a value naming no function. +func ProtoToRuntimeValue(rt *runtime.Context, pv *pb.Value, idx *symbols.Index, sem *semantics.Model) (runtime.Value, error) { if pv == nil { return runtime.Value{Kind: runtime.ValNull}, nil } @@ -583,11 +626,13 @@ func ProtoToValueIn(pv *pb.Value, idx *symbols.Index, sem *semantics.Model) (run return ProtoToQuantity(k.Quantity, idx, sem) case *pb.Value_EnumLiteral: return enumLiteralFromProto(k.EnumLiteral, idx) + case *pb.Value_Function: + return functionFromProto(rt, k.Function, idx) case *pb.Value_Sequence: seq := runtime.NewSequence() if k.Sequence != nil { for _, elem := range k.Sequence.Elements { - val, err := ProtoToValueIn(elem, idx, sem) + val, err := ProtoToRuntimeValue(rt, elem, idx, sem) if err != nil { return runtime.Value{}, err } @@ -596,7 +641,7 @@ func ProtoToValueIn(pv *pb.Value, idx *symbols.Index, sem *semantics.Model) (run } return runtime.NewSequenceValue(seq), nil case *pb.Value_Array: - return protoToArray(k.Array, idx, sem) + return protoToArray(rt, k.Array, idx, sem) case *pb.Value_Vector: return protoToVector(k.Vector) case *pb.Value_VectorQuantity: @@ -613,16 +658,45 @@ func ProtoToValueIn(pv *pb.Value, idx *symbols.Index, sem *semantics.Model) (run } } +// functionFromProto binds a function to the calc its calc_id names in rt's +// model, read as a value in its own scope. Objects live only within the call that +// created them, so a self_id names none of this call's: it is refused rather +// than matched to whichever object this call happened to number the same. +func functionFromProto(rt *runtime.Context, fn *pb.Function, idx *symbols.Index) (runtime.Value, error) { + if fn == nil || fn.GetCalcId() == "" { + return runtime.Value{}, fmt.Errorf("%w: calc_id is empty", ErrFunctionUnbound) + } + if rt == nil || idx == nil { + return runtime.Value{}, fmt.Errorf("%w: function %s", ErrFunctionNeedsRuntime, fn.GetCalcId()) + } + if fn.GetSelfId() != 0 { + return runtime.Value{}, fmt.Errorf( + "%w: %s: self_id %d names no object of this call: an object lives only within the response that created it", + ErrFunctionUnbound, fn.GetCalcId(), fn.GetSelfId()) + } + for _, sym := range idx.LookupQualified(fn.GetCalcId()) { + val, isFunction, err := rt.FunctionValue(sym) + if !isFunction { + continue + } + if err != nil { + return runtime.Value{}, fmt.Errorf("%w: %s: %v", ErrFunctionUnbound, fn.GetCalcId(), err) + } + return val, nil + } + return runtime.Value{}, fmt.Errorf("%w: %s is not a calc", ErrFunctionUnbound, fn.GetCalcId()) +} + // protoToArray rebuilds an array, refusing a shape its elements do not fill // rather than reading them under some other shape. -func protoToArray(pa *pb.Array, idx *symbols.Index, sem *semantics.Model) (runtime.Value, error) { +func protoToArray(rt *runtime.Context, pa *pb.Array, idx *symbols.Index, sem *semantics.Model) (runtime.Value, error) { dimensions := slices.Clone(pa.GetDimensions()) if err := CheckArrayShape(dimensions, len(pa.GetElements())); err != nil { return runtime.Value{}, err } elements := make([]runtime.Value, 0, len(pa.GetElements())) for _, elem := range pa.GetElements() { - val, err := ProtoToValueIn(elem, idx, sem) + val, err := ProtoToRuntimeValue(rt, elem, idx, sem) if err != nil { return runtime.Value{}, err } diff --git a/internal/grpc/convert_function_test.go b/internal/grpc/convert_function_test.go new file mode 100644 index 000000000..99d2801e5 --- /dev/null +++ b/internal/grpc/convert_function_test.go @@ -0,0 +1,384 @@ +package grpc + +import ( + "context" + "errors" + "strings" + "testing" + + "connectrpc.com/connect" + + pb "github.com/Open-MBEE/OpenSysML/api/proto" + "github.com/Open-MBEE/OpenSysML/internal/core/runtime" +) + +// functionWireModel yields calcs as values — a definition, a usage with an +// unsupplied input, one read off a part, and one closing over a body — and +// takes one back as a calc argument and an action input that apply it. +const functionWireModel = ` +package F { + private import ScalarValues::*; + + calc def Unary { in v : Real; return : Real; } + calc def Sq :> Unary { in :>> v; return : Real = v * v; } + calc def Cube :> Unary { in :>> v; return : Real = v * v * v; } + calc def Apply { in calc f : Unary; in a : Real; return : Real = f(a); } + calc apply : Apply; + calc sq : Sq; + calc def PickSq { return : Unary = sq; } + calc pickSq : PickSq; + attribute fns = (Sq, Cube); + + part def Holder { + attribute k : Real = 2.0; + calc scale :> Unary { in :>> v; return : Real = v * k; } + } + part holder : Holder; + part def Other { attribute k : Real = 100.0; } + part other : Other; + analysis def ApplyCase { + subject s : Other; + in calc f : Unary; + in a : Real; + out y : Real = f(a); + } + + calc def Outer { + in k : Real; + calc inner :> Unary { in :>> v; return : Real = v * k; } + return : Unary = inner; + } + calc outer : Outer; + + calc def Mul { in a : Real; in b : Real; return : Real = a * b; } + calc def Fixed { in k : Real; calc inner : Sq; return : Unary = inner; } + calc fixed : Fixed; + calc def Scaled { in k : Real; calc inner : Mul { in :>> b = k; } return : Mul = inner; } + calc scaled : Scaled; + + action run { + in calc f { in v : Real; return : Real; } + in a : Real; + out y : Real; + first start; + action inner { assign y := f(a); } + then done; + succession first start then inner; + } +} +` + +// mustEvaluateIn evaluates expr with the subject named instantiated as self. +func mustEvaluateIn(t *testing.T, srv *Service, modelHash, subject, expr string) *pb.Value { + t.Helper() + resp, err := srv.Evaluate(context.Background(), &pb.EvaluateRequest{ModelHash: modelHash, SubjectSymbolId: subject, Expression: expr}) + if err != nil { + t.Fatalf("Evaluate(%s in %s): %v", expr, subject, err) + } + if resp.Error != "" { + t.Fatalf("Evaluate(%s in %s): %s", expr, subject, resp.Error) + } + return resp.Result +} + +func functionValue(calcID string, selfID int64) *pb.Value { + return &pb.Value{Kind: &pb.Value_Function{Function: &pb.Function{CalcId: calcID, SelfId: selfID}}} +} + +// A calc held as a value crosses as the declaration it is a value of; a client +// echoing what the service sent reads back the same function, which applies. +func TestFunctionRoundTrip(t *testing.T) { + ctx := context.Background() + srv := mustNewService(t, 4) + modelHash := mustParse(t, srv, functionWireModel) + cached, ok := srv.cache.Get(modelHash) + if !ok { + t.Fatal("parsed model is not cached") + } + idx, sem := cached.Index, NewSymbolContext(cached.Index).Semantics + + for expr, want := range map[string]string{ + "F::Sq": "F::Sq", + "F::sq": "F::sq", + "F::pickSq": "F::sq", + "F::apply": "F::apply", + } { + pv := mustEvaluate(t, srv, modelHash, expr) + fn := pv.GetFunction() + if fn == nil { + t.Fatalf("%s crossed as %T: %v", expr, pv.GetKind(), pv) + } + if fn.GetCalcId() != want || fn.GetSelfId() != 0 { + t.Errorf("%s = %v, want calc_id %q closing over no object", expr, fn, want) + } + + rt, _, release := srv.newRuntime(cached) + back, err := ProtoToRuntimeValue(rt, pv, idx, sem) + if err != nil { + release() + t.Fatalf("ProtoToRuntimeValue(%s): %v", expr, err) + } + if back.Kind != runtime.ValFunction || runtime.FormatValue(back) != want { + t.Errorf("%s read back as %s %s, want the function %s", expr, back.Kind, runtime.FormatValue(back), want) + } + release() + } + + fns := mustEvaluate(t, srv, modelHash, "F::fns") + elems := fns.GetSequence().GetElements() + if len(elems) != 2 || elems[0].GetFunction().GetCalcId() != "F::Sq" || elems[1].GetFunction().GetCalcId() != "F::Cube" { + t.Fatalf("F::fns = %v, want a sequence of the functions Sq and Cube", fns) + } + + // A function read in applies as the argument of a calc and an action. + calc, err := srv.EvaluateCalc(ctx, &pb.EvaluateCalcRequest{ModelHash: modelHash, SymbolId: "F::apply", Arguments: []*pb.Value{functionValue("F::Sq", 0), realValue(3)}}) + if err != nil || calc.Error != "" { + t.Fatalf("EvaluateCalc(apply): err = %v, error = %q", err, calc.GetError()) + } + if got := calc.Result.GetRealValue(); got != 9 { + t.Errorf("apply(Sq, 3.0) = %v, want 9.0", calc.Result) + } + act, err := srv.ExecuteAction(ctx, &pb.ExecuteActionRequest{ + ModelHash: modelHash, + ActionSymbolId: "F::run", + Inputs: map[string]*pb.Value{"f": functionValue("F::Cube", 0), "a": realValue(2)}, + }) + if err != nil || act.Error != "" { + t.Fatalf("ExecuteAction: err = %v, error = %q", err, act.GetError()) + } + if y := act.Outputs["y"].GetRealValue(); y != 8 { + t.Errorf("output y = %v, want 8.0", act.Outputs["y"]) + } + sweep, err := srv.RunSweep(ctx, &pb.RunSweepRequest{ + ModelHash: modelHash, SymbolId: "F::apply", + NamedArguments: map[string]*pb.Value{"f": functionValue("F::Sq", 0)}, + Ranges: []*pb.SweepRange{{Parameter: "a", Start: realValue(1), End: realValue(3), Step: realValue(1)}}, + }) + if err != nil || sweep.Error != "" { + t.Fatalf("RunSweep(apply over a, f = Sq): err = %v, error = %q", err, sweep.GetError()) + } + if got, want := rowText(sweep), "a=1.0 result -> 1.0\na=2.0 result -> 4.0\na=3.0 result -> 9.0"; got != want { + t.Errorf("sweep rows are\n%s\nwant\n%s", got, want) + } + + // A function closing over a body's bindings has no wire form: it is named + // as unsupported rather than sent as a calc it could not be rebuilt from. + closed := mustEvaluate(t, srv, modelHash, "F::outer(3.0)") + if closed.GetFunction() != nil || !strings.Contains(closed.GetNull(), "unsupported: function F::Outer::inner") { + t.Errorf("F::outer(3.0) = %v, want an unsupported null naming F::Outer::inner", closed) + } + // So does one whose inherited body reads nothing of the run, but whose own default does. + scaled := mustEvaluate(t, srv, modelHash, "F::scaled(3.0)") + if scaled.GetFunction() != nil || !strings.Contains(scaled.GetNull(), "unsupported: function F::Scaled::inner") { + t.Errorf("F::scaled(3.0) = %v, want an unsupported null naming F::Scaled::inner", scaled) + } + + // A nested usage reading nothing of the run that returned it crosses as the + // calc it names, and comes back applying that calc's inherited body. + fixed := mustEvaluate(t, srv, modelHash, "F::fixed(3.0)") + if fixed.GetFunction().GetCalcId() != "F::Fixed::inner" || fixed.GetFunction().GetSelfId() != 0 { + t.Fatalf("F::fixed(3.0) = %v, want the calc F::Fixed::inner closing over no object", fixed) + } + applied, err := srv.EvaluateCalc(ctx, &pb.EvaluateCalcRequest{ + ModelHash: modelHash, SymbolId: "F::apply", Arguments: []*pb.Value{fixed, realValue(3)}, + }) + if err != nil || applied.Error != "" { + t.Fatalf("EvaluateCalc(apply, fixed inner): %v %s", err, applied.GetError()) + } + if got := applied.Result.GetRealValue(); got != 9 { + t.Errorf("apply(F::Fixed::inner, 3.0) = %v, want 9.0", applied.Result) + } +} + +// A calc usage read off an object crosses with the object's id, which lives as +// long as the response; sent back to any later call, it is refused rather than +// matched to whatever object that call numbers the same. The object-bound calc +// applies within the one call that reads it off its object. +func TestObjectBoundFunctionsDoNotCrossCalls(t *testing.T) { + ctx := context.Background() + srv := mustNewService(t, 4) + modelHash := mustParse(t, srv, functionWireModel) + + scale := mustEvaluateIn(t, srv, modelHash, "F::holder", "scale") + if scale.GetFunction().GetCalcId() != "F::Holder::scale" || scale.GetFunction().GetSelfId() == 0 { + t.Fatalf("holder.scale = %v, want the calc F::Holder::scale closing over holder", scale) + } + refused := func(what, msg string) { + t.Helper() + if !strings.Contains(msg, "self_id") || !strings.Contains(msg, "lives only within the response") { + t.Errorf("%s with holder.scale: error %q, want the self_id refused as outliving its response", what, msg) + } + } + + calc, err := srv.EvaluateCalc(ctx, &pb.EvaluateCalcRequest{ModelHash: modelHash, SymbolId: "F::apply", Arguments: []*pb.Value{scale, realValue(3)}}) + if err != nil || calc.Result != nil { + t.Fatalf("EvaluateCalc(apply, holder.scale): err = %v, result = %v, want an in-body refusal", err, calc.GetResult()) + } + refused("EvaluateCalc", calc.Error) + + act, err := srv.ExecuteAction(ctx, &pb.ExecuteActionRequest{ + ModelHash: modelHash, ActionSymbolId: "F::run", Inputs: map[string]*pb.Value{"f": scale, "a": realValue(2)}, + }) + if err != nil || len(act.Outputs) != 0 { + t.Fatalf("ExecuteAction(run, holder.scale): err = %v, outputs = %v, want an in-body refusal", err, act.GetOutputs()) + } + refused("ExecuteAction", act.Error) + + // An analysis instantiates its subject before reading its arguments, so this + // call holds an object numbered as holder was; the function does not bind to it. + an, err := srv.RunAnalysis(ctx, &pb.RunAnalysisRequest{ + ModelHash: modelHash, SymbolId: "F::ApplyCase", SubjectSymbolId: "F::other", Arguments: []*pb.Value{scale, realValue(3)}, + }) + if err != nil || len(an.Outputs) != 0 { + t.Fatalf("RunAnalysis(ApplyCase over other, holder.scale): err = %v, outputs = %v, want an in-body refusal", err, an.GetOutputs()) + } + refused("RunAnalysis", an.Error) + + // Within one call, the object is read and the calc applied over it. + if got := mustEvaluate(t, srv, modelHash, "F::apply(F::holder.scale, 3.0)").GetRealValue(); got != 6 { + t.Errorf("apply(holder.scale, 3.0) = %v, want 6.0", got) + } +} + +// A function naming no calc of the model, an object the runtime does not +// hold, or read with no runtime to bind it in, is refused with a typed error. +func TestMalformedFunctionsAreRejected(t *testing.T) { + ctx := context.Background() + srv := mustNewService(t, 4) + modelHash := mustParse(t, srv, functionWireModel) + cached, _ := srv.cache.Get(modelHash) + idx, sem := cached.Index, NewSymbolContext(cached.Index).Semantics + rt, _, release := srv.newRuntime(cached) + + cases := []struct { + name string + val *pb.Value + want error + }{ + {"empty", functionValue("", 0), ErrFunctionUnbound}, + {"unknown declaration", functionValue("F::Nope", 0), ErrFunctionUnbound}, + {"declaration that is not a calc", functionValue("F::holder", 0), ErrFunctionUnbound}, + {"calc usage computing a result", functionValue("F::pickSq", 0), ErrFunctionUnbound}, + {"object of another call", functionValue("F::Sq", 12345), ErrFunctionUnbound}, + {"nested in a sequence", &pb.Value{Kind: &pb.Value_Sequence{Sequence: &pb.ValueSequence{Elements: []*pb.Value{ + intValue(1), functionValue("F::Nope", 0), + }}}}, ErrFunctionUnbound}, + {"nested in an array", arrayValue([]int64{1}, functionValue("", 0)), ErrFunctionUnbound}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + val, err := ProtoToRuntimeValue(rt, tc.val, idx, sem) + if !errors.Is(err, tc.want) { + t.Fatalf("ProtoToRuntimeValue = %v, %v; want %v", val, err, tc.want) + } + if val.Kind != runtime.ValInvalid { + t.Errorf("a rejected value was still returned: %v", val) + } + }) + } + + for name, val := range map[string]*pb.Value{ + "bare": functionValue("F::Sq", 0), + "in array": arrayValue([]int64{1}, functionValue("F::Sq", 0)), + } { + if _, err := ProtoToValueIn(val, idx, sem); !errors.Is(err, ErrFunctionNeedsRuntime) { + t.Errorf("%s without a runtime: err = %v, want %v", name, err, ErrFunctionNeedsRuntime) + } + } + release() + + // Over the service, a malformed argument is an in-band error, as a + // malformed quantity is; a function where a number is due is a type error. + calc, err := srv.EvaluateCalc(ctx, &pb.EvaluateCalcRequest{ModelHash: modelHash, SymbolId: "F::apply", Arguments: []*pb.Value{functionValue("F::Nope", 0), realValue(3)}}) + if err != nil { + t.Fatalf("EvaluateCalc(unknown): %v", err) + } + if !strings.Contains(calc.Error, ErrFunctionUnbound.Error()) { + t.Errorf("EvaluateCalc(unknown) error = %q, want one naming %v", calc.Error, ErrFunctionUnbound) + } + calc, err = srv.EvaluateCalc(ctx, &pb.EvaluateCalcRequest{ModelHash: modelHash, SymbolId: "F::apply", Arguments: []*pb.Value{realValue(3), realValue(3)}}) + if err != nil { + t.Fatalf("EvaluateCalc(scalar as function): %v", err) + } + if !strings.Contains(calc.Error, runtime.ErrTypeMismatch.Error()) { + t.Errorf("EvaluateCalc(scalar as function) error = %q, want one naming %v", calc.Error, runtime.ErrTypeMismatch) + } +} + +// The arm is advertised under its own capability; a service withholding it +// names the function as unsupported and refuses one sent to it. +func TestFunctionCapability(t *testing.T) { + ctx := context.Background() + found := false + for _, c := range Capabilities() { + found = found || c == CapabilityFunctionValues + } + if !found { + t.Errorf("capabilities %v do not include %q", Capabilities(), CapabilityFunctionValues) + } + + withheld := mustNewServiceWithout(t, CapabilityFunctionValues) + modelHash := mustParse(t, withheld, functionWireModel) + for expr, want := range map[string]string{ + "F::Sq": "unsupported: function F::Sq", + "F::pickSq": "unsupported: function F::sq", + } { + got := mustEvaluate(t, withheld, modelHash, expr) + if got.GetFunction() != nil { + t.Errorf("%s crossed as a function without %s: %v", expr, CapabilityFunctionValues, got) + } + if got.GetNull() != want { + t.Errorf("%s without %s = %v, want null %q", expr, CapabilityFunctionValues, got, want) + } + } + fns := mustEvaluate(t, withheld, modelHash, "F::fns") + for i, want := range []string{"F::Sq", "F::Cube"} { + if got := fns.GetSequence().GetElements()[i].GetNull(); got != "unsupported: function "+want { + t.Errorf("F::fns#%d without %s = %q", i+1, CapabilityFunctionValues, got) + } + } + + sq := functionValue("F::Sq", 0) + nested := &pb.Value{Kind: &pb.Value_Sequence{Sequence: &pb.ValueSequence{Elements: []*pb.Value{sq}}}} + for name, input := range map[string]*pb.Value{"function": sq, "nested": nested} { + _, err := withheld.ExecuteAction(ctx, &pb.ExecuteActionRequest{ + ModelHash: modelHash, + ActionSymbolId: "F::run", + Inputs: map[string]*pb.Value{"f": input, "a": realValue(2)}, + }) + if connect.CodeOf(err) != connect.CodeUnimplemented || !strings.Contains(err.Error(), CapabilityFunctionValues) { + t.Errorf("ExecuteAction with %s input without %s: err = %v, want UNIMPLEMENTED naming the capability", name, CapabilityFunctionValues, err) + } + _, err = withheld.EvaluateCalc(ctx, &pb.EvaluateCalcRequest{ModelHash: modelHash, SymbolId: "F::apply", Arguments: []*pb.Value{input, realValue(3)}}) + if connect.CodeOf(err) != connect.CodeUnimplemented || !strings.Contains(err.Error(), CapabilityFunctionValues) { + t.Errorf("EvaluateCalc with %s argument without %s: err = %v, want UNIMPLEMENTED naming the capability", name, CapabilityFunctionValues, err) + } + } +} + +func TestValueCarriesFunction(t *testing.T) { + one := intValue(1) + sq := functionValue("F::Sq", 0) + sequence := func(elements ...*pb.Value) *pb.Value { + return &pb.Value{Kind: &pb.Value_Sequence{Sequence: &pb.ValueSequence{Elements: elements}}} + } + for _, tc := range []struct { + name string + value *pb.Value + want bool + }{ + {"nil", nil, false}, + {"int", one, false}, + {"function", sq, true}, + {"sequence of ints", sequence(one, one), false}, + {"sequence with a function", sequence(one, sequence(sq)), true}, + {"array of functions", arrayValue([]int64{1}, sq), true}, + } { + if got := ValueCarriesFunction(tc.value); got != tc.want { + t.Errorf("ValueCarriesFunction(%s) = %v, want %v", tc.name, got, tc.want) + } + } + if ValueCarriesStructured(sq) { + t.Error("a bare function is not a structured value") + } +} diff --git a/internal/grpc/service.go b/internal/grpc/service.go index b35332ad5..d08827ce9 100644 --- a/internal/grpc/service.go +++ b/internal/grpc/service.go @@ -104,6 +104,11 @@ const CapabilityStructuredValues = "structured_values" // unsupported null. Distinct from structured_values, which predates the arm. const CapabilityMeasurementRefs = "measurement_refs" +// CapabilityFunctionValues names the capability of carrying a calc held as a +// value as Value.function, named by its declaration, rather than reporting it +// as an unsupported null. +const CapabilityFunctionValues = "function_values" + // CapabilityVerificationVerdicts names the capability of reporting what the body // of a verification case answered as VerificationVerdict, and of running a // verification case through RunAnalysis. @@ -122,7 +127,7 @@ var capabilities = []string{ CapabilityApplyEdits, CapabilityAuthoring, CapabilityInlineLanguage, CapabilityStrictConformance, CapabilityDocumentQuery, CapabilityRenderDocument, CapabilityParseSources, CapabilityComplexValues, CapabilityStructuredValues, - CapabilityMeasurementRefs, CapabilityVerificationVerdicts, + CapabilityMeasurementRefs, CapabilityFunctionValues, CapabilityVerificationVerdicts, CapabilityInfinityValue, } @@ -278,6 +283,11 @@ func (s *Service) requireValueCapabilities(pv *pb.Value) error { return err } } + if ValueCarriesFunction(pv) { + if err := s.requireCapability(CapabilityFunctionValues); err != nil { + return err + } + } if ValueCarriesInfinity(pv) { return s.requireCapability(CapabilityInfinityValue) } @@ -732,7 +742,7 @@ func (s *Service) ExecuteAction(ctx context.Context, req *pb.ExecuteActionReques if err := s.requireValueCapabilities(pv); err != nil { return nil, err } - val, cerr := ProtoToValueIn(pv, cached.Index, semModel) + val, cerr := ProtoToRuntimeValue(runtimeCtx, pv, cached.Index, semModel) if cerr != nil { return &pb.ExecuteActionResponse{ Error: fmt.Sprintf("input %q could not be read: %v", name, cerr), diff --git a/internal/grpc/sweep.go b/internal/grpc/sweep.go index 7ce7e7278..2d3b00bb2 100644 --- a/internal/grpc/sweep.go +++ b/internal/grpc/sweep.go @@ -189,13 +189,13 @@ func endpointName(r *pb.SweepRange) string { return "end" } -// sweepValue reads one value off the wire against the model's index, so a -// quantity keeps the units it is commensurable with. +// sweepValue reads one value off the wire against the model's index and the +// run's runtime, so a quantity keeps its units and a function binds its calc. func (v *verifyContext) sweepValue(val *pb.Value, what string) (runtime.Value, *pb.RunSweepResponse, error) { if err := v.service.requireValueCapabilities(val); err != nil { return runtime.Value{}, nil, err } - out, err := ProtoToValueIn(val, v.cached.Index, v.sem) + out, err := ProtoToRuntimeValue(v.runtime, val, v.cached.Index, v.sem) if err != nil { return runtime.Value{}, &pb.RunSweepResponse{ Error: fmt.Sprintf("sweep %s could not be read: %v", what, err), diff --git a/internal/grpc/verify.go b/internal/grpc/verify.go index 2c005ae06..f0739c0f7 100644 --- a/internal/grpc/verify.go +++ b/internal/grpc/verify.go @@ -361,7 +361,7 @@ func (s *Service) EvaluateCalc(ctx context.Context, req *pb.EvaluateCalcRequest) if err := s.requireValueCapabilities(arg); err != nil { return nil, err } - val, cerr := ProtoToValueIn(arg, v.cached.Index, v.sem) + val, cerr := ProtoToRuntimeValue(v.runtime, arg, v.cached.Index, v.sem) if cerr != nil { return &pb.EvaluateCalcResponse{ Error: fmt.Sprintf("calc argument could not be read: %v", cerr), diff --git a/internal/repl/compile_test.go b/internal/repl/compile_test.go index b2ba4788c..ba1ad7273 100644 --- a/internal/repl/compile_test.go +++ b/internal/repl/compile_test.go @@ -424,6 +424,8 @@ func TestCompileRefusesWhatItCannotCompile(t *testing.T) { {"MixedEquality", "a Integer[0..*] at the left operand of '==', which holds Real[0..*]"}, {"MixedSame", "same over Integer and Real collections"}, {"MixedUnion", "union over Integer and Real collections"}, + {"CalcParam", "parameter f binds a function value"}, + {"FunctionArgument", "parameter f binds a function value"}, } { _, err := s.CompileCalc("Refused::" + tc.calc) if err == nil { diff --git a/internal/repl/evalin_test.go b/internal/repl/evalin_test.go index 481cc469b..f0cc4c2b0 100644 --- a/internal/repl/evalin_test.go +++ b/internal/repl/evalin_test.go @@ -238,9 +238,10 @@ func TestEvalInDeclarationScopeChainOverValuelessOperandStillResolvesItsMembers( wants(t, run(t, s, "%eval in car : wheels.radius"), "✓ wheels.radius (in car)", "= "+runtime.UnsetText) } -// A KerML type declaration — a class, struct, behavior, datatype or function — -// is a type, not a feature: reading one in declaration scope is the error a -// definition gets, never unset. +// A KerML type declaration — a class, struct, behavior or datatype — is a type, +// not a feature: reading one in declaration scope is the error a definition +// gets, never unset. A function is the one type that is a value: reading it +// denotes the function itself. func TestEvalInDeclarationScopeDoesNotReadTypeDeclarationsAsUnset(t *testing.T) { s := NewSession() if errs := errorDiagnostics(s.Submit(`private import ScalarValues::*; @@ -255,11 +256,14 @@ package K { }`).Diagnostics); len(errs) > 0 { t.Fatalf("model has errors: %v", errs) } - for _, name := range []string{"Vehicle", "Frame", "Drive", "Mass", "Twice", "Car"} { + for _, name := range []string{"Vehicle", "Frame", "Drive", "Mass", "Car"} { got := run(t, s, "%eval in K::car : "+name) wants(t, got, "error", "cannot evaluate definition "+name) rejects(t, got, "✓", "= "+runtime.UnsetText, "unresolved reference", "no value") } + got := run(t, s, "%eval in K::car : Twice") + wants(t, got, "✓ Twice (in K::car)", "= K::Twice") + rejects(t, got, "error", "= "+runtime.UnsetText) wants(t, run(t, s, "%eval in K::car : unsetMass"), "✓ unsetMass (in K::car)", "= "+runtime.UnsetText) } diff --git a/internal/repl/meta.go b/internal/repl/meta.go index e5848521b..1a4f18493 100644 --- a/internal/repl/meta.go +++ b/internal/repl/meta.go @@ -789,6 +789,16 @@ func (s *Session) evalExpr(expr string) ([]string, error) { fmt.Sprintf(" = %s", formatValue(ctx, val)), }, nil } + // A calc definition is the function it declares. + if val, isFunction, err := ctx.FunctionValue(sym); isFunction { + if err != nil { + return nil, fmt.Errorf("evaluation failed: %w", err) + } + return []string{ + fmt.Sprintf("✓ %s", expr), + fmt.Sprintf(" = %s", formatValue(ctx, val)), + }, nil + } // A valueless usage may still name a value its own features shape. if _, isUsage := sym.Decl.(*ast.Usage); !isUsage && !declaresValue(sym) { return nil, fmt.Errorf("%q has no value to evaluate", expr) diff --git a/internal/repl/testdata/compile_calcs.sysml b/internal/repl/testdata/compile_calcs.sysml index 4c3a3721c..05b5875b0 100644 --- a/internal/repl/testdata/compile_calcs.sysml +++ b/internal/repl/testdata/compile_calcs.sysml @@ -380,6 +380,9 @@ package Refused { calc def MixedEquality { in x : Integer[0..*]; in y : Real[0..*]; return : Boolean = x == y; } calc def MixedSame { in x : Integer[0..*]; in y : Real[0..*]; return : Boolean = same(x, y); } calc def MixedUnion { in x : Integer[0..*]; in y : Real[0..*]; return : Real[0..*] = union(x, y); } + calc def Sq { in v : Real; return : Real = v * v; } + calc def CalcParam { in calc f { in v : Real; return : Real; } in a : Real; return : Real = f(a); } + calc def FunctionArgument { in a : Real; return : Real = CalcParam(Sq, a); } calc def StringResult { in s : String;