基本的类Pyth语法检查器


25

Pyth是一种基于Python的高尔夫语言。它使用前缀表示法,每个命令具有不同的Arity(接受的参数数量)。

您的任务是为(不存在的)类Pyth语言Pith编写语法检查器。

Pith的语法

Pith只有8个单字符命令:

01234()"

01234每个都有对应的数字,因此期望后面有很多参数。例如,

400010

是一个正确的髓部程序,因为4后面四个参数0 0 010,是其中最后一个1接着一个参数0。为了可视化,我们可以看下面的树:

      R
      |
      4
      |
-------------
|   |   |   |
0   0   0   1
            |
            0

R根节点在哪里。考虑此问题的另一种方法是,每个数字都指相应节点在上面的树中具有的子代数。

这是另一个有效的Pith程序,具有多个基本命令:

210010

对应于

           R
           |
     -------------
     |           |
     2           1
     |           |
 ---------       0
 |       |
 1       0
 |
 0

另一方面,

3120102100

不是正确的Pith程序,因为开头3只有两个参数,可以通过查看下面的树来查看它们:

                R
                |
                3
                |
     ------------------------ ??
     |          |
     1          2
     |          |
     2        ------
     |        |    |
   ------     1    0
   |    |     |
   0    1     0
        |
        0

下一步(开始一个无界,并)结束一个无界。无边界的(贪婪地)接受任意数量的参数,并且作为任何父命令的单个参数。程序结束时仍未打开的所有无界限都将自动关闭。一),如果没有unboundeds打开命令是不是一个错误-它只是什么也不做*。

例如,Pith程序

)31(0)0(201000100

对应于树

            R
            |
            3
            |
    ------------------------------
    |       |                    |
    1       0                    (
    |                            |
    (              -----------------------------
    |              |      |      |      |      |
    0              2      0      0      1      0
                   |                    |
                -------                 0
                |     |
                0     1
                      |
                      0

空无界也可以,()有效的Pith程序也可以。

无限的无效Pith程序是

12(010

因为2唯一的接收一个参数(无界)。

最后,"开始和结束一个字符串,该字符串始终为0 arity并计为单个参数,例如

2"010""44)()4"

这只是一个2传递两个字符串参数"010""44)()4"。像无界一样,字符串也可能为空,并且程序结束时所有未闭合的字符串都会自动关闭。

*这部分是从原来的Pyth实际上不同确实在这样的情况下做一些事情1),结束了1元数和提高的错误。

输入输出

输入将是一个仅由字符组成的单个非空字符串01234()"。您可以选择假设始终存在其他尾随换行符。您可以为此挑战编写函数或完整程序。

如果输入在语法上是有效的Pith,则应输出真实值,否则应输出虚假值。真值和伪值必须固定,因此您无法输出1一个有效程序和2另一个有效程序。

计分

这是代码高尔夫球,因此,最少字节的代码将获胜。

测试用例

真相:

0
)
(
"
()
""
10
400010
210010
("")00
3"""""
(0)))0)1)0
2(2(2(0)0)0)0
2"010""44)()4"
)31(0)0(201000100
())2)1))0"3())"))
3("4321("301(0)21100"4")"123"00)40"121"31000""01010

虚假:

1
1(310
(1)0)
12(010
4"00010"
3120102100
20(2((0)(0)))
2(2(2(0)0)0)01)
4(0102)00)00000
2"00"("00"2(""))

20100100的树(在第一个无边界示例中)不应该是[( [2 [0] [1 [0] ] ] [0] [1 [0]] [0] ]?您拥有的一个分支有2、0、0、1和0的分支-第二个分支不应在那里。
bcsb1001 2015年

@ bcsb1001感谢并纠正。我想表明
界限

@Ypnypn有测试用例说它具有多个根是有效的
Optimizer

您能否为添加测试用例())2)1))0"3())"))(我认为应该是正确的)。
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

@ n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳添加-的确是正确的(因为它基本上()210""没有很多操作)
Sp3000 2015年

Answers:


12

CJam,65个字节

q'"/La+2%{0s*}:Z~_,:L')*+{L{4{)_Z+s/Z}/L{'(\Z')++/Z}/}*')-}2*0s-!

天哪,我希望CJam使用Regex,这可以在不到50个字节的时间内完成

其主要思想是保持减少的东西0,即1002000等。一旦做到这一点,我们减少了所有匹配的括号0,即()0(0)0(00)0等等。我们重复循环L时间,这里L是输入长度。

输入字符串最初经过额外的处理,在此我们会针对不匹配"项进行调整,并添加大量内容)以补偿不匹配项(

这样可以确保在所有迭代之后,我们只应在字符串中保留0(和no-op ))。

更新-修复了顶级)无操作被视为有害的错误

代码扩展

q'"/La+2%{0s*}:Z~_,:L')*      "Part 1 - Preprocessing";
q'"/                          "Read the input and split it on quote";
    La+                       "Add an extra empty array to the split array to compensate";
                              "for unmatched ending quote";
        2%                    "Take every other splitted part, ignoring the contents";
                              "of the strings in the code";
          {0s*}:Z~            "Join the parts by string 0. Also create a function Z";
                              "that does that for future use. We are now done with";
                              "reducing "XYZ" to 0 ";
                  _,:L        "Store the length of the remaining code in L";
                      ')*     "Append L ) to the string to compensate for unmatched (";

{L{4{)_Z+s/Z}/L{'(\Z')++/Z}/}*')-}2*   "Part 2 - the reducing loop";
 L{                         }*         "Run the reduction logic L times";
   4{)_Z+s/Z}/                         "Part 2a - reducing arities";
   4{       }/                         "Run the iteration for 0, 1, 2 and 3";
     )_                                "Increment the iteration number and make a copy";
       Z+s                             "Get that many 0 and append it to the number";
          /Z                           "Split the code onto this number and convert it";
                                       "to 0. This successfully reduces 10, 200, ";
                                       "3000 and 4000 to 0";
              L{'(\Z')++/Z}/           "Part 2b - reducing groups";
              L{          }/           "Run the iteration for 0 through L - 1";
                '(\Z')++               "Get the string '(<iteration number of 0>)'";
                        /Z             "split on it and join by 0. This successfully";
                                       "reduces (), (0), (00) and so on .. to 0";
                              ')-      "After running the part 2 loop L times, all";
                                       "reducible arities and brackets should be reduced";
                                       "except for cases like '30)00' where the no-op )";
                                       "breaks the flow. So we remove ) from the code";
                                       "and run Part 2 L times again";

0s-!                          "Now, if the code was valid, we should only have 0 in the";
                              "remaining code. If not, the code was invalid";

在此处在线尝试运行整个套件


11

正则表达式,PCRE风格,83字节

^(?3)*\)*$(()((?(2)|\)*)(\((?1)*(\)|$)|0|"[^"]*.?|(1|(2|(3|4(?3))(?3))(?3))(?3))))?

在这里尝试。

正则表达式,PCRE风格,85字节

^((?(3)|\)*)(?>\((?2)*(()(?1))?(\)|$)|0|"[^"]*.?|(1|(2|(3|4(?1))(?1))(?1))(?1)))*\)*$

在这里尝试。

dan1111的答案中使用了一些想法。

关于的一些解释(?2)*(()(?1))?


(?2)*(()(?1))?是我一直在寻找的最后一块拼图。好发现!;)
Martin Ender 2015年

如果我(?2)*(()(?1))?正确地理解了该部分,那么该(()(?1))?部分将永远不会匹配任何东西,因为(?2)*已经吞噬了所有(()(?1))?可以匹配的东西,并且当我们进入构造时,此构造用于设置捕获组3,(而当我们不在()构造时,则取消设置捕获组3 (以便进行匹配)未配对))。
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

4

lex,182字节(157 w /固定大小的堆栈)

这些程序要求输入为有效字符的换行符结尾的单个字符串。

%%
 int n=0,s=0,*t=0;
[0-4] n+=*yytext-48-!!n;
\( (t=realloc(t,(s+1)*sizeof n))[s++]=n-!!n;n=0;
\) if(s&&n)exit(1);s&&(n=t[--s]);
\"[^"]*.? n-=!!n;
\n while(s)t[--s]&&n++;exit(!!n);

如果内存不足,上述程序就会出现段错误,如果您提供足够的内存,理论上可能会发生这种情况(。但是由于段故障被视为失败,因此我将其视为“错误”,尽管问题描述没有说明如果资源不足,该怎么办。

我仅使用固定大小的堆栈就将其减少了157个字节,但这似乎是作弊行为。

%%
 int n=0,s=0,t[9999];
[0-4] n+=*yytext-48-!!n;
\( t[s++]=n-!!n;n=0;
\) if(s&&n)exit(1);s&&(n=t[--s]);
\"[^"]*.? n-=!!n;
\n while(s)t[--s]&&n++;exit(!!n);

编译:

flex -o pith.c pith.l    # pith.l is the above file
c99 -o pith pith.c -lfl

测试:

while IFS= read -r test; do
  printf %-78s "$test"
  if ./pith <<<"$test"; then echo "OK"; else echo "NO"; fi
done <<EOT
0
)
(
"
()
""
10
400010
210010
("")00
3"""""
2(2(2(0)0)0)0
2"010""44)()4"
)31(0)0(201000100
3("4321("301(0)21100"4")"123"00)40"121"31000""01010
1
1(310
12(010
4"00010"
3120102100
20(2((0)(0)))
2(2(2(0)0)0)01)
4(0102)00)00000
2"00"("00"2(""))
EOT

测试输出:

0                                                                             OK
)                                                                             OK
(                                                                             OK
"                                                                             OK
()                                                                            OK
""                                                                            OK
10                                                                            OK
400010                                                                        OK
210010                                                                        OK
("")00                                                                        OK
3"""""                                                                        OK
2(2(2(0)0)0)0                                                                 OK
2"010""44)()4"                                                                OK
)31(0)0(201000100                                                             OK
3("4321("301(0)21100"4")"123"00)40"121"31000""01010                           OK
1                                                                             NO
1(310                                                                         NO
12(010                                                                        NO
4"00010"                                                                      NO
3120102100                                                                    NO
20(2((0)(0)))                                                                 NO
2(2(2(0)0)0)01)                                                               NO
4(0102)00)00000                                                               NO
2"00"("00"2(""))                                                              NO

我想我应该更清楚一些-您可以假设换行符永远不在那里,或者一直在那里。有帮助吗?
Sp3000

可以使用固定大小的堆栈程序,但可以将堆栈的大小设置为输入的长度吗?
isaacg 2015年

@isaacg由于输入为stdin,因此在读取之前我们不知道。我可以很容易地编写一个使用命令行arg或string的驱动程序,但是golf还有其他优先级。25个字符的动态堆栈按c标准来说还不错,但是我相信它仍然可以打高尔夫球。
rici

4

80386汇编程序,97字节

十六进制转储:

0000000: 8b54 2404 5589 e531 c96a ff8a 022c 303c  .T$.U..1.j...,0<
0000010: f275 04f6 d530 c084 ed75 263c f875 0141  .u...0...u&<.u.A
0000020: 3cf9 750f 84c9 7419 4958 3c00 7c03 50eb  <.u...t.IX<.|.P.
0000030: 2430 c084 c075 0958 483c ff7f 0140 ebf3  $0...u.XH<...@..
0000040: 5042 8a02 84c0 75c5 4a84 edb0 2275 be84  PB....u.J..."u..
0000050: c9b0 2975 b85a 31c0 39e5 7501 4089 ec5d  ..)u.Z1.9.u.@..]
0000060: c3                                       .

这将贯穿输入一次,将大于零的数字压入堆栈,并在处理零时将其递减。无界处理为-1。

函数原型(在C中)(如果无效,则函数返回0,如果有效,则返回1):

int __cdecl test(char *in);

等效组件(NASM):

bits 32
; get input pointer into edx
mov edx, [esp+4]                ; 8B 54 24 04

; save ebp; set ebp = esp
push ebp                        ; 55
mov ebp, esp                    ; 89 E5

; clear ecx
xor ecx, ecx                    ; 31 C9

; push base -1
push byte(-1)                   ; 6A FF

; get top char
mov al, [edx]                   ; 8A 02

    sub al, 0x30                ; 2C 30

    ; if al == quote
    cmp al, 0xF2                ; 3C F2
    jne $+6                     ; 75 04
        ; set ch (in quote?) to opposite
        not ch                  ; F6 D5
        ; set value to 0
        xor al, al              ; 30 C0

    ; if in quote, continue
    test ch, ch                 ; 84 ED
    jnz $+40                    ; 75 26

    cmp al, 0xF8                ; 3C F8
    jne $+3                     ; 75 01
        ; increment cl=depth
        inc ecx                 ; 41

    cmp al, 0xF9                ; 3C F9
    jne $+17                    ; 75 0F
        ; check depth = 0
        test cl, cl             ; 84 C9
        jz $+27                 ; 74 19
        ; decrement cl=depth
        dec ecx                 ; 49
        ; pop and check -1
        pop eax                 ; 58
        cmp al, 0               ; 3C 00
        jl $+5                  ; 7C 03
            push eax            ; 50
            jmp $+38            ; EB 24
        xor al, al              ; 30 C0

    test al, al                 ; 84 C0
    jnz $+11                    ; 75 09
        pop eax                 ; 58
        dec eax                 ; 48
        cmp al, -1              ; 3C FF
        jg $+3                  ; 7F 01
            inc eax             ; 40
        jmp $-11                ; EB F3
    push eax                    ; 50

    inc edx                     ; 42
    mov al, [edx]               ; 8A 02
    test al, al                 ; 84 C0
    jnz $-57                    ; 75 C5

    dec edx                     ; 4A

    ; in quote?
    test ch, ch                 ; 84 ED
    mov al, 0x22                ; B0 22
    jnz $-64                    ; 75 BE

    ; depth not zero?
    test cl, cl                 ; 84 C9
    mov al, 0x29                ; B0 29
    jnz $-70                    ; 75 B8

; pop base -1
pop edx                         ; 5A

; set return value based on ebp/esp comparison
xor eax, eax                    ; 31 C0
cmp ebp, esp                    ; 39 E5
jne $+3                         ; 75 01
inc eax                         ; 40
; restore esp
mov esp, ebp                    ; 89 EC
; restore ebp
pop ebp                         ; 5D
; return
ret                             ; C3

以下C语言代码可与POSIX系统上的GCC一起使用以进行测试:

#include <sys/mman.h>
#include <stdio.h>
#include <string.h>

int main(){
    char code[] = {
        0x8b, 0x54, 0x24, 0x04, 0x55, 0x89, 0xe5, 0x31, 0xc9, 0x6a, 0xff,
        0x8a, 0x02, 0x2c, 0x30, 0x3c, 0xf2, 0x75, 0x04, 0xf6, 0xd5, 0x30, 
        0xc0, 0x84, 0xed, 0x75, 0x26, 0x3c, 0xf8, 0x75, 0x01, 0x41, 0x3c, 
        0xf9, 0x75, 0x0f, 0x84, 0xc9, 0x74, 0x19, 0x49, 0x58, 0x3c, 0x00, 
        0x7c, 0x03, 0x50, 0xeb, 0x24, 0x30, 0xc0, 0x84, 0xc0, 0x75, 0x09, 
        0x58, 0x48, 0x3c, 0xff, 0x7f, 0x01, 0x40, 0xeb, 0xf3, 0x50, 0x42, 
        0x8a, 0x02, 0x84, 0xc0, 0x75, 0xc5, 0x4a, 0x84, 0xed, 0xb0, 0x22, 
        0x75, 0xbe, 0x84, 0xc9, 0xb0, 0x29, 0x75, 0xb8, 0x5a, 0x31, 0xc0, 
        0x39, 0xe5, 0x75, 0x01, 0x40, 0x89, 0xec, 0x5d, 0xc3,
    };
    void *mem = mmap(0, sizeof(code), PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0);
    memcpy(mem, code, sizeof(code));
    int __cdecl (*test)(char *) = (int __cdecl (*)(char *)) mem;

    #define TRY(s) printf(s ": %d\n", test(s))

    printf("Truthy tests:\n");
    TRY("0");
    TRY(")");
    TRY("(");
    TRY("\"");
    TRY("()");
    TRY("\"\"");
    TRY("10");
    TRY("400010");
    TRY("210010");
    TRY("(\"\")00");
    TRY("3\"\"\"\"\"");
    TRY("(0)))0)1)0");
    TRY("2(2(2(0)0)0)0");
    TRY("2\"010\"\"44)()4\"");
    TRY(")31(0)0(201000100");
    TRY("())2)1))0\"3())\"))");
    TRY("3(\"4321(\"301(0)21100\"4\")\"123\"00)40\"121\"31000\"\"01010");

    printf("\nFalsy tests:\n");
    TRY("1");
    TRY("1(310");
    TRY("(1)0)");
    TRY("12(010");
    TRY("4\"00010\"");
    TRY("3120102100");
    TRY("20(2((0)(0)))");
    TRY("2(2(2(0)0)0)01)");
    TRY("4(0102)00)00000");
    TRY("2\"00\"(\"00\"2(\"\"))");

    munmap(mem, sizeof(code));
    return 0;
}

3

Python 2,353字节

解析函数一次地遍历令牌,并构建程序结构树。无效的程序会触发一个异常,该异常导致打印零(Falsy),否则成功解析将结果为1。

def h(f,k=0):
 b=[]
 if k:
  while f:b+=[h(f)]
  return b
 q=f.pop(0)
 if q==')':return[]
 elif q=='"':
  while f:
   q+=f.pop(0)
   if q[-1]=='"':break
 elif q=='(':
  while f:
   if f and f[0]==')':f.pop(0);break
   b+=h(f)
 else:
  for i in range(int(q)):b+=h(f)
  assert len(b)==int(q)
 return[[q,b]]
try:h(list(raw_input()));r=1
except:r=0
print r

测试的输出显示了解析器的输出:

------------------------------------------------------------
True: 0
    0

------------------------------------------------------------
True: )

------------------------------------------------------------
True: (
    (

------------------------------------------------------------
True: "
    "

------------------------------------------------------------
True: ()
    (

------------------------------------------------------------
True: ""
    ""

------------------------------------------------------------
True: 10
    1
        0

------------------------------------------------------------
True: 400010
    4
        0
        0
        0
        1
            0

------------------------------------------------------------
True: 210010
    2
        1
            0
        0
    1
        0

------------------------------------------------------------
True: ("")00
    (
        ""
    0
    0

------------------------------------------------------------
True: 3"""""
    3
        ""
        ""
        "

------------------------------------------------------------
True: 2(2(2(0)0)0)0
    2
        (
            2
                (
                    2
                        (
                            0
                        0
                0
        0

------------------------------------------------------------
True: 2"010""44)()4"
    2
        "010"
        "44)()4"

------------------------------------------------------------
True: )31(0)0(201000100
    3
        1
            (
                0
        0
        (
            2
                0
                1
                    0
            0
            0
            1
                0
            0

------------------------------------------------------------
True: 3("4321("301(0)21100"4")"123"00)40"121"31000""01010
    3
        (
            "4321("
            3
                0
                1
                    (
                        0
                2
                    1
                        1
                            0
                    0
            "4"
        "123"
        0
    0
    4
        0
        "121"
        3
            1
                0
            0
            0
        ""
    0
    1
        0
    1
        0

------------------------------------------------------------
False: 1
0
------------------------------------------------------------
False: 1(310
0
------------------------------------------------------------
False: 12(010
0
------------------------------------------------------------
False: 4"00010"
0
------------------------------------------------------------
False: 3120102100
0
------------------------------------------------------------
False: 20(2((0)(0)))
0
------------------------------------------------------------
False: 2(2(2(0)0)0)01)
0
------------------------------------------------------------
False: 4(0102)00)00000
0
------------------------------------------------------------
False: 2"00"("00"2(""))
0

缩小器之前的代码:

def parse(tokens, first=False):
    toklist = []
    if first:
        while tokens :
            toklist += [parse(tokens)]
        return toklist
    tok = tokens.pop(0)
    if tok == ')' :
        return []
    elif tok == '"':
        while tokens:
            tok += tokens.pop(0)
            if tok[-1] == '"' :
                break
    elif tok == '(':
        while tokens:
            if tokens and tokens[0] == ')' :
                tokens.pop(0);
                break
            toklist += parse(tokens)
    else:
        for i in range(int(tok)) :
            toklist += parse(tokens)
        assert len(toklist) == int(tok)
    return [[tok, toklist]]

try :
    parse(list(raw_input()));
    r = 1
except :
    r = 0
print r

很好(滥用)使用例外!您可以通过==在测试中交换操作数的顺序来节省一些空间-首先输入字符串意味着您可以做到if')'==q。我相信其中一个break语句可以替换为f=0,因为那样也会使您跳出while f循环。最后,而不是assert x==y可以使用1/(x==y)ZeroDivisionError。;)
DLosc

@DLosc,感谢您提供一些非常有用的高尔夫技巧。如果我是高尔夫比赛的领导者之一,我会用你的想法进行比赛。由于我的参赛作品远非竞争性(高尔夫),所以我宁愿将其作为一个易于阅读的例子。我已经注意到了您的聪明技巧,但可以将来使用;-)
逻辑骑士

1

88 72字节

想法取自Optimizer的CJam。我最初对递归下降解析器所遇到的问题的...望是……时间更长。

Qpz:,5.iX,5AL'(.0X,#p.')p^:'"Fj,#pIj%2p@j:0p:Jpp.:')X#pL#ppR:z0!pRM')Rz0

格式化,并附有解释:

Qp                Query user for p
z:                Store the following list in z:
  ,5 . 0X,5         For x in range(5), concatenate x zeros to it
  AL                (append list)
  '(. 0X,#p .')     For x in range(len(p)), concatenate x zeros inside parens
p^: '"            Split p on " and assign result back to p
Fi,#p             Loop over the indices of the resulting list:
 Ii%2               If index is odd:
  p@i:0               Replace that item with 0
p: Jp             Concatenate the results and assign back to p
p.: ')X#p         Append len(p) closing parens to p
L#p               Loop len(p) times:
 pR:z0              Replace every occurrence in p of items of z with 0
! pRM')Rz0        Remove ) from result and replace z with 0 one more time; then
                  take logical negation, which will be true iff string is empty OR
                  consists only of zeros

有趣的技巧:

  • 许多运算符在列表和范围上逐项工作。因此0X,5,例如0 X [0 1 2 3 4] == ["" "0" "00" "000" "0000"]
  • 由于前几天,三元RE放置运营商可以采取列表,它的任何参数:"abracadabra" R ["br" "ca"] 'b给出ababdaba,例如。我z在这里充分利用了此功能。
  • Pip中的伪造值包括空字符串"",空列表[]以及任何零的标量。因此,0是假的,但也0.0"0000000"。有时此功能"0"很不方便(要测试一个字符串是否为空,也必须测试字符串的长度,因为它也是假的),但是对于这个问题,它是完美的。

1

使用Javascript(ES6),289 288 285 282 278个 244 241 230字节

c=prompt(k="0"),j=c[l="length"];if((c.match(/"/g)||[])[l]%2)c+='"';c=c[R="replace"](/".*?"/g,k);c+=")".repeat(j);while(j--)c=c[R](/\(0*\)/,k)[R](/10/g,k)[R](/200/g,k)[R](/3000/g,k)[R](/40000/g,k);alert(!c[R](/0/g,"")[R](/\)/g,""))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.