[밑바닥부터 만드는 컴퓨팅] 9장 고수준 언어
해당 장에서는 고수준 프로그램을 작성할 수 있는 잭(jack)이라는 언어를 소개합니다. 잭은 간단한 객체지향 언어입니다. 자바나 C# 같은 언어와 비슷하지만 문법이 더 단순하며, 상속을 지원하지 않습니다.
10장과 11장에서는 잭 프로그램을 VM 코드로 변역하는 컴파일러를 만들고, 12장에서는 잭/핵 플랫폼의 간단한 운영체제를 작성할 예정입니다.
1. 배경
1.1 예제 1: Hello world
잭 언어는 Main.main 함수에서 시작됩니다. Main이라는 클래스 안에 main이라는 메서드가 있어야 합니다.
/* Hello World 프로그램 */
class Main {
function void main() {
/* 표준 라이브러리를 이용해서 텍스트를 출력한다. */
do Output.printString("Hello World");
do Output.println(); // 새 라인
return;
}
}
1.2 절차적 프로그래밍과 배열 처리
/* 정수열의 평균을 계산한다. */
class Main {
function void main() {
var Array a;
var int length;
var int i, sum;
let length = Keyboard. readInt("How many numbers? ");
let a = Array.new(length); // 배열을 구성한다.
let i = 0;
while (i < length) {
let a[i] = Keyboard. readInt("Enter the next number: ");
let sum = sum + a[i];
let i = i + 1;
}
do Output.printString("The average is: ");
do Output.printInt(sum / length);
do Output.println();
return;
}
}
잭 프로그램은 표준 라이브러리에 내장된 Array 클래스를 통해 배열을 선언합니다. 잭 언어의 배열에는 타입이 없습니다. 정수, 객체 등등 어떤 것이라도 넣을 수 있습니다.
1.3 예제 3: 추상 데이터 타입
프로그래밍 언어는 기본 데이터 타입이 정해져 있는데, 잭은 int, char, boolean 세가지를 지원합니다. 추가로 필요한 경우 직접 클래스를 만들어 사용할 수 있습니다.
1.3.1 클래스 인터페이스 정의하기
이번 예시는 분수를 추상화하려 합니다.
// Fraction은 n/m을 표현하는 객체다. (n, m은 정수)
field int numerator, denominator // Fraction 객체 속성
constructor Fraction new(int a, int b) // 새로운 Fraction 객체를 반환한다.
method int getNumerator() // 이 분수의 분자를 반환한다.
method int getDenominator() // 이 분수의 분모를 반환한다.
method Fraction plus (Fraction other) // 이 분수와 또 다른 분수의 합을 분수 객체로 반환한다.
method void print() // 이 분수를 "분자/분모" 형식으로 출력한다. 필요한 분수 관련 기능을 여기에 추가한다
잭에서 현재 객체 수준의 연산은 메서드로 표현되고, 클래스 수준의 연산은 함수로 표현됩니다.
1.3.2 클래스 사용하기
// 2/3와 1/5를 더한다.
class Main {
function void main() {
var Fraction a, b, c;
let a = Fraction. new(2,3);
let b = Fraction. new(1,5);
let c = a.plus(b); // c = a + b 24
do c.print(); // "13/15"가 출력되어야 한다.
return;
}
}
1.3.3 클래스 구현하기
/* Fraction 타입 및 관련 기능을 제공한다. */
class Fraction {
field int numerator, denominator;
/* 주어진 분자 및 분모에서
* 새로운 (약분된) 분수를 생성한다. */
constructor Fraction new(int a, int b) {
let numerator = a;
let denominator = b;
do reduce(); // a/b가 약분되지 않았으면 약분한다.
return this;
}
/* 이 분수를 약분한다. */
method void reduce() {
var int g;
let g = Fraction.gcd(numerator, denominator);
if (g > 1) {
let numerator = numerator / g;
let denominator = denominator / 9;
}
return;
}
/** a와 b의 최대 공약수를 계산한다. */
function int gcd(int a, int b){
var int r;
while (~(b = 0)) { // 유클리드 알고리즘을 적용한다.
let r = a - (b * (a / b)); // rea/b의 나머지
let a = b;
let b = r;
}
return a;
}
/** 접근자. */
method int getNumerator() { return numerator; }
method int getDenominator() { return denominator; }
/* 이 분수와 또 다른 분수의 합을 반환한다. */
method Fraction plus ( Fraction other){
var int sum;
let sum = (numerator * other.getDenominator()) + (other.getNumerator() * denominator());
return Fraction.new(sum, denominator * other.getDenominator());
}
// 추가 분수 관련 메서드들; minus, times, div 등
/* 이 분수를 출력한다. */
method void print() {
do Output.printInt(numerator);
do Output.printString("/");
do Output.printInt(denominator);
return;
}
}// Fraction 클래스
1.4 예제 4 : 연결 리스트 구현
/** List 클래스는 연결 리스트 추상화를 제공한다. */
class List {
field int data;
field List next;
/* 새로운 List 객체를 생성한다. */
constructor List new(int car, List cdr) {
let data = car;
let next = cdr;
return this;
}
/* 이 List를 리스트 꼬리부터 재귀적으로 제거한다. */
method void dispose() {
if (~(next = null)) {
do next.dispose();
}
// OS 루틴을 이용해서 이 객체가
// 점유했던 메모리를 재활용한다.
do Memory.deAlloc(this);
return;
}
// 추가적인 리스트 관련 메서드는 여기에 쓴다. } // List 클래스
} List 클래스
이후 jack 언어에 대한 자세한 내용은 다루지 않겠습니다. 기본적으로 고수준 프로그래밍 언어를 다룰 줄 안다면 jack 언어를 사용하는데는 문제 없을것으로 생각됩니다.
Leave a comment