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

Initial commit.

parents
No related branches found
No related tags found
No related merge requests found
Showing
with 548 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 "stylelint-config"]
path = stylelint-config
url = git@git.unl.edu:soft-core/soft-260/stylelint-config.git
[submodule "eslint-config"]
path = eslint-config
url = git@git.unl.edu:soft-core/soft-260/eslint-config.git
# Quick Start
Recursively clone this repository and `cd` into the root folder:
```
$ git clone --recursive git@git.unl.edu:soft-core/soft-260/generators-in-class.git
$ cd generators-in-class
```
(If you forget `--recursive` when cloning, you can `cd` into your clone and run
`git submodule update --init --recursive` instead.)
Install dependencies:
```
$ npm install
```
(Near the end you may see some warnings because `create-react-app` transitively
depends on some deprecated packages.)
And then serve the application locally:
```
$ npm start
```
When you are done, press control-c to stop the server.
Subproject commit 24df42fb655d234b83c93b0fb24d012e4d9ecb58
This diff is collapsed.
{
"name": "@unlsoft/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": "LF",
"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": "@unlsoft/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.6.0",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.0.0",
"@testing-library/user-event": "^13.1.9",
"classnames": "^2.3.1",
"npm-run-all": "^4.1.5",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-redux": "^7.2.4",
"react-router-dom": "^5.2.0",
"react-scripts": "^4.0.3",
"workbox-background-sync": "^5.1.3",
"workbox-broadcast-update": "^5.1.3",
"workbox-cacheable-response": "^5.1.3",
"workbox-core": "^5.1.3",
"workbox-expiration": "^5.1.3",
"workbox-google-analytics": "^5.1.3",
"workbox-navigation-preload": "^5.1.3",
"workbox-precaching": "^5.1.3",
"workbox-range-requests": "^5.1.3",
"workbox-routing": "^5.1.3",
"workbox-strategies": "^5.1.3",
"workbox-streams": "^5.1.3"
},
"devDependencies": {
"@unlsoft/eslint-config": "file:../eslint-config",
"@unlsoft/stylelint-config": "file:../stylelint-config",
"eslint-plugin-jest-dom": "^3.9.0",
"stylelint": "^13.13.1"
},
"stylelint": {
"extends": "@unlsoft/stylelint-config"
},
"eslintConfig": {
"extends": [
"react-app",
"@unlsoft/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="rgba(208 0 0 / 100%)" />
<link rel="icon" href="%PUBLIC_URL%/logo.svg" />
<link rel="apple-touch-icon" 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>
<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"
}
],
"start_url": ".",
"display": "standalone",
"orientation": "portrait",
"theme_color": "rgba(208 0 0 / 100%)",
"background_color": "rgba(255 255 255 / 100%)"
}
import { Route } from 'react-router-dom';
import { Simplifier } from './features/expressions/simplifier.js';
export function App() {
return (
<>
<Route exact path={'/'}>
<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>
</Route>
</>
);
}
/* 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>
);
}
:root {
/* Colors */
--letterbox-color: rgba(0 0 0 / 100%);
--app-background-color: rgba(239 239 239 / 100%);
--font-color: rgba(0 0 0 / 100%);
/* Sizes */
--minimum-app-size: 300px;
}
body {
margin: 0;
font-family: sans-serif;
text-align: center;
color: var(--font-color);
}
#root {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: var(--letterbox-color);
}
#portrait {
position: relative;
box-sizing: border-box;
margin: auto;
padding: 1em;
min-width: var(--minimum-app-size);
min-height: var(--minimum-app-size);
width: 100%;
height: 100%;
max-width: 62.5vh;
background: var(--app-background-color);
overflow-x: hidden;
overflow-y: scroll;
transform: scale(1);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment