นอกจากนี้ยังมีที่GitHub
คุณต้องมีโผ 1.12 และผับ เพียงรันpub get
เพื่อดาวน์โหลดการพึ่งพาเท่านั้นซึ่งเป็นไลบรารีการแยกวิเคราะห์
นี่คือการหวังว่าจะใช้เวลานานกว่า 30 นาที! โอ้
ภาษา
สังกะสีมุ่งเน้นไปที่การนิยามตัวดำเนินการใหม่ คุณสามารถกำหนดตัวดำเนินการทั้งหมดในภาษาใหม่ได้อย่างง่ายดาย!
โครงสร้างของโปรแกรมสังกะสีทั่วไปดูเหมือนว่า:
let
<operator overrides>
in <expression>
มีเพียงสองชนิดข้อมูล: จำนวนเต็มและชุด ไม่มีสิ่งใดที่เหมือนตัวอักษรเซตและเซตว่างจะไม่ได้รับอนุญาต
การแสดงออก
ต่อไปนี้เป็นนิพจน์ที่ถูกต้องใน Zinc:
ตัวอักษร
สังกะสีสนับสนุนทุกตัวอักษรของจำนวนเต็มปกติเหมือนและ1
-2
ตัวแปร
สังกะสีมีตัวแปร (เช่นภาษาส่วนใหญ่) หากต้องการอ้างอิงพวกเขาเพียงใช้ชื่อ ชอบภาษาส่วนใหญ่อีกครั้ง!
อย่างไรก็ตามมีตัวแปรพิเศษที่เรียกS
ว่าทำงานเหมือนของ
Q
พีท เมื่อคุณใช้มันครั้งแรกมันจะอ่านในบรรทัดจากอินพุตมาตรฐานและตีความมันเป็นชุดของตัวเลข ยกตัวอย่างเช่นสายการป้อนข้อมูลจะกลายเป็นชุด1234231
{1, 2, 3, 4, 3, 2, 1}
โน๊ตสำคัญ!!! ในบางกรณีตัวอักษรที่ส่วนท้ายของการแทนที่โอเปอเรเตอร์จะถูกวิเคราะห์อย่างไม่ถูกต้องดังนั้นคุณต้องล้อมรอบด้วยวงเล็บ
การดำเนินงานไบนารี
สนับสนุนการดำเนินงานไบนารีต่อไปนี้:
- นอกจากผ่าน:
+
1+1
- ลบผ่าน:
-
1-1
- การคูณทาง
*
: 2*2
.
- หารผ่าน
/
: 4/2
.
- ความเท่าเทียมกับ
=
: 3=3
.
นอกจากนี้ยังสนับสนุนการดำเนินการ unary ต่อไปนี้ด้วย:
ลำดับความสำคัญมีความสัมพันธ์ที่เหมาะสมเสมอ คุณสามารถใช้วงเล็บเพื่อแทนที่สิ่งนี้
ความเท่าเทียมกันและความยาวทำงานในชุดเท่านั้น เมื่อคุณพยายามรับความยาวของจำนวนเต็มคุณจะได้จำนวนของตัวเลขในการแทนค่าสตริง
กำหนดความเข้าใจ
เพื่อจัดการกับชุดสังกะสีได้กำหนดความเข้าใจ พวกเขามีลักษณะเช่นนี้:
{<variable>:<set><clause>}
ข้อเป็นทั้งประโยคเมื่อหรือข้อเรียง
ประโยคเมื่อ^<expression>
ดูเหมือนว่า การแสดงออกหลังเครื่องหมายรูปหมวกจะต้องส่งผลให้จำนวนเต็ม การใช้คำสั่ง when จะรับเฉพาะองค์ประกอบในชุดซึ่งexpression
ไม่ใช่ศูนย์ ภายในนิพจน์ตัวแปร_
จะถูกตั้งค่าเป็นดัชนีปัจจุบันในชุด มันเทียบเท่ากับ Python นี้คร่าวๆ:
[<variable> for _, <variable> in enumerate(<set>) when <expression> != 0]
ข้อเรียงลำดับซึ่งมีลักษณะเหมือน$<expression>
, <expression>
เรียงลำดับจากมากไปน้อยชุดด้วยค่าของ มันเท่ากับ Python นี้:
sorted(<set>, key=lambda <variable>: <expression>)[::-1]
นี่คือตัวอย่างความเข้าใจ:
การแทนที่
การแทนที่โอเปอเรเตอร์ช่วยให้คุณกำหนดผู้ประกอบการใหม่ พวกเขามีลักษณะเช่นนี้:
<operator>=<operator>
หรือ:
<variable><operator><variable>=<expression>
ในกรณีแรกคุณสามารถกำหนดตัวดำเนินการให้เท่ากับตัวดำเนินการอื่น ตัวอย่างเช่นฉันสามารถกำหนด+
ให้ลบจริงผ่าน:
+=-
เมื่อคุณทำสิ่งนี้คุณสามารถกำหนดโอเปอเรเตอร์ให้เป็นโอเปอเรเตอร์เวทมนตร์อีกครั้ง มีตัวดำเนินการวิเศษสองอย่าง:
join
ใช้เวลาชุดและจำนวนเต็มและรวมเนื้อหาของชุด ยกตัวอย่างเช่นการเข้าร่วม{1, 2, 3}
ด้วยจะมีผลในจำนวนเต็ม4
14243
cut
ยังใช้ชุดและจำนวนเต็มและจะแบ่งชุดที่เกิดขึ้นทุกจำนวน ใช้cut
บน{1, 3, 9, 4, 3, 2}
และ3
จะสร้าง{{1}, {9, 4}, {2}}
... แต่ใด ๆ {1, {9, 4}, 2}
ชุดเดียวองค์ประกอบแบนดังนั้นผลที่ตามมาจะเป็นจริง
นี่คือตัวอย่างที่ให้คำจำกัดความใหม่แก่+
โอเปอเรเตอร์join
:
+=join
สำหรับกรณีหลังคุณสามารถกำหนดโอเปอเรเตอร์ให้เป็นนิพจน์ที่กำหนดใหม่ ตัวอย่างนี้กำหนดการดำเนินการบวกเพื่อเพิ่มค่าแล้วเพิ่ม 1:
x+y=1+:x+:y
แต่สิ่งที่+:
? คุณสามารถต่อท้ายเครื่องหมายจุดคู่:
กับตัวดำเนินการเพื่อใช้เวอร์ชันในตัว ตัวอย่างนี้ใช้ builtin +
ผ่าน+:
เพื่อเพิ่มตัวเลขเข้าด้วยกันจากนั้นเพิ่ม 1 (จำไว้ว่าทุกอย่างเชื่อมโยงกันได้)
การเอาชนะโอเปอเรเตอร์ความยาวจะมีลักษณะดังนี้:
#x=<expression>
โปรดทราบว่าการดำเนินการในตัวเกือบทั้งหมด (ยกเว้นความเท่าเทียมกัน) จะใช้ตัวดำเนินการความยาวนี้เพื่อกำหนดความยาวของชุด หากคุณกำหนดให้เป็น:
#x=1
ทุกส่วนของสังกะสีที่ทำงานในชุดยกเว้น=
จะทำงานเฉพาะในองค์ประกอบแรกของชุดที่ได้รับ
การแทนที่หลายรายการ
คุณสามารถลบล้างโอเปอเรเตอร์หลายรายการโดยคั่นด้วยเครื่องหมายจุลภาค:
let
+=-,
*=/
in 1+2*3
การพิมพ์
คุณไม่สามารถพิมพ์สิ่งใดในสังกะสีโดยตรง ผลลัพธ์ของนิพจน์ต่อไปนี้in
จะถูกพิมพ์ ค่าของชุดจะถูกต่อกับตัวคั่น ตัวอย่างเช่นใช้สิ่งนี้:
let
...
in expr
ถ้าexpr
เป็นชุด{1, 3, {2, 4}}
,1324
จะถูกพิมพ์ไปที่หน้าจอทันทีที่โปรแกรมเสร็จสิ้น
วางมันทั้งหมดเข้าด้วยกัน
นี่คือโปรแกรมสังกะสีอย่างง่ายที่ดูเหมือนจะเพิ่ม2+2
แต่ทำให้ผลลัพธ์เป็น 5:
let
x+y=1+:x+:y
in 1+2
ล่าม
สิ่งนี้ไปใน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());
}
และนี่ไปในpubspec.yaml
:
name: zinc
dependencies:
parsers: any
แนวทางแก้ไขปัญหา
let
#x=((x=S)*(-2))+#:x,
/=cut
in {y:{x:S/0$#:x}^_=2}