Skip to content

Commit

Permalink
Merge pull request #1 from isaacphysics/improvement/molecule-permutat…
Browse files Browse the repository at this point in the history
…ions

Allow valid permutations of molecules
  • Loading branch information
jsharkey13 authored Sep 27, 2024
2 parents d79cb7e + 2946fc4 commit 5a715d6
Show file tree
Hide file tree
Showing 9 changed files with 256 additions and 141 deletions.
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
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

0 comments on commit 5a715d6

Please sign in to comment.