Skip to content
Snippets Groups Projects
Commit 4e41cf30 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
.eslintrc 0 → 100644
{
"env": {
"es6": true,
"browser": true,
"jquery": true,
},
"parserOptions": {
"sourceType": "module",
},
"rules": {
"no-await-in-loop": "warn",
"no-compare-neg-zero": "warn",
"no-cond-assign": "warn",
"no-console": "off",
"no-constant-condition": "warn",
"no-control-regex": "warn",
"no-debugger": "warn",
"no-dupe-args": "warn",
"no-dupe-keys": "warn",
"no-duplicate-case": "warn",
"no-empty": "warn",
"no-empty-character-class": "warn",
"no-ex-assign": "warn",
"no-extra-boolean-cast": "warn",
"no-extra-parens": ["warn", "all", {
"nestedBinaryExpressions": false,
}],
"no-extra-semi": "warn",
"no-func-assign": "warn",
"no-inner-declarations": "warn",
"no-invalid-regexp": "warn",
"no-irregular-whitespace": "warn",
"no-obj-calls": "warn",
"no-prototype-builtins": "warn",
"no-regex-spaces": "warn",
"no-sparse-arrays": "warn",
"no-template-curly-in-string": "warn",
"no-unexpected-multiline": "warn",
"no-unreachable": "warn",
"no-unsafe-finally": "warn",
"no-unsafe-negation": "warn",
"use-isnan": "warn",
"valid-jsdoc": "warn",
"valid-typeof": "warn",
"accessor-pairs": "warn",
"array-callback-return": "warn",
"block-scoped-var": "warn",
"class-methods-use-this": "warn",
"complexity": "off",
"consistent-return": "warn",
"curly": "warn",
"default-case": "off",
"dot-location": "warn",
"dot-notation": "warn",
"eqeqeq": "warn",
"guard-for-in": "off", // see no-restricted-syntax
"no-alert": "warn",
"no-caller": "warn",
"no-case-declarations": "warn",
"no-div-regex": "warn",
"no-else-return": "warn",
"no-empty-function": "warn",
"no-empty-pattern": "warn",
"no-eq-null": "warn",
"no-eval": "warn",
"no-extend-native": "off",
"no-extra-bind": "warn",
"no-extra-label": "warn",
"no-fallthrough": "warn",
"no-floating-decimal": "warn",
"no-global-assign": "warn",
"no-implicit-coercion": "warn",
"no-implicit-globals": "off",
"no-implied-eval": "warn",
"no-invalid-this": "off",
"no-iterator": "warn",
"no-labels": "off",
"no-lone-blocks": "off",
"no-loop-func": "warn",
"no-magic-numbers": ["warn", {
"ignore": [-1, 0, 0.001, 0.5, 1, 2, 10, 100, 180, 360, 1000],
"detectObjects": true,
}],
"no-multi-spaces": "warn",
"no-multi-str": "warn",
"no-new": "warn",
"no-new-func": "warn",
"no-new-wrappers": "warn",
"no-octal": "warn",
"no-octal-escape": "warn",
"no-param-reassign": "warn",
"no-proto": "warn",
"no-redeclare": "warn",
"no-restricted-properties": "warn",
"no-return-assign": "warn",
"no-return-await": "warn",
"no-script-url": "warn",
"no-self-assign": "warn",
"no-self-compare": "warn",
"no-sequences": "warn",
"no-throw-literal": "warn",
"no-unmodified-loop-condition": "warn",
"no-unused-expressions": "warn",
"no-unused-labels": "warn",
"no-useless-call": "warn",
"no-useless-concat": "warn",
"no-useless-escape": "warn",
"no-useless-return": "warn",
"no-void": "warn",
"no-warning-comments": "warn",
"no-with": "warn",
"prefer-promise-reject-errors": "warn",
"radix": "warn",
"require-await": "warn",
"vars-on-top": "off",
"wrap-iife": "warn",
"yoda": "warn",
"strict": ["warn", "never"],
"init-declarations": "warn",
"no-catch-shadow": "warn",
"no-delete-var": "warn",
"no-label-var": "warn",
"no-restricted-globals": "warn",
"no-shadow": "warn",
"no-shadow-restricted-names": "warn",
"no-undef": "warn",
"no-undef-init": "off",
"no-undefined": "off",
"no-unused-vars": ["warn", {
"varsIgnorePattern": "_+",
}],
"no-use-before-define": "warn",
"array-bracket-spacing": "warn",
"block-spacing": "warn",
"brace-style": "warn",
"camelcase": "warn",
"capitalized-comments": "off",
"comma-dangle": ["warn", "always-multiline"],
"comma-spacing": "warn",
"comma-style": "warn",
"computed-property-spacing": "warn",
"consistent-this": "warn",
"eol-last": "warn",
"func-call-spacing": "warn",
"func-name-matching": "warn",
"func-names": ["warn", "as-needed"],
"func-style": "off",
"id-blacklist": ["warn", "temp"],
"id-length": "off",
"id-match": "off",
"indent": ["warn", 2],
"jsx-quotes": "warn",
"key-spacing": "warn",
"keyword-spacing": "warn",
"line-comment-position": "off",
"linebreak-style": "warn",
"lines-around-comment": "off",
"lines-around-directive": "warn",
"max-depth": "off",
"max-len": "off",
"max-lines": "off",
"max-nested-callbacks": "off",
"max-params": "off",
"max-statements": "off",
"max-statements-per-line": "warn",
"multiline-ternary": "off",
"new-cap": "warn",
"new-parens": "warn",
"newline-after-var": "off",
"newline-before-return": "off",
"newline-per-chained-call": "off",
"no-array-constructor": "warn",
"no-bitwise": "off",
"no-continue": "off",
"no-inline-comments": "off",
"no-lonely-if": "warn",
"no-mixed-operators": "off",
"no-mixed-spaces-and-tabs": "warn",
"no-multi-assign": "warn",
"no-multiple-empty-lines": ["warn", {
"maxBOF": 0,
"max": 1,
"maxEOF": 0,
}],
"no-negated-condition": "off",
"no-nested-ternary": "off",
"no-new-object": "warn",
"no-plusplus": "off",
"no-restricted-syntax": ["warn", {
"selector": "ForInStatement",
"message": "for-in loop used (did you mean to write a for-of loop?)",
}, {
"selector": "BinaryExpression[operator='in']",
"message": "in operator used (did you mean to call a membership-testing method?)",
}],
"no-tabs": "warn",
"no-ternary": "off",
"no-trailing-spaces": "warn",
"no-underscore-dangle": ["warn", {
"allowAfterThis": true,
"allowAfterSuper": true,
}],
"no-unneeded-ternary": "warn",
"no-whitespace-before-property": "warn",
"nonblock-statement-body-position": "off", // see curly
"object-curly-newline": "warn",
"object-curly-spacing": "warn",
"object-property-newline": "warn",
"one-var": ["warn", "never"],
"one-var-declaration-per-line": "warn",
"operator-assignment": "warn",
"operator-linebreak": "warn",
"padded-blocks": ["warn", "never"],
"quote-props": ["warn", "as-needed"],
"quotes": ["warn", "single"],
"require-jsdoc": "off",
"semi": "warn",
"semi-spacing": "warn",
"sort-keys": "off",
"sort-vars": "off",
"space-before-blocks": "off",
"space-before-function-paren": ["warn", "never"],
"space-in-parens": "warn",
"space-infix-ops": "warn",
"space-unary-ops": "warn",
"spaced-comment": "warn",
"template-tag-spacing": "off",
"unicode-bom": "warn",
"wrap-regex": "off",
"arrow-body-style": "warn",
"arrow-parens": "warn",
"arrow-spacing": "warn",
"constructor-super": "warn",
"generator-star-spacing": ["warn", "neither"],
"no-class-assign": "warn",
"no-confusing-arrow": "off",
"no-const-assign": "warn",
"no-dupe-class-members": "warn",
"no-duplicate-imports": "warn",
"no-new-symbol": "warn",
"no-restricted-imports": "off",
"no-this-before-super": "warn",
"no-useless-computed-key": "warn",
"no-useless-constructor": "warn",
"no-useless-rename": "warn",
"no-var": "warn",
"object-shorthand": "warn",
"prefer-arrow-callback": "warn",
"prefer-const": "warn",
"prefer-destructuring": "off",
"prefer-numeric-literals": "warn",
"prefer-rest-params": "warn",
"prefer-spread": "warn",
"prefer-template": "warn",
"require-yield": "warn",
"rest-spread-spacing": "warn",
"sort-imports": "off",
"symbol-description": "warn",
"template-curly-spacing": "warn",
"yield-star-spacing": "warn",
},
}
*~
.idea
*.aux
*.fdb_latexmk
*.fls
*.log
*.synctex.gz
*.pdf
{
"defaultSeverity": "warning",
"rules": {
"at-rule-empty-line-before": [ "always", {
except: [
"blockless-after-same-name-blockless",
"first-nested",
],
ignore: ["after-comment"],
} ],
"at-rule-name-case": "lower",
"at-rule-name-space-after": "always-single-line",
"at-rule-semicolon-newline-after": "always",
"block-closing-brace-empty-line-before": "never",
"block-closing-brace-newline-after": "always",
"block-closing-brace-newline-before": "always-multi-line",
"block-closing-brace-space-before": "always-single-line",
"block-no-empty": true,
"block-opening-brace-newline-after": "always-multi-line",
"block-opening-brace-space-after": "always-single-line",
"block-opening-brace-space-before": "always",
"color-hex-case": "lower",
"color-hex-length": "short",
"color-no-invalid-hex": true,
"comment-empty-line-before": [ "always", {
except: ["first-nested"],
ignore: ["stylelint-commands"],
} ],
"comment-no-empty": true,
"comment-whitespace-inside": "always",
"custom-property-empty-line-before": [ "always", {
except: [
"after-custom-property",
"first-nested",
],
ignore: [
"after-comment",
"inside-single-line-block",
],
} ],
"declaration-bang-space-after": "never",
"declaration-bang-space-before": "always",
"declaration-block-no-duplicate-properties": [ true, {
ignore: ["consecutive-duplicates-with-different-values"],
} ],
"declaration-block-no-redundant-longhand-properties": true,
"declaration-block-no-shorthand-property-overrides": true,
"declaration-block-semicolon-newline-after": "always-multi-line",
"declaration-block-semicolon-space-after": "always-single-line",
"declaration-block-semicolon-space-before": "never",
"declaration-block-single-line-max-declarations": 1,
"declaration-block-trailing-semicolon": "always",
"declaration-colon-newline-after": "always-multi-line",
"declaration-colon-space-after": "always-single-line",
"declaration-colon-space-before": "never",
"declaration-empty-line-before": [ "always", {
except: [
"after-declaration",
"first-nested",
],
ignore: [
"after-comment",
"inside-single-line-block",
],
} ],
"font-family-no-duplicate-names": true,
"function-calc-no-unspaced-operator": true,
"function-comma-newline-after": "always-multi-line",
"function-comma-space-after": "always-single-line",
"function-comma-space-before": "never",
"function-linear-gradient-no-nonstandard-direction": true,
"function-max-empty-lines": 0,
"function-name-case": "lower",
"function-parentheses-newline-inside": "always-multi-line",
"function-parentheses-space-inside": "never-single-line",
"function-whitespace-after": "always",
"indentation": 2,
"keyframe-declaration-no-important": true,
"length-zero-no-unit": true,
"max-empty-lines": 1,
"media-feature-colon-space-after": "always",
"media-feature-colon-space-before": "never",
"media-feature-name-case": "lower",
"media-feature-name-no-unknown": true,
"media-feature-parentheses-space-inside": "never",
"media-feature-range-operator-space-after": "always",
"media-feature-range-operator-space-before": "always",
"media-query-list-comma-newline-after": "always-multi-line",
"media-query-list-comma-space-after": "always-single-line",
"media-query-list-comma-space-before": "never",
"no-empty-source": true,
"no-eol-whitespace": true,
"no-extra-semicolons": true,
"no-invalid-double-slash-comments": true,
"no-missing-end-of-source-newline": true,
"number-leading-zero": "always",
"property-case": "lower",
"property-no-unknown": true,
"rule-empty-line-before": [ "always-multi-line", {
except: ["first-nested"],
ignore: ["after-comment"],
} ],
"selector-attribute-brackets-space-inside": "never",
"selector-attribute-operator-space-after": "never",
"selector-attribute-operator-space-before": "never",
"selector-combinator-space-after": "always",
"selector-combinator-space-before": "always",
"selector-descendant-combinator-no-non-space": true,
"selector-list-comma-newline-after": "always",
"selector-list-comma-space-before": "never",
"selector-max-empty-lines": 0,
"selector-pseudo-class-case": "lower",
"selector-pseudo-class-no-unknown": true,
"selector-pseudo-class-parentheses-space-inside": "never",
"selector-pseudo-element-case": "lower",
"selector-pseudo-element-colon-notation": "double",
"selector-pseudo-element-no-unknown": true,
"selector-type-case": "lower",
"selector-type-no-unknown": true,
"shorthand-property-no-redundant-values": true,
"string-no-newline": true,
"unit-case": "lower",
"unit-no-unknown": true,
"value-list-comma-newline-after": "always-multi-line",
"value-list-comma-space-after": "always-single-line",
"value-list-comma-space-before": "never",
"value-list-max-empty-lines": 0,
},
}
{
"ecmaVersion": 6,
"libs": [
"browser",
"jquery"
],
"loadEagerly": [],
"dontLoad": [
"node_module/**"
],
"plugins": {
"doc_comment": true,
"complete_strings": {
"maxLength": 15
}
}
}
\ No newline at end of file
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Expression Simplifier</title>
<script defer="true" src="libraries/jquery/jquery.min.js"></script>
<script defer="true" type="module" src="js/index.js"></script>
</head>
<body>
<div>
Boolean variables: <code>x</code> and <code>y</code>
</div>
<div>
<label>Expression to simplify:
<input id="unsimplified" type="text" autocomplete="off" value="">
</label>
<label>Maximum number of operators:
<input id="operators" type="number" autocomplete="off" min="0" value="1">
</label>
</div>
<div>
<label>Simplified:
<code><output id="simplified"></output></code>
</label>
</div>
<hr>
<div>
Examples:
<ul>
<li><code>(x && y) !== (x || y)</code></li>
<li><code>(!x && !y) || (x && !y) || (x && y)</code></li>
</ul>
</div>
</body>
</html>
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() { // eslint-disable-line class-methods-use-this
return Infinity;
}
evaluate(assignments) {
return assignments.get(this.name);
}
toString() {
return this.name;
}
}
export class Environment {
constructor(variables, unaryOperators, binaryOperators) {
console.assert(unaryOperators.every((operator) => operator.arity === 1));
console.assert(binaryOperators.every((operator) => operator.arity === 2));
this.variables = variables;
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}`);
if (this.operator.arity === 1) {
return `${this.operator.symbol}${operands[0]}`;
}
if (this.operator.arity === 2) {
return `${operands[0]} ${this.operator.symbol} ${operands[1]}`;
}
console.assert(false, `Tried to print an expression with the unsupported operator arity ${this.operator.arity} for ${this.operator}.`);
return `${this.operator}(${operands.join(', ')})`;
}
}
import {Operator, Variable, Environment} from './expressions.js';
import {simplify} from './simplifier.js';
/* eslint-disable no-magic-numbers */
const ENVIRONMENT = new Environment(
[
new Variable('x'),
new Variable('y'),
],
[
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),
]
);
function simplifyUserInput() {
try {
const semantics = eval(`(${ENVIRONMENT.variables.join(', ')}) => ${$('#unsimplified').val()}`); // eslint-disable-line no-eval
const simplification = simplify(semantics, ENVIRONMENT, $('#operators').val());
$('#simplified').text(simplification !== undefined ? simplification : '[no simplification available; try allowing more operators]');
} catch (_) {
$('#simplified').text('[syntax error in input]');
}
}
$(() => {
$('#unsimplified, #operators').on('input', simplifyUserInput);
simplifyUserInput();
});
// examples of unsimplified boolean expressions
window.firstExample = (x, y) => (x && y) !== (x || y);
window.secondExample = (x, y) => (!x && !y) || (x && !y) || (x && y);
// for debugging in the console:
window.ENVIRONMENT = ENVIRONMENT;
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) {
const semanticsTakingMap = (assignments) => semantics(...variables.map((variable) => assignments.get(variable.name)));
for (const assignments of booleanAssignments(variables)) {
if (expression.evaluate(assignments) !== semanticsTakingMap(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 (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;
}
// for debugging in the console:
window.expressionsOfSize = expressionsOfSize;
window.expressionsUpToSize = expressionsUpToSize;
window.booleanAssignments = booleanAssignments;
window.matches = matches;
window.simplify = simplify;
window.logAll = (generator) => [...generator].map((expression) => console.log(`${expression}`));
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment