Skip to content
Snippets Groups Projects
Commit d1aadc45 authored by Brady James Garvin's avatar Brady James Garvin
Browse files

Initial commit.

parents
Branches
No related tags found
No related merge requests found
Showing
with 518 additions and 0 deletions
# Disable line-ending conversions for this repository.
* -text
# dependencies
/node_modules
# testing
/coverage
# production
/build
# environments
.env.local
.env.development.local
.env.test.local
.env.production.local
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# misc
*~
.DS_Store
[submodule "eslint-config"]
path = eslint-config
url = git@git.unl.edu:csce-310/eslint-config.git
[submodule "stylelint-config"]
path = stylelint-config
url = git@git.unl.edu:csce-310/stylelint-config.git
# Quick Start
Recursively clone this repository and `cd` into the root folder:
```
$ git clone --recursive git@git.unl.edu:csce-310/generators.git
$ cd generators
```
(If you forget `--recursive` when cloning, you can `cd` into your clone and run
`git submodule update --init --recursive` instead.)
Install dependencies:
```
$ npm install
```
And then serve the application locally:
```
$ npm start
```
When you are done, press control-c to stop the server.
Subproject commit 16524f5ac58d4b13378261f1ab29bc1acc2a2099
This diff is collapsed.
{
"name": "@unlcsce/generators-in-class",
"version": "1.0.0",
"description": "Starter code for the in-class demonstration of generators.",
"private": true,
"license": "UNLICENSED",
"scripts": {
"postinstall:stylelint-config": "cd stylelint-config && npm install",
"postinstall:eslint-config": "cd eslint-config && npm install",
"postinstall:app": "cd simplifier && npm install",
"postinstall": "run-s postinstall:**",
"lint:app": "cd simplifier && npm run lint",
"lint": "run-s --continue-on-error lint:**",
"test-once:app": "cd simplifier && npm run test-once",
"test-once": "run-s --continue-on-error test-once:**",
"test": "run-s test-once",
"start": "cd simplifier && npm run start",
"build:app": "cd simplifier && npm run build",
"build": "run-s build:**"
},
"devDependencies": {
"ghooks": "^2.0.4",
"npm-run-all": "^4.1.5"
},
"config": {
"ghooks": {
"pre-commit": "npm run lint"
}
}
}
{
"folders": [
{
"path": "."
}
],
"settings": {
"files.eol": "\n",
"files.exclude": {
"**/node_modules": true
},
"files.trimFinalNewlines": true,
"files.trimTrailingWhitespace": true
}
}
# dependencies
/node_modules
# testing
/coverage
# production
/build
# environments
.env.local
.env.development.local
.env.test.local
.env.production.local
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# misc
*~
.DS_Store
This diff is collapsed.
{
"name": "@unlcsce/simplifier",
"version": "1.0.0",
"description": "Starter code for the in-class demonstration of generators.",
"private": true,
"license": "UNLICENSED",
"scripts": {
"lint:css": "stylelint \"**/*.css\" \"**/*.module.css\" \"!coverage/**\"",
"lint:js": "eslint --max-warnings 0 ./src",
"lint": "run-s --continue-on-error lint:**",
"test-once": "react-scripts test --watchAll=false --coverage",
"test": "react-scripts test --watchAll --coverage",
"start": "react-scripts start",
"build": "react-scripts build",
"eject": "react-scripts eject"
},
"homepage": ".",
"dependencies": {
"@reduxjs/toolkit": "^1.8.3",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^14.3.0",
"classnames": "^2.3.1",
"npm-run-all": "^4.1.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^8.0.2",
"react-router-dom": "^6.3.0",
"react-scripts": "^5.0.1",
"workbox-background-sync": "^6.5.3",
"workbox-broadcast-update": "^6.5.3",
"workbox-cacheable-response": "^6.5.3",
"workbox-core": "^6.5.3",
"workbox-expiration": "^6.5.3",
"workbox-navigation-preload": "^6.5.3",
"workbox-precaching": "^6.5.3",
"workbox-range-requests": "^6.5.3",
"workbox-routing": "^6.5.3",
"workbox-strategies": "^6.5.3",
"workbox-streams": "^6.5.3"
},
"devDependencies": {
"@unlcsce/eslint-config": "file:../eslint-config",
"@unlcsce/stylelint-config": "file:../stylelint-config",
"eslint-plugin-jest-dom": "^4.0.2",
"stylelint": "^14.9.1"
},
"stylelint": {
"extends": "@unlcsce/stylelint-config"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest",
"plugin:jest-dom/recommended",
"plugin:testing-library/react",
"@unlcsce/eslint-config/react"
]
},
"jest": {
"clearMocks": true,
"collectCoverageFrom": [
"src/features/**/*.js"
],
"resetMocks": false,
"restoreMocks": false
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<meta
name="description"
content="Starter code for the lab on graph search."
/>
<meta name="theme-color" content="#d00000" />
<link rel="icon" href="%PUBLIC_URL%/logo.png" />
<link rel="icon" href="%PUBLIC_URL%/logo.svg" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo.png" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo.svg" />
<link rel="apple-touch-startup-image" href="%PUBLIC_URL%/logo.png" />
<link rel="apple-touch-startup-image" href="%PUBLIC_URL%/logo.svg" />
<title>Boolean Expression Simplifier</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
simplifier/public/logo.png

9.58 KiB

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 152 152">
<rect x="0" y="0" width="152" height="152" fill="rgba(0 0 0 / 100%)" />
<path d="M147,1H90V42h10V75.673L53.532,2.393,52.648,1H2V42H12v66H2v41H62V108H52V74.336l46.467,73.271L99.351,149H150V108H140V42h10V1Z" stroke-width="3" stroke="rgba(255 255 255 / 100%)" fill="rgba(208 0 0 / 100%)">
</path>
</svg>
{
"short_name": "Simplifier",
"name": "Boolean Expression Simplifier",
"description": "Starter code for the in-class demonstration of generators.",
"icons": [
{
"src": "logo.svg",
"type": "image/svg+xml",
"sizes": "192x192 512x512",
"purpose": "any maskable"
},
{
"src": "logo.png",
"type": "image/png",
"sizes": "512x512",
"purpose": "any maskable"
}
],
"start_url": ".",
"display": "standalone",
"orientation": "portrait",
"theme_color": "#d00000",
"background_color": "#ffffff"
}
import { Routes, Route } from 'react-router-dom';
import { Simplifier } from './features/expressions/simplifier.js';
export function App() {
const page =
<>
<h1>Boolean Expression Simplifier</h1>
<Simplifier />
<hr />
<div>
Examples:
<ul>
<li><code>(x && y) !== (x || y)</code></li>
<li><code>(!x && !y) || (x && !y) || (x && y)</code></li>
</ul>
</div>
</>;
return (
<Routes>
<Route path={'/*'} element={page} />
</Routes>
);
}
/* eslint-disable no-magic-numbers */
import { Operator, Variable, Environment } from './expressions.js';
export const EXAMPLE_ENVIRONMENT = new Environment(
[
new Variable('x'),
new Variable('y'),
],
[
new Operator('false', 0, () => false, 20),
new Operator('true', 0, () => true, 20),
],
[
new Operator('!', 1, (value) => !value, 16),
],
[
new Operator('&&', 2, (left, right) => left && right, 6),
new Operator('||', 2, (left, right) => left || right, 5),
new Operator('===', 2, (left, right) => left === right, 10),
new Operator('!==', 2, (left, right) => left !== right, 10),
],
);
export class Operator {
constructor(symbol, arity, semantics, precedence) {
this.symbol = symbol;
this.arity = arity;
this.apply = semantics;
this.precedence = precedence;
}
toString() {
return this.symbol;
}
}
export class Variable {
constructor(name) {
this.name = name;
}
get precedence() {
return Infinity;
}
evaluate(assignments) {
return assignments.get(this.name);
}
toString() {
return this.name;
}
}
export class Environment {
constructor(variables, nullaryOperators, unaryOperators, binaryOperators) {
console.assert(nullaryOperators.every((operator) => operator.arity === 0));
console.assert(unaryOperators.every((operator) => operator.arity === 1));
console.assert(binaryOperators.every((operator) => operator.arity === 2));
this.variables = variables;
this.nullaryOperators = nullaryOperators;
this.unaryOperators = unaryOperators;
this.binaryOperators = binaryOperators;
}
}
export class Expression {
constructor(operator, operands) {
console.assert(
operator.arity === operands.length,
`Tried to apply the operator ${operator} to ${operands.length} operands.`,
);
this.operator = operator;
this.operands = operands;
}
get precedence() {
return this.operator.precedence;
}
evaluate(assignments) {
return this.operator.apply(...this.operands.map((operand) => operand.evaluate(assignments)));
}
toString() {
const operands = this.operands.map(
(operand) => operand.precedence <= this.precedence ? `(${operand})` : `${operand}`,
);
switch (this.operator.arity) {
case 0:
return `${this.operator.symbol}`;
case 1:
return `${this.operator.symbol}${operands[0]}`;
case 2:
return `${operands[0]} ${this.operator.symbol} ${operands[1]}`;
default:
console.assert(
false,
`Tried to format the unsupported operator arity ${this.operator.arity} for ${this.operator}.`,
);
return `${this.operator}(${operands.join(', ')})`;
}
}
}
import { Expression } from './expressions.js';
function booleanAssignments(variables) {
const results = [];
for (let bits = 1 << variables.length; bits--;) {
const assignments = new Map();
for (let index = variables.length; index--;) {
assignments.set(variables[index].name, ((bits >> index) & 1) !== 0);
}
results.push(assignments);
}
return results;
}
function matches(expression, semantics, variables) {
for (const assignments of booleanAssignments(variables)) {
if (expression.evaluate(assignments) !== semantics(assignments)) {
return false;
}
}
return true;
}
function expressionsOfSize(environment, operatorCount) {
const results = [];
if (operatorCount === 0) {
for (const variable of environment.variables) {
results.push(variable);
}
} else {
for (const operator of environment.unaryOperators) {
for (const subexpression of expressionsOfSize(environment, operatorCount - 1)) {
results.push(new Expression(operator, [subexpression]));
}
}
for (let leftOperatorCount = 0; leftOperatorCount < operatorCount; ++leftOperatorCount) {
const rightOperatorCount = operatorCount - leftOperatorCount - 1;
for (const operator of environment.binaryOperators) {
for (const leftSubexpression of expressionsOfSize(environment, leftOperatorCount)) {
for (const rightSubexpression of expressionsOfSize(environment, rightOperatorCount)) {
results.push(new Expression(operator, [leftSubexpression, rightSubexpression]));
}
}
}
}
}
return results;
}
function expressionsUpToSize(environment, maximumOperatorCount) {
const results = [];
for (const operator of environment.nullaryOperators) {
results.push(new Expression(operator, []));
}
for (let operatorCount = 0; operatorCount <= maximumOperatorCount; ++operatorCount) {
results.push(...expressionsOfSize(environment, operatorCount));
}
return results;
}
export function simplify(semantics, environment, maximumOperatorCount) {
for (const expression of expressionsUpToSize(environment, maximumOperatorCount)) {
if (matches(expression, semantics, environment.variables)) {
return expression;
}
}
return undefined;
}
import { useState } from 'react';
import { EXAMPLE_ENVIRONMENT } from './exampleEnvironment.js';
import { simplify } from './simplification.js';
const MAXIMUM_NUMBER_OF_OPERATORS = 4;
const SYNTAX_ERROR = '[no simplification available; check the expression syntax]';
const NO_SIMPLIFICATION = '[no simplification available; try allowing more operators]';
export function Simplifier() {
const [expression, setExpression] = useState('');
const [operatorCount, setOperatorCount] = useState(1);
const onExpressionChange = (event) => setExpression(event.target.value);
const onOperatorCountChange = (event) => setOperatorCount(Number(event.target.value));
let simplification = SYNTAX_ERROR;
try {
// eslint-disable-next-line no-eval
const rawSemantics = eval(`(${EXAMPLE_ENVIRONMENT.variables.join(', ')}) => ${expression}`);
const semantics = (assignments) =>
rawSemantics(...EXAMPLE_ENVIRONMENT.variables.map((variable) => assignments.get(variable.name)));
simplification = simplify(semantics, EXAMPLE_ENVIRONMENT, operatorCount);
} catch (_) {} // eslint-disable-line no-empty
if (simplification === undefined) {
simplification = NO_SIMPLIFICATION;
}
return (
<section>
<div>
Boolean variables:<br />
<code>x</code> and <code>y</code>
</div>
<hr />
<div>
<label>Expression to simplify:<br />
<input
type={'text'}
autoComplete={'off'}
value={expression}
onChange={onExpressionChange} />
</label>
</div>
<hr />
<div>
<label>Maximum number of operators:<br />
<input
type={'number'}
autoComplete={'off'}
min={0}
max={MAXIMUM_NUMBER_OF_OPERATORS}
value={operatorCount}
onChange={onOperatorCountChange} />
</label>
</div>
<hr />
<div>
<label>Simplified:<br />
<code><output>{`${simplification}`}</output></code>
</label>
</div>
</section>
);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment