lang: 배타 대여를 호출에 넘기는 것은 이동이 아니라 재대여다
&mut T 를 &mut T 파라미터에 넘기면 호출이 끝날 때 돌려받는다. 호출이 도는 동안 호출자는 그 값에 손댈 수 없으므로 별칭이 생기지 않는다. 이것이 없으면 배타 파라미터를 다시 넘기는 일이 함수당 한 번만 가능해서 &mut 가 사실상 쓸 수 없었다 -- 재귀 하강 파서를 쓰다가 걸렸다. 페이로드 없는 enum 은 이름 붙은 수라서 수로 읽을 수 있다. 반대 방향은 안 된다: 임의의 수는 변이가 아니다. calc 프로그램: 재귀 하강 수식 계산기. 우선순위, 괄호, 오류 전파. 1+2*3 = 7 (1+2)*3 = 9 2*(3+4)-5 = 9 10/3 = 3 1+ = error (1+2 = error
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
// EXIT:0
|
||||
// OUTPUT:1+2*3 = 7
|
||||
// OUTPUT:(1+2)*3 = 9
|
||||
// OUTPUT:2*(3+4)-5 = 9
|
||||
// OUTPUT:10/3 = 3
|
||||
// OUTPUT:1+ = error
|
||||
// OUTPUT:(1+2 = error
|
||||
unit calc;
|
||||
import std.io;
|
||||
import std.fmt;
|
||||
|
||||
// A recursive-descent evaluator over a byte slice.
|
||||
//
|
||||
// The position travels in a `&mut usize` rather than in a struct beside the
|
||||
// text: a struct cannot hold a slice, because a slice is a borrowed view and
|
||||
// R4 keeps borrows out of aggregate storage. Passing both is the honest way
|
||||
// to say "this text, and how far we have read".
|
||||
|
||||
fn done(src: []u8, at: usize) -> bool { return at >= src.n; }
|
||||
|
||||
fn peek(src: []u8, at: usize) -> u8 {
|
||||
if done(src, at) { return 0; }
|
||||
return src[at];
|
||||
}
|
||||
|
||||
fn skip_spaces(src: []u8, at: &mut usize) -> void {
|
||||
while not done(src, at.^) {
|
||||
if src[at.^] != 32 { break; }
|
||||
at.^ = at.^ + 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn number(src: []u8, at: &mut usize) -> !i32 {
|
||||
var value: i32 = 0;
|
||||
var digits: usize = 0;
|
||||
while not done(src, at.^) {
|
||||
let c: u8 = src[at.^];
|
||||
if c < 48 or c > 57 { break; }
|
||||
value = value * 10 + ((c - 48) as i32);
|
||||
digits = digits + 1;
|
||||
at.^ = at.^ + 1;
|
||||
}
|
||||
if digits == 0 { return error.BadNumber; }
|
||||
return value;
|
||||
}
|
||||
|
||||
fn factor(src: []u8, at: &mut usize) -> !i32 {
|
||||
skip_spaces(src, at);
|
||||
if peek(src, at.^) == 40 {
|
||||
at.^ = at.^ + 1;
|
||||
let inner: i32 = try expr(src, at);
|
||||
skip_spaces(src, at);
|
||||
if peek(src, at.^) != 41 { return error.Unbalanced; }
|
||||
at.^ = at.^ + 1;
|
||||
return inner;
|
||||
}
|
||||
return number(src, at);
|
||||
}
|
||||
|
||||
fn term(src: []u8, at: &mut usize) -> !i32 {
|
||||
var left: i32 = try factor(src, at);
|
||||
while true {
|
||||
skip_spaces(src, at);
|
||||
let op: u8 = peek(src, at.^);
|
||||
if op != 42 and op != 47 { break; }
|
||||
at.^ = at.^ + 1;
|
||||
let right: i32 = try factor(src, at);
|
||||
if op == 42 { left = left * right; }
|
||||
else {
|
||||
if right == 0 { return error.DivideByZero; }
|
||||
left = left / right;
|
||||
}
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
fn expr(src: []u8, at: &mut usize) -> !i32 {
|
||||
var left: i32 = try term(src, at);
|
||||
while true {
|
||||
skip_spaces(src, at);
|
||||
let op: u8 = peek(src, at.^);
|
||||
if op != 43 and op != 45 { break; }
|
||||
at.^ = at.^ + 1;
|
||||
let right: i32 = try term(src, at);
|
||||
if op == 43 { left = left + right; }
|
||||
else { left = left - right; }
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
fn evaluate(text: []u8) -> !i32 {
|
||||
var at: usize = 0;
|
||||
let value: i32 = try expr(text, &mut at);
|
||||
skip_spaces(text, &mut at);
|
||||
if not done(text, at) { return error.Trailing; }
|
||||
return value;
|
||||
}
|
||||
|
||||
fn show(text: []u8) -> void {
|
||||
var buf: [16]u8 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
|
||||
io.print(text);
|
||||
io.print(" = ");
|
||||
let value: i32 = evaluate(text) catch |e| {
|
||||
io.print("error\n");
|
||||
return;
|
||||
};
|
||||
let n: usize = fmt.fmt_i32(buf[..], value);
|
||||
io.print(buf[0..n]);
|
||||
io.print("\n");
|
||||
}
|
||||
|
||||
fn main() -> i32 {
|
||||
show("1+2*3");
|
||||
show("(1+2)*3");
|
||||
show("2*(3+4)-5");
|
||||
show("10/3");
|
||||
show("1+");
|
||||
show("(1+2");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user