-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: rework the service registration, rework the exposed methods
- Loading branch information
1 parent
674cda8
commit 6f02bf7
Showing
16 changed files
with
1,185 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 29 additions & 0 deletions
29
packages/safe-ds-lang/src/language/graphical-editor/ast-parser/argument.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { isSdsLiteral, SdsArgument } from '../../generated/ast.js'; | ||
import { CustomError } from '../global.js'; | ||
import { Call } from './call.js'; | ||
import { Placeholder } from './placeholder.js'; | ||
import { Expression, GenericExpression } from './expression.js'; | ||
import { Parameter } from './parameter.js'; | ||
import { Parser } from './parser.js'; | ||
|
||
export class Argument { | ||
constructor( | ||
public readonly text: string, | ||
public readonly reference: GenericExpression | Call | Placeholder | Parameter | undefined, | ||
public readonly parameterName?: string, | ||
) {} | ||
|
||
public static parse(node: SdsArgument, parser: Parser) { | ||
if (!node.value.$cstNode) return parser.pushError('CstNode missing', node.value); | ||
const text = node.value.$cstNode.text; | ||
|
||
let expression; | ||
if (!isSdsLiteral(node.value)) expression = Expression.parse(node.value, parser); | ||
if (expression instanceof CustomError) return expression; | ||
|
||
if (node.parameter && !node.parameter.ref) return parser.pushError('Missing Parameterreference', node); | ||
const parameterName = node.parameter?.ref?.name; | ||
|
||
return new Argument(text, expression, parameterName); | ||
} | ||
} |
236 changes: 236 additions & 0 deletions
236
packages/safe-ds-lang/src/language/graphical-editor/ast-parser/call.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,236 @@ | ||
import { | ||
SdsCall, | ||
SdsClass, | ||
SdsExpression, | ||
SdsFunction, | ||
SdsMemberAccess, | ||
SdsPlaceholder, | ||
SdsReference, | ||
SdsSegment, | ||
isSdsCall, | ||
isSdsClass, | ||
isSdsFunction, | ||
isSdsMemberAccess, | ||
isSdsPlaceholder, | ||
isSdsReference, | ||
isSdsSegment, | ||
} from '../../generated/ast.js'; | ||
import { CustomError } from '../global.js'; | ||
import { Argument } from './argument.js'; | ||
import { Edge, Port } from './edge.js'; | ||
import { GenericExpression } from './expression.js'; | ||
import { Parameter } from './parameter.js'; | ||
import { Placeholder } from './placeholder.js'; | ||
import { Result } from './result.js'; | ||
import { filterErrors } from './utils.js'; | ||
import { Parser } from './parser.js'; | ||
|
||
export class Call { | ||
private constructor( | ||
public readonly id: number, | ||
public readonly name: string, | ||
public readonly self: string | undefined, | ||
public readonly parameterList: Parameter[], | ||
public readonly resultList: Result[], | ||
public readonly category: string, | ||
public readonly uniquePath: string, | ||
) {} | ||
|
||
public static parse(node: SdsCall, parser: Parser): Call | CustomError { | ||
const id = parser.getNewId(); | ||
|
||
if (!isValidCallReceiver(node.receiver)) { | ||
return parser.pushError(`Invalid Call receiver: ${debugInvalidCallReceiver(node.receiver)}`, node.receiver); | ||
} | ||
|
||
let name = ''; | ||
let self: string | undefined = undefined; | ||
let category = ''; | ||
let argumentList: Argument[] = []; | ||
let parameterList: Parameter[] = []; | ||
let resultList: Result[] = []; | ||
|
||
argumentList = filterErrors(node.argumentList.arguments.map((argument) => Argument.parse(argument, parser))); | ||
|
||
if (isSdsMemberAccess(node.receiver)) { | ||
const tmp = Call.parseSelf(node.receiver, id, parser); | ||
if (tmp instanceof CustomError) return tmp; | ||
self = tmp; | ||
|
||
const functionDeclaration = node.receiver.member.target.ref; | ||
name = functionDeclaration.name; | ||
category = parser.getCategory(functionDeclaration)?.name ?? ''; | ||
|
||
resultList = filterErrors( | ||
(functionDeclaration.resultList?.results ?? []).map((result) => Result.parse(result, parser)), | ||
); | ||
parameterList = filterErrors( | ||
(functionDeclaration.parameterList?.parameters ?? []).map((parameter) => | ||
Parameter.parse(parameter, parser), | ||
), | ||
); | ||
} | ||
|
||
if (isSdsReference(node.receiver) && isSdsClass(node.receiver.target.ref)) { | ||
const classDeclaration = node.receiver.target.ref; | ||
|
||
name = 'new'; | ||
self = classDeclaration.name; | ||
category = 'Modeling'; | ||
|
||
if (!classDeclaration.parameterList) | ||
return parser.pushError('Missing constructor parameters', classDeclaration); | ||
parameterList = filterErrors( | ||
classDeclaration.parameterList.parameters.map((parameter) => Parameter.parse(parameter, parser)), | ||
); | ||
resultList = [new Result('new', classDeclaration.name)]; | ||
} | ||
|
||
if (isSdsReference(node.receiver) && isSdsSegment(node.receiver.target.ref)) { | ||
const segmentDeclaration = node.receiver.target.ref; | ||
|
||
self = ''; | ||
name = segmentDeclaration.name; | ||
category = 'Segment'; | ||
|
||
resultList = filterErrors( | ||
(segmentDeclaration.resultList?.results ?? []).map((result) => Result.parse(result, parser)), | ||
); | ||
parameterList = filterErrors( | ||
(segmentDeclaration.parameterList?.parameters ?? []).map((parameter) => | ||
Parameter.parse(parameter, parser), | ||
), | ||
); | ||
} | ||
|
||
const parameterListCompleted = matchArgumentsToParameter(parameterList, argumentList, node, id, parser); | ||
if (parameterListCompleted instanceof CustomError) return parameterListCompleted; | ||
|
||
const call = new Call(id, name, self, parameterListCompleted, resultList, category, parser.getUniquePath(node)); | ||
parser.graph.callList.push(call); | ||
return call; | ||
} | ||
|
||
private static parseSelf(node: CallReceiver, id: number, parser: Parser) { | ||
if (isSdsMemberAccess(node)) { | ||
if (isSdsCall(node.receiver)) { | ||
const call = Call.parse(node.receiver, parser); | ||
if (call instanceof CustomError) return call; | ||
|
||
if (call.resultList.length > 1) return parser.pushError('To many result', node.receiver); | ||
if (call.resultList.length < 1) return parser.pushError('Missing result', node.receiver); | ||
|
||
Edge.create(Port.fromResult(call.resultList[0]!, call.id), Port.fromName(id, 'self'), parser); | ||
} else if (isSdsReference(node.receiver)) { | ||
const receiver = node.receiver.target.ref; | ||
|
||
if (isSdsClass(receiver)) { | ||
return receiver.name; | ||
} else if (isSdsPlaceholder(receiver)) { | ||
const placeholder = Placeholder.parse(receiver, parser); | ||
Edge.create(Port.fromPlaceholder(placeholder, false), Port.fromName(id, 'self'), parser); | ||
} | ||
} | ||
} | ||
return ''; | ||
} | ||
} | ||
|
||
const matchArgumentsToParameter = ( | ||
parameterList: Parameter[], | ||
argumentList: Argument[], | ||
callNode: SdsCall, | ||
id: number, | ||
parser: Parser, | ||
): Parameter[] | CustomError => { | ||
for (const [i, parameter] of parameterList.entries()) { | ||
const argumentIndexMatched = argumentList[i]; | ||
if (argumentIndexMatched instanceof CustomError) return argumentIndexMatched; | ||
|
||
const argumentNameMatched = argumentList.find( | ||
(argument) => !(argument instanceof CustomError) && argument.parameterName === parameter.name, | ||
) as Argument | undefined; | ||
|
||
if (argumentIndexMatched && argumentNameMatched && argumentIndexMatched !== argumentNameMatched) | ||
return parser.pushError(`To many matches for ${parameter.name}`, callNode.argumentList); | ||
const argument = argumentIndexMatched ?? argumentNameMatched; | ||
|
||
if (argument) { | ||
parameter.argumentText = argument.text; | ||
if (argument.reference instanceof Call) { | ||
const call = argument.reference; | ||
if (call.resultList.length !== 1) return parser.pushError('Type missmatch', callNode.argumentList); | ||
Edge.create(Port.fromResult(call.resultList[0]!, call.id), Port.fromParameter(parameter, id), parser); | ||
} | ||
if (argument.reference instanceof GenericExpression) { | ||
const experession = argument.reference; | ||
Edge.create(Port.fromGenericExpression(experession, false), Port.fromParameter(parameter, id), parser); | ||
} | ||
if (argument.reference instanceof Placeholder) { | ||
const placeholder = argument.reference; | ||
Edge.create(Port.fromPlaceholder(placeholder, false), Port.fromParameter(parameter, id), parser); | ||
} | ||
if (argument.reference instanceof Parameter) { | ||
const segmentParameter = argument.reference; | ||
Edge.create(Port.fromParameter(segmentParameter, -1), Port.fromParameter(parameter, id), parser); | ||
} | ||
continue; | ||
} | ||
|
||
if (!argument && parameter.defaultValue) { | ||
continue; | ||
} | ||
|
||
if (!argument && !parameter.defaultValue) { | ||
return parser.pushError(`Missing Argument for ${parameter.name}`, callNode); | ||
} | ||
} | ||
return parameterList; | ||
}; | ||
|
||
type CallReceiver = | ||
| (SdsReference & { target: { ref: SdsClass | SdsSegment } }) | ||
| (SdsMemberAccess & { | ||
member: { | ||
target: { ref: SdsFunction }; | ||
}; | ||
receiver: SdsCall | { target: { ref: SdsPlaceholder | SdsClass } }; | ||
}); | ||
|
||
const isValidCallReceiver = (receiver: SdsExpression): receiver is CallReceiver => { | ||
/* eslint-disable no-implicit-coercion */ | ||
return ( | ||
(isSdsMemberAccess(receiver) && | ||
!!receiver.member && | ||
!!receiver.member.target.ref && | ||
isSdsFunction(receiver.member.target.ref) && | ||
((isSdsReference(receiver.receiver) && | ||
(isSdsClass(receiver.receiver.target.ref) || isSdsPlaceholder(receiver.receiver.target.ref))) || | ||
isSdsCall(receiver.receiver))) || | ||
(isSdsReference(receiver) && (isSdsClass(receiver.target.ref) || isSdsSegment(receiver.target.ref))) | ||
); | ||
}; | ||
|
||
const debugInvalidCallReceiver = (receiver: SdsExpression): string => { | ||
/* eslint-disable no-implicit-coercion */ | ||
if (isSdsMemberAccess(receiver)) { | ||
if (!receiver.member) return 'MemberAccess: Missing member'; | ||
if (!receiver.member.target.ref) return 'MemberAccess: Missing member declaration'; | ||
if (!isSdsFunction(receiver.member.target.ref)) return 'MemberAccess: Member is not a function'; | ||
if (!isSdsCall(receiver.receiver) && !isSdsReference(receiver.receiver)) | ||
return `MemberAccess: Receiver is not a Reference or Call but - ${receiver.receiver.$type}`; | ||
if ( | ||
isSdsReference(receiver.receiver) && | ||
!isSdsClass(receiver.receiver.target.ref) && | ||
isSdsReference(receiver.receiver) && | ||
!isSdsPlaceholder(receiver.receiver.target.ref) | ||
) | ||
return 'MemberAccess: Reference Receiver is not Class of Placeholder'; | ||
} | ||
if (isSdsReference(receiver)) { | ||
if (!isSdsClass(receiver.target.ref) && !isSdsSegment(receiver.target.ref)) | ||
return 'Reference: Not a class or segment'; | ||
} | ||
|
||
return receiver.$type; | ||
}; |
55 changes: 55 additions & 0 deletions
55
packages/safe-ds-lang/src/language/graphical-editor/ast-parser/edge.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { GenericExpression } from './expression.js'; | ||
import { Parameter } from './parameter.js'; | ||
import { Parser } from './parser.js'; | ||
import { Placeholder } from './placeholder.js'; | ||
import { Result } from './result.js'; | ||
import { SegmentGroupId } from './segment.js'; | ||
|
||
export class Edge { | ||
public constructor( | ||
public readonly from: Port, | ||
public readonly to: Port, | ||
) {} | ||
|
||
public static create(from: Port, to: Port, parser: Parser) { | ||
parser.graph.edgeList.push(new Edge(from, to)); | ||
} | ||
} | ||
|
||
export class Port { | ||
private constructor( | ||
public readonly nodeId: string, | ||
public readonly portIdentifier: string, | ||
) {} | ||
|
||
public static fromName = (nodeId: number, name: string): Port => { | ||
return new Port(nodeId.toString(), name); | ||
}; | ||
|
||
public static fromPlaceholder = (placeholder: Placeholder, input: boolean): Port => { | ||
return new Port(placeholder.name, input ? 'target' : 'source'); | ||
}; | ||
|
||
public static fromResult = (result: Result, nodeId: number): Port => { | ||
return new Port(nodeId.toString(), result.name); | ||
}; | ||
|
||
public static fromParameter = (parameter: Parameter, nodeId: number): Port => { | ||
return new Port(nodeId.toString(), parameter.name); | ||
}; | ||
|
||
public static fromGenericExpression(node: GenericExpression, input: boolean) { | ||
return new Port(node.id.toString(), input ? 'target' : 'source'); | ||
} | ||
|
||
public static fromAssignee = (node: Placeholder | Result, input: boolean): Port => { | ||
if (node instanceof Placeholder) { | ||
return new Port(node.name, input ? 'target' : 'source'); | ||
} | ||
return new Port(SegmentGroupId.toString(), node.name); | ||
}; | ||
|
||
public static isPortList(object: any): object is Port[] { | ||
return Array.isArray(object) && object.every((element) => element instanceof Port); | ||
} | ||
} |
Oops, something went wrong.