Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow valid permutations of molecules #1

Merged
merged 11 commits into from
Sep 27, 2024
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"dev": "nodemon",
"test": "jest --coverage",
"start": "ts-node src/index.ts",
"build": "tsc",
"build": "yarn && tsc",
"serve": "node dist/index.js"
},
"devDependencies": {
Expand All @@ -28,7 +28,7 @@
"dotenv": "^16.4.5",
"express": "^4.19.2",
"express-validator": "^7.1.0",
"inequality-grammar": "^1.2.2",
"inequality-grammar": "^1.3.2",
"lodash": "^4.17.21"
}
}
208 changes: 141 additions & 67 deletions src/models/Chemistry.ts

Large diffs are not rendered by default.

32 changes: 17 additions & 15 deletions src/models/Nuclear.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,21 +71,21 @@ export interface NuclearAST {
result: Result;
}

function flattenNode<T extends ASTNode>(node: T): T {
function augmentNode<T extends ASTNode>(node: T): T {
// The if statements signal to the type checker what we already know
switch (node.type) {
case "expr": {
if(isExpression(node)) {
let terms: Term[] = [];

// Recursively flatten
// Recursively augmentten
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/flat/augment/g not quite right everywhere 😂

if (node.rest) {
const flatTerms: Expression | Term = flattenNode(node.rest);
const augmentedTerms: Expression | Term = augmentNode(node.rest);

if (isExpression(flatTerms)) {
terms = flatTerms.terms ?? [];
if (isExpression(augmentedTerms)) {
terms = augmentedTerms.terms ?? [];
} else {
terms = [flatTerms];
terms = [augmentedTerms];
}

// Append the current term
Expand All @@ -102,23 +102,23 @@ function flattenNode<T extends ASTNode>(node: T): T {
// Nodes with subtrees but no lists
case "statement": {
if (isStatement(node)) {
node.left = flattenNode(node.left);
node.right = flattenNode(node.right);
node.left = augmentNode(node.left);
node.right = augmentNode(node.right);
return node;
}
}

// Leaves of the AST
case "term": // Term doesn't have subtrees that could be flattened
case "term": // Term doesn't have subtrees that could be augmented
case "error":
case "particle":
case "isotope": return node;
}
}

export function flatten(ast: NuclearAST): NuclearAST {
const flatResult: Result = flattenNode(ast.result);
return { result: flatResult };
export function augment(ast: NuclearAST): NuclearAST {
const augmentResult: Result = augmentNode(ast.result);
return { result: augmentResult };
}

function isValidAtomicNumber(test: Particle | Isotope): boolean {
Expand Down Expand Up @@ -229,9 +229,9 @@ function checkNodesEqual(test: ASTNode, target: ASTNode, response: CheckerRespon

return listComparison(test.terms, target.terms, response, checkNodesEqual);
} else {
console.error("[server] Encountered unflattened AST. Returning error");
console.error("[server] Encountered unaugmented AST. Returning error");
response.containsError = true;
response.error = { message: "Received unflattened AST during checking process." };
response.error = { message: "Received unaugmented AST during checking process." };
return response;
}
} else if (isStatement(test) && isStatement(target)) {
Expand Down Expand Up @@ -259,7 +259,7 @@ function checkNodesEqual(test: ASTNode, target: ASTNode, response: CheckerRespon
}
}

export function check(test: NuclearAST, target: NuclearAST): CheckerResponse {
export function check(test: NuclearAST, target: NuclearAST, allowPermutations?: boolean): CheckerResponse {
const response = {
containsError: false,
error: { message: "" },
Expand All @@ -268,9 +268,11 @@ export function check(test: NuclearAST, target: NuclearAST): CheckerResponse {
typeMismatch: false,
sameState: true,
sameCoefficient: true,
sameElements: true,
isBalanced: true,
isEqual: true,
isNuclear: true,
allowPermutations: allowPermutations ?? false,
}
// Return shortcut response
if (target.result.type === "error" || test.result.type === "error") {
Expand Down
10 changes: 9 additions & 1 deletion src/models/common.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export const chemicalSymbol = ['H','He','Li','Be','B','C','N','O','F','Ne','Na','Mg','Al','Si','P','S','Cl','Ar','K','Ca','Sc','Ti','V','Cr','Mn','Fe','Co','Ni','Cu','Zn','Ga','Ge','As','Se','Br','Kr','Rb','Sr','Y','Zr','Nb','Mo','Tc','Ru','Rh','Pd','Ag','Cd','In','Sn','Sb','Te','I','Xe','Cs','Ba','La','Ce','Pr','Nd','Pm','Sm','Eu','Gd','Tb','Dy','Ho','Er','Tm','Yb','Lu','Hf','Ta','W','Re','Os','Ir','Pt','Au','Hg','Tl','Pb','Bi','Po','At','Rn','Fr','Ra','Ac','Th','Pa','U','Np','Pu','Am','Cm','Bk','Cf','Es','Fm','Md','No','Lr','Rf','Db','Sg','Bh','Hs','Mt','Ds','Rg','Cn','Nh','Fl','Mc','Lv','Ts','Og'] as const;
export type ChemicalSymbol = typeof chemicalSymbol[number];

export type ReturnType = 'term'|'expr'|'statement'|'error';
export type ReturnType = 'term'|'expr'|'statement'|'error'|'unknown';

type Aggregates = 'atomCount' | 'chargeCount' | 'nucleonCount';

Expand All @@ -21,13 +21,19 @@ export interface CheckerResponse {
typeMismatch: boolean;
sameState: boolean;
sameCoefficient: boolean;
sameElements: boolean;
allowPermutations: boolean;
// properties dependent on type
sameArrow?: boolean;
sameBrackets?: boolean;
balancedCharge?: boolean;
validAtomicNumber?: boolean;
balancedAtom?: boolean;
balancedMass?: boolean;
// book keeping
checkingPermutations? : boolean;
termAtomCount?: Record<ChemicalSymbol, number | undefined>;
bracketAtomCount?: Record<ChemicalSymbol, number | undefined>;
atomCount?: Record<ChemicalSymbol, number | undefined>;
chargeCount?: number;
nucleonCount?: [number, number];
Expand Down Expand Up @@ -80,6 +86,8 @@ export function listComparison<T>(

// Attach actual aggregate values
returnResponse.chargeCount = aggregatesResponse.chargeCount;
returnResponse.bracketAtomCount = aggregatesResponse.bracketAtomCount;
returnResponse.termAtomCount = aggregatesResponse.termAtomCount;
returnResponse.atomCount = aggregatesResponse.atomCount;
returnResponse.nucleonCount = aggregatesResponse.nucleonCount;

Expand Down
23 changes: 14 additions & 9 deletions src/routes/Chemistry.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Request, Response, Router } from "express";
import { ValidationChain, body, validationResult } from "express-validator";
import { parseChemistryExpression } from "inequality-grammar";
import { check, flatten } from "../models/Chemistry";
import { check, augment } from "../models/Chemistry";
import { CheckerResponse } from "../models/common";

const router = Router();
Expand All @@ -24,14 +24,19 @@ router.post('/check', checkValidationRules, (req: Request, res: Response) => {
return res.status(400).json({ errors: errors.array() });
}

const target: ChemAST = flatten(parseChemistryExpression(req.body.target)[0]);
const test: ChemAST = flatten(parseChemistryExpression(req.body.test)[0]);
const result: CheckerResponse = check(test, target);
const target: ChemAST = augment(parseChemistryExpression(req.body.target)[0]);
const test: ChemAST = augment(parseChemistryExpression(req.body.test)[0]);
const allowPermutations: boolean = req.body.allowPermutations === "true";
const result: CheckerResponse = check(test, target, allowPermutations);

res.status(201).send(result);

const str: string = JSON.stringify(result, null, 4);
console.log(`[server]: checker response ${str}`);
const resultStr: string = JSON.stringify(result, null, 4);

console.log(`[server]: question ID: ${req.body.questionID}`);
console.log(`[server]: target expression: ${req.body.target}`);
console.log(`[server]: test expression: ${req.body.test}`);
console.log(`[server]: checker response: ${resultStr}`);
});

router.post('/parse', parseValidationRules, (req: Request, res: Response) => {
Expand All @@ -42,11 +47,11 @@ router.post('/parse', parseValidationRules, (req: Request, res: Response) => {
}

const parse: ChemAST = parseChemistryExpression(req.body.test)[0];
const flattened: ChemAST = flatten(parse);
const augmented: ChemAST = augment(parse);

res.status(201).send(flattened);
res.status(201).send(augmented);

const str: string = JSON.stringify(flattened, null, 4);
const str: string = JSON.stringify(augmented, null, 4);
const request: string = req.body.description ? " '" + req.body.description + "'" : "";
console.log(`[server]: Parsed request${request}`);
console.log(`[server]: \n${str}`);
Expand Down
23 changes: 14 additions & 9 deletions src/routes/Nuclear.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Request, Response, Router } from "express";
import { ValidationChain, body, validationResult } from "express-validator";
import { parseNuclearExpression } from "inequality-grammar";
import { check, flatten } from "../models/Nuclear";
import { check, augment } from "../models/Nuclear";
import { CheckerResponse } from "../models/common";

const router = Router();
Expand All @@ -23,14 +23,19 @@ router.post('/check', checkValidationRules, (req: Request, res: Response) => {
return res.status(400).json({ errors: errors.array() });
}

const target: NuclearAST = flatten(parseNuclearExpression(req.body.target)[0]);
const test: NuclearAST = flatten(parseNuclearExpression(req.body.test)[0]);
const result: CheckerResponse = check(test, target);
const target: NuclearAST = augment(parseNuclearExpression(req.body.target)[0]);
const test: NuclearAST = augment(parseNuclearExpression(req.body.test)[0]);
const allowPermutations: boolean = req.body.allowPermutations === "true";
const result: CheckerResponse = check(test, target, allowPermutations);

res.status(201).send(result);

const resultStr: string = JSON.stringify(result, null, 4);

const str: string = JSON.stringify(result, null, 4);
console.log(`[server]: checker response ${str}`);
console.log(`[server]: question ID: ${req.body.questionID}`);
console.log(`[server]: target expression: ${req.body.target}`);
console.log(`[server]: test expression: ${req.body.test}`);
console.log(`[server]: checker response: ${resultStr}`);
});

router.post('/parse', parseValidationRules, (req: Request, res: Response) => {
Expand All @@ -41,10 +46,10 @@ router.post('/parse', parseValidationRules, (req: Request, res: Response) => {
}

const parse: NuclearAST = parseNuclearExpression(req.body.test)[0];
const flat: NuclearAST = flatten(parse);
res.status(201).send(flat);
const augmented: NuclearAST = augment(parse);
res.status(201).send(augmented);

const str: string = JSON.stringify(flat, null, 4);
const str: string = JSON.stringify(augmented, null, 4);
const request: string = req.body.description ? " '" + req.body.description + "'" : "";
console.log(`[server]: Parsed request${request}`);
console.log(`[server]: \n${str}`);
Expand Down
Loading