Sabr 는 단순한 스택 기반 프로그래밍 언어입니다.
git clone --depth=1 https://github.com/sabrlang/sabr.git
cd sabr
mkdir build
cd build
cmake ..
make
2 5 + puti 2.5 3.14 f* putf
7 7.850000
$puts macro
loop dup 0 > while swap putc -- end drop
end
"Hello, world!" puts
Hello, world!
$cr macro '\n' putc end
$draw func
$count set
$i for count to
$j for i ++ to
'*' putc
end
cr
end
end
5 draw
10 draw
*
**
***
****
*****
*
**
***
****
*****
******
*******
********
*********
**********
$ sabrc {source file name} {output file name}
$ sabre {bytecode file name}
Sabr 프로그램은 UTF-8로 쓰여져야 합니다.
Sabr는 타입 검사가 없습니다. 모든 스택 값들은 8 바이트입니다.
x
: 셀 (아무 값)n
: 정수s
: 부호 있는 정수u
: 부호 없는 정수f
: 부동소수점 실수b
: 불리언addr
: 주소id
: 식별자
0은 거짓이고, 0이 아닌 값은 참입니다.
(flag) if
(code)
end
if
는 스택으로부터 플래그 값을 뽑습니다.
만약 (flag)
가 참이면, (code)
가 실행됩니다.
(flag) if
(code 1)
else
(code 2)
end
만약 (flag)
가 참이면, (code 1)
이 실행됩니다.
만약 (flag)
가 거짓이면, (code 2)
가 실행됩니다.
(value) switch
(case 1) case (code 1) pass
(case 2) case (code 2) pass
(case 3) case
(case 4) case
(code 3)
pass
(code 4)
end
switch
와 case
는 스택으로부터 값을 뽑습니다.
만약 (value)
의 값이 (case 1)
와 같다면, (code 1)
이 실행됩니다.
만약 (value)
의 값이 (case 3)
또는 (case 4)
와 같다면, (code 3)
가 실행됩니다.
일치하는 값이 없다면, (code 4)
가 실행됩니다.
(non-zero) switch
(flag 1) case (code 1) pass
(flag 2) case (code 2) pass
(flag 3) case
(flag 4) case
(code 3)
pass
(code 4)
end
만약 (flag 1)
이 참이면, (code 1)
이 실행됩니다.
만약 (flag 3)
또는 (flag 4)
가 참이면, (code 3)
이 실행됩니다.
만약 모든 플래그가 거짓이라면, (code 4)
가 실행됩니다.
loop
(code 1)
end
무한 루프입니다.
loop
(code)
(flag)
while
end
우선 (code)
가 실행됩니다. while
은 스택으로부터 플래그 값을 뽑습니다.
만약 (flag)
가 참이면, 반복문이 재시작됩니다.
loop
(code 1)
(flag)
while
(code 2)
end
우선 (code 1)
이 실행됩니다. while
은 스택으로부터 플래그 값을 뽑습니다.
만약 (flag)
가 참이면, (code 2)
가 실행되고 반복문이 재시작됩니다.
$(keyward) for (start) from (end) to (step) step
(code)
end
for
, from
, to
, step
은 스택으로부터 각각 값을 뽑습니다.
(keyward)
는 카운터 변수의 이름이 됩니다.
(start)
는 시작하는 값입니다.
만약 (start) from
이 없다면, (start)
는 0으로 취급됩니다.
변수의 값이 (end)
를 넘으면 반복문이 끝납니다.
만약 (end) to
가 없다면, 무한 루프가 됩니다.
매번 반복할 때마다 변수의 값은 (step)
만큼 증가합니다.
만약 (step) step
이 없다면, (step)
은 1로 취급됩니다.
for
는 부호 있는 정수로만 작동합니다.
부호 없는 정수에 대해서 ufor
과 부동소수점 실수에 대해 ffor
를 사용할 수 있습니다.
loop
continue
end
continue
는 반복문의 남은 부분을 넘깁니다.
loop
break
end
break
는 반복문을 즉시 종료합니다.
$(keyword) func
(code)
end
$(keyword) macro
(code)
end
$(struct keyword) struct
$(member keyword 1) member
$(member keyword 2) member
$(member keyword 3) member
...
end
#(file) import
if
else
loop
while
break
continue
switch
case
pass
func
macro
return
defer
end
import
+
( n1 n2 -- n )
n = n1 + n2-
( n1 n2 -- n )
n = n1 - n2*
( n1 n2 -- n )
n = n1 × n20-
( n1 -- n )
n = -n11+
( n1 -- n )
n = n1 + 11-
( n1 -- n )
n = n1 - 1
/
( s1 s2 -- s )
s = s1 ÷ s2%
( s1 s2 -- s )
s = s1 mod s2u/
( u1 u2 -- u )
u = u1 ÷ u2u%
( u1 u2 -- u )
u = u1 mod u2
=
( x1 x2 -- b )!=
( x1 x2 -- b )
>
( s1 s2 -- b )>=
( s1 s2 -- b )<
( s1 s2 -- b )<=
( s1 s2 -- b )u>
( u1 u2 -- b )u>=
( u1 u2 -- b )u<
( u1 u2 -- b )u<=
( u1 u2 -- b )
f+
( f1 f2 -- f )
f = f1 + f2f-
( f1 f2 -- f )
f = f1 - f2f*
( f1 f2 -- f )
f = f1 × f2f/
( f1 f2 -- f )
f = f1 ÷ f2f%
( f1 f2 -- f )
f = f1 mod f2f0-
( f1 -- f )
f = -f1f=
( f1 f2 -- b )f!=
( f1 f2 -- b )f>
( f1 f2 -- b )f>=
( f1 f2 -- b )f<
( f1 f2 -- b )f<=
( f1 f2 -- b )
&
( x1 x2 -- x )|
( x1 x2 -- x )^
( x1 x2 -- x )~
( x1 -- x )<<
( x1 u -- x )>>
( x1 u -- x )
drop
( x -- )nip
( x1 x2 -- x2 )dup
( x - x x )over
( x1 x2 -- x1 x2 x1 )tuck
( x1 x2 -- x2 x1 x2 )swap
( x1 x2 -- x2 x1 )rot
( x1 x2 x3 -- x2 x3 x1 )2drop
( x1 x2 -- )2nip
( x1 x2 x3 x4 -- x3 x4 )2dup
( x1 x2 -- x1 x2 x1 x2 )2over
( x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2 )2tuck
( x1 x2 x3 x4 -- x3 x4 x1 x2 x3 x4 )2swap
( x1 x2 x3 x4 -- x3 x4 x1 x2 )2rot
( x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2 )
set
( x id -- )
10 $var set \ 변수 'var'를 초기값 10으로 선언합니다.
20 $var set \ 변수 'var'의 값을 20으로 바꿉니다.
call
( id -- ? )
10 $var set \ 변수 'var'를 초기값 10으로 선언합니다.
$var call \ 변수 'var'의 값을 얻습니다.
var \ 변수 'var'의 값을 얻습니다.
$cr macro 10 putc end \ 매크로 'cr'을 선언합니다.
$cr call \ 매크로 'cr'을 호출합니다.
cr \ 매크로 'cr'을 호출합니다.
address
( id -- addr )
id가 변수일 경우, id의 주소를 반환합니다.ref
( addr id -- )
주소 addr을 참조하는 변수 id를 선언합니다.
addr이 0일 경우 오류가 발생합니다.
alloc
( u -- addr )
u 바이트를 동적 할당하고, 할당된 메모리의 주소를 반환합니다.resize
( u addr -- addr )
메모리 블럭 addr의 크기를 u 바이트로 변경합니다. 그리고 재할당된 메모리의 포인터를 반환합니다.free
( addr -- )
메모리 블럭 addr을 해제합니다.
allot
( u -- addr )
u 바이트를 메모리 풀에서 예약하고, 예약된 메모리의 주소를 반환합니다.
예약된 메모리는 함수 호출이 끝나면 해제됩니다.
fetch
( addr -- x )
addr에 저장된 값을 인출합니다.store
( x addr -- )
x 값을 addr 메모리 공간에 저장합니다.
s>f
( s -- f )u>f
( u -- f )f>s
( f -- s )f>u
( f -- u )
geti
( -- s )getu
( -- u )getf
( -- f )getcs
( -- ... u1 u)
u는 문자열의 길이이고, u1과 그 뒤의 값들은 문자열의 유니코드 값입니다.putc
( u -- )puti
( s -- )putu
( u -- )putf
( f -- )show
( -- )
스택 값들을 표시합니다.
- 정수 :
255
,0255
,0xff
,0o377
,0b11111111
- 부동소수점 :
0.25
,.25
,00.250
,0.25e0
,2.5e-1
,0.025e1
- 유니코드 문자 :
'あ'
->[ 12354 ]
- 문자열 :
'Hello'
->[ 111 108 108 101 72 ]
- 문자열과 길이 :
"안녕하세요!"
->[ 33 50836 49464 54616 45397 50504 6 ]
\a
-> 7\b
-> 8\e
-> 27\f
-> 12\n
-> 10\r
-> 13\t
-> 9\v
-> 11\\
-> 92\'
-> 39\"
-> 34\nnn
-> nnn으로 주어진 8진수 값입니다.\xhh
-> hh로 주어진 16진수 값입니다.\uhhhh
-> 10000 아래의 16진수 유니코드 값입니다.\Uhhhhhhhh
-> 16진수 유니코드 값입니다.
컨트롤 키워드, 내장 연산자, 리터럴은 식별자가 될 수 없습니다.
- 식별자 정의하기 :
$main
,$a
,$Pos
,$x
함수, 매크로, 구조체, 변수를 정의할 때 사용합니다. 저 값들은 고유한 부호 없는 정수형 값입니다. - 식별자 호출하기 :
main
,a
,Pos
변수의 값을 얻거나 함수나 매크로를 호출합니다. 구조체의 경우 구조체의 크기를 얻습니다. - 구조체 멤버의 주소 가져오기 :
Pos.x
자세한 사용은 '구조체' 참조.
- 한 줄 주석 :
\이것은 주석입니다
,\ 이것은 주석입니다
,2 5 + \이것은 주석입니다
- 여러 줄 주석 / 스택 효과 주석 :
(이것은 주석입니다)
( 이것은
주석입니다 )
$add func ( n1 n2 -- x ) \ 이것은 스택 효과 주석입니다.
+
end
구조체는 다음과 같이 선언합니다.
$Pos struct
$x member
$y member
end
Pos는 구조체의 이름이고, x와 y의 두 개의 멤버 변수를 가지고 있습니다.
구조체를 사용하려면 구조체의 크기 만큼의 메모리 블럭을 할당합니다. 구조체의 이름은 구조체의 크기를 반환합니다.
구조체의 멤버는 Pos.x
꼴로 호출하는데, 앞에 해당 구조체 형식의 메모리 블럭 주소가 있어야 합니다. 그러면 멤버 변수의 주소를 반환합니다.
Pos allot $p1 set \ Pos 구조체를 할당한 뒤 p1 변수에 대입.
50 p1 Pos.x store \ p1의 멤버 변수 x에 50의 값 저장.
p1 Pos.x fetch puti \ p1의 멤버 변수 x의 값을 출력.