Skip to content

Commit df4fe53

Browse files
committed
Created a dedicated solution parsing module
1 parent f97980b commit df4fe53

5 files changed

Lines changed: 116 additions & 74 deletions

File tree

solves/base-template/internals/global.d.ts.ejs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,9 @@ type ConstrainedInputKeys = <%-
4848
-%>;
4949
type AllInputKeys = BaseInputKeys | ConstrainedInputKeys;
5050

51+
type OutputKeys = <%-
52+
Object.entries(output).map(([k, v]) =>
53+
`"${k}"`
54+
).join(" | ")
55+
-%>;
56+

solves/base-template/src/components/App.tsx.ejs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
11
import React from "react";
22

3-
import "./App.css";
4-
53
import {LineHighlighterTextbox} from "./generic/LineHighlighterTextbox";
64
import {ResultDisplay} from "./ResultDisplay";
75

86
import {
9-
type ClingoResult,
10-
runSolver
7+
type ParsedSolution,
8+
runSolver,
119
} from "../runSolver";
1210

11+
import "./App.css";
12+
13+
1314
function parseInput(s: string): string[] {
1415
return s.split("\n");
1516
}
1617

1718
interface State {
18-
initialized: boolean;
19-
2019
<%-
2120
Object.entries({...inputBase, ...inputConstrained}).map(([k, v]) => {
2221
const x = {};
@@ -25,16 +24,14 @@ interface State {
2524
}).join("")
2625
-%>
2726

28-
outputResult: null | ClingoResult;
27+
solution: ParsedSolution | "not-initialized";
2928
invalidInputs: Map<AllInputKeys, Set<string>>;
3029
}
3130

3231
export class App extends React.Component<{}, State> {
3332
constructor(props: {}) {
3433
super(props);
3534
this.state = {
36-
initialized: false,
37-
3835
// TODO: This is NOT the source of truth for input values. This input
3936
// value state should technically no longer exist, and we should
4037
// instead set up refs to read directly from their uncontrolled
@@ -53,7 +50,7 @@ export class App extends React.Component<{}, State> {
5350
}).join("")
5451
-%>
5552

56-
outputResult: null,
53+
solution: "not-initialized",
5754
invalidInputs: new Map(),
5855
};
5956
}
@@ -68,8 +65,7 @@ export class App extends React.Component<{}, State> {
6865
%>
6966
});
7067
this.setState({
71-
initialized: true,
72-
outputResult: result.resultObj,
68+
solution: result.solution,
7369
invalidInputs: result.invalidConstantTuples,
7470
});
7571
}
@@ -101,8 +97,12 @@ export class App extends React.Component<{}, State> {
10197
-%>
10298
<div className="output-table-wrapper">
10399
<ResultDisplay
104-
clingoResult={this.state.outputResult}
105-
initialized={this.state.initialized}
100+
fieldLabels={["Vertex", "Colour"]}
101+
solutionData={
102+
(typeof this.state.solution === "string")
103+
? this.state.solution
104+
: this.state.solution.color
105+
}
106106
/>
107107
</div>
108108
</div>;
Lines changed: 33 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,52 @@
11
import React from "react";
22

3-
import {type ClingoResult} from "../runSolver";
3+
import {type ParsedSolutionSpecialCode} from "../runSolver";
44

55
import "./ResultDisplay.css";
66

7-
const solutionRE = /solution\(color\(([^,]+),([^,]+)*\)\)/g;
8-
9-
function parseSolutionStr(s: string) {
10-
const matches = [...s.matchAll(solutionRE)];
11-
if (matches.length !== 1) return null;
12-
const match = matches[0];
13-
if (match?.length !== 3) return null;
14-
return {
15-
vertex: match?.[1] || "<INVALID>",
16-
colour: match?.[2] || "<INVALID>",
17-
};
18-
}
19-
20-
function renderRow(s: string, i: number) {
21-
const parsed = parseSolutionStr(s);
22-
if (parsed === null) return null;
7+
function renderRow(strs: string[], i: number) {
238
return <tr key={i}>
24-
<td>{parsed.vertex}</td>
25-
<td>{parsed.colour}</td>
9+
{strs.map((x: string, j: number) =>
10+
<td key={j}>{x}</td>
11+
)}
2612
</tr>;
2713
}
2814

29-
function renderSatisfiable(solution: string[]) {
30-
return <table className="result-satisfiable">
31-
<thead>
32-
<tr>
33-
<th>Vertex</th>
34-
<th>Colour</th>
35-
</tr>
36-
</thead>
37-
<tbody>
38-
{solution.map(renderRow)}
39-
</tbody>
40-
</table>;
41-
}
42-
4315
/*** ***/
4416

4517
interface Props {
46-
initialized: boolean;
47-
clingoResult: null | ClingoResult;
18+
fieldLabels: string[];
19+
solutionData: string[][] | ParsedSolutionSpecialCode | "not-initialized";
4820
}
4921

5022
export function ResultDisplay(props: Props) {
51-
if (!props.initialized) {
52-
console.assert(props.clingoResult === null);
53-
return <>Loading...</>;
54-
} else if (props.clingoResult === null) {
55-
return <>Invalid Input.</>;
56-
}
57-
58-
switch (props.clingoResult.result) {
59-
case "SATISFIABLE":
60-
const solution = props.clingoResult.solution;
61-
if (solution !== null && solution.length !== 0) {
62-
return renderSatisfiable(solution);
63-
} else {
64-
return <>There is a solution, but it has no values.</>;
65-
}
66-
case "UNSATISFIABLE":
23+
switch (props.solutionData) {
24+
case "not-initialized":
25+
return <>Loading...</>;
26+
case "no-solution":
6727
return <>No solution.</>;
68-
default: console.error("Invalid value."); // Fallthrough
69-
case "UNKNOWN":
70-
return <>{props.clingoResult.stderr || "UNKNOWN" }</>;
28+
case "invalid-input":
29+
return <>Invalid input.</>;
30+
case "error":
31+
return <>An error has occurred.</>;
32+
default: // Fallthrough
33+
}
34+
if (props.solutionData.length === 0) {
35+
return <>There is a solution, but it has no values.</>;
7136
}
37+
38+
console.log(props.solutionData);
39+
return <table className="result-satisfiable">
40+
<thead>
41+
<tr key={0}>
42+
{props.fieldLabels.map((x: string, i: number) =>
43+
<th key={i}>{x}</th>
44+
)}
45+
</tr>
46+
</thead>
47+
<tbody>
48+
{props.solutionData.map(renderRow)}
49+
</tbody>
50+
</table>;
7251
}
7352

solves/base-template/src/runSolver/index.ts.ejs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
import {
2-
type ClingoResult as _ClingoResult,
2+
type ClingoResult,
33
runClingo,
44
} from "clingo-wrapper";
55

6+
import {
7+
type ParsedSolutionSpecialCode as _ParsedSolutionSpecialCode,
8+
type ParsedSolution as _ParsedSolution,
9+
parseSolution,
10+
} from "./parseSolution";
11+
612
import logicSpecStr from "./logicSpec.lp";
713
import validationSpecStr from "./validationSpec.lp";
814

9-
export type ClingoResult = _ClingoResult;
15+
export type ParsedSolutionSpecialCode = _ParsedSolutionSpecialCode;
16+
export type ParsedSolution = _ParsedSolution;
1017

1118
const reConstant = /^[a-z][a-zA-Z0-9]*$/;
1219

@@ -122,9 +129,7 @@ export interface SolverParameters {
122129
}
123130

124131
export interface SolverResult {
125-
// Clingo-specific output.
126-
// TODO: Make SolverResult backend-agnostic!
127-
resultObj: null | ClingoResult;
132+
solution: ParsedSolution;
128133
invalidConstantTuples: Map<AllInputKeys, Set<string>>;
129134
}
130135

@@ -182,6 +187,9 @@ export async function runSolver(params: SolverParameters): Promise<SolverResult>
182187
return [inputID, constTups];
183188
}),
184189
);
185-
return {resultObj, invalidConstantTuples};
190+
return {
191+
solution: resultObj ? parseSolution(resultObj) : "invalid-input",
192+
invalidConstantTuples,
193+
};
186194
}
187195

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import {
2+
type ClingoResult,
3+
} from "clingo-wrapper";
4+
5+
6+
export interface SatisfiableParsedSolution {
7+
//[K of OutputKeys]: string[][]; // Table of constants
8+
color: string[][]; // Table of constants
9+
}
10+
11+
export type ParsedSolutionSpecialCode = "no-solution"
12+
| "invalid-input"
13+
| "error";
14+
15+
export type ParsedSolution = SatisfiableParsedSolution | ParsedSolutionSpecialCode;
16+
17+
const solutionRE = /solution\(color\(([^,]+),([^,]+)*\)\)/g;
18+
19+
function parseSolutionStr(s: string): string[] {
20+
const matches = [...s.matchAll(solutionRE)];
21+
if (matches.length !== 1) return [];
22+
const match = matches[0];
23+
if (match?.length !== 3) return [];
24+
return [
25+
match?.[1] || "<INVALID>",
26+
match?.[2] || "<INVALID>",
27+
];
28+
}
29+
30+
export function parseSolution(result: ClingoResult): ParsedSolution {
31+
switch (result.result) {
32+
case "UNSATISFIABLE":
33+
return "no-solution";
34+
case "UNKNOWN":
35+
return "error";
36+
default: console.error("Invalid value."); // Fallthrough
37+
case "SATISFIABLE": // Fallthrough
38+
}
39+
40+
if (result.solution === null) {
41+
console.error("Expected non-null solution from Clingo.");
42+
return "error";
43+
}
44+
45+
return {
46+
color: result.solution.map(parseSolutionStr).filter(x => x.length),
47+
};
48+
}
49+

0 commit comments

Comments
 (0)