하리Hari

1. 언어 개요 및 문법

하리(Hari) 언어의 기술 명세입니다.

1. 기술 상세 명세 (Technical Specification)

파서(Parser) 및 런타임 구현자를 위한 엄격한 규칙 정의예요.

1.1 어휘 및 구문 규칙 (Lexical & Syntax)

  • 식별자(Identifiers): 모든 변수명, 속성명, 특수 예약어 '나'(self)는 반드시 작은따옴표 ''로 감싸야 해요. 문자열 "" 내부에 쓰인 '는 무시되고 텍스트로 취급됩니다.
  • 타입 어노테이션: 타입은 반드시 대괄호 []로 명시해요. 렉서는 [ 기호 뒤에 이름이 오면 타입으로([숫자]), 값이 오면 목록으로(["사과"]) 명확히 구분하여 파싱합니다. 단, 빈 목록 []을 생성할 때는 파서의 모호성을 없애기 위해 반드시 '빈목록'을 [목록]인 []로 정하자처럼 타입을 명시해야 해요.
  • 블록 정의: 콜론(:)과 **들여쓰기(Indentation)**로 코드 블록을 구분해요. 줄바꿈이 문장의 종료를 의미합니다.
  • 논리 연산자: 그리고또는이 섞인 복잡한 조건식은 모호성을 피하기 위해 반드시 소괄호 ()를 사용하여 우선순위를 강제해야 해요.

1.2 타입과 객체지향 규약 (Types & OOP)

  • 점진적 타이핑(Gradual Typing): 교육용 언어라는 철학에 맞추어 타입 어노테이션([타입]인, [타입]을 돌려주는 등)은 완벽히 생략 가능해요. 타입을 생략하면 파서는 이를 동적 타입([아무거나])으로 취급하여 유연하게 동작하며, 명시적으로 타입을 적어주면 실행(평가) 시점에 엄격하게 검사하는 정적 타입 모드로 동작합니다.
  • 접근 제어(Access Modifiers): 숨기자(Private), 물려주자(Protected), 정하자/만들자(Public) 동사로 캡슐화를 강제해요. 숨기자로 선언된 속성은 자식 클래스 인스턴스 메모리 내부에는 존재하지만, 자식 스코프에서는 절대 접근할 수 없어요 (완전한 은닉).
  • 단일 상속과 인터페이스: 다이아몬드 상속 문제를 방지하기 위해 클래스는 단일 상속만 지원해요. 다중 구현(다형성)이 필요할 때는 인터페이스를 사용합니다. 인터페이스(규약 선언)는 따른다 키워드로 상속받아요. 클래스 선언부에서 [인터페이스]를 따른다고 선언한 뒤, 해당 필수 메서드를 다 구현했는지의 여부는 클래스 선언문이 평가되는 시점(런타임)에 즉시 검사되어 오류로 처리됩니다.
  • 상속 시 생성자: 자식 클래스에서 생성자를 생략하면 부모의 생성자가 자동 호출돼요. 덮어쓸 경우 부모의 <처음 만들어질 때>()를 실행하자로 명시적 호출이 가능합니다.

1.3 함수와 변수 스코프 (Functions & Scope)

  • 어휘적 스코프와 클로저(Closure): 하리 언어는 어휘적 스코프(Lexical Scoping) 기반의 클로저를 지원해요. 바깥의 키워드를 사용하면 부모 환경(Scope) 체인을 타고 올라가며 상위 스코프의 변수를 탐색하고 조작할 수 있어요.
  • 일급 객체 함수 (First-class Function): 함수 자체는 일급 값으로 취급돼요. 즉, '비교함수'를 <크다>로 정하자처럼 변수에 함수를 담아 콜백으로 전달하는 패턴을 정상적으로 지원합니다.
  • 반환값(Return): 함수에서 명시적으로 돌려주자를 호출하지 않으면, 기본적으로 비어있음을 반환해요.

1.4 타입 시스템 및 호환성 상세 (Type System & Compatibility)

  • [아무거나] 타입: 타입을 생략한 변수나 매개변수는 내부적으로 [아무거나](Any) 타입으로 취급되며, 타입 검사기가 일체의 런타임 제약을 무시(무조건 통과)해요.
  • Nullable 기본 허용: '변수'를 [타입]으로 준비하자로 선언만 할 경우 메모리에 비어있음(Null)이 할당돼요. 즉, 모든 타입은 기본적으로 비어있음을 허용합니다.
  • 재대입 (Reassignment): 이미 선언된 변수에 값을 변경할 때는 타입을 명시하지 않아요. 단, 초기 선언 시 정해진 타입과 다른 타입의 값을 재대입하거나 연산하려 하면 런타임 에러(TypeError)가 발생해요.
  • 묵시적 형변환 금지: 10 + "안녕"과 같은 묵시적(Implicit) 형변환은 엄격히 금지돼요. 반드시 <숫자로>(), <문자로>() 내장 도구를 통해 명시적으로 변환해야 합니다.
  • 업캐스팅 (Upcasting): 부모 타입으로 선언된 변수에 자식 객체를 할당하거나, 인터페이스 타입 매개변수에 이를 따르는(구현한) 객체를 전달하는 것은 런타임에 안전한 호환으로 통과돼요.
  • 제네릭(Generics): 데이터 묶음 선언 시 [(숫자)목록]이나 [(문자열, 숫자)사전]과 같이 괄호를 사용하여 내부 요소의 타입을 강제할 수 있어요. 런타임은 값이 추가될 때 해당 제네릭 타입을 재귀적으로 검사해요. 단, 함수의 매개변수나 반환형에 대한 제네릭은 언어의 단순성을 위해 제공하지 않으며 [아무거나]를 통해 유연하게 처리합니다.

1.5 모듈 및 예외 (Modules & Exceptions)

  • 도구(모듈) 가져오기 규칙: 내 파일은 문자열("파일이름.hr")로, 표준 라이브러리 같은 모듈은 대괄호([모듈명])로 가져와 이름 충돌을 방지해요. 임포트 없이 쓰는 것은 출력하자 같은 문법과 <숫자로>() 같은 원시 타입 변환뿐이고, 수학 같은 나머지 도구는 [수학]에서 <올림>을 가져오자처럼 가져와야 해요. 여러 항목은 [수학]에서 <올림>과 <버림>을 가져오자처럼 /로 이어 한 문장에 가져오고, 모듈 전체는 [수학]에서 전부 가져오자로 가져와요(카나데는 から全部持ってこよう). 이름이 겹치면 항목 하나에 한해 [수학]에서 <올림>을 <반올림>으로 가져오자처럼 별칭을 붙여요. 전부는 그 모듈이 선언한 함수·클래스·인터페이스(내장 모듈은 함수 전부)를 가져와요.
  • 임포트된 파일의 언어: 가져온 파일은 자기 확장자의 언어(.hr 하자, .knd 카나데)로 실행돼서, 하리 파일과 카나데 파일이 서로를 가져올 수 있어요. 브라우저 놀이터에서는 표준 라이브러리만 가져올 수 있고 파일은 가져올 수 없어요.
  • 순환 임포트(Circular Import): 실행 시점에 모듈 캐싱을 통해 무한 루프를 방지해요.
  • 예외 객체 구조: 발생된 에러 객체는 내장된 [오류] 클래스의 인스턴스이며, 기본적으로 '메시지' 속성을 가져요.
놀이터
결과
코드를 작성하고 '실행' 버튼을 눌러보세요.