From 898c2d8288eeac2972722212111c5b8ebe825cf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20B=20Nagy?= <20251272+BNAndras@users.noreply.github.com> Date: Fri, 20 Sep 2024 22:57:02 -0700 Subject: [PATCH] Add matching-brackets --- config.json | 8 + .../matching-brackets/.docs/instructions.md | 5 + .../matching-brackets/.docs/introduction.md | 8 + .../matching-brackets/.meta/config.json | 18 ++ .../matching-brackets/.meta/example.red | 40 ++++ .../matching-brackets/.meta/tests.toml | 70 ++++++ .../matching-brackets-test.red | 184 +++++++++++++++ .../matching-brackets/matching-brackets.red | 11 + .../practice/matching-brackets/testlib.red | 217 ++++++++++++++++++ 9 files changed, 561 insertions(+) create mode 100644 exercises/practice/matching-brackets/.docs/instructions.md create mode 100644 exercises/practice/matching-brackets/.docs/introduction.md create mode 100644 exercises/practice/matching-brackets/.meta/config.json create mode 100644 exercises/practice/matching-brackets/.meta/example.red create mode 100644 exercises/practice/matching-brackets/.meta/tests.toml create mode 100644 exercises/practice/matching-brackets/matching-brackets-test.red create mode 100644 exercises/practice/matching-brackets/matching-brackets.red create mode 100644 exercises/practice/matching-brackets/testlib.red diff --git a/config.json b/config.json index 59589d2..2cc17ca 100644 --- a/config.json +++ b/config.json @@ -352,6 +352,14 @@ "prerequisites": [], "difficulty": 5 }, + { + "slug": "matching-brackets", + "name": "Matching Brackets", + "uuid": "aef1c956-98fa-4caa-96fe-264f68aa1e01", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, { "slug": "high-scores", "name": "High Scores", diff --git a/exercises/practice/matching-brackets/.docs/instructions.md b/exercises/practice/matching-brackets/.docs/instructions.md new file mode 100644 index 0000000..ea17084 --- /dev/null +++ b/exercises/practice/matching-brackets/.docs/instructions.md @@ -0,0 +1,5 @@ +# Instructions + +Given a string containing brackets `[]`, braces `{}`, parentheses `()`, or any combination thereof, verify that any and all pairs are matched and nested correctly. +Any other characters should be ignored. +For example, `"{what is (42)}?"` is balanced and `"[text}"` is not. diff --git a/exercises/practice/matching-brackets/.docs/introduction.md b/exercises/practice/matching-brackets/.docs/introduction.md new file mode 100644 index 0000000..0618221 --- /dev/null +++ b/exercises/practice/matching-brackets/.docs/introduction.md @@ -0,0 +1,8 @@ +# Introduction + +You're given the opportunity to write software for the Bracketeer™, an ancient but powerful mainframe. +The software that runs on it is written in a proprietary language. +Much of its syntax is familiar, but you notice _lots_ of brackets, braces and parentheses. +Despite the Bracketeer™ being powerful, it lacks flexibility. +If the source code has any unbalanced brackets, braces or parentheses, the Bracketeer™ crashes and must be rebooted. +To avoid such a scenario, you start writing code that can verify that brackets, braces, and parentheses are balanced before attempting to run it on the Bracketeer™. diff --git a/exercises/practice/matching-brackets/.meta/config.json b/exercises/practice/matching-brackets/.meta/config.json new file mode 100644 index 0000000..f439445 --- /dev/null +++ b/exercises/practice/matching-brackets/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "matching-brackets.red" + ], + "test": [ + "matching-brackets-test.red" + ], + "example": [ + ".meta/example.red" + ] + }, + "blurb": "Make sure the brackets and braces all match.", + "source": "Ginna Baker" +} diff --git a/exercises/practice/matching-brackets/.meta/example.red b/exercises/practice/matching-brackets/.meta/example.red new file mode 100644 index 0000000..5d579fa --- /dev/null +++ b/exercises/practice/matching-brackets/.meta/example.red @@ -0,0 +1,40 @@ +Red [ + description: {"Matching Brackets" exercise solution for exercism platform} + author: "BNAndras" +] + +is-paired: function [ + value +] [ + stack: [] + + foreach char value [ + + if find [#"[" #"{" #"("] char [ + append stack char + ] + + if find [#"]" #"}" #")"] char [ + if empty? stack [ + return false + ] + + candidate: take/last stack + + if and~ candidate = #"[" char <> #"]" [ + return false + ] + + if and~ candidate = #"{" char <> #"}" [ + return false + ] + if and~ candidate = #"(" char <> #")" [ + return false + ] + ] + ] + + + empty? stack +] + diff --git a/exercises/practice/matching-brackets/.meta/tests.toml b/exercises/practice/matching-brackets/.meta/tests.toml new file mode 100644 index 0000000..35a98a0 --- /dev/null +++ b/exercises/practice/matching-brackets/.meta/tests.toml @@ -0,0 +1,70 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[81ec11da-38dd-442a-bcf9-3de7754609a5] +description = "paired square brackets" + +[287f0167-ac60-4b64-8452-a0aa8f4e5238] +description = "empty string" + +[6c3615a3-df01-4130-a731-8ef5f5d78dac] +description = "unpaired brackets" + +[9d414171-9b98-4cac-a4e5-941039a97a77] +description = "wrong ordered brackets" + +[f0f97c94-a149-4736-bc61-f2c5148ffb85] +description = "wrong closing bracket" + +[754468e0-4696-4582-a30e-534d47d69756] +description = "paired with whitespace" + +[ba84f6ee-8164-434a-9c3e-b02c7f8e8545] +description = "partially paired brackets" + +[3c86c897-5ff3-4a2b-ad9b-47ac3a30651d] +description = "simple nested brackets" + +[2d137f2c-a19e-4993-9830-83967a2d4726] +description = "several paired brackets" + +[2e1f7b56-c137-4c92-9781-958638885a44] +description = "paired and nested brackets" + +[84f6233b-e0f7-4077-8966-8085d295c19b] +description = "unopened closing brackets" + +[9b18c67d-7595-4982-b2c5-4cb949745d49] +description = "unpaired and nested brackets" + +[a0205e34-c2ac-49e6-a88a-899508d7d68e] +description = "paired and wrong nested brackets" + +[1d5c093f-fc84-41fb-8c2a-e052f9581602] +description = "paired and wrong nested brackets but innermost are correct" + +[ef47c21b-bcfd-4998-844c-7ad5daad90a8] +description = "paired and incomplete brackets" + +[a4675a40-a8be-4fc2-bc47-2a282ce6edbe] +description = "too many closing brackets" + +[a345a753-d889-4b7e-99ae-34ac85910d1a] +description = "early unexpected brackets" + +[21f81d61-1608-465a-b850-baa44c5def83] +description = "early mismatched brackets" + +[99255f93-261b-4435-a352-02bdecc9bdf2] +description = "math expression" + +[8e357d79-f302-469a-8515-2561877256a1] +description = "complex latex expression" diff --git a/exercises/practice/matching-brackets/matching-brackets-test.red b/exercises/practice/matching-brackets/matching-brackets-test.red new file mode 100644 index 0000000..fcd8f7a --- /dev/null +++ b/exercises/practice/matching-brackets/matching-brackets-test.red @@ -0,0 +1,184 @@ +Red [ + description: {Tests for "Matching Brackets" Exercism exercise} + author: "loziniak" +] + +#include %testlib.red + +test-init/limit %matching-brackets.red 1 +; test-init/limit %.meta/example.red 1 ; test example solution + +canonical-cases: [#[ + description: "paired square brackets" + input: #[ + value: "[]" + ] + expected: true + function: "is-paired" + uuid: "81ec11da-38dd-442a-bcf9-3de7754609a5" +] #[ + description: "empty string" + input: #[ + value: "" + ] + expected: true + function: "is-paired" + uuid: "287f0167-ac60-4b64-8452-a0aa8f4e5238" +] #[ + description: "unpaired brackets" + input: #[ + value: "[[" + ] + expected: false + function: "is-paired" + uuid: "6c3615a3-df01-4130-a731-8ef5f5d78dac" +] #[ + description: "wrong ordered brackets" + input: #[ + value: "}{" + ] + expected: false + function: "is-paired" + uuid: "9d414171-9b98-4cac-a4e5-941039a97a77" +] #[ + description: "wrong closing bracket" + input: #[ + value: "{]" + ] + expected: false + function: "is-paired" + uuid: "f0f97c94-a149-4736-bc61-f2c5148ffb85" +] #[ + description: "paired with whitespace" + input: #[ + value: "{ }" + ] + expected: true + function: "is-paired" + uuid: "754468e0-4696-4582-a30e-534d47d69756" +] #[ + description: "partially paired brackets" + input: #[ + value: "{[])" + ] + expected: false + function: "is-paired" + uuid: "ba84f6ee-8164-434a-9c3e-b02c7f8e8545" +] #[ + description: "simple nested brackets" + input: #[ + value: "{[]}" + ] + expected: true + function: "is-paired" + uuid: "3c86c897-5ff3-4a2b-ad9b-47ac3a30651d" +] #[ + description: "several paired brackets" + input: #[ + value: "{}[]" + ] + expected: true + function: "is-paired" + uuid: "2d137f2c-a19e-4993-9830-83967a2d4726" +] #[ + description: "paired and nested brackets" + input: #[ + value: "([{}({}[])])" + ] + expected: true + function: "is-paired" + uuid: "2e1f7b56-c137-4c92-9781-958638885a44" +] #[ + description: "unopened closing brackets" + input: #[ + value: "{[)][]}" + ] + expected: false + function: "is-paired" + uuid: "84f6233b-e0f7-4077-8966-8085d295c19b" +] #[ + description: "unpaired and nested brackets" + input: #[ + value: "([{])" + ] + expected: false + function: "is-paired" + uuid: "9b18c67d-7595-4982-b2c5-4cb949745d49" +] #[ + description: "paired and wrong nested brackets" + input: #[ + value: "[({]})" + ] + expected: false + function: "is-paired" + uuid: "a0205e34-c2ac-49e6-a88a-899508d7d68e" +] #[ + description: {paired and wrong nested brackets but innermost are correct} + input: #[ + value: "[({}])" + ] + expected: false + function: "is-paired" + uuid: "1d5c093f-fc84-41fb-8c2a-e052f9581602" +] #[ + description: "paired and incomplete brackets" + input: #[ + value: "{}[" + ] + expected: false + function: "is-paired" + uuid: "ef47c21b-bcfd-4998-844c-7ad5daad90a8" +] #[ + description: "too many closing brackets" + input: #[ + value: "[]]" + ] + expected: false + function: "is-paired" + uuid: "a4675a40-a8be-4fc2-bc47-2a282ce6edbe" +] #[ + description: "early unexpected brackets" + input: #[ + value: ")()" + ] + expected: false + function: "is-paired" + uuid: "a345a753-d889-4b7e-99ae-34ac85910d1a" +] #[ + description: "early mismatched brackets" + input: #[ + value: "{)()" + ] + expected: false + function: "is-paired" + uuid: "21f81d61-1608-465a-b850-baa44c5def83" +] #[ + description: "math expression" + input: #[ + value: "(((185 + 223.85) * 15) - 543)/2" + ] + expected: true + function: "is-paired" + uuid: "99255f93-261b-4435-a352-02bdecc9bdf2" +] #[ + description: "complex latex expression" + input: #[ + value: {\left(\begin{array}{cc} \frac{1}{3} & x\\ \mathrm{e}^^{x} &... x^^2 \end{array}\right)} + ] + expected: true + function: "is-paired" + uuid: "8e357d79-f302-469a-8515-2561877256a1" +]] + + +foreach c-case canonical-cases [ + case-code: reduce [ + 'expect c-case/expected compose [ + (to word! c-case/function) (values-of c-case/input) + ] + ] + + test c-case/description case-code +] + +test-results/print diff --git a/exercises/practice/matching-brackets/matching-brackets.red b/exercises/practice/matching-brackets/matching-brackets.red new file mode 100644 index 0000000..607d7da --- /dev/null +++ b/exercises/practice/matching-brackets/matching-brackets.red @@ -0,0 +1,11 @@ +Red [ + description: {"Matching Brackets" exercise solution for exercism platform} + author: "" ; you can write your name here, in quotes +] + +is-paired: function [ + value +] [ + cause-error 'user 'message "You need to implement is-paired function." +] + diff --git a/exercises/practice/matching-brackets/testlib.red b/exercises/practice/matching-brackets/testlib.red new file mode 100644 index 0000000..9b0b79e --- /dev/null +++ b/exercises/practice/matching-brackets/testlib.red @@ -0,0 +1,217 @@ +Red [ + description: {Unit testing library} + author: "loziniak" +] + + +context [ + tested: ignore-after: test-file: results: output: none + + set 'test-init function [ + file [file!] + /limit + ia [integer!] + ] [ + self/tested: 0 + self/ignore-after: either limit [ia] [none] + self/test-file: file + self/results: copy [] + self/output: copy "" + ] + + sandbox!: context [ + + assert: function [ + code [block!] + /local result + ] [ + res: last results + + set/any 'result do code + either :result = true [ + res/status: 'pass + ] [ + res/status: 'fail + throw/name none 'expect-fail + ] + + :result + ] + + expect: function [ + expectation [any-type!] + code [block!] + /local result + ] [ + res: last results + res/expected: :expectation + + set/any 'result do code + res/actual: :result + + either :result = :expectation [ + res/status: 'pass + ] [ + res/status: 'fail + throw/name none 'expect-fail + ] + + :result + ] + + expect-error: function [ + type [word!] + code [block!] + /message + msg [string!] + /local result result-or-error + ] [ + returned-error?: no + set/any 'result-or-error try [ + set/any 'result do code + returned-error?: yes + :result + ] + + res: last results + res/actual: :result-or-error + res/expected: compose [type: (type)] + if message [append res/expected compose [id: 'message arg1: (msg)]] + + either all [ + error? :result-or-error + not returned-error? + result-or-error/type = type + any [ + not message + all [ + result-or-error/id = 'message + result-or-error/arg1 = msg + ] + ] + ] [ + res/status: 'pass + ] [ + res/status: 'fail + throw/name none 'expect-fail + ] + + :result-or-error + ] + ] + + set 'test function [ + summary [string!] + code [block!] + /extern + tested + ] [ + append results result: make map! compose/only [ + summary: (summary) ;@@ [string!] + test-code: (copy code) ;@@ [block!] + status: none ;@@ [word!] : 'pass | 'fail | 'error | 'ignored + ;-- expected (optional field) + ;-- actual (optional field) + ;-- output (optional field) + ] + + either any [ + none? ignore-after + tested < ignore-after + ] [ + clear output + old-functions: override-console + + exercise: make sandbox! load test-file + code: bind code exercise + uncaught?: yes + outcome: catch [ + outcome: try [ + catch/name [ + do code + ] 'expect-fail + none + ] + uncaught?: no + outcome + ] + + case [ + error? outcome [ + result/status: 'error + result/actual: outcome + ] + uncaught? [ + result/status: 'error + result/actual: make error! [type: 'throw id: 'throw arg1: outcome] + ] + ] + + restore-console old-functions + result/output: copy output + ] [ + result/status: 'ignored + ] + + tested: tested + 1 + ] + + set 'test-results function [ + /print + ] [ + either print [ + foreach result self/results [ + system/words/print rejoin [ + pad/with result/summary 40 #"." + "... " + switch result/status [ + pass ["✓"] + fail [rejoin [ + {FAILED.} + either find result 'expected [rejoin [ + { Expected: } result/expected + either find result 'actual [rejoin [ + {, but got } result/actual + ]] [] + ]] [] + newline + result/output + ]] + error [rejoin [ + newline + result/output + form result/actual + ]] + ignored ["(ignored)"] + ] + ] + ] + ] [ + self/results + ] + ] + + + override-console: function [] [ + old-functions: reduce [:prin :print :probe] + + system/words/prin: function [value [any-type!]] [ + append self/output form :value + return () + ] + system/words/print: function [value [any-type!]] [ + append self/output reduce [form :value #"^/"] + return () + ] + system/words/probe: function [value [any-type!]] [ + append self/output reduce [mold :value #"^/"] + return :value + ] + return old-functions + ] + + restore-console: function [old-functions [block!]] [ + set [prin print probe] old-functions + ] + +]