Cũng có sẵn tại GitHub .
Bạn cần có Dart 1.12 và Pub. Chỉ cần chạy pub get
để tải xuống phụ thuộc duy nhất, một thư viện phân tích cú pháp.
Đây là hy vọng điều này kéo dài hơn 30 phút! : O
Ngôn ngữ
Kẽm được định hướng xung quanh các nhà khai thác xác định lại. Bạn có thể xác định lại tất cả các toán tử trong ngôn ngữ một cách dễ dàng!
Cấu trúc của một chương trình Kẽm điển hình trông giống như:
let
<operator overrides>
in <expression>
Chỉ có hai loại dữ liệu: số nguyên và bộ. Không có thứ gọi là một tập hợp theo nghĩa đen và các tập hợp trống không được phép.
Biểu thức
Sau đây là các biểu thức hợp lệ trong Kẽm:
Văn học
Kẽm hỗ trợ tất cả các số nguyên bình thường, như 1
và -2
.
Biến
Kẽm có các biến (giống như hầu hết các ngôn ngữ). Để tham khảo chúng, chỉ cần sử dụng tên. Một lần nữa giống như hầu hết các ngôn ngữ!
Tuy nhiên, có một biến đặc biệt gọi là S
hành vi giống như của Pyth
Q
. Khi bạn lần đầu tiên sử dụng nó, nó sẽ đọc một dòng từ đầu vào tiêu chuẩn và diễn giải nó dưới dạng một tập hợp số. Chẳng hạn, dòng đầu vào 1234231
sẽ biến thành tập hợp {1, 2, 3, 4, 3, 2, 1}
.
LƯU Ý QUAN TRỌNG!!! Trong một số trường hợp, một chữ ở cuối ghi đè toán tử được phân tích cú pháp không chính xác, do đó bạn phải bao quanh nó bằng dấu ngoặc đơn.
Hoạt động nhị phân
Các hoạt động nhị phân sau đây được hỗ trợ:
- Ngoài ra thông qua
+
: 1+1
.
- Phép trừ thông qua
-
: 1-1
.
- Nhân thông qua
*
: 2*2
.
- Phân chia qua
/
: 4/2
.
- Bình đẳng với
=
: 3=3
.
Ngoài ra, hoạt động đơn nguyên sau đây cũng được hỗ trợ:
Ưu tiên luôn luôn đúng. Bạn có thể sử dụng dấu ngoặc đơn để ghi đè lên điều này.
Chỉ có sự bình đẳng và độ dài làm việc trên bộ. Khi bạn cố gắng lấy độ dài của một số nguyên, bạn sẽ nhận được số chữ số trong biểu diễn chuỗi của nó.
Đặt hiểu
Để thao túng các bộ, Zinc đã thiết lập sự hiểu biết. Họ trông như thế này:
{<variable>:<set><clause>}
Mệnh đề là mệnh đề khi hoặc mệnh đề sắp xếp.
Một mệnh đề khi trông như thế nào ^<expression>
. Biểu thức theo dấu mũ phải dẫn đến một số nguyên. Sử dụng mệnh đề khi sẽ chỉ lấy các phần tử trong tập hợp expression
khác không. Trong biểu thức, biến _
sẽ được đặt thành chỉ mục hiện tại trong tập hợp. Nó gần tương đương với Python này:
[<variable> for _, <variable> in enumerate(<set>) when <expression> != 0]
Một mệnh đề sắp xếp , trông giống như $<expression>
, sắp xếp tập hợp giảm dần theo giá trị của <expression>
. Nó tương đương với Python này:
sorted(<set>, key=lambda <variable>: <expression>)[::-1]
Dưới đây là một số ví dụ hiểu:
Ghi đè
Ghi đè toán tử cho phép bạn xác định lại toán tử. Họ trông như thế này:
<operator>=<operator>
hoặc là:
<variable><operator><variable>=<expression>
Trong trường hợp đầu tiên, bạn có thể định nghĩa một toán tử bằng với một toán tử khác. Chẳng hạn, tôi có thể định nghĩa +
để thực sự trừ qua:
+=-
Khi bạn làm điều này, bạn có thể xác định lại một toán tử để trở thành một toán tử ma thuật . Có hai toán tử ma thuật:
join
lấy một tập hợp và một số nguyên và tham gia nội dung của tập hợp. Ví dụ, tham gia {1, 2, 3}
với 4
sẽ dẫn đến số nguyên 14243
.
cut
cũng lấy một tập hợp và một số nguyên và sẽ phân vùng tập hợp ở mọi lần xuất hiện của số nguyên. Sử dụng cut
trên {1, 3, 9, 4, 3, 2}
và 3
sẽ tạo {{1}, {9, 4}, {2}}
... NHƯNG bất kỳ bộ phần tử đơn nào đều được làm phẳng, vì vậy kết quả sẽ thực sự là {1, {9, 4}, 2}
.
Đây là một ví dụ xác định lại +
toán tử có nghĩa là join
:
+=join
Đối với trường hợp sau, bạn có thể xác định lại một toán tử cho biểu thức đã cho. Ví dụ, điều này xác định thao tác cộng để thêm các giá trị và sau đó thêm 1:
x+y=1+:x+:y
Nhưng cái gì +:
? Bạn có thể nối dấu hai chấm :
cho một toán tử để luôn sử dụng phiên bản dựng sẵn. Ví dụ này sử dụng nội dung +
thông qua +:
để cộng các số lại với nhau, sau đó thêm 1 (hãy nhớ rằng, mọi thứ đều liên kết đúng).
Ghi đè toán tử độ dài trông giống như:
#x=<expression>
Lưu ý rằng hầu hết tất cả các hoạt động dựng sẵn (ngoại trừ đẳng thức) sẽ sử dụng toán tử độ dài này để xác định độ dài của tập hợp. Nếu bạn xác định nó là:
#x=1
mọi phần của Kẽm hoạt động trên các bộ ngoại trừ =
sẽ chỉ hoạt động trên phần tử đầu tiên của bộ được cung cấp.
Nhiều ghi đè
Bạn có thể ghi đè nhiều toán tử bằng cách tách chúng bằng dấu phẩy:
let
+=-,
*=/
in 1+2*3
In ấn
Bạn không thể in trực tiếp bất cứ thứ gì bằng Kẽm. Kết quả của biểu thức sau đây in
sẽ được in. Các giá trị của một tập hợp sẽ được nối với dấu phân cách. Ví dụ: lấy cái này:
let
...
in expr
Nếu expr
là tập hợp {1, 3, {2, 4}}
, 1324
sẽ được in ra màn hình sau khi chương trình kết thúc.
Để tất cả chúng cùng nhau
Đây là một chương trình Kẽm đơn giản xuất hiện để thêm 2+2
nhưng kết quả là 5:
let
x+y=1+:x+:y
in 1+2
Thông dịch viên
Điều này đi vào bin/zinc.dart
:
import 'package:parsers/parsers.dart';
import 'dart:io';
// An error.
class Error implements Exception {
String cause;
Error(this.cause);
String toString() => 'error in Zinc script: $cause';
}
// AST.
class Node {
Obj interpret(ZincInterpreter interp) => null;
}
// Identifier.
class Id extends Node {
final String id;
Id(this.id);
String toString() => 'Id($id)';
Obj interpret(ZincInterpreter interp) => interp.getv(id);
}
// Integer literal.
class IntLiteral extends Node {
final int value;
IntLiteral(this.value);
String toString() => 'IntLiteral($value)';
Obj interpret(ZincInterpreter interp) => new IntObj(value);
}
// Any kind of operator.
class Anyop extends Node {
void set(ZincInterpreter interp, OpFuncType func) {}
}
// Operator.
class Op extends Anyop {
final String op;
final bool orig;
Op(this.op, [this.orig = false]);
String toString() => 'Op($op, $orig)';
OpFuncType get(ZincInterpreter interp) =>
this.orig ? interp.op0[op] : interp.op1[op];
void set(ZincInterpreter interp, OpFuncType func) { interp.op1[op] = func; }
}
// Unary operator (len).
class Lenop extends Anyop {
final bool orig;
Lenop([this.orig = false]);
String toString() => 'Lenop($orig)';
OpFuncType get(ZincInterpreter interp) =>
this.orig ? interp.op0['#'] : interp.op1['#'];
void set(ZincInterpreter interp, OpFuncType func) { interp.op1['#'] = func; }
}
// Magic operator.
class Magicop extends Anyop {
final String op;
Magicop(this.op);
String toString() => 'Magicop($op)';
Obj interpret_with(ZincInterpreter interp, Obj x, Obj y) {
if (op == 'cut') {
if (y is! IntObj) { throw new Error('cannot cut int with non-int'); }
if (x is IntObj) {
return new SetObj(x.value.toString().split(y.value.toString()).map(
int.parse));
} else {
assert(x is SetObj);
List<List<Obj>> res = [[]];
for (Obj obj in x.vals(interp)) {
if (obj == y) { res.add([]); }
else { res.last.add(obj); }
}
return new SetObj(new List.from(res.map((l) =>
l.length == 1 ? l[0] : new SetObj(l))));
}
} else if (op == 'join') {
if (x is! SetObj) { throw new Error('can only join set'); }
if (y is! IntObj) { throw new Error('can only join set with int'); }
String res = '';
for (Obj obj in x.vals(interp)) {
if (obj is! IntObj) { throw new Error('joining set must contain ints'); }
res += obj.value.toString();
}
return new IntObj(int.parse(res));
}
}
}
// Unary operator (len) expression.
class Len extends Node {
final Lenop op;
final Node value;
Len(this.op, this.value);
String toString() => 'Len($op, $value)';
Obj interpret(ZincInterpreter interp) =>
op.get(interp)(interp, value.interpret(interp), null);
}
// Binary operator expression.
class Binop extends Node {
final Node lhs, rhs;
final Op op;
Binop(this.lhs, this.op, this.rhs);
String toString() => 'Binop($lhs, $op, $rhs)';
Obj interpret(ZincInterpreter interp) =>
op.get(interp)(interp, lhs.interpret(interp), rhs.interpret(interp));
}
// Clause.
enum ClauseKind { Where, Sort }
class Clause extends Node {
final ClauseKind kind;
final Node expr;
Clause(this.kind, this.expr);
String toString() => 'Clause($kind, $expr)';
Obj interpret_with(ZincInterpreter interp, SetObj set, Id id) {
List<Obj> res = [];
List<Obj> values = set.vals(interp);
switch (kind) {
case ClauseKind.Where:
for (int i=0; i<values.length; i++) {
Obj obj = values[i];
interp.push_scope();
interp.setv(id.id, obj);
interp.setv('_', new IntObj(i));
Obj x = expr.interpret(interp);
interp.pop_scope();
if (x is IntObj) {
if (x.value != 0) { res.add(obj); }
} else { throw new Error('where clause condition must be an integer'); }
}
break;
case ClauseKind.Sort:
res = values;
res.sort((x, y) {
interp.push_scope();
interp.setv(id.id, x);
Obj x_by = expr.interpret(interp);
interp.setv(id.id, y);
Obj y_by = expr.interpret(interp);
interp.pop_scope();
if (x_by is IntObj && y_by is IntObj) {
return x_by.value.compareTo(y_by.value);
} else { throw new Error('sort clause result must be an integer'); }
});
break;
}
return new SetObj(new List.from(res.reversed));
}
}
// Set comprehension.
class SetComp extends Node {
final Id id;
final Node set;
final Clause clause;
SetComp(this.id, this.set, this.clause);
String toString() => 'SetComp($id, $set, $clause)';
Obj interpret(ZincInterpreter interp) {
Obj setobj = set.interpret(interp);
if (setobj is SetObj) {
return clause.interpret_with(interp, setobj, id);
} else { throw new Error('set comprehension rhs must be set type'); }
}
}
// Operator rewrite.
class OpRewrite extends Node {
final Anyop op;
final Node value;
final Id lid, rid; // Can be null!
OpRewrite(this.op, this.value, [this.lid, this.rid]);
String toString() => 'OpRewrite($lid, $op, $rid, $value)';
Obj interpret(ZincInterpreter interp) {
if (lid != null) {
// Not bare.
op.set(interp, (interp,x,y) {
interp.push_scope();
interp.setv(lid.id, x);
if (rid == null) { assert(y == null); }
else { interp.setv(rid.id, y); }
Obj res = value.interpret(interp);
interp.pop_scope();
return res;
});
} else {
// Bare.
if (value is Magicop) {
op.set(interp, (interp,x,y) => value.interpret_with(interp, x, y));
} else {
op.set(interp, (interp,x,y) => (value as Anyop).get(interp)(x, y));
}
}
return null;
}
}
class Program extends Node {
final List<OpRewrite> rws;
final Node expr;
Program(this.rws, this.expr);
String toString() => 'Program($rws, $expr)';
Obj interpret(ZincInterpreter interp) {
rws.forEach((n) => n.interpret(interp));
return expr.interpret(interp);
}
}
// Runtime objects.
typedef Obj OpFuncType(ZincInterpreter interp, Obj x, Obj y);
class Obj {}
class IntObj extends Obj {
final int value;
IntObj(this.value);
String toString() => 'IntObj($value)';
bool operator==(Obj rhs) => rhs is IntObj && value == rhs.value;
String dump() => value.toString();
}
class SetObj extends Obj {
final List<Obj> values;
SetObj(this.values) {
if (values.length == 0) { throw new Error('set cannot be empty'); }
}
String toString() => 'SetObj($values)';
bool operator==(Obj rhs) => rhs is SetObj && values == rhs.values;
String dump() => values.map((x) => x.dump()).reduce((x,y) => x+y);
List<Obj> vals(ZincInterpreter interp) {
Obj lenobj = interp.op1['#'](interp, this, null);
int len;
if (lenobj is! IntObj) { throw new Error('# operator must return an int'); }
len = lenobj.value;
if (len < 0) { throw new Error('result of # operator must be positive'); }
return new List<Obj>.from(values.getRange(0, len));
}
}
// Parser.
class ZincParser extends LanguageParsers {
ZincParser(): super(reservedNames: ['let', 'in', 'join', 'cut']);
get start => prog().between(spaces, eof);
get comma => char(',') < spaces;
get lp => symbol('(');
get rp => symbol(')');
get lb => symbol('{');
get rb => symbol('}');
get colon => symbol(':');
get plus => symbol('+');
get minus => symbol('-');
get star => symbol('*');
get slash => symbol('/');
get eq => symbol('=');
get len => symbol('#');
get in_ => char(':');
get where => char('^');
get sort => char('\$');
prog() => reserved['let'] + oprw().sepBy(comma) + reserved['in'] + expr() ^
(_1,o,_2,x) => new Program(o,x);
oprw() => oprw1() | oprw2() | oprw3();
oprw1() => (basicop() | lenop()) + eq + (magicop() | op()) ^
(o,_,r) => new OpRewrite(o,r);
oprw2() => (id() + op() + id()).list + eq + expr() ^
(l,_,x) => new OpRewrite(l[1], x, l[0], l[2]);
oprw3() => lenop() + id() + eq + expr() ^ (o,a,_,x) => new OpRewrite(o, x, a);
magicop() => (reserved['join'] | reserved['cut']) ^ (s) => new Magicop(s);
basicop() => (plus | minus | star | slash | eq) ^ (op) => new Op(op);
op() => (basicop() + colon ^ (op,_) => new Op(op.op, true)) | basicop();
lenop() => (len + colon ^ (_1,_2) => new Lenop(true)) |
len ^ (_) => new Lenop();
expr() => setcomp() | unop() | binop() | prim();
setcomp() => lb + id() + in_ + rec(expr) + clause() + rb ^
(_1,i,_2,x,c,_3) => new SetComp(i,x,c);
clausekind() => (where ^ (_) => ClauseKind.Where) |
(sort ^ (_) => ClauseKind.Sort);
clause() => clausekind() + rec(expr) ^ (k,x) => new Clause(k,x);
unop() => lenop() + rec(expr) ^ (o,x) => new Len(o,x);
binop() => prim() + op() + rec(expr) ^ (l,o,r) => new Binop(l,o,r);
prim() => id() | intlit() | parens(rec(expr));
id() => identifier ^ (i) => new Id(i);
intlit() => intLiteral ^ (i) => new IntLiteral(i);
}
// Interpreter.
class ZincInterpreter {
Map<String, OpFuncType> op0, op1;
List<Map<String, Obj>> scopes;
ZincInterpreter() {
var beInt = (v) {
if (v is IntObj) { return v.value; }
else { throw new Error('argument to binary operator must be integer'); }
};
op0 = {
'+': (_,x,y) => new IntObj(beInt(x)+beInt(y)),
'-': (_,x,y) => new IntObj(beInt(x)-beInt(y)),
'*': (_,x,y) => new IntObj(beInt(x)*beInt(y)),
'/': (_,x,y) => new IntObj(beInt(x)/beInt(y)),
'=': (_,x,y) => new IntObj(x == y ? 1 : 0),
'#': (i,x,_2) =>
new IntObj(x is IntObj ? x.value.toString().length : x.values.length)
};
op1 = new Map<String, OpFuncType>.from(op0);
scopes = [{}];
}
void push_scope() { scopes.add({}); }
void pop_scope() { scopes.removeLast(); }
void setv(String name, Obj value) { scopes[scopes.length-1][name] = value; }
Obj getv(String name) {
for (var scope in scopes.reversed) {
if (scope[name] != null) { return scope[name]; }
}
if (name == 'S') {
var input = stdin.readLineSync() ?? '';
var list = new List.from(input.codeUnits.map((c) =>
new IntObj(int.parse(new String.fromCharCodes([c])))));
setv('S', new SetObj(list));
return getv('S');
} else throw new Error('undefined variable $name');
}
}
void main(List<String> args) {
if (args.length != 1) {
print('usage: ${Platform.script.toFilePath()} <file to run>');
return;
}
var file = new File(args[0]);
if (!file.existsSync()) {
print('cannot open ${args[0]}');
return;
}
Program root = new ZincParser().start.parse(file.readAsStringSync());
ZincInterpreter interp = new ZincInterpreter();
var res = root.interpret(interp);
print(res.dump());
}
Và điều này đi vào pubspec.yaml
:
name: zinc
dependencies:
parsers: any
Giải pháp dự định
let
#x=((x=S)*(-2))+#:x,
/=cut
in {y:{x:S/0$#:x}^_=2}