diff --git a/README.md b/README.md index 13420b29..aaf3363c 100644 --- a/README.md +++ b/README.md @@ -1 +1,18 @@ -# javascript-calculator-precourse \ No newline at end of file +## ➕ 문자열 덧셈 계산기 체크리스트 + +### 기능 구현 + +- [ ] 빈 문자열 입력 시 `0` 반환 +- [ ] 기본 구분자 (`,` `:`) 로 숫자 분리 +- [ ] 커스텀 구분자 `"//<구분자>\n"` 형식 지원 +- [ ] 잘못된 입력 시 `[ERROR]` 메시지 출력 +- [ ] 양수만 허용 +- [ ] `Console.readLineAsync`, `Console.print` 사용 +- [ ] 출력 형식: `결과 : <합계>` + +### 예외 처리 + +- [ ] 음수 입력 시 에러 발생 +- [ ] 숫자 아닌 값 입력 시 에러 발생 +- [ ] 잘못된 포맷 입력 시 에러 발생 +- [ ] 연속 구분자 처리 정책 반영 diff --git a/src/App.js b/src/App.js index 091aa0a5..eee77a1f 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,20 @@ +import { readInput, printResult, printError } from "./io/InputHandler.js"; +import { calculateSum } from "./calculator/Calculator.js"; + class App { - async run() {} + async run() { + try { + const input = await readInput(); + const sum = calculateSum(input); + printResult(sum); + } catch (err) { + const message = `[ERROR] ${( + err?.message ?? "알 수 없는 오류가 발생했습니다." + ).replace(/^\[ERROR\]\s*/, "")}`; + printError(message); + throw new Error(message); + } + } } export default App; diff --git a/src/calculator/Calculator.js b/src/calculator/Calculator.js new file mode 100644 index 00000000..18d2fd87 --- /dev/null +++ b/src/calculator/Calculator.js @@ -0,0 +1,14 @@ +import Parser from "./Parser.js"; +import { validateTokens } from "./Validator.js"; + +export function calculateSum(input) { + if (input == null) throw new Error("입력이 필요합니다."); + if (input === "") return 0; + + const normalized = String(input).replace(/\\n/g, "\n"); + + const tokens = Parser.parse(normalized); + validateTokens(tokens); + + return tokens.reduce((sum, t) => sum + Number(t), 0); +} diff --git a/src/calculator/Parser.js b/src/calculator/Parser.js new file mode 100644 index 00000000..8f91afc2 --- /dev/null +++ b/src/calculator/Parser.js @@ -0,0 +1,25 @@ +class Parser { + static escapeForRegExp(str) { + return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + } + + static parse(input) { + if (!input) return []; + + let delimiters = [",", ":"]; + let expression = input; + + const m = input.match(/^\/\/(.)\n/); + if (m) { + delimiters.push(m[1]); + expression = input.slice(m[0].length); + } + + const pattern = new RegExp( + `[${Parser.escapeForRegExp(delimiters.join(""))}]` + ); + return expression.split(pattern); + } +} + +export default Parser; diff --git a/src/calculator/Validator.js b/src/calculator/Validator.js new file mode 100644 index 00000000..cdf5c65a --- /dev/null +++ b/src/calculator/Validator.js @@ -0,0 +1,17 @@ +export function validateTokens(tokens) { + if (!Array.isArray(tokens)) { + throw new Error("유효하지 않은 입력입니다."); + } + + for (const t of tokens) { + if (t === "") throw new Error("유효하지 않은 구분자 사용입니다."); + + if (!/^-?\d+$/.test(t)) throw new Error("숫자만 입력할 수 있습니다."); + + const n = Number(t); + + if (n < 0) throw new Error("음수는 허용되지 않습니다."); + + if (n === 0) throw new Error("양수만 입력할 수 있습니다."); + } +} diff --git a/src/io/InputHandler.js b/src/io/InputHandler.js new file mode 100644 index 00000000..ee3c99c3 --- /dev/null +++ b/src/io/InputHandler.js @@ -0,0 +1,13 @@ +import { Console } from "@woowacourse/mission-utils"; + +export async function readInput() { + Console.print("덧셈할 문자열을 입력해 주세요."); + const input = await Console.readLineAsync(""); + return input; +} +export function printResult(result) { + Console.print(`결과 : ${result}`); +} +export function printError(message) { + Console.print(message); +}