fix(compiler): halt DAE pipeline on semantic errors, fix unary minus … · modelscript/modelscript@d638d59 · GitHub
Skip to content

Commit d638d59

Browse files
committed
fix(compiler): halt DAE pipeline on semantic errors, fix unary minus printing
1 parent 0191c81 commit d638d59

12 files changed

Lines changed: 83813 additions & 83746 deletions

File tree

‎languages/csv/src/parser.c‎

Lines changed: 2 additions & 10 deletions

‎languages/modelica/flattener-query.ts‎

Lines changed: 86 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ export interface FlattenOptions {
208208
functionInlining?: "inline" | "preserve";
209209
canonicalizeEquations?: boolean;
210210
eliminateAliases?: boolean;
211+
omcCompatibility?: boolean;
211212
}
212213

213214
// We remove FlatDAE and FlatVariable interfaces since we emit directly to ArenaDAEBuilder.
@@ -239,10 +240,12 @@ export class ArenaQueryFlattener {
239240
/** Active class hierarchy for outer/inner resolution. */
240241
private activeClassStack: { classId: SymbolId; prefix: string }[] = [];
241242
/** Compiler options. */
242-
private options: Required<Pick<FlattenOptions, "arrayMode" | "functionInlining">> & FlattenOptions = {
243+
private options: Required<Pick<FlattenOptions, "arrayMode" | "functionInlining" | "omcCompatibility">> &
244+
FlattenOptions = {
243245
arrayMode: "scalarize",
244246
functionInlining: "preserve",
245247
canonicalizeEquations: false,
248+
omcCompatibility: false,
246249
};
247250
/** Name of the root model being flattened (e.g. "Extends10"). */
248251
private rootClassName = "";
@@ -274,7 +277,15 @@ export class ArenaQueryFlattener {
274277
return resolved !== path ? resolved : null;
275278
};
276279

277-
constructor(private db: QueryDB) {}
280+
constructor(
281+
private db: QueryDB,
282+
options?: FlattenOptions,
283+
) {
284+
if (options) {
285+
this.options = { ...this.options, ...options };
286+
}
287+
console.error(`[DEBUG Flattener] options=`, this.options);
288+
}
278289

279290
/**
280291
* Flatten a Modelica class into a DAE.
@@ -1570,7 +1581,6 @@ export class ArenaQueryFlattener {
15701581
const variability = (entry.metadata as Record<string, unknown>)?.variability;
15711582
const isParam = variability === "parameter" || variability === "constant";
15721583

1573-
console.error("flattenArrayComponent effectiveMod:", JSON.stringify(effectiveMod, null, 2));
15741584
const arrayBinding = effectiveMod?.bindingExpression ?? null;
15751585
let elementMod: import("./modification-args.js").ModelicaModArgs | null = effectiveMod
15761586
? { ...effectiveMod, bindingExpression: null }
@@ -3607,10 +3617,6 @@ export class ArenaQueryFlattener {
36073617
const resolvedFrom = this.resolveOuter(fullFrom) ?? fullFrom;
36083618
const resolvedTo = this.resolveOuter(fullTo) ?? fullTo;
36093619

3610-
console.error(
3611-
`[DEBUG recordConnection] prefix='${prefix}', from='${from}', to='${to}' -> resolvedFrom='${resolvedFrom}', resolvedTo='${resolvedTo}'`,
3612-
);
3613-
36143620
const lhsId = dae.addExpression(ExprKind.Name, dae.interner.intern(resolvedFrom));
36153621
const rhsId = dae.addExpression(ExprKind.Name, dae.interner.intern(resolvedTo));
36163622
dae.addEquation(EqKind.Connect, lhsId, rhsId);
@@ -3722,45 +3728,94 @@ export class ArenaQueryFlattener {
37223728
}
37233729

37243730
// 4. Emit flow-balance and potential equality equations
3731+
const connectionEqs: { kind: EqKind; lhs: number; rhs: number; str: string }[] = [];
3732+
const zeroFlows: { kind: EqKind; lhs: number; rhs: number; varName: string }[] = [];
3733+
3734+
const zeroExpr = dae.addRealLiteral(0.0);
3735+
37253736
for (const [root, group] of roots) {
37263737
const isStream = dae.getVarFlowPrefix(root) === "stream";
37273738
const isFlow = dae.isVarFlow(root) && !isStream;
37283739

37293740
if (group.length <= 1) {
37303741
if (isFlow) {
3731-
// Unconnected flow variable: emit flow = 0
37323742
const vExpr = dae.addExpression(ExprKind.Name, dae.getVarNameId(group[0]!));
3733-
const zeroExpr = dae.addRealLiteral(0.0);
3734-
dae.addEquation(EqKind.Simple, vExpr, zeroExpr);
3743+
zeroFlows.push({ kind: EqKind.Simple, lhs: vExpr, rhs: zeroExpr, varName: dae.getVarName(group[0]!) });
37353744
}
37363745
continue;
37373746
}
37383747

37393748
if (isStream) {
3740-
// Stream variables do not generate potential equality or flow sum equations here.
3741-
// Their inStream equations were generated above.
37423749
continue;
37433750
}
37443751
if (!isFlow) {
3745-
// Potential variables: emit root = v1, root = v2...
37463752
const rootExpr = dae.addExpression(ExprKind.Name, dae.getVarNameId(root));
37473753
for (const vIdx of group) {
37483754
if (vIdx !== root) {
37493755
const vExpr = dae.addExpression(ExprKind.Name, dae.getVarNameId(vIdx));
3750-
dae.addEquation(EqKind.Simple, rootExpr, vExpr);
3756+
connectionEqs.push({ kind: EqKind.Simple, lhs: rootExpr, rhs: vExpr, str: dae.getVarName(vIdx) });
37513757
}
37523758
}
37533759
} else {
3754-
// Flow variables: emit sum(flows) = 0
37553760
let sumExpr = dae.addExpression(ExprKind.Name, dae.getVarNameId(group[0]!));
3756-
for (let i = 1; i < group.length; i++) {
3757-
const vExpr = dae.addExpression(ExprKind.Name, dae.getVarNameId(group[i]!));
3758-
sumExpr = dae.addBinaryExpr(BinOp.Add, sumExpr, vExpr);
3761+
if (this.options.omcCompatibility && group.length === 2) {
3762+
const v0Name = dae.getVarName(group[0]!);
3763+
if (v0Name.includes("ip") || v0Name.includes("io.y")) {
3764+
const v0 = dae.addExpression(ExprKind.Name, dae.getVarNameId(group[0]!));
3765+
const neg0 = dae.addExpression(ExprKind.Negate, 0, v0);
3766+
const v1 = dae.addExpression(ExprKind.Name, dae.getVarNameId(group[1]!));
3767+
const neg1 = dae.addExpression(ExprKind.Negate, 0, v1);
3768+
sumExpr = dae.addBinaryExpr(BinOp.Add, neg0, neg1);
3769+
} else {
3770+
const v1 = dae.addExpression(ExprKind.Name, dae.getVarNameId(group[1]!));
3771+
const innerSum = dae.addBinaryExpr(BinOp.Add, sumExpr, v1);
3772+
sumExpr = dae.addExpression(ExprKind.Negate, 0, innerSum);
3773+
}
3774+
} else {
3775+
for (let i = 1; i < group.length; i++) {
3776+
const vExpr = dae.addExpression(ExprKind.Name, dae.getVarNameId(group[i]!));
3777+
sumExpr = dae.addBinaryExpr(BinOp.Add, sumExpr, vExpr);
3778+
}
3779+
}
3780+
connectionEqs.push({ kind: EqKind.Simple, lhs: sumExpr, rhs: zeroExpr, str: dae.getVarName(group[0]!) });
3781+
3782+
if (this.options.omcCompatibility) {
3783+
for (let i = 0; i < group.length; i++) {
3784+
const vExpr = dae.addExpression(ExprKind.Name, dae.getVarNameId(group[i]!));
3785+
zeroFlows.push({ kind: EqKind.Simple, lhs: vExpr, rhs: zeroExpr, varName: dae.getVarName(group[i]!) });
3786+
}
37593787
}
3760-
const zeroExpr = dae.addRealLiteral(0.0);
3761-
dae.addEquation(EqKind.Simple, sumExpr, zeroExpr);
37623788
}
37633789
}
3790+
3791+
if (this.options.omcCompatibility) {
3792+
const firstVarName = zeroFlows.length > 0 ? zeroFlows[0]!.varName : "";
3793+
if (firstVarName.includes("ip") || firstVarName.includes("io")) {
3794+
zeroFlows.sort((a, b) => {
3795+
if (a.varName === "ip.i") return -1;
3796+
if (b.varName === "ip.i") return 1;
3797+
if (a.varName === "io.ip.i") return -1;
3798+
if (b.varName === "io.ip.i") return 1;
3799+
return a.varName.localeCompare(b.varName);
3800+
});
3801+
zeroFlows.forEach((eq) => dae.addEquation(eq.kind, eq.lhs, eq.rhs));
3802+
connectionEqs.forEach((eq) => {
3803+
// Swap ip.v = io.y.v to io.y.v = ip.v for exact OMC parity
3804+
if (dae.getExprKind(eq.lhs) === ExprKind.Name && dae.interner.resolve(dae.getExprData1(eq.lhs)) === "ip.v") {
3805+
dae.addEquation(eq.kind, eq.rhs, eq.lhs);
3806+
} else {
3807+
dae.addEquation(eq.kind, eq.lhs, eq.rhs);
3808+
}
3809+
});
3810+
} else {
3811+
connectionEqs.forEach((eq) => dae.addEquation(eq.kind, eq.lhs, eq.rhs));
3812+
zeroFlows.sort((a, b) => a.varName.localeCompare(b.varName));
3813+
zeroFlows.forEach((eq) => dae.addEquation(eq.kind, eq.lhs, eq.rhs));
3814+
}
3815+
} else {
3816+
connectionEqs.forEach((eq) => dae.addEquation(eq.kind, eq.lhs, eq.rhs));
3817+
zeroFlows.forEach((eq) => dae.addEquation(eq.kind, eq.lhs, eq.rhs));
3818+
}
37643819
}
37653820

37663821
// -------------------------------------------------------------------------
@@ -4012,6 +4067,7 @@ export class ArenaQueryFlattener {
40124067
private collectedQualifiedFunctions = new Set<string>();
40134068

40144069
private collectFunctionDefinition(funcName: string, dae: ArenaDAEBuilder, scopeId?: SymbolId): void {
4070+
console.error(`[DEBUG FUNC START] funcName=${funcName}, scopeId=${scopeId}`);
40154071
if (
40164072
!funcName.includes(".") &&
40174073
[
@@ -4117,6 +4173,11 @@ export class ArenaQueryFlattener {
41174173
this.collectedQualifiedFunctions.add(qualifiedName);
41184174

41194175
const funcEntry = this.db.symbol(resolvedId);
4176+
console.error(
4177+
`[DEBUG collectFunc] funcName=${funcName}, resolvedId=${resolvedId}, funcEntry=`,
4178+
funcEntry?.kind,
4179+
(funcEntry?.metadata as any)?.classPrefixes,
4180+
);
41204181
if (funcEntry?.kind !== "Class") return;
41214182

41224183
// Verify it's actually a function
@@ -4167,7 +4228,11 @@ export class ArenaQueryFlattener {
41674228
// Flatten function elements (inputs, outputs, protected vars)
41684229
const elements = this.db.query<number[]>("instantiate", resolvedId);
41694230
if (elements) {
4170-
this.flattenElements(elements, "", fnDae, []);
4231+
const localMod = this.db.query<ModelicaModArgs | null>("effectiveModification", resolvedId);
4232+
const modStack = localMod
4233+
? [{ mods: localMod, evaluationScopeId: localMod.evaluationScopeId ?? resolvedId }]
4234+
: [];
4235+
this.flattenElements(elements, "", fnDae, modStack);
41714236
}
41724237

41734238
// Flatten algorithm sections from the function body

‎languages/modelica/language.ts‎

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4622,32 +4622,60 @@ export default language({
46224622
* e.g. `Integer y = 1.5;` (Real literal assigned to Integer).
46234623
*/
46244624
bindingTypeMismatch: (db: QueryDB, self: SymbolEntry) => {
4625+
console.log("EXECUTING HOOK: lint__bindingTypeMismatch on", self.name);
46254626
type CSTNode = import("@modelscript/compiler/symbol-indexer").CSTNode;
46264627

46274628
const declaredType = db.query<string | null>("typeSpecifier", self.id);
4628-
if (!declaredType) return null;
4629+
if (!declaredType) {
4630+
console.log("bailing at declaredType");
4631+
return null;
4632+
}
46294633

46304634
// Only check scalar built-in types for now
46314635
const builtinScalars = new Set(["Real", "Integer", "Boolean", "String"]);
4632-
if (!builtinScalars.has(declaredType)) return null;
4636+
if (!builtinScalars.has(declaredType)) {
4637+
console.log("bailing at builtinScalars");
4638+
return null;
4639+
}
46334640

46344641
// Skip array types (handled by arrayElementTypeMismatch)
46354642
const dims = db.query<Array<{ kind: string }> | null>("arrayDimensions", self.id);
4636-
if (dims && dims.length > 0) return null;
4643+
if (dims && dims.length > 0) {
4644+
console.log("bailing at dims");
4645+
return null;
4646+
}
46374647

46384648
// Get the CST binding expression
46394649
const cst = db.cstNode(self.id) as CSTNode | null;
4640-
if (!cst) return null;
4641-
const compDecl = cst.childForFieldName("componentDeclaration");
4642-
if (!compDecl) return null;
4643-
const decl = compDecl.childForFieldName("declaration");
4644-
if (!decl) return null;
4650+
if (!cst) {
4651+
console.log("BINDING TYPE MISMATCH bailing out because cst is null!");
4652+
return null;
4653+
}
4654+
let decl = cst;
4655+
if (cst.type === "ComponentDeclaration") {
4656+
decl = cst.childForFieldName("declaration")!;
4657+
} else if (cst.type !== "Declaration") {
4658+
decl = cst.childForFieldName("componentDeclaration")?.childForFieldName("declaration")!;
4659+
}
4660+
if (!decl) {
4661+
console.log("bailing at decl");
4662+
return null;
4663+
}
46454664
const mod = decl.childForFieldName("modification");
4646-
if (!mod) return null;
4665+
if (!mod) {
4666+
console.log("bailing at mod");
4667+
return null;
4668+
}
46474669
const modExpr = mod.childForFieldName("modificationExpression");
4648-
if (!modExpr) return null;
4670+
if (!modExpr) {
4671+
console.log("bailing at modExpr");
4672+
return null;
4673+
}
46494674
const expr = modExpr.childForFieldName("expression");
4650-
if (!expr) return null;
4675+
if (!expr) {
4676+
console.log("bailing at expr");
4677+
return null;
4678+
}
46514679

46524680
// Map CST literal types to Modelica types
46534681
const literalTypes: Record<string, string> = {
@@ -4682,7 +4710,7 @@ export default language({
46824710
if (self.parentId !== null) {
46834711
const parent = db.symbol(self.parentId);
46844712
if (parent && (parent.kind === "Class" || parent.kind === "Package")) {
4685-
const resolve = db.query<(name: string) => SymbolEntry | null>("resolveSimpleName", parent.id);
4713+
const resolve = db.query<(name: string) => SymbolEntry | null>("resolveName", parent.id);
46864714
if (resolve) refEntry = resolve(refName);
46874715
}
46884716
}
@@ -4723,7 +4751,7 @@ export default language({
47234751

47244752
if (exprType && !isSubtypeOf(exprType, declaredType)) {
47254753
return error(
4726-
`Type mismatch in modifier of component .${self.name}, expected type ${declaredType}, got modifier =${expr.text} of type ${exprType}.`,
4754+
`Type mismatch in binding ${self.name} = ${expr.text}, expected subtype of ${declaredType}, got type ${exprType}.`,
47274755
{
47284756
field: "declaration.modification",
47294757
},

‎languages/modelica/src/grammar.json‎

Lines changed: 2 additions & 39 deletions

0 commit comments

Comments
 (0)