Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 63 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,63 @@
# javascript-calculator-precourse
# 🧮 JavaScript Calculator Precourse

문자열로 전달된 입력값을 숫자와 구분자로 분리하고 모든 숫자의 합계를 계산하는 프로젝트 입니다.

---

## 📋 기능 요구사항

### 1️⃣ 입력 처리

- 사용자로부터 한 줄의 문자열을 입력받습니다.

### 2️⃣ 구분자 인식

- **기본 구분자**
- 쉼표(`,`), 세미콜론(`;`)
- **커스텀 구분자**
- 입력 문자열의 시작 부분에서 `"//"`와 `"\n"` 사이의 문자를 커스텀 구분자로 정의합니다.
- ex)
```
//~!@#\n1~2!3@4#5;6,7
```
→ 커스텀 구분자: `~`, `!`, `@`, `#`
→ 기본 구분자(`,`, `;`) 혼용 가능

### 3️⃣ 입력값 검증

- 다음 조건을 만족하지 않으면 프로그램은 `[ERROR]` 메시지를 출력하고 종료합니다.
- 구분자 외의 문자가 포함된 경우

### 4️⃣ 숫자 추출 및 합산

- 모든 유효한 숫자를 추출하여 합계를 계산합니다.

### 5️⃣ 종료 처리

- 계산 결과를 출력한 뒤 프로그램을 정상 종료합니다.

---

## 💡 실행 예시

```bash
# good
덧셈할 문자열을 입력해 주세요.
1,2:3
결과 : 6

# good
덧셈할 문자열을 입력해 주세요.
//~!@#\n1~2!3@4#5;6,7
결과 : 28

# bad
덧셈할 문자열을 입력해 주세요.
1,2,a,3
[ERROR] 잘못된 입력입니다.

# bad
덧셈할 문자열을 입력해 주세요.
1,-2,3
[ERROR] 음수는 입력할 수 없습니다.
```
1,668 changes: 824 additions & 844 deletions package-lock.json

Large diffs are not rendered by default.

16 changes: 15 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
import InputView from "./view/InputView.js";
import OutputView from "./view/OutputView.js";
import Calculator from "./Calculator.js";
import { Console } from "@woowacourse/mission-utils";

class App {
async run() {}
async run() {
try {
const input = await InputView.read();
const result = Calculator.sum(input);
OutputView.result(result);
} catch (error) {
Console.print(error.message);
throw error;
}
}
}

export default App;
9 changes: 9 additions & 0 deletions src/Calculator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Parser from "./Parser.js";

export default class Calculator {
static sum(input) {
const numbers = Parser.parse(input);
const total = numbers.reduce((acc, cur) => acc + cur, 0);
return total;
}
}
35 changes: 35 additions & 0 deletions src/Parser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import Valid from "./Valid.js";
import { ERROR_MESSAGE } from "./constant.js";

export default class Parser {
static parse(input) {
if (Valid.isEmpty(input)) return [0];
Valid.isString(input);

input = input.replace(/\\n/g, "\n");

const defaultSeparators = [",", ";"];
let separators = [...defaultSeparators];
let numbersPart = input;

if (input.startsWith("//")) {
const [customPart, numbers] = input.split("\n");
if (!numbers) {
throw new Error(ERROR_MESSAGE.PREFIX + ERROR_MESSAGE.INVALID_CUSTOM_FORMAT);
}

const customSeparators = customPart.slice(2).split("");
separators = [...new Set([...defaultSeparators, ...customSeparators])];
numbersPart = numbers;
}

const escapeRegex = (ch) => ch.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
const regex = new RegExp(`[${separators.map(escapeRegex).join("")}]`);
const tokens = numbersPart.split(regex);

Valid.checkSeparator(numbersPart, separators);
Valid.checkNumber(tokens);

return tokens.map(Number);
}
}
34 changes: 34 additions & 0 deletions src/Valid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ERROR_MESSAGE } from "./constant.js";

export default class Valid {
static isEmpty(input) {
return !input || input.trim() === "";
}

static isString(input) {
if (typeof input !== "string") {
throw new Error(ERROR_MESSAGE.PREFIX + ERROR_MESSAGE.INVALID_IS_NOT_STRING);
}
}

static checkSeparator(input, allowedSeparators) {
for (const char of input) {
if (/[a-zA-Z]/.test(char)) continue;
if (!allowedSeparators.includes(char) && !/[0-9\n]/.test(char)) {
throw new Error(ERROR_MESSAGE.PREFIX + ERROR_MESSAGE.INVALID_SEPERATOR + ` (${char})`);
}
}
}

static checkNumber(tokens) {
tokens.forEach((t) => {
const num = Number(t);
if (isNaN(num)) {
throw new Error(ERROR_MESSAGE.PREFIX + `${ERROR_MESSAGE.NOT_NUMBER}: ${t}`);
}
if (num < 0) {
throw new Error(ERROR_MESSAGE.PREFIX + `${ERROR_MESSAGE.NEGATIVE_NUMBER}: ${t}`);
}
});
}
}
17 changes: 17 additions & 0 deletions src/constant.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// 사용자 입력 및 출력 안내 메시지
export const INPUT_MESSAGE = {
INPUT: `덧셈할 문자열을 입력해주세요.\n`,
};

export const OUTPUT_MESSAGE = {
OUTPUT: `결과 : `,
};

export const ERROR_MESSAGE = {
PREFIX: `[ERROR] `,
INVALID_IS_NOT_STRING: `타입 문자열 불일치`,
INVALID_SEPERATOR: `허용되지 않은 구분자 입력`,
INVALID_CUSTOM_FORMAT: `커스텀 구분자 형식이 잘못되었습니다. (예: //;\n1;2;3)`,
NOT_NUMBER: `숫자가 아닌 값`,
NEGATIVE_NUMBER: `음수는 허용되지 않습니다`,
};
8 changes: 8 additions & 0 deletions src/view/InputView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Console } from "@woowacourse/mission-utils";
import { INPUT_MESSAGE } from "../constant.js";

export default class InputView {
static async read() {
return await Console.readLineAsync(INPUT_MESSAGE.INPUT);
}
}
8 changes: 8 additions & 0 deletions src/view/OutputView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Console } from "@woowacourse/mission-utils";
import { OUTPUT_MESSAGE } from "../constant.js";

export default class OutputView {
static result(res) {
Console.print(OUTPUT_MESSAGE.OUTPUT + res);
}
}