Compare commits

...

20 Commits

Author SHA1 Message Date
df58d7d122 Updated workflow
All checks were successful
Build & test / build (push) Successful in 2m28s
2024-12-17 17:19:55 +00:00
621925b30c
Do not error on lint warnings
All checks were successful
Build & test / build (push) Successful in 12m3s
2024-10-05 15:25:00 +02:00
d75c480e6a
Added setup rust toolchain
Some checks failed
Build & test / build (push) Failing after 12m13s
2024-10-05 15:08:08 +02:00
8df3809a6a
Replaced .github workflow with gitea workflow
Some checks failed
Build & test / build (push) Failing after 5s
2024-10-05 14:56:34 +02:00
Martin Berg Alstad
25b708a6fd fmt. Removed some usage of macros 2024-09-05 21:57:50 +02:00
Martin Berg Alstad
1350e09bde Simplifies some parsing functions 2024-07-31 19:39:01 +02:00
Martin Berg Alstad
ee86e3be2f Merged branches 2024-07-18 18:41:04 +02:00
Martin Berg Alstad
3dad5754f9 fmt and todo 2024-07-18 17:45:57 +02:00
Martin Berg Alstad
57b5f82c8d Removed one(1) argument from a macro! 2024-07-18 17:06:13 +02:00
Martin Berg Alstad
d948471ed5 Updated lib to latest version and fmt 2024-07-17 13:04:05 +02:00
Martin Berg Alstad
4447d800df Fixed a few bugs in absorption and doubled the code😎 2024-07-14 19:31:45 +02:00
Martin Berg Alstad
abad6181cb format with rustfmt 2024-07-14 19:08:58 +02:00
Martin Berg Alstad
60b9bdc28a More tests 2024-07-08 11:56:32 +02:00
Martin Berg Alstad
9b6a24e222 Merged a few branches in absorption law together 2024-07-04 13:05:52 +02:00
Martin Berg Alstad
7044dc304d Helper functions on Expression 2024-07-02 01:38:35 +02:00
Martin Berg Alstad
1c6d400f4a Fixed bugs in simplify 2024-06-28 17:58:56 +02:00
Martin Berg Alstad
ba4f3b78ad Merge branch 'master' into operations-fix 2024-06-27 19:09:50 +02:00
Martin Berg Alstad
d18b7bdaf8 Operations on absorption law and split the function in two.
Distributive law operations.

Added some new unit tests.
2024-06-27 18:31:37 +02:00
Martin Berg Alstad
08d7fa2319 Fixed extra parentheses on or expressions wrapped in not.
Fixed order of operations in eliminate not functions
2024-06-26 18:35:23 +02:00
Martin Berg Alstad
21f66d9409 Fixed bug surrounding implies with parentheses when unnecessary.
Fixed correct saving of operation in elimination of implication.

Must use macro on helpers
2024-06-26 17:35:53 +02:00
29 changed files with 1758 additions and 605 deletions

View File

@ -12,6 +12,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Rust toolchain
# https://github.com/actions-rust-lang/setup-rust-toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
rustflags: "" # Override the default error on warnings
cache: false
- name: Build
run: cargo build --verbose
- name: Run tests

View File

@ -7,10 +7,10 @@ on:
jobs:
deploy:
name: Build and run
runs-on: self-hosted
runs-on: host
steps:
- name: Check out the repo
uses: actions/checkout@v4
- name: Run
run: docker-compose up -d --build
run: docker compose up -d --build

102
Cargo.lock generated
View File

@ -48,6 +48,7 @@ dependencies = [
"matchit",
"memchr",
"mime",
"multer",
"percent-encoding",
"pin-project-lite",
"rustversion",
@ -136,12 +137,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "derive"
version = "1.0.0"
source = "git+https://github.com/emberal/rust-lib.git?tag=1.1.1#752d1a9d102a6ed7026909a46d4e0b2cb1e0cb3d"
name = "encoding_rs"
version = "0.8.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
dependencies = [
"quote",
"syn",
"cfg-if",
]
[[package]]
@ -174,12 +175,6 @@ version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]]
name = "futures-sink"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
[[package]]
name = "futures-task"
version = "0.3.30"
@ -296,6 +291,15 @@ dependencies = [
"tokio",
]
[[package]]
name = "into-response-derive"
version = "1.1.0"
source = "git+https://github.com/emberal/rust-lib?tag=1.4.1-hotfix#8cbb2757a5030622ec32931c44ea2d489e85640a"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "itoa"
version = "1.0.11"
@ -310,15 +314,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lib"
version = "1.1.1"
source = "git+https://github.com/emberal/rust-lib.git?tag=1.1.1#752d1a9d102a6ed7026909a46d4e0b2cb1e0cb3d"
version = "1.4.1-hotfix"
source = "git+https://github.com/emberal/rust-lib?tag=1.4.1-hotfix#8cbb2757a5030622ec32931c44ea2d489e85640a"
dependencies = [
"axum",
"derive",
"into-response-derive",
"nom",
"serde",
"thiserror",
"tokio",
"tokio-util",
"tower",
"tower-http",
"tracing",
@ -381,6 +385,23 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "multer"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b"
dependencies = [
"bytes",
"encoding_rs",
"futures-util",
"http",
"httparse",
"memchr",
"mime",
"spin",
"version_check",
]
[[package]]
name = "nom"
version = "7.1.3"
@ -578,8 +599,6 @@ dependencies = [
"serde",
"tokio",
"tower-http",
"tracing",
"tracing-subscriber",
]
[[package]]
@ -598,6 +617,12 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "syn"
version = "2.0.66"
@ -621,6 +646,26 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
[[package]]
name = "thiserror"
version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thread_local"
version = "1.1.8"
@ -658,19 +703,6 @@ dependencies = [
"syn",
]
[[package]]
name = "tokio-util"
version = "0.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
]
[[package]]
name = "tower"
version = "0.4.13"
@ -706,9 +738,9 @@ dependencies = [
[[package]]
name = "tower-layer"
version = "0.3.2"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
[[package]]
name = "tower-service"
@ -786,6 +818,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"

View File

@ -13,9 +13,6 @@ tokio = { version = "1.38.0", features = ["macros", "rt-multi-thread"] }
serde = { version = "1.0.203", features = ["derive", "rc"] }
# API
axum = { version = "0.7.5", features = ["macros"] }
tower-http = { version = "0.5.2", features = ["cors", "trace", "normalize-path"] }
# Logging
tracing = "0.1.40"
tracing-subscriber = "0.3.18"
tower-http = { version = "0.5.2", features = ["cors"] }
lib = { git = "https://github.com/emberal/rust-lib.git", tag = "1.1.1", features = ["axum", "vec", "nom", "serde", "derive", "tokio"] }
lib = { git = "https://github.com/emberal/rust-lib", tag = "1.4.1-hotfix", features = ["axum", "iter", "nom", "serde", "derive"] }

View File

@ -12,7 +12,7 @@ RUN npm install
RUN USER=root npm install -g @typespec/compiler && npm install -g @redocly/cli
RUN npm run tsp-compile && npm run redoc-build
FROM rust:1.79 as build
FROM rust:1.80.1 as build
COPY --from=static ./src/resources/static ./static

View File

@ -10,8 +10,6 @@ tags:
paths:
/:
get:
tags:
- Common
operationId: Index_index
summary: Information
description: Information about this API.
@ -23,11 +21,10 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Responses.InfoResponse'
/is-valid/{exp}:
get:
tags:
- Common
- Expression
/is-valid/{exp}:
get:
operationId: Index_isValid
summary: Check if an expression is valid
description: Check if an expression is valid.
@ -44,10 +41,11 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Responses.IsValidResponse'
/openapi:
get:
tags:
- Common
- Expression
/openapi:
get:
operationId: Index_openAPI
summary: The OpenAPI specification
description: The OpenAPI specification for this API.
@ -59,11 +57,10 @@ paths:
text/html:
schema:
type: string
tags:
- Common
/simplify/table/{exp}:
get:
tags:
- Expression
- Table
operationId: Simplify_simplifyTable
summary: Simplify and generate a truth table
parameters:
@ -77,6 +74,7 @@ paths:
required: false
schema:
$ref: '#/components/schemas/Options.SimplifyTableOptions'
explode: false
responses:
'200':
description: The request has succeeded.
@ -84,10 +82,11 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Responses.SimplifyTableResponse'
/simplify/{exp}:
get:
tags:
- Expression
- Table
/simplify/{exp}:
get:
operationId: Simplify_simplify
summary: Simplify a truth expression
parameters:
@ -101,6 +100,7 @@ paths:
required: false
schema:
$ref: '#/components/schemas/Options.SimplifyOptions'
explode: false
responses:
'200':
description: The request has succeeded.
@ -108,10 +108,10 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Responses.SimplifyResponse'
tags:
- Expression
/table/{exp}:
get:
tags:
- Table
operationId: TruthTable_simplify
summary: Generate a truth table
parameters:
@ -125,6 +125,7 @@ paths:
required: false
schema:
$ref: '#/components/schemas/Options.TableOptions'
explode: false
responses:
'200':
description: The request has succeeded.
@ -132,14 +133,10 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Responses.TruthTableResponse'
tags:
- Table
components:
schemas:
Expression:
oneOf:
- $ref: '#/components/schemas/Models.ExpressionNot'
- $ref: '#/components/schemas/Models.ExpressionBinary'
- $ref: '#/components/schemas/Models.ExpressionAtomic'
title: A truth expression
Models.BinaryOperator:
type: string
enum:
@ -147,6 +144,12 @@ components:
- OR
- IMPLICATION
title: A binary operator
Models.Expression:
oneOf:
- $ref: '#/components/schemas/Models.ExpressionNot'
- $ref: '#/components/schemas/Models.ExpressionBinary'
- $ref: '#/components/schemas/Models.ExpressionAtomic'
title: A truth expression
Models.ExpressionAtomic:
type: object
required:
@ -165,7 +168,7 @@ components:
properties:
left:
allOf:
- $ref: '#/components/schemas/Expression'
- $ref: '#/components/schemas/Models.Expression'
title: The left expression
operator:
allOf:
@ -173,7 +176,7 @@ components:
title: The binary operator
right:
allOf:
- $ref: '#/components/schemas/Expression'
- $ref: '#/components/schemas/Models.Expression'
title: The right expression
title: A binary expression
Models.ExpressionNot:
@ -183,7 +186,7 @@ components:
properties:
not:
allOf:
- $ref: '#/components/schemas/Expression'
- $ref: '#/components/schemas/Models.Expression'
title: The expression to negate
title: The inverse of an expression
Models.TruthTable:
@ -326,7 +329,7 @@ components:
title: Steps taken to simplify
expression:
allOf:
- $ref: '#/components/schemas/Expression'
- $ref: '#/components/schemas/Models.Expression'
title: The simplified expression
description: Response after simplifying an expression.
title: Simplify Response
@ -352,7 +355,7 @@ components:
title: Steps taken to simplify
expression:
allOf:
- $ref: '#/components/schemas/Expression'
- $ref: '#/components/schemas/Models.Expression'
title: The simplified expression
truthTable:
allOf:

422
spec/package-lock.json generated
View File

@ -9,9 +9,15 @@
"version": "0.1.0",
"dependencies": {
"@typespec/compiler": "latest",
"@typespec/openapi3": "^0.57.0"
"@typespec/openapi3": "^0.60.0"
}
},
"node_modules/@apidevtools/swagger-methods": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz",
"integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==",
"license": "MIT"
},
"node_modules/@babel/code-frame": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz",
@ -49,6 +55,33 @@
"node": ">=6.9.0"
}
},
"node_modules/@babel/runtime": {
"version": "7.25.7",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz",
"integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==",
"license": "MIT",
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@humanwhocodes/momoa": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@humanwhocodes/momoa/-/momoa-2.0.4.tgz",
"integrity": "sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==",
"license": "Apache-2.0",
"engines": {
"node": ">=10.10.0"
}
},
"node_modules/@jsdevtools/ono": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
"license": "MIT"
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -84,6 +117,140 @@
"node": ">= 8"
}
},
"node_modules/@readme/better-ajv-errors": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@readme/better-ajv-errors/-/better-ajv-errors-1.6.0.tgz",
"integrity": "sha512-9gO9rld84Jgu13kcbKRU+WHseNhaVt76wYMeRDGsUGYxwJtI3RmEJ9LY9dZCYQGI8eUZLuxb5qDja0nqklpFjQ==",
"license": "Apache-2.0",
"dependencies": {
"@babel/code-frame": "^7.16.0",
"@babel/runtime": "^7.21.0",
"@humanwhocodes/momoa": "^2.0.3",
"chalk": "^4.1.2",
"json-to-ast": "^2.0.3",
"jsonpointer": "^5.0.0",
"leven": "^3.1.0"
},
"engines": {
"node": ">=14"
},
"peerDependencies": {
"ajv": "4.11.8 - 8"
}
},
"node_modules/@readme/better-ajv-errors/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/@readme/better-ajv-errors/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/@readme/better-ajv-errors/node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/@readme/better-ajv-errors/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT"
},
"node_modules/@readme/better-ajv-errors/node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@readme/better-ajv-errors/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@readme/json-schema-ref-parser": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@readme/json-schema-ref-parser/-/json-schema-ref-parser-1.2.0.tgz",
"integrity": "sha512-Bt3QVovFSua4QmHa65EHUmh2xS0XJ3rgTEUPH998f4OW4VVJke3BuS16f+kM0ZLOGdvIrzrPRqwihuv5BAjtrA==",
"license": "MIT",
"dependencies": {
"@jsdevtools/ono": "^7.1.3",
"@types/json-schema": "^7.0.6",
"call-me-maybe": "^1.0.1",
"js-yaml": "^4.1.0"
}
},
"node_modules/@readme/openapi-parser": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/@readme/openapi-parser/-/openapi-parser-2.6.0.tgz",
"integrity": "sha512-pyFJXezWj9WI1O+gdp95CoxfY+i+Uq3kKk4zXIFuRAZi9YnHpHOpjumWWr67wkmRTw19Hskh9spyY0Iyikf3fA==",
"license": "MIT",
"dependencies": {
"@apidevtools/swagger-methods": "^3.0.2",
"@jsdevtools/ono": "^7.1.3",
"@readme/better-ajv-errors": "^1.6.0",
"@readme/json-schema-ref-parser": "^1.2.0",
"@readme/openapi-schemas": "^3.1.0",
"ajv": "^8.12.0",
"ajv-draft-04": "^1.0.0",
"call-me-maybe": "^1.0.1"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"openapi-types": ">=7"
}
},
"node_modules/@readme/openapi-schemas": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@readme/openapi-schemas/-/openapi-schemas-3.1.0.tgz",
"integrity": "sha512-9FC/6ho8uFa8fV50+FPy/ngWN53jaUu4GRXlAjcxIRrzhltJnpKkBG2Tp0IDraFJeWrOpk84RJ9EMEEYzaI1Bw==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/@sindresorhus/merge-streams": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz",
@ -96,24 +263,31 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
"license": "MIT"
},
"node_modules/@typespec/compiler": {
"version": "0.57.0",
"resolved": "https://registry.npmjs.org/@typespec/compiler/-/compiler-0.57.0.tgz",
"integrity": "sha512-Z5L7J90Ol21IbzU+rBD2wzKy2vJ2Yg2FIzi+yB5rtb7/c4oBea/CgEByMVHBtT7uw45ZXJpHOiepuGSPVXw2EA==",
"version": "0.60.1",
"resolved": "https://registry.npmjs.org/@typespec/compiler/-/compiler-0.60.1.tgz",
"integrity": "sha512-I6Vcpvd7mBP7SI5vCBh9rZGXAtVy95BKhAd33Enw32psswiSzRpA7zdyZhOMekTOGVXNS/+E5l2PGGCzQddB4w==",
"license": "MIT",
"dependencies": {
"@babel/code-frame": "~7.24.2",
"ajv": "~8.13.0",
"@babel/code-frame": "~7.24.7",
"ajv": "~8.17.1",
"change-case": "~5.4.4",
"globby": "~14.0.1",
"globby": "~14.0.2",
"mustache": "~4.2.0",
"picocolors": "~1.0.1",
"prettier": "~3.2.5",
"prettier": "~3.3.3",
"prompts": "~2.4.2",
"semver": "^7.6.2",
"semver": "^7.6.3",
"temporal-polyfill": "^0.2.5",
"vscode-languageserver": "~9.0.1",
"vscode-languageserver-textdocument": "~1.0.11",
"yaml": "~2.4.2",
"yaml": "~2.4.5",
"yargs": "~17.7.2"
},
"bin": {
@ -125,79 +299,97 @@
}
},
"node_modules/@typespec/http": {
"version": "0.57.0",
"resolved": "https://registry.npmjs.org/@typespec/http/-/http-0.57.0.tgz",
"integrity": "sha512-k3bWOTPNqlRB3/TmrXVBtObmxj2J20l2FnhGXvs+tjdtbXLxCQWmvQz6xlne9nkLAtWVB/pQRUn+oMJfhWta3w==",
"version": "0.60.0",
"resolved": "https://registry.npmjs.org/@typespec/http/-/http-0.60.0.tgz",
"integrity": "sha512-ktfS9vpHfltyeAaQLNAZdqrn6Per3vmB/HDH/iyudYLA5wWblT1siKvpFCMWq53CJorRO7yeOKv+Q/M26zwEtg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=18.0.0"
},
"peerDependencies": {
"@typespec/compiler": "~0.57.0"
"@typespec/compiler": "~0.60.0"
}
},
"node_modules/@typespec/openapi": {
"version": "0.57.0",
"resolved": "https://registry.npmjs.org/@typespec/openapi/-/openapi-0.57.0.tgz",
"integrity": "sha512-35wK/BqjOXSlhWuGMwoYN3FSgIYFOKtw8ot4ErcgmxAGuKaS2GkUhZvtQJXUn2ByU0Fl4jqslPmTz8SEcz7rbw==",
"version": "0.60.0",
"resolved": "https://registry.npmjs.org/@typespec/openapi/-/openapi-0.60.0.tgz",
"integrity": "sha512-YVwLppgHY8r/MudHNSLSUXzdw+CIpjmb31gI2a0KDGnI6sWDwY7LSWfjGU4TY/ubt0+X0Tjoy330mTvw71YBTg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=18.0.0"
},
"peerDependencies": {
"@typespec/compiler": "~0.57.0",
"@typespec/http": "~0.57.0"
"@typespec/compiler": "~0.60.0",
"@typespec/http": "~0.60.0"
}
},
"node_modules/@typespec/openapi3": {
"version": "0.57.0",
"resolved": "https://registry.npmjs.org/@typespec/openapi3/-/openapi3-0.57.0.tgz",
"integrity": "sha512-spNLzwCTduPISJBTWhqsMLTjuGC3Tdh/FVI1rTGnRunB7ZXjhRyz031o1bCe2BZeW1w1sacZGfe+ba8sXqgMxA==",
"version": "0.60.0",
"resolved": "https://registry.npmjs.org/@typespec/openapi3/-/openapi3-0.60.0.tgz",
"integrity": "sha512-gvrTHZACdeQtV7GfhVOHqkyTgMFyM2nKAIiz2P83LIncMCDUc00bGKGmaBk+xpuwKtCJyxBeVpCbID31YAq96g==",
"license": "MIT",
"dependencies": {
"yaml": "~2.4.2"
"@readme/openapi-parser": "~2.6.0",
"yaml": "~2.4.5"
},
"bin": {
"tsp-openapi3": "cmd/tsp-openapi3.js"
},
"engines": {
"node": ">=18.0.0"
},
"peerDependencies": {
"@typespec/compiler": "~0.57.0",
"@typespec/http": "~0.57.0",
"@typespec/openapi": "~0.57.0",
"@typespec/versioning": "~0.57.0"
"@typespec/compiler": "~0.60.0",
"@typespec/http": "~0.60.0",
"@typespec/openapi": "~0.60.0",
"@typespec/versioning": "~0.60.0"
}
},
"node_modules/@typespec/versioning": {
"version": "0.57.0",
"resolved": "https://registry.npmjs.org/@typespec/versioning/-/versioning-0.57.0.tgz",
"integrity": "sha512-kk6zCNSwcqqYB9isNNagTy+Zv6wEIRA4NkcZ/X1riTj2zhJwKsIFNXQWm1yxpZn+BY4+1QtuaQHuBLo8HbgR/w==",
"version": "0.60.1",
"resolved": "https://registry.npmjs.org/@typespec/versioning/-/versioning-0.60.1.tgz",
"integrity": "sha512-HogYL7P9uOPoSvkLLDjF22S6E9td6EY3c6TcIHhCzDTAQoi54csikD0gNrtcCkFG0UeQk29HgQymV397j+vp4g==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=18.0.0"
},
"peerDependencies": {
"@typespec/compiler": "~0.57.0"
"@typespec/compiler": "~0.60.0"
}
},
"node_modules/ajv": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz",
"integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==",
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.4.1"
"require-from-string": "^2.0.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/ajv-draft-04": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz",
"integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==",
"license": "MIT",
"peerDependencies": {
"ajv": "^8.5.0"
},
"peerDependenciesMeta": {
"ajv": {
"optional": true
}
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
@ -219,6 +411,12 @@
"node": ">=4"
}
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"license": "Python-2.0"
},
"node_modules/braces": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
@ -231,6 +429,12 @@
"node": ">=8"
}
},
"node_modules/call-me-maybe": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz",
"integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==",
"license": "MIT"
},
"node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@ -265,6 +469,15 @@
"node": ">=12"
}
},
"node_modules/code-error-fragment": {
"version": "0.0.230",
"resolved": "https://registry.npmjs.org/code-error-fragment/-/code-error-fragment-0.0.230.tgz",
"integrity": "sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw==",
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@ -326,6 +539,12 @@
"node": ">=8.6.0"
}
},
"node_modules/fast-uri": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.2.tgz",
"integrity": "sha512-GR6f0hD7XXyNJa25Tb9BuIdN0tdr+0BMi6/CJPH3wJO1JjNG3n/VsSw38AwRdKZABm8lGbPfakLRkYzx2V9row==",
"license": "MIT"
},
"node_modules/fastq": {
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
@ -369,9 +588,9 @@
}
},
"node_modules/globby": {
"version": "14.0.1",
"resolved": "https://registry.npmjs.org/globby/-/globby-14.0.1.tgz",
"integrity": "sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==",
"version": "14.0.2",
"resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz",
"integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==",
"license": "MIT",
"dependencies": {
"@sindresorhus/merge-streams": "^2.1.0",
@ -388,6 +607,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/grapheme-splitter": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
"license": "MIT"
},
"node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@ -398,9 +623,9 @@
}
},
"node_modules/ignore": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
"integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
"license": "MIT",
"engines": {
"node": ">= 4"
@ -451,12 +676,46 @@
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT"
},
"node_modules/json-to-ast": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/json-to-ast/-/json-to-ast-2.1.0.tgz",
"integrity": "sha512-W9Lq347r8tA1DfMvAGn9QNcgYm4Wm7Yc+k8e6vezpMnRT+NHbtlxgNBXRVjXe9YM6eTn6+p/MKOlV/aABJcSnQ==",
"license": "MIT",
"dependencies": {
"code-error-fragment": "0.0.230",
"grapheme-splitter": "^1.0.4"
},
"engines": {
"node": ">= 4"
}
},
"node_modules/jsonpointer": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz",
"integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/kleur": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
@ -466,6 +725,15 @@
"node": ">=6"
}
},
"node_modules/leven": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
"integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@ -476,9 +744,9 @@
}
},
"node_modules/micromatch": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz",
"integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==",
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"license": "MIT",
"dependencies": {
"braces": "^3.0.3",
@ -497,6 +765,13 @@
"mustache": "bin/mustache"
}
},
"node_modules/openapi-types": {
"version": "12.1.3",
"resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz",
"integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==",
"license": "MIT",
"peer": true
},
"node_modules/path-type": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz",
@ -528,9 +803,9 @@
}
},
"node_modules/prettier": {
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
"integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz",
"integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==",
"license": "MIT",
"bin": {
"prettier": "bin/prettier.cjs"
@ -555,15 +830,6 @@
"node": ">= 6"
}
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@ -584,6 +850,12 @@
],
"license": "MIT"
},
"node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
"license": "MIT"
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@ -636,9 +908,9 @@
}
},
"node_modules/semver": {
"version": "7.6.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
"integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
"version": "7.6.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@ -703,6 +975,21 @@
"node": ">=4"
}
},
"node_modules/temporal-polyfill": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/temporal-polyfill/-/temporal-polyfill-0.2.5.tgz",
"integrity": "sha512-ye47xp8Cb0nDguAhrrDS1JT1SzwEV9e26sSsrWzVu+yPZ7LzceEcH0i2gci9jWfOfSCCgM3Qv5nOYShVUUFUXA==",
"license": "MIT",
"dependencies": {
"temporal-spec": "^0.2.4"
}
},
"node_modules/temporal-spec": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/temporal-spec/-/temporal-spec-0.2.4.tgz",
"integrity": "sha512-lDMFv4nKQrSjlkHKAlHVqKrBG4DyFfa9F74cmBZ3Iy3ed8yvWnlWSIdi4IKfSqwmazAohBNwiN64qGx4y5Q3IQ==",
"license": "ISC"
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@ -727,15 +1014,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"license": "BSD-2-Clause",
"dependencies": {
"punycode": "^2.1.0"
}
},
"node_modules/vscode-jsonrpc": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
@ -839,9 +1117,9 @@
}
},
"node_modules/yaml": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.3.tgz",
"integrity": "sha512-sntgmxj8o7DE7g/Qi60cqpLBA3HG3STcDA0kO+WfB05jEKhZMbY7umNm2rBpQvsmZ16/lPXCJGW2672dgOUkrg==",
"version": "2.4.5",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz",
"integrity": "sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==",
"license": "ISC",
"bin": {
"yaml": "bin.mjs"

View File

@ -10,7 +10,7 @@
},
"dependencies": {
"@typespec/compiler": "latest",
"@typespec/openapi3": "^0.57.0"
"@typespec/openapi3": "^0.60.0"
},
"private": true
}

View File

@ -11,16 +11,26 @@ use crate::parsing::expression_parser::parse_expression;
#[serde(rename_all = "camelCase")]
pub enum Expression {
Not(Rc<Expression>),
Binary { left: Rc<Expression>, operator: BinaryOperator, right: Rc<Expression> },
Binary {
left: Rc<Expression>,
operator: BinaryOperator,
right: Rc<Expression>,
},
Atomic(String),
}
impl PartialEq<Rc<Expression>> for Expression {
fn eq(&self, other: &Rc<Expression>) -> bool {
self == other.as_ref()
}
}
impl Expression {
pub fn is_atomic(&self) -> bool {
match self {
Expression::Not(expr) => expr.is_atomic(),
Expression::Binary { .. } => false,
Expression::Atomic(_) => true
Expression::Atomic(_) => true,
}
}
@ -32,15 +42,28 @@ impl Expression {
values.extend(right.get_atomic_values());
values
}
Expression::Atomic(value) => HashSet::from([value.clone()])
Expression::Atomic(value) => HashSet::from([value.clone()]),
}
}
//TODO replace with eq_ignore_case and use a macro to select correct eq function
pub fn eq(&self, other: &Self, ignore_case: bool) -> bool {
match (self, other) {
(Expression::Not(left), Expression::Not(right)) => Expression::eq(left, right, ignore_case),
(Expression::Binary { left: left_left, operator: left_operator, right: left_right },
Expression::Binary { left: right_left, operator: right_operator, right: right_right }) => {
(Expression::Not(left), Expression::Not(right)) => {
Expression::eq(left, right, ignore_case)
}
(
Expression::Binary {
left: left_left,
operator: left_operator,
right: left_right,
},
Expression::Binary {
left: right_left,
operator: right_operator,
right: right_right,
},
) => {
Expression::eq(left_left, right_left, ignore_case)
&& left_operator == right_operator
&& Expression::eq(left_right, right_right, ignore_case)
@ -52,7 +75,7 @@ impl Expression {
left == right
}
}
_ => false
_ => false,
}
}
@ -64,6 +87,34 @@ impl Expression {
_ => false,
}
}
pub fn is_in(&self, other: &Self) -> bool {
if let Expression::Binary { left, right, .. } = other {
self == left || self == right
} else {
false
}
}
pub fn is_or(&self) -> bool {
matches!(
self,
Expression::Binary {
operator: BinaryOperator::Or,
..
}
)
}
pub fn is_and(&self) -> bool {
matches!(
self,
Expression::Binary {
operator: BinaryOperator::And,
..
}
)
}
}
impl<'a> TryFrom<&'a str> for Expression {
@ -87,20 +138,61 @@ impl Display for Expression {
fn fmt_helper(expression: &Expression, parent: Option<&Expression>) -> String {
match expression {
Expression::Not(expr) if expr.is_atomic() => format!("¬{}", fmt_helper(expr, Some(expression))),
Expression::Not(expr) => format!("¬({})", fmt_helper(expr, Some(expression))),
Expression::Binary { left, operator: BinaryOperator::And, right } => {
format!("{}{}", fmt_helper(left, Some(expression)), fmt_helper(right, Some(expression)))
Expression::Not(expr) if expr.is_atomic() => {
format!("¬{}", fmt_helper(expr, Some(expression)))
}
Expression::Binary { left, operator: BinaryOperator::Or, right } => {
if parent.is_none() || matches!(parent, Some(Expression::Binary { operator: BinaryOperator::Or, .. })) {
format!("{} {}", fmt_helper(left, Some(expression)), fmt_helper(right, Some(expression)))
Expression::Not(expr) => format!("¬({})", fmt_helper(expr, Some(expression))),
Expression::Binary {
left,
operator: BinaryOperator::And,
right,
} => {
format!(
"{} ⋀ {}",
fmt_helper(left, Some(expression)),
fmt_helper(right, Some(expression))
)
}
Expression::Binary {
left,
operator: BinaryOperator::Or,
right,
} => {
if parent.is_none()
|| matches!(
parent,
Some(
Expression::Not(_)
| Expression::Binary {
operator: BinaryOperator::Or | BinaryOperator::Implication,
..
}
)
)
{
format!(
"{} {}",
fmt_helper(left, Some(expression)),
fmt_helper(right, Some(expression))
)
} else {
format!("({} {})", fmt_helper(left, Some(expression)), fmt_helper(right, Some(expression)))
format!(
"({} {})",
fmt_helper(left, Some(expression)),
fmt_helper(right, Some(expression))
)
}
}
Expression::Binary { left, operator: BinaryOperator::Implication, right } => {
format!("{}{}", fmt_helper(left, Some(expression)), fmt_helper(right, Some(expression)))
Expression::Binary {
left,
operator: BinaryOperator::Implication,
right,
} => {
format!(
"{} ➔ {}",
fmt_helper(left, Some(expression)),
fmt_helper(right, Some(expression))
)
}
Expression::Atomic(value) => value.clone(),
}
@ -157,102 +249,55 @@ mod tests {
#[test]
fn test_expression_a_and_not_b_display() {
let expression = and(
atomic("a"),
not(atomic("b")),
);
let expression = and(atomic("a"), not(atomic("b")));
assert_eq!(expression.to_string(), "a ⋀ ¬b");
}
#[test]
fn test_expression_a_or_b_and_c_display() {
let expression = or(
atomic("a"),
and(
atomic("b"),
atomic("c"),
));
let expression = or(atomic("a"), and(atomic("b"), atomic("c")));
assert_eq!(expression.to_string(), "a b ⋀ c");
}
#[test]
fn test_expression_a_or_b() {
let expression = or(
atomic("a"),
atomic("b"),
);
let expression = or(atomic("a"), atomic("b"));
assert_eq!(expression.to_string(), "a b");
}
#[test]
fn test_expression_double_or() {
let expression = or(
atomic("a"),
or(
atomic("b"),
atomic("c"),
),
);
let expression = or(atomic("a"), or(atomic("b"), atomic("c")));
assert_eq!(expression.to_string(), "a b c");
}
#[test]
fn test_expression_triple_or() {
let expression = or(
atomic("a"),
or(
atomic("b"),
or(
atomic("c"),
atomic("d"),
),
),
);
let expression = or(atomic("a"), or(atomic("b"), or(atomic("c"), atomic("d"))));
assert_eq!(expression.to_string(), "a b c d");
}
#[test]
fn test_expression_nested_parenthesized_or() {
let expression = or(
atomic("a"),
and(
atomic("b"),
or(
atomic("b"),
atomic("c"),
),
),
);
let expression = or(atomic("a"), and(atomic("b"), or(atomic("b"), atomic("c"))));
assert_eq!(expression.to_string(), "a b ⋀ (b c)");
}
#[test]
fn test_expression_c_and_a_or_b_display() {
let expression = and(
or(
atomic("a"),
atomic("b"),
),
atomic("c"),
);
let expression = and(or(atomic("a"), atomic("b")), atomic("c"));
assert_eq!(expression.to_string(), "(a b) ⋀ c");
}
#[test]
fn test_expression_a_implies_b_display() {
let expression = implies(
atomic("a"),
atomic("b"),
);
let expression = implies(atomic("a"), atomic("b"));
assert_eq!(expression.to_string(), "a ➔ b");
}
#[test]
fn test_expression_not_a_and_b_display() {
let expression = not(and(
atomic("a"),
atomic("b"),
));
let expression = not(and(atomic("a"), atomic("b")));
assert_eq!(expression.to_string(), "¬(a ⋀ b)");
}
@ -301,7 +346,13 @@ mod tests {
#[test]
fn test_from_str_into_expression_very_complex_parentheses() {
let expression: Expression = "(a & b) | c => (d & e)".try_into().unwrap();
assert_eq!(expression, implies(or(and(atomic("a"), atomic("b")), atomic("c")), and(atomic("d"), atomic("e"))));
assert_eq!(
expression,
implies(
or(and(atomic("a"), atomic("b")), atomic("c")),
and(atomic("d"), atomic("e"))
)
);
}
#[test]

View File

@ -1,8 +1,35 @@
use std::rc::Rc;
use crate::expressions::expression::Expression;
use crate::expressions::operator::BinaryOperator;
use std::rc::Rc;
impl Expression {
#[inline]
#[must_use]
pub fn and(self, other: impl Into<Rc<Expression>>) -> Expression {
and(self, other)
}
#[inline]
#[must_use]
pub fn or(self, other: impl Into<Rc<Expression>>) -> Expression {
or(self, other)
}
#[inline]
#[must_use]
pub fn implies(self, other: impl Into<Rc<Expression>>) -> Expression {
implies(self, other)
}
#[inline]
#[must_use]
pub fn not(self) -> Expression {
not(self)
}
}
#[inline]
#[must_use]
pub fn and<L, R>(left: L, right: R) -> Expression
where
L: Into<Rc<Expression>>,
@ -12,6 +39,7 @@ where
}
#[inline]
#[must_use]
pub fn or<L, R>(left: L, right: R) -> Expression
where
L: Into<Rc<Expression>>,
@ -21,6 +49,7 @@ where
}
#[inline]
#[must_use]
pub fn implies<L, R>(left: L, right: R) -> Expression
where
L: Into<Rc<Expression>>,
@ -30,20 +59,27 @@ where
}
#[inline]
#[must_use]
pub fn binary<L, R>(left: L, operator: BinaryOperator, right: R) -> Expression
where
L: Into<Rc<Expression>>,
R: Into<Rc<Expression>>,
{
Expression::Binary { left: left.into(), operator, right: right.into() }
Expression::Binary {
left: left.into(),
operator,
right: right.into(),
}
}
#[inline]
#[must_use]
pub fn not<T: Into<Rc<Expression>>>(value: T) -> Expression {
Expression::Not(value.into())
}
#[inline]
#[must_use]
pub fn atomic<T: Into<String>>(value: T) -> Expression {
Expression::Atomic(value.into())
}

View File

@ -1,5 +1,5 @@
pub mod expression;
pub mod operator;
pub mod helpers;
pub mod operator;
pub mod simplify;
pub mod truth_table;
pub mod truth_table;

View File

@ -9,6 +9,7 @@ pub enum BinaryOperator {
}
impl BinaryOperator {
#[must_use]
pub fn eval(&self, left: bool, right: bool) -> bool {
match self {
BinaryOperator::And => left && right,
@ -16,4 +17,14 @@ impl BinaryOperator {
BinaryOperator::Implication => !left || right,
}
}
#[must_use]
pub fn is_and(&self) -> bool {
matches!(self, BinaryOperator::And)
}
#[must_use]
pub fn is_or(&self) -> bool {
matches!(self, BinaryOperator::Or)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -36,16 +36,27 @@ pub enum Sort {
}
impl TruthTable {
pub fn new(expression: &Expression, options: TruthTableOptions) -> Self {
pub fn new(
expression: &Expression,
TruthTableOptions {
hide,
hide_intermediate_steps,
sort,
}: TruthTableOptions,
) -> Self {
let mut header = Self::extract_header(expression);
let mut truth_matrix = Self::generate_truth_matrix(expression, &header, options.hide, options.hide_intermediate_steps);
if !matches!(options.sort, Sort::Default) {
Self::sort_matrix(&mut truth_matrix, options.sort);
let mut truth_matrix =
Self::generate_truth_matrix(expression, &header, hide, hide_intermediate_steps);
if !matches!(sort, Sort::Default) {
Self::sort_matrix(&mut truth_matrix, sort);
}
if options.hide_intermediate_steps {
if hide_intermediate_steps {
header = Self::remove_non_atomic_from_header(&header);
}
Self { header, truth_matrix }
Self {
header,
truth_matrix,
}
}
fn sort_matrix(truth_matrix: &mut TruthMatrix, sort: Sort) {
@ -57,7 +68,8 @@ impl TruthTable {
}
fn remove_non_atomic_from_header(header: &[String]) -> Vec<String> {
header.iter()
header
.iter()
.enumerate()
.filter_map(|(index, s)| {
if !Self::contains_operator(s) || index == header.len() - 1 {
@ -105,38 +117,62 @@ impl TruthTable {
}
}
fn generate_truth_matrix(expression: &Expression, header: &[String], hide: Hide, hide_intermediate: bool) -> TruthMatrix {
let mut atomics = expression.get_atomic_values()
.into_iter().collect::<Vec<String>>();
fn generate_truth_matrix(
expression: &Expression,
header: &[String],
hide: Hide,
hide_intermediate: bool,
) -> TruthMatrix {
let mut atomics = expression
.get_atomic_values()
.into_iter()
.collect::<Vec<String>>();
if atomics.is_empty() {
return vec![];
}
atomics.sort();
Self::truth_combinations(atomics.len() as u32).iter()
Self::truth_combinations(atomics.len() as u32)
.iter()
.filter_map(|combo| {
let expression = Self::resolve_expression(expression, &atomics.iter()
.enumerate()
.map(|(index, value)| (value.clone(), combo[index]))
.collect(), header, hide_intermediate);
let expression = Self::resolve_expression(
expression,
&atomics
.iter()
.enumerate()
.map(|(index, value)| (value.clone(), combo[index]))
.collect(),
header,
hide_intermediate,
);
match (hide, expression.last()) {
(Hide::True, Some(false)) => Some(expression),
(Hide::False, Some(true)) => Some(expression),
(Hide::None, _) => Some(expression),
_ => None,
}
}).collect()
})
.collect()
}
fn truth_combinations(count: u32) -> TruthMatrix {
let row_len = 2usize.pow(count);
let rows = 0..row_len;
rows.map(|index| (0..count).rev()
// Just trust me bro
.map(|shift| (index >> shift) & 1 == 0).collect()
).collect()
rows.map(|index| {
(0..count)
.rev()
// Just trust me bro
.map(|shift| (index >> shift) & 1 == 0)
.collect()
})
.collect()
}
fn resolve_expression(expression: &Expression, booleans: &HashMap<String, bool>, header: &[String], hide_intermediate: bool) -> Vec<bool> {
fn resolve_expression(
expression: &Expression,
booleans: &HashMap<String, bool>,
header: &[String],
hide_intermediate: bool,
) -> Vec<bool> {
let Some(last_expression) = header.last() else {
return vec![];
};
@ -145,17 +181,23 @@ impl TruthTable {
if hide_intermediate {
expression_map = Self::remove_intermediate_steps(expression_map, last_expression);
}
let string_map = expression_map.into_iter()
let string_map = expression_map
.into_iter()
.map(|(key, value)| (key.to_string(), value))
.collect::<HashMap<String, bool>>();
header.iter()
header
.iter()
.filter_map(|s_expr| string_map.get(s_expr).copied())
.collect()
}
fn remove_intermediate_steps<'a>(expression_map: HashMap<&'a Expression, bool>, top_level_expression: &'a str) -> HashMap<&'a Expression, bool> {
expression_map.into_iter()
fn remove_intermediate_steps<'a>(
expression_map: HashMap<&'a Expression, bool>,
top_level_expression: &'a str,
) -> HashMap<&'a Expression, bool> {
expression_map
.into_iter()
.filter_map(|(key, value)| {
if key.is_atomic() || key.to_string() == top_level_expression {
Some((key, value))
@ -166,7 +208,10 @@ impl TruthTable {
.collect()
}
fn _resolve_expression<'a>(expression: &'a Expression, booleans: &HashMap<String, bool>) -> HashMap<&'a Expression, bool> {
fn _resolve_expression<'a>(
expression: &'a Expression,
booleans: &HashMap<String, bool>,
) -> HashMap<&'a Expression, bool> {
match expression {
Expression::Not(expr) => {
let mut map = Self::_resolve_expression(expr, booleans);
@ -175,12 +220,18 @@ impl TruthTable {
}
map
}
Expression::Binary { left, right, operator } => {
Expression::Binary {
left,
right,
operator,
} => {
let left_map = Self::_resolve_expression(left, booleans);
let right_map = Self::_resolve_expression(right, booleans);
let mut map = left_map;
map.extend(right_map);
if let (Some(left_value), Some(right_value)) = (map.get(left.as_ref()), map.get(right.as_ref())) {
if let (Some(left_value), Some(right_value)) =
(map.get(left.as_ref()), map.get(right.as_ref()))
{
map.insert(expression, operator.eval(*left_value, *right_value));
}
map
@ -209,18 +260,24 @@ mod tests {
let expression = and(atomic("A"), atomic("B"));
let truth_table = TruthTable::new(&expression, Default::default());
assert_eq!(truth_table.header, vec!["A", "B", "A ⋀ B"]);
assert_ne!(truth_table.truth_matrix, matrix![
true, true, true;
false, true, false;
true, false, false;
false, false, false
]);
assert_eq!(truth_table.truth_matrix, matrix![
true, true, true;
true, false, false;
false, true, false;
false, false, false
]);
assert_ne!(
truth_table.truth_matrix,
matrix![
true, true, true;
false, true, false;
true, false, false;
false, false, false
]
);
assert_eq!(
truth_table.truth_matrix,
matrix![
true, true, true;
true, false, false;
false, true, false;
false, false, false
]
);
}
#[test]
@ -229,26 +286,56 @@ mod tests {
let truth_table = TruthTable::new(&expression, Default::default());
let atomics = 3;
assert_eq!(truth_table.header, vec!["A", "C", "A C", "B", "B C", "(A C) ⋀ (B C)"]);
assert_eq!(
truth_table.header,
vec!["A", "C", "A C", "B", "B C", "(A C) ⋀ (B C)"]
);
assert_eq!(truth_table.truth_matrix.len(), 2usize.pow(atomics as u32));
assert_eq!(truth_table.truth_matrix[0].len(), 6);
assert_eq!(truth_table.truth_matrix[0], vec![true, true, true, true, true, true]);
assert_eq!(truth_table.truth_matrix[1], vec![true, false, true, true, true, true]);
assert_eq!(truth_table.truth_matrix[2], vec![true, true, true, false, true, true]);
assert_eq!(truth_table.truth_matrix[3], vec![true, false, true, false, false, false]);
assert_eq!(truth_table.truth_matrix[4], vec![false, true, true, true, true, true]);
assert_eq!(truth_table.truth_matrix[5], vec![false, false, false, true, true, false]);
assert_eq!(truth_table.truth_matrix[6], vec![false, true, true, false, true, true]);
assert_eq!(truth_table.truth_matrix[7], vec![false, false, false, false, false, false]);
assert_eq!(
truth_table.truth_matrix[0],
vec![true, true, true, true, true, true]
);
assert_eq!(
truth_table.truth_matrix[1],
vec![true, false, true, true, true, true]
);
assert_eq!(
truth_table.truth_matrix[2],
vec![true, true, true, false, true, true]
);
assert_eq!(
truth_table.truth_matrix[3],
vec![true, false, true, false, false, false]
);
assert_eq!(
truth_table.truth_matrix[4],
vec![false, true, true, true, true, true]
);
assert_eq!(
truth_table.truth_matrix[5],
vec![false, false, false, true, true, false]
);
assert_eq!(
truth_table.truth_matrix[6],
vec![false, true, true, false, true, true]
);
assert_eq!(
truth_table.truth_matrix[7],
vec![false, false, false, false, false, false]
);
}
#[test]
fn test_new_truth_table_and_hide_intermediate_steps() {
let expression = and(atomic("A"), or(atomic("B"), atomic("C")));
let truth_table = TruthTable::new(&expression, TruthTableOptions {
hide_intermediate_steps: true,
..Default::default()
});
let truth_table = TruthTable::new(
&expression,
TruthTableOptions {
hide_intermediate_steps: true,
..Default::default()
},
);
assert_eq!(truth_table.header, vec!["A", "B", "C", "A ⋀ (B C)"]);
for (index, row) in truth_table.truth_matrix.iter().enumerate() {
assert_eq!(row.len(), 4, "Row at {index}: {:?}", row);
@ -264,12 +351,15 @@ mod tests {
false, false, false
];
TruthTable::sort_matrix(&mut matrix, Sort::TrueFirst);
assert_eq!(matrix, matrix![
true, true, true;
false, true, true;
true, false, false;
false, false, false
]);
assert_eq!(
matrix,
matrix![
true, true, true;
false, true, true;
true, false, false;
false, false, false
]
);
}
#[test]
@ -281,12 +371,15 @@ mod tests {
true, false, false
];
TruthTable::sort_matrix(&mut matrix, Sort::TrueFirst);
assert_eq!(matrix, matrix![
false, true, false;
false, true, false;
true, false, false;
true, false, false
]);
assert_eq!(
matrix,
matrix![
false, true, false;
false, true, false;
true, false, false;
true, false, false
]
);
}
#[test]
@ -298,12 +391,15 @@ mod tests {
false, false, false
];
TruthTable::sort_matrix(&mut matrix, Sort::Default);
assert_eq!(matrix, matrix![
true, true, true;
true, false, false;
false, true, true;
false, false, false
]);
assert_eq!(
matrix,
matrix![
true, true, true;
true, false, false;
false, true, true;
false, false, false
]
);
}
#[test]
@ -315,12 +411,15 @@ mod tests {
false, false, false
];
TruthTable::sort_matrix(&mut matrix, Sort::FalseFirst);
assert_eq!(matrix, matrix![
true, false, false;
false, false, false;
true, true, true;
false, true, true
]);
assert_eq!(
matrix,
matrix![
true, false, false;
false, false, false;
true, true, true;
false, true, true
]
);
}
#[test]
@ -333,20 +432,20 @@ mod tests {
let matrix = TruthTable::generate_truth_matrix(
&and(atomic("A"), atomic("B")),
&["A".into(), "B".into(), "A ⋀ B".into()],
Hide::True, false,
Hide::True,
false,
);
assert_eq!(expected, matrix);
}
#[test]
fn test_hide_false_values() {
let expected = matrix![
true, true, true
];
let expected = matrix![true, true, true];
let matrix = TruthTable::generate_truth_matrix(
&and(atomic("A"), atomic("B")),
&["A".into(), "B".into(), "A ⋀ B".into()],
Hide::False, false,
Hide::False,
false,
);
assert_eq!(expected, matrix);
}
@ -362,7 +461,8 @@ mod tests {
let matrix = TruthTable::generate_truth_matrix(
&and(atomic("A"), atomic("B")),
&["A".into(), "B".into(), "A ⋀ B".into()],
Hide::None, false,
Hide::None,
false,
);
assert_eq!(expected, matrix);
}
@ -370,34 +470,46 @@ mod tests {
#[test]
fn test_truth_combinations_2() {
let combinations = TruthTable::truth_combinations(2);
assert_eq!(combinations, matrix![
true, true;
true, false;
false, true;
false, false
]);
assert_eq!(
combinations,
matrix![
true, true;
true, false;
false, true;
false, false
]
);
}
#[test]
fn test_truth_combinations_3() {
let combinations = TruthTable::truth_combinations(3);
assert_eq!(combinations, matrix![
true, true, true;
true, true, false;
true, false, true;
true, false, false;
false, true, true;
false, true, false;
false, false, true;
false, false, false
]);
assert_eq!(
combinations,
matrix![
true, true, true;
true, true, false;
true, false, true;
true, false, false;
false, true, true;
false, true, false;
false, false, true;
false, false, false
]
);
}
#[test]
fn test_resolve_expression_hide_intermediate_steps() {
let expression = and(atomic("A"), or(atomic("B"), atomic("C")));
let booleans = map!["A".into() => true, "B".into() => false, "C".into() => true];
let header = vec!["A".into(), "B".into(), "C".into(), "B C".into(), "A ⋀ (B C)".into()];
let header = vec![
"A".into(),
"B".into(),
"C".into(),
"B C".into(),
"A ⋀ (B C)".into(),
];
let values = TruthTable::resolve_expression(&expression, &booleans, &header, true);
assert_eq!(values.len(), 4);
assert_eq!(values, vec![true, false, true, true]);
@ -443,7 +555,12 @@ mod tests {
fn test_resolve_expression_even_more_duplicates() {
let expression = and(atomic("A"), and(atomic("A"), and(atomic("A"), atomic("A"))));
let booleans = HashMap::from([("A".into(), true)]);
let header = vec!["A".into(), "A ⋀ A".into(), "A ⋀ A ⋀ A".into(), "A ⋀ A ⋀ A ⋀ A".into()];
let header = vec![
"A".into(),
"A ⋀ A".into(),
"A ⋀ A ⋀ A".into(),
"A ⋀ A ⋀ A ⋀ A".into(),
];
let values = TruthTable::resolve_expression(&expression, &booleans, &header, false);
assert_eq!(values, vec![true, true, true, true]);
}
@ -453,15 +570,20 @@ mod tests {
let expression = and(atomic("A"), and(atomic("A"), and(atomic("A"), atomic("A"))));
let booleans = HashMap::from([("A".into(), true)]);
let values = TruthTable::_resolve_expression(&expression, &booleans);
assert_eq!(values, HashMap::from([
(&atomic("A"), true),
(&and(atomic("A"), atomic("A")), true),
(&and(atomic("A"), and(atomic("A"), atomic("A"))), true),
(&and(atomic("A"), and(atomic("A"), and(atomic("A"), atomic("A")))), true),
]));
assert_eq!(
values,
HashMap::from([
(&atomic("A"), true),
(&and(atomic("A"), atomic("A")), true),
(&and(atomic("A"), and(atomic("A"), atomic("A"))), true),
(
&and(atomic("A"), and(atomic("A"), and(atomic("A"), atomic("A")))),
true
),
])
);
}
#[test]
fn test_atomic_expression() {
let expression = atomic("A");
@ -501,7 +623,10 @@ mod tests {
fn test_complex_expression() {
let expression = implies(and(atomic("A"), atomic("B")), or(atomic("C"), atomic("D")));
let header = TruthTable::extract_header(&expression);
assert_eq!(header, vec!["A", "B", "A ⋀ B", "C", "D", "C D", "A ⋀ B ➔ (C D)"]);
assert_eq!(
header,
vec!["A", "B", "A ⋀ B", "C", "D", "C D", "A ⋀ B ➔ C D"]
);
}
#[test]
@ -513,8 +638,14 @@ mod tests {
#[test]
fn test_somewhat_equal() {
let expression = and(atomic("A"), and(or(not(atomic("A")), atomic("B")), atomic("A")));
let expression = and(
atomic("A"),
and(or(not(atomic("A")), atomic("B")), atomic("A")),
);
let header = TruthTable::extract_header(&expression);
assert_eq!(header, vec!["A", "¬A", "B", "¬A B", "(¬A B) ⋀ A", "A ⋀ (¬A B) ⋀ A"]);
assert_eq!(
header,
vec!["A", "¬A", "B", "¬A B", "(¬A B) ⋀ A", "A ⋀ (¬A B) ⋀ A"]
);
}
}

View File

@ -1,19 +1,19 @@
use lib::axum::app::AppBuilder;
use tower_http::cors::CorsLayer;
use crate::routing::routes::*;
use crate::routing::routes::index::not_found;
use crate::routing::routes::*;
mod config;
mod expressions;
mod parsing;
mod routing;
mod config;
mod utils;
#[tokio::main]
async fn main() {
AppBuilder::new()
.routes(&[index::router(), simplify::router(), table::router()])
.routes([index::router(), simplify::router(), table::router()])
.fallback(not_found)
.cors(CorsLayer::permissive())
.serve()

View File

@ -1,15 +1,15 @@
use lib::nom::combinators::{exhausted, parenthesized, trim};
use lib::nom::util::IntoResult;
use lib::traits::IntoResult;
use nom::branch::alt;
use nom::bytes::complete::{tag, take_while, take_while1};
use nom::character::complete::char;
use nom::combinator::{opt, peek};
use nom::combinator::{map, opt, peek};
use nom::error::Error;
use nom::IResult;
use nom::sequence::{pair, preceded};
use nom::IResult;
use crate::expressions::expression::Expression;
use crate::expressions::helpers::{and, atomic, implies, not, or};
use crate::expressions::helpers::atomic;
pub fn parse_expression(input: &str) -> Result<Expression, nom::Err<Error<&str>>> {
exhausted(_parse_expression)(input).into_result()
@ -17,7 +17,9 @@ pub fn parse_expression(input: &str) -> Result<Expression, nom::Err<Error<&str>>
fn _parse_expression(input: &str) -> IResult<&str, Expression> {
let (remaining, atomic_expression) = left_hand_side(input)?;
if let (remaining, Some(complex_expression)) = opt(expression(atomic_expression.clone()))(remaining)? {
if let (remaining, Some(complex_expression)) =
opt(expression(atomic_expression.clone()))(remaining)?
{
Ok((remaining, complex_expression))
} else {
Ok((remaining, atomic_expression))
@ -25,11 +27,7 @@ fn _parse_expression(input: &str) -> IResult<&str, Expression> {
}
fn left_hand_side(input: &str) -> IResult<&str, Expression> {
alt((
value,
not_expression,
parenthesized_expression
))(input)
alt((value, not_expression, parenthesized_expression))(input)
}
fn expression<'a>(previous: Expression) -> impl Fn(&'a str) -> IResult<&'a str, Expression> {
@ -43,21 +41,21 @@ fn expression<'a>(previous: Expression) -> impl Fn(&'a str) -> IResult<&'a str,
}
}
fn operator_combinators(expression: Expression) -> impl Fn(&str) -> IResult<&str, Expression> {
move |input: &str| {
alt((
implication_expression(expression.clone()),
or_expression(expression.clone()),
and_expression(expression.clone()),
not_expression,
))(input)
}
fn operator_combinators<'a>(
expression: Expression,
) -> impl FnMut(&'a str) -> IResult<&'a str, Expression> {
alt((
implication_expression(expression.clone()),
or_expression(expression.clone()),
and_expression(expression.clone()),
not_expression,
))
}
fn parenthesized_expression(input: &str) -> IResult<&str, Expression> {
parenthesized(|input| {
let (remaining, atomic) = left_hand_side(input)?;
let (remaining, expression) = operator_combinators(atomic)(remaining)?;
let (remaining, atomic) = trim(left_hand_side)(input)?;
let (remaining, expression) = trim(operator_combinators(atomic))(remaining)?;
if peek(trim(char(')')))(remaining).is_ok() {
Ok((remaining, expression))
} else {
@ -66,15 +64,10 @@ fn parenthesized_expression(input: &str) -> IResult<&str, Expression> {
})(input)
}
fn and_expression<'a>(previous: Expression) -> impl Fn(&'a str) -> IResult<&'a str, Expression> {
move |input: &'a str| {
preceded(
trim(char('&')),
left_hand_side,
)(input).map(|(remaining, right)| {
(remaining, and(previous.clone(), right))
})
}
fn and_expression<'a>(previous: Expression) -> impl FnMut(&'a str) -> IResult<&'a str, Expression> {
map(preceded(trim(char('&')), left_hand_side), move |right| {
previous.clone().and(right)
})
}
fn complete_and(input: &str) -> IResult<&str, Expression> {
@ -82,18 +75,11 @@ fn complete_and(input: &str) -> IResult<&str, Expression> {
and_expression(atomic.clone())(remaining)
}
fn or_expression<'a>(previous: Expression) -> impl Fn(&'a str) -> IResult<&'a str, Expression> {
move |input: &'a str| {
preceded(
trim(char('|')),
alt((
complete_and,
left_hand_side,
)),
)(input).map(|(remaining, right)| {
(remaining, or(previous.clone(), right))
})
}
fn or_expression<'a>(previous: Expression) -> impl FnMut(&'a str) -> IResult<&'a str, Expression> {
map(
preceded(trim(char('|')), alt((complete_and, left_hand_side))),
move |right| previous.clone().or(right),
)
}
fn complete_or(input: &str) -> IResult<&str, Expression> {
@ -101,39 +87,33 @@ fn complete_or(input: &str) -> IResult<&str, Expression> {
or_expression(atomic.clone())(remaining)
}
fn implication_expression<'a>(previous: Expression) -> impl Fn(&'a str) -> IResult<&'a str, Expression> {
move |input: &'a str| {
fn implication_expression<'a>(
previous: Expression,
) -> impl FnMut(&'a str) -> IResult<&'a str, Expression> {
map(
preceded(
trim(tag("=>")),
alt((
complete_and,
complete_or,
left_hand_side,
)),
)(input).map(|(remaining, right)| {
(remaining, implies(previous.clone(), right))
})
}
alt((complete_and, complete_or, left_hand_side)),
),
move |right| previous.clone().implies(right),
)
}
fn not_expression(input: &str) -> IResult<&str, Expression> {
preceded(
char('!'),
left_hand_side,
)(input).map(|(remaining, right)| {
(remaining, not(right))
})
map(preceded(char('!'), left_hand_side), Expression::not)(input)
}
fn value(input: &str) -> IResult<&str, Expression> {
pair(
take_while1(|c: char| c.is_ascii_alphabetic()),
take_while(|c: char| c.is_ascii_alphanumeric() || c == '_'),
)(input)
.map(|(remaining, (first, rest))| {
map(
pair(
take_while1(|c: char| c.is_ascii_alphabetic()),
take_while(|c: char| c.is_ascii_alphanumeric() || c == '_'),
),
|(first, rest)| {
let value = format!("{first}{rest}");
(remaining, atomic(value))
})
atomic(value)
},
)(input)
}
#[cfg(test)]
@ -144,49 +124,62 @@ mod tests {
fn test_parse() {
let input = "a & b => c";
let result = super::parse_expression(input);
assert_eq!(result, Ok(implies(and(atomic("a"), atomic("b")), atomic("c"))));
assert_eq!(
result,
Ok(implies(and(atomic("a"), atomic("b")), atomic("c")))
);
}
#[test]
fn test_parse_complex() {
let input = "a => b | !(!c | d & e) => b";
let result = super::parse_expression(input);
assert_eq!(result, Ok(implies(
implies(
atomic("a"),
or(
atomic("b"),
not(
or(
not(atomic("c")),
and(atomic("d"), atomic("e")),
)
assert_eq!(
result,
Ok(implies(
implies(
atomic("a"),
or(
atomic("b"),
not(or(not(atomic("c")), and(atomic("d"), atomic("e")),)),
),
),
),
atomic("b"),
)));
atomic("b"),
))
);
}
#[test]
fn test_operator_weight() {
let input = "A & B | C => D | E & F";
let result = super::parse_expression(input);
assert_eq!(result, Ok(implies(or(and(atomic("A"), atomic("B")), atomic("C")), or(atomic("D"), and(atomic("E"), atomic("F"))))));
assert_eq!(
result,
Ok(implies(
or(and(atomic("A"), atomic("B")), atomic("C")),
or(atomic("D"), and(atomic("E"), atomic("F")))
))
);
}
#[test]
fn test_implies_chain() {
let input = "a => b => c";
let result = super::parse_expression(input);
assert_eq!(result, Ok(implies(implies(atomic("a"), atomic("b")), atomic("c"))));
assert_eq!(
result,
Ok(implies(implies(atomic("a"), atomic("b")), atomic("c")))
);
}
#[test]
fn test_parse_parentheses() {
let input = "a & (b => c)";
let result = super::parse_expression(input);
assert_eq!(result, Ok(and(atomic("a"), implies(atomic("b"), atomic("c")))));
assert_eq!(
result,
Ok(and(atomic("a"), implies(atomic("b"), atomic("c"))))
);
}
#[test]
@ -228,7 +221,10 @@ mod tests {
fn test_expression_with_not_inside_parentheses() {
let input = "a & !(b | c)";
let result = super::parse_expression(input);
assert_eq!(result, Ok(and(atomic("a"), not(or(atomic("b"), atomic("c"))))));
assert_eq!(
result,
Ok(and(atomic("a"), not(or(atomic("b"), atomic("c")))))
);
}
#[test]
@ -277,7 +273,19 @@ mod tests {
fn test_or_chain() {
let input = "a | b | c | d | e | f | g";
let result = super::parse_expression(input);
assert_eq!(result, Ok(or(or(or(or(or(or(atomic("a"), atomic("b")), atomic("c")), atomic("d")), atomic("e")), atomic("f")), atomic("g"))));
assert_eq!(
result,
Ok(or(
or(
or(
or(or(or(atomic("a"), atomic("b")), atomic("c")), atomic("d")),
atomic("e")
),
atomic("f")
),
atomic("g")
))
);
}
#[test]
@ -300,7 +308,10 @@ mod tests {
let expression = atomic("a");
let input = " & b | c";
let result = super::expression(expression)(input);
assert_eq!(result, Ok(("", or(and(atomic("a"), atomic("b")), atomic("c")))));
assert_eq!(
result,
Ok(("", or(and(atomic("a"), atomic("b")), atomic("c"))))
);
}
#[test]
@ -308,7 +319,13 @@ mod tests {
let expression = atomic("a");
let input = " & b | c => d";
let result = super::expression(expression)(input);
assert_eq!(result, Ok(("", implies(or(and(atomic("a"), atomic("b")), atomic("c")), atomic("d")))));
assert_eq!(
result,
Ok((
"",
implies(or(and(atomic("a"), atomic("b")), atomic("c")), atomic("d"))
))
);
}
#[test]
@ -316,14 +333,26 @@ mod tests {
let expression = atomic("a");
let input = " & (b | c) => d";
let result = super::expression(expression)(input);
assert_eq!(result, Ok(("", implies(and(atomic("a"), or(atomic("b"), atomic("c"))), atomic("d")))));
assert_eq!(
result,
Ok((
"",
implies(and(atomic("a"), or(atomic("b"), atomic("c"))), atomic("d"))
))
);
}
#[test]
fn test_expression_parentheses_and() {
let input = "(a & b) | (c & d)";
let result = super::_parse_expression(input);
assert_eq!(result, Ok(("", or(and(atomic("a"), atomic("b")), and(atomic("c"), atomic("d"))))));
assert_eq!(
result,
Ok((
"",
or(and(atomic("a"), atomic("b")), and(atomic("c"), atomic("d")))
))
);
}
#[test]
@ -331,7 +360,16 @@ mod tests {
let expression = atomic("a");
let input = " & b | (c => d)";
let result = super::expression(expression)(input);
assert_eq!(result, Ok(("", or(and(atomic("a"), atomic("b")), implies(atomic("c"), atomic("d"))))));
assert_eq!(
result,
Ok((
"",
or(
and(atomic("a"), atomic("b")),
implies(atomic("c"), atomic("d"))
)
))
);
}
#[test]
@ -339,7 +377,16 @@ mod tests {
let expression = atomic("a");
let input = " & (b | (c => d))";
let result = super::expression(expression)(input);
assert_eq!(result, Ok(("", and(atomic("a"), or(atomic("b"), implies(atomic("c"), atomic("d")))))));
assert_eq!(
result,
Ok((
"",
and(
atomic("a"),
or(atomic("b"), implies(atomic("c"), atomic("d")))
)
))
);
}
#[test]
@ -361,6 +408,9 @@ mod tests {
fn test_parenthesized_expression_3_atomics() {
let input = "(A | B | C)";
let result = super::parenthesized_expression(input);
assert_eq!(result, Ok(("", or(or(atomic("A"), atomic("B")), atomic("C")))));
assert_eq!(
result,
Ok(("", or(or(atomic("A"), atomic("B")), atomic("C"))))
);
}
}

View File

@ -1 +1 @@
pub(crate) mod expression_parser;
pub(crate) mod expression_parser;

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
use axum::Json;
use axum::response::{IntoResponse, Response};
use axum::Json;
use serde::Serialize;
#[derive(Serialize, Default)]

View File

@ -1,4 +1,4 @@
pub(crate) mod response;
pub(crate) mod error;
pub(crate) mod options;
pub(crate) mod response;
pub(crate) mod routes;
pub(crate) mod options;

View File

@ -1,15 +1,12 @@
use serde::Deserialize;
use crate::expressions::truth_table::{Hide, Sort};
use crate::utils::serialize::{ret_true, deserialize_bool};
use crate::utils::serialize::{deserialize_bool, ret_true};
use serde::Deserialize;
// TODO deserialize_bool should not be necessary
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SimplifyOptions {
#[serde(
default = "ret_true",
deserialize_with = "deserialize_bool"
)]
#[serde(default = "ret_true", deserialize_with = "deserialize_bool")]
pub simplify: bool,
#[serde(default, deserialize_with = "deserialize_bool")]
pub ignore_case: bool,

View File

@ -1,5 +1,5 @@
use axum::response::{IntoResponse, Response};
use lib::derive::IntoResponse;
use lib::into_response_derive::IntoResponse;
use serde::Serialize;
use crate::expressions::expression::Expression;
@ -16,7 +16,11 @@ pub struct Operation {
impl Operation {
pub fn new(before: &Expression, after: &Expression, law: Law) -> Option<Self> {
if before != after {
Some(Self { before: before.to_string(), after: after.to_string(), law })
Some(Self {
before: before.to_string(),
after: after.to_string(),
law,
})
} else {
None
}
@ -50,4 +54,4 @@ impl IsValidResponse {
#[serde(rename_all = "camelCase")]
pub struct TruthTableResponse {
pub truth_table: TruthTable,
}
}

View File

@ -1,7 +1,7 @@
use axum::extract::Path;
use axum::http::StatusCode;
use axum::Json;
use axum::response::{IntoResponse, Response};
use axum::Json;
use lib::router;
use serde::Serialize;
@ -40,7 +40,7 @@ async fn open_api() -> impl IntoResponse {
async fn is_valid(Path(path): Path<String>) -> Response {
match Expression::try_from(path.as_str()) {
Ok(_) => IsValidResponse::valid().into_response(),
Err(error) => Error::new(error.to_string(), ErrorKind::InvalidExpression).into_response()
Err(error) => Error::new(error.to_string(), ErrorKind::InvalidExpression).into_response(),
}
}

View File

@ -1,5 +1,3 @@
pub(crate) mod index;
pub(crate) mod simplify;
pub(crate) mod table;

View File

@ -9,10 +9,13 @@ use crate::routing::error::{Error, ErrorKind};
use crate::routing::options::{SimplifyAndTableOptions, SimplifyOptions};
use crate::routing::response::SimplifyResponse;
router!("/simplify", routes!(
get "/:exp" => simplify,
get "/table/:exp" => simplify_and_table
));
router!(
"/simplify",
routes!(
get "/:exp" => simplify,
get "/table/:exp" => simplify_and_table
)
);
async fn simplify(Path(path): Path<String>, Query(query): Query<SimplifyOptions>) -> Response {
match Expression::try_from(path.as_str()) {
@ -28,15 +31,21 @@ async fn simplify(Path(path): Path<String>, Query(query): Query<SimplifyOptions>
operations,
expression,
truth_table: None,
}.into_response()
}
Err(error) => {
(StatusCode::BAD_REQUEST, Error::new(error.to_string(), ErrorKind::InvalidExpression)).into_response()
}
.into_response()
}
Err(error) => (
StatusCode::BAD_REQUEST,
Error::new(error.to_string(), ErrorKind::InvalidExpression),
)
.into_response(),
}
}
async fn simplify_and_table(Path(path): Path<String>, Query(query): Query<SimplifyAndTableOptions>) -> Response {
async fn simplify_and_table(
Path(path): Path<String>,
Query(query): Query<SimplifyAndTableOptions>,
) -> Response {
match Expression::try_from(path.as_str()) {
Ok(mut expression) => {
let before = expression.to_string();
@ -51,10 +60,13 @@ async fn simplify_and_table(Path(path): Path<String>, Query(query): Query<Simpli
operations,
expression,
truth_table: Some(truth_table),
}.into_response()
}
Err(error) => {
(StatusCode::BAD_REQUEST, Error::new(error.to_string(), ErrorKind::InvalidExpression)).into_response()
}
.into_response()
}
Err(error) => (
StatusCode::BAD_REQUEST,
Error::new(error.to_string(), ErrorKind::InvalidExpression),
)
.into_response(),
}
}

View File

@ -9,16 +9,24 @@ use crate::routing::error::{Error, ErrorKind};
use crate::routing::options::TruthTableOptions;
use crate::routing::response::TruthTableResponse;
router!("/table", routes!(
get "/:exp" => table
));
router!(
"/table",
routes!(
get "/:exp" => table
)
);
// TODO Expression as input in body
async fn table(Path(value): Path<String>, Query(query): Query<TruthTableOptions>) -> Response {
match Expression::try_from(value) {
Ok(expression) => {
TruthTableResponse { truth_table: TruthTable::new(&expression, query) }.into_response()
Ok(expression) => TruthTableResponse {
truth_table: TruthTable::new(&expression, query),
}
Err(e) => (StatusCode::BAD_REQUEST, Error::new(e.to_string(), ErrorKind::InvalidExpression)).into_response(),
.into_response(),
Err(e) => (
StatusCode::BAD_REQUEST,
Error::new(e.to_string(), ErrorKind::InvalidExpression),
)
.into_response(),
}
}

View File

@ -19,7 +19,11 @@ macro_rules! load_html {
#[cfg(debug_assertions)]
macro_rules! absolute_path {
($filename:literal) => {
concat!(env!("CARGO_MANIFEST_DIR"), "/src/resources/static/", $filename)
concat!(
env!("CARGO_MANIFEST_DIR"),
"/src/resources/static/",
$filename
)
};
}

View File

@ -1,2 +1,2 @@
pub mod axum;
pub mod serialize;
pub mod axum;

View File

@ -4,8 +4,9 @@ pub(crate) const fn ret_true() -> bool {
true
}
pub(crate) fn deserialize_bool<'de, D: Deserializer<'de>>(deserializer: D) -> Result<bool, D::Error> {
pub(crate) fn deserialize_bool<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<bool, D::Error> {
let s: &str = Deserialize::deserialize(deserializer)?;
match s {
@ -13,4 +14,4 @@ pub(crate) fn deserialize_bool<'de, D: Deserializer<'de>>(deserializer: D) -> Re
"false" => Ok(false),
_ => Err(de::Error::unknown_variant(s, &["true", "false"])),
}
}
}