Складні вирази кочення з кістки


23

Фон

Я регулярно граю в науково-дослідні роботи з деякими друзями. Говорячи про складність деяких систем / версій, коли справа доходить до кочення кісток і застосування бонусів та штрафних санкцій, ми жартома придумали додаткову складність для виразів прокатки кісток. Деякі з них були занадто обурливими (як розширення простих виразів з кістки, як 2d6аргументи матриці 1 ), а решта створюють цікаву систему.

Змагання

Давши складний вираз кубиків, оцініть його за наступними правилами та виведіть результат.

Основні правила оцінювання

  • Щоразу, коли оператор очікує ціле число, але отримує список для операнду, використовується сума цього списку
  • Кожного разу, коли оператор очікує список, але отримує ціле число для операнда, ціле число розглядається як список одного елемента, що містить це ціле число

Оператори

Усі оператори є операторами бінарної інфіксації. З метою пояснення aбуде лівий операнд, а bправий операнд. Позначення списків використовуватимуться для прикладів, коли оператори можуть приймати списки як операнди, але фактичні вирази складаються лише з натуральних чисел та операторів.

  • d: вивести aнезалежні рівномірні випадкові цілі числа в діапазоні[1, b]
    • Перевага: 3
    • Обидва операнди є цілими числами
    • Приклади: 3d4 => [1, 4, 3],[1, 2]d6 => [3, 2, 6]
  • t: прийняти bнайнижчі значення відa
    • Пріоритетність: 2
    • aє списком, bє цілим числом
    • Якщо b > len(a)всі значення повертаються
    • Приклади: [1, 5, 7]t1 => [1], [5, 18, 3, 9]t2 => [3, 5],3t5 => [3]
  • T: приймайте bнайвищі значення уa
    • Пріоритетність: 2
    • aє списком, bє цілим числом
    • Якщо b > len(a)всі значення повертаються
    • Приклади: [1, 5, 7]T1 => [7], [5, 18, 3, 9]T2 => [18, 9],3T5 => [3]
  • r: Якщо якісь - або елементи bв a, перекинути ці елементи, використовуючи будь-якийd заяву генеруватися їх
    • Пріоритетність: 2
    • Обидва операнди - це списки
    • Перепродаж проводиться лише один раз, тому bв результаті все одно можуть бути елементи
    • Приклади: 3d6r1 => [1, 3, 4] => [6, 3, 4], 2d4r2 => [2, 2] => [3, 2],3d8r[1,8] => [1, 8, 4] => [2, 2, 4]
  • R: Якщо якісь - або елементи bв a, перекинути ці елементи , поки ніяких елементів bнемає, використовуючи будь-якийd заяву генеруватися їх
    • Пріоритетність: 2
    • Обидва операнди - це списки
    • Приклади: 3d6R1 => [1, 3, 4] => [6, 3, 4], 2d4R2 => [2, 2] => [3, 2] => [3, 1],3d8R[1,8] => [1, 8, 4] => [2, 2, 4]
  • +: додати aіb разом
    • Пріоритетність: 1
    • Обидва операнди є цілими числами
    • Приклади: 2+2 => 4, [2]+[2] => 4,[3, 1]+2 => 6
  • -: відняти bвідa
    • Пріоритетність: 1
    • Обидва операнди є цілими числами
    • b завжди буде менше, ніж a
    • Приклади: 2-1 => 1, 5-[2] => 3,[8, 3]-1 => 10
  • .: з'єднати aіb разом
    • Пріоритетність: 1
    • Обидва операнди - це списки
    • Приклади: 2.2 => [2, 2], [1].[2] => [1, 2],3.[4] => [3, 4]
  • _: вихід aз усіма елементамиb видаленими
    • Пріоритетність: 1
    • Обидва операнди - це списки
    • Приклади: [3, 4]_[3] => [4], [2, 3, 3]_3 => [2],1_2 => [1]

Додаткові правила

  • Якщо кінцеве значення виразу є списком, воно підсумовується перед виведенням
  • Оцінка термінів призведе лише до позитивних цілих чисел або списків натуральних чисел - будь-яке вираження, що призводить до непозитивного цілого чи списку, що містить принаймні одне невід’ємне ціле число, замінить на значення 1s
  • Дужки можна використовувати для групування термінів та вказівки порядку оцінки
  • Оператори оцінюються в порядку найвищого до найнижчого пріоритету, при цьому оцінка проводиться зліва направо у випадку прив'язки пріоритету (так 1d4d4би оцінювалося як (1d4)d4)
  • Порядок елементів у списках не має значення - цілком прийнятно для оператора, який модифікує список, повертати його зі своїми елементами в іншому відносному порядку
  • Терміни, які неможливо оцінити або призведе до нескінченного циклу (наприклад, 1d1R1або 3d6R[1, 2, 3, 4, 5, 6]), не є дійсними

Випробування

Формат: input => possible output

1d20 => 13
2d6 => 8
4d6T3 => 11
2d20t1 => 13
5d8r1 => 34
5d6R1 => 20
2d6d6 => 23
3d2R1d2 => 3
(3d2R1)d2 => 11
1d8+3 => 10
1d8-3 => 4
1d6-1d2 => 2
2d6.2d6 => 12
3d6_1 => 8
1d((8d20t4T2)d(6d6R1r6)-2d4+1d2).(1d(4d6_3d3)) => 61

Усі, крім останнього тестового випадку, були створені при здійсненні посилання.

Приклад роботи

Вираз: 1d((8d20t4T2)d(6d6R1r6)-2d4+1d2).(1d(4d6_3d3))

  1. 8d20t4T2 => [19, 5, 11, 6, 19, 15, 4, 20]t4T2 => [4, 5, 6, 11]T2 => [11, 6](повна 1d(([11, 6])d(6d6R1r6)-2d4+1d2).(1d(4d6_3d3)):)
  2. 6d6R1r6 => [2, 5, 1, 5, 2, 3]r1R6 => [2, 5, 3, 5, 2, 3]R6 => [2, 5, 3, 5, 2, 3]( 1d([11, 6]d[2, 5, 3, 5, 2, 3]-2d4+1d2).(1d(4d6_3d3)))
  3. [11, 6]d[2, 5, 3, 5, 2, 3] => 17d20 => [1, 6, 11, 7, 2, 8, 15, 3, 4, 18, 11, 11, 1, 10, 8, 6, 11]( 1d([1, 6, 11, 7, 2, 8, 15, 3, 4, 18, 11, 11, 1, 10, 8, 6, 11]-2d4+1d2).(1d(4d6_3d3)))
  4. 2d4 => 7( 1d([1, 6, 11, 7, 2, 8, 15, 3, 4, 18, 11, 11, 1, 10, 8, 6, 11]-7+1d2).(1d(4d6_3d3)))
  5. 1d2 => 2( 1d([1, 6, 11, 7, 2, 8, 15, 3, 4, 18, 11, 11, 1, 10, 8, 6, 11]-7+2).(1d(4d6_3d3)))
  6. [1, 6, 11, 7, 2, 8, 15, 3, 4, 18, 11, 11, 1, 10, 8, 6, 11]-7+2 => 133-7+2 => 128( 1d128).(1d(4d6_3d3)))
  7. 4d6_3d3 => [1, 3, 3, 6]_[3, 2, 2] => [1, 3, 3, 6, 3, 2, 2]( 1d128).(1d[1, 3, 3, 6, 3, 2, 2]))
  8. 1d[1, 3, 3, 6, 3, 2, 2] => 1d20 => 6( 1d128).(6))
  9. 1d128 => 55( 55.6)
  10. 55.6 => [55, 6]( [55, 6])
  11. [55, 6] => 61 (зроблено)

Довідкова реалізація

Ця контрольна реалізація використовує те саме постійне насіння ( 0) для оцінки кожного виразу для перевірених, послідовних результатів. Він очікує введення на STDIN, причому нові виразки розділяють кожен вираз.

#!/usr/bin/env python3

import re
from random import randint, seed
from collections import Iterable
from functools import total_ordering

def as_list(x):
    if isinstance(x, Iterable):
        return list(x)
    else:
        return [x]

def roll(num_sides):
    return Die(randint(1, num_sides), num_sides)

def roll_many(num_dice, num_sides):
    num_dice = sum(as_list(num_dice))
    num_sides = sum(as_list(num_sides))
    return [roll(num_sides) for _ in range(num_dice)]

def reroll(dice, values):
    dice, values = as_list(dice), as_list(values)
    return [die.reroll() if die in values else die for die in dice]

def reroll_all(dice, values):
    dice, values = as_list(dice), as_list(values)
    while any(die in values for die in dice):
        dice = [die.reroll() if die in values else die for die in dice]
    return dice

def take_low(dice, num_values):
    dice = as_list(dice)
    num_values = sum(as_list(num_values))
    return sorted(dice)[:num_values]

def take_high(dice, num_values):
    dice = as_list(dice)
    num_values = sum(as_list(num_values))
    return sorted(dice, reverse=True)[:num_values]

def add(a, b):
    a = sum(as_list(a))
    b = sum(as_list(b))
    return a+b

def sub(a, b):
    a = sum(as_list(a))
    b = sum(as_list(b))
    return max(a-b, 1)

def concat(a, b):
    return as_list(a)+as_list(b)

def list_diff(a, b):
    return [x for x in as_list(a) if x not in as_list(b)]

@total_ordering
class Die:
    def __init__(self, value, sides):
        self.value = value
        self.sides = sides
    def reroll(self):
        self.value = roll(self.sides).value
        return self
    def __int__(self):
        return self.value
    __index__ = __int__
    def __lt__(self, other):
        return int(self) < int(other)
    def __eq__(self, other):
        return int(self) == int(other)
    def __add__(self, other):
        return int(self) + int(other)
    def __sub__(self, other):
        return int(self) - int(other)
    __radd__ = __add__
    __rsub__ = __sub__
    def __str__(self):
        return str(int(self))
    def __repr__(self):
        return "{} ({})".format(self.value, self.sides)

class Operator:
    def __init__(self, str, precedence, func):
        self.str = str
        self.precedence = precedence
        self.func = func
    def __call__(self, *args):
        return self.func(*args)
    def __str__(self):
        return self.str
    __repr__ = __str__

ops = {
    'd': Operator('d', 3, roll_many),
    'r': Operator('r', 2, reroll),
    'R': Operator('R', 2, reroll_all),
    't': Operator('t', 2, take_low),
    'T': Operator('T', 2, take_high),
    '+': Operator('+', 1, add),
    '-': Operator('-', 1, sub),
    '.': Operator('.', 1, concat),
    '_': Operator('_', 1, list_diff),
}

def evaluate_dice(expr):
    return max(sum(as_list(evaluate_rpn(shunting_yard(tokenize(expr))))), 1)

def evaluate_rpn(expr):
    stack = []
    while expr:
        tok = expr.pop()
        if isinstance(tok, Operator):
            a, b = stack.pop(), stack.pop()
            stack.append(tok(b, a))
        else:
            stack.append(tok)
    return stack[0]

def shunting_yard(tokens):
    outqueue = []
    opstack = []
    for tok in tokens:
        if isinstance(tok, int):
            outqueue = [tok] + outqueue
        elif tok == '(':
            opstack.append(tok)
        elif tok == ')':
            while opstack[-1] != '(':
                outqueue = [opstack.pop()] + outqueue
            opstack.pop()
        else:
            while opstack and opstack[-1] != '(' and opstack[-1].precedence > tok.precedence:
                outqueue = [opstack.pop()] + outqueue
            opstack.append(tok)
    while opstack:
        outqueue = [opstack.pop()] + outqueue
    return outqueue

def tokenize(expr):
    while expr:
        tok, expr = expr[0], expr[1:]
        if tok in "0123456789":
            while expr and expr[0] in "0123456789":
                tok, expr = tok + expr[0], expr[1:]
            tok = int(tok)
        else:
            tok = ops[tok] if tok in ops else tok
        yield tok

if __name__ == '__main__':
    import sys
    while True:
        try:
            dice_str = input()
            seed(0)
            print("{} => {}".format(dice_str, evaluate_dice(dice_str)))
        except EOFError:
            exit()

[1]: Нашим визначенням adbдля аргументів матриці було прокручування AdXкожного Xв a * b, де A = det(a * b). Очевидно, що це занадто абсурдно для цього виклику.



Якщо гарантія на -це bзавжди буде меншою, ніж aя не бачу способу отримати непозитивні цілі числа, тому друге додаткове правило здається безглуздим. OTOH _може призвести до порожнього списку, який здається корисним у тих же випадках, але що це означає, коли потрібне ціле число? Зазвичай я б сказав, що сума 0...
Крістіан Сіверс

@ChristianSievers 1) Я додав додаткову примітку про непозитивні цілі числа для ясності. 2) Сума порожнього списку дорівнює 0. За правилом непозитивності, це оцінюється як a 1.
Мего

Гаразд, але чи добре це як проміжний результат? Так і [1,2]_([1]_[1])є [1,2]?
Крістіан Сіверс

@ChristianSievers. Ні. Це призведе до того [2], що [1]_[1] -> [] -> 0 -> 1 -> [1].
Мего

Відповіді:


9

Python 3, 803 788 753 749 744 748 745 740 700 695 682 байт

exec(r'''from random import*
import re
class k(int):
 p=0;j=Xl([d(randint(1,int(b)),b)Zrange(a)]);__mul__=Xl(sorted(Y)[:b]);__matmul__=Xl(sorted(Y)[-b:]);__truediv__=Xl([d(randint(1,int(_.i)),_.i)if _==b else _ ZY]);__sub__=Xk(max(1,int.__sub__(a,b)))
 def __mod__(a,b):
  x=[]
  while x!=Y:x=Y;a=a/b
  Wl(x)
 def V:
  if b.p:p=b.p;del b.p;Wl(Y+b.l)if~-p else l([_ZY if~-(_ in b.l)])
  Wk(int.V)
 def __neg__(a):a.p+=1;Wa
def l(x):a=k(sum(x)or 1);Y=x;Wa
def d(v,i):d=k(v);d.i=i;Wd
lambda a:eval(re.sub("(\d+)","(k(\\1))",a).translate({100:".j",116:"*",84:"@",114:"/",82:"%",46:"+--",95:"+-"}))'''.translate({90:" for _ in ",89:"a.l",88:"lambda a,b:",87:"return ",86:"__add__(a,b)"}))

-5 байт завдяки Mr.Xcoder

Ще 5 байт завдяки NGN

- приблизно 40 байт завдяки Джонатану Французу

Юк, яка хитрість! Це працює, використовуючи регулярний вираз, щоб обернути всі числа в моєму kкласі, і перетворити всі оператори в оператори python розуміє, потім використовуючи магічні методи kкласу для обробки математики. The +-і +--в кінці для .і _є хак, щоб зберегти пріоритет правильним. Так само я не можу використовувати **оператор для d, оскільки це зробить 1d4d4синтаксичний аналіз 1d(4d4). Натомість я загортаю всі числа в додатковий набір паролів і роблю d так .j, оскільки виклики методів мають більшу перевагу перед операторами. Останній рядок оцінюється як анонімна функція, яка оцінює вираз.


def __mod__(a, b)... Чому простір між a,і b?
Містер Xcoder


@ Mr.Xcoder Я думаю , що ви можете зберегти байти, видаляючи непотрібне простір: ; __sub__. І, можливо, також тут : lambda a,b: l(.
Джонатан Фрех

1
Ви можете зберегти кілька байт, загорнувши весь код у exec("""...""".replace("...","..."))виписку та замінивши рядки, які часто (наприклад return ) зустрічаються, одним символом. Однак мені exec-стратегія завжди здається трохи не елегантною ...
Джонатан

органи __mod__та __add__не потребують такого великого відступу
пр.

3

APL (Dyalog Classic) , 367 байт

d←{(⊢⍪⍨1+?)⍉⍪⊃⍴/⊃¨+/¨⍺⍵}⋄r←{z←⊣⌿⍺⋄((m×?n)+z×~m←z∊⊣⌿⍵)⍪n←⊢⌿⍺}⋄R←{⍬≡⊃∩/⊣⌿¨⍺⍵:⍺⋄⍵∇⍨⍺r⍵}
u←{⍺[;((⊃+/⍵)⌊≢⍉⍺)↑⍺⍺⊣⌿⍺]}⋄t←⍋u⋄T←⍒u⋄A←+/,⋄S←+/,∘-⋄C←,⋄D←{⍺/⍨~⊃∊/⊣⌿¨⍺⍵}
hv←⍬⋄o'drRtT+-._'f←{8<io⍳⊃⍵:0v⊢←(¯2v),(⍎i'drRtTASCD')/¯2v}
{⊃⍵∊⎕d:v,←⊂⍪2↑⍎⍵⋄'('=⍵:h,←⍵⋄')'=⍵:h↑⍨←i-1f¨⌽h↓⍨i←+/∨\⌽h='('⋄h,←⍵⊣h↓⍨←-i⊣f¨⌽h↑⍨-i←+/\⌽≤/(1 4 4 1/⌽⍳4)[o⍳↑⍵,¨h]}¨'\d+' '.'s'&'⊢⍞
f¨⌽h1⌈+/⊣⌿⊃v

Спробуйте в Інтернеті!

Це алгоритм маневрового двору з еталонної реалізації, об'єднаної evaluate_dice()без суворої та об'єктно-орієнтованої дурниці. Використовуються лише два стеки: hдля операторів і vдля значень. Розбір і оцінка переплетені.

Проміжні результати представлені у вигляді матриць 2 × N, де перший рядок є випадковими значеннями, а другий ряд - кількістю сторін на кістяках, які їх створили. Коли результат не створюється оператором "d", що кидає кістки, другий рядок містить довільні числа. Одиничне випадкове значення є матрицею 2 × 1 і, таким чином, не відрізняється від списку 1 елементів.


3

Пітон 3: 723 722 714 711 707 675 653 665 байт

import re
from random import*
S=re.subn
e=eval
r=randint
s=lambda a:sum(g(e(a)))or 1
g=lambda a:next(zip(*a))
def o(m):a,o,b=m.groups();A=sorted(e(a));t=g(e(b));return str(o in"rR"and([([v,(r(1,d)*(o>"R")or choice([n+1for n in range(d)if~-(n+1in t)]))][v in t],d)for(v,d)in A])or{"t":A[:s(b)],"T":A[-s(b):],"+":[(s(a)+s(b),0)],"-":[(s(a)-s(b),0)],".":e(a)+e(b),"_":[t for t in e(a)if~-(t[0]in g(e(b)))]}[o])
def d(m):a,b=map(s,m.groups());return str([(r(1,b),b)for _ in" "*a])
p=r"(\[[^]]+\])"
def E(D):
 D,n=S(r"(\d+)",r"[(\1,0)]",D)
 while n:
  n=0
  for e in[("\(("+p+")\)",r"\1"),(p+"d"+p,d),(p+"([tTrR])"+p,o),(p+"(.)"+p,o)]:
   if n<1:D,n=S(*e,D)
 return s(D)

Точка входу - E. Це застосовує регулярні вирази ітеративно. Спочатку він замінює всі цілі числа xкортежем списку синглів [(x,0)]. Тоді перший регулярний вираз виконує dоперацію, замінюючи все [(x,0)]d[(b,0)]рядковим представленням масиву подібних кортежів [(1,b),(2,b),(3,b)]. Другий елемент кожного кортежу - другий операнд до d. Потім наступні регулярні вирази виконують кожен з інших операторів. Існує спеціальний регулярний вираз для видалення паролів з повністю обчислених виразів.


3

Clojure, 731 720 байт

(коли видаляються нові рядки)

Оновлення: коротша реалізація F.

(defn N[i](if(seq? i)(apply + i)i))
(defn g[i](let[L(fn[i](let[v(g i)](if(seq? v)v(list v))))R remove T take](if(seq? i)(let[[o a b :as A]i](if(some symbol? A)(case o d(repeatedly(N(g a))(fn[](inc(rand-int(N(g b))))))t(T(N(g b))(sort(g a)))T(T(N(g b))(sort-by -(g a)))r(for[i(L a)](if((set(L b))i)(nth(L a)0)i))R(T(count(L a))(R(set(L b))(for[_(range)i(L a)]i)))+(+(N(g a))(N(g b)))-(-(N(g a))(N(g b))).(into(L a)(L b))_(R(set(L b))(g a)))A))i)))
(defn F[T](if(seq? T)(if(next T)(loop[[s & S]'[_ . - + R r T t d]](let[R reverse[b a](map R(split-with(comp not #{s})(R T)))a(butlast a)](if a(cons s(map F[a b]))(recur S))))(F(first T)))T))
(defn f[i](N(g(F(read-string(clojure.string/replace(str"("i")")#"[^0-9]"" $0 "))))))

Складається з чотирьох основних частин:

  • N: примушує список до числа
  • g: оцінює абстрактне синтаксичне дерево (S-вирази з 3 елементами)
  • F: перетворює інфікс AST у позначення префікса (S-вирази), також застосовує пріоритет порядку операнду
  • f: використовує read-stringдля перетворення рядка в вкладену послідовність чисел та символів (інфікція AST), передає їх через F -> g -> N, повертаючи число результату.

Я не впевнений, як всебічно це перевірити, можливо, за допомогою статистичних тестів проти впровадження еталонних даних? Принаймні AST та його оцінку простежити порівняно легко.

Приклад S-вираження від 1d((8d20t4T2)d(6d6R1r6)-2d4+1d2).(1d(4d6_3d3)):

(. (d 1 (- (d (T (t (d 8 20) 4) 2)
              (R (d 6 6) (r 1 6)))
           (+ (d 2 4)
              (d 1 2))))
   (d 1 (_ (d 4 6) (d 3 3))))

Менше гольфу з результатами і тестами інтернедіатів:

(def f #(read-string(clojure.string/replace(str"("%")")#"[^0-9]"" $0 ")))

(defn F [T]
  (println {:T T})
  (cond
    (not(seq? T))T
    (symbol?(first T))T
    (=(count T)1)(F(first T))
    1(loop [[s & S] '[_ . - + R r T t d]]
      (let[[b a](map reverse(split-with(comp not #{s})(reverse T)))
           _ (println [s a b])
           a(butlast a)]
        (cond
          a(do(println {:s s :a a :b b})(cons s(map F[a b])))
          S(recur S))))))


(->> "3d6" f F)
(->> "3d6t2" f F)
(->> "3d2R1" f F)
(->> "1d4d4" f F)
(->> "2d6.2d6" f F)
(->> "(3d2R1)d2" f F)
(->> "1d((8d20t4T2)d(6d6R1r6)-2d4+1d2).(1d(4d6_3d3))" f F)

(defn N[i](if(seq? i)(apply + i)i))

(defn g[i]
  (let[L(fn[i](let[v(g i)](if(seq? v)v(list v))))]
    (if(seq? i)
      (let[[o a b :as A] i]
        (println {:o o :a a :b b :all A})
        (if(every? number? A)(do(println{:A A})A)
           (case o
            d (repeatedly(N (g a))(fn[](inc(rand-int(N (g b))))))
            t (take(N (g b))(sort(g a)))
            T (take(N (g b))(sort-by -(g a)))
            r (for[i(L a)](if((set(L b))i)(nth(L a)0)i))
            R (take(count(g a))(remove(set(L b))(for[_(range)i(g a)]i)))
            + (+(N (g a))(N (g b)))
            - (-(N (g a))(N (g b)))
            . (into(L a)(L b))
            _ (remove(set(L b))(g a)))))
      (do(println {:i i})i))))


(g '(. (d 3 5) (d 4 3)))
(g '(. 1 (2 3)))
(g '(+ 1 (2 3)))
(g '(R (d 10 5) (d 1 3)))
(g '(T (d 5 20) 3))
(g '(t (d 5 20) 3))
(g '(d (d 3 4) 10))
(g '(d 4 3))
(g '(_ (d 4 6) (d 3 3)))

(->> "1d(4d6_3d3)" f F g)
(->> "1r6" f F g)
(->> "(8d20t4T2)d(6d6R1r6)" f F g)
(->> "(8d20t4T2)d(6d6R1r6)-2d4+1d2).(1d(4d6_3d3)" f F g)
(->> "1d((8d20t4T2)d(6d6R1r6)-2d4+1d2).(1d(4d6_3d3))" f F g))

2

Пітон 3, 695 байт

import random,tatsu
A=lambda x:sum(x)or 1
H=lambda x:[[d,D(d.s)][d in x[2]]for d in x[0]]
R=lambda x:R([H(x)]+x[1:])if{*x[0]}&{*x[2]}else x[0]
class D(int):
 def __new__(cls,s):o=super().__new__(cls,random.randint(1,s));o.s = s;return o
class S:
 o=lambda s,x:{'+':[A(x[0])+A(x[2])],'-':[A(x[0])-A(x[2])],'.':x[0]+x[2],'_':[d for d in x[0]if d not in x[2]]}[x[1]]
 f=lambda s,x:{'r':H(x),'R':R(x),'t':sorted(x[0])[:A(x[2])],'T':sorted(x[0])[-A(x[2]):]}[x[1]]
 d=lambda s,x:[D(A(x[2]))for _ in' '*A(x[0])]
 n=lambda s,x:[int(x)]
 l=lambda s,x:sum(x,[])
lambda i:tatsu.parse("s=e$;e=o|t;o=e/[-+._]/t;t=f|r;f=t/[rRtT]/r;r=d|a;d=r/d/a;a=n|l|p;n=/\d+/;l='['@:','.{n}']';p='('@:e')';",i,semantics=S())

Інтерпретатор, побудований за допомогою tatsuбібліотеки парсера PEG Перший аргумент tatsu.parser()- граматика PEG.

class D(для Die) підкласи вбудованого intтипу. Це значення - результат рулону. Атрибут .s- кількість сторін на плашці.

class S має смислові дії для парсера і реалізує інтерпретатор.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.