문제

할 수 있는 새로운 문(아 print, raise, with 용)Python 의 구문은?

말 할 수 있도록,..

mystatement "Something"

거나,

new_if True:
    print "example"

그렇지 않은 경우 , 지만,오히려 가능한 경우(short 을 수정하는 파이썬 통역 코드)

도움이 되었습니까?

해결책

이 유용한 것을 알 수 있습니다. Python Internals : Python에 새로운 진술을 추가합니다, 여기에 인용 :


이 기사는 Python의 프론트 엔드가 어떻게 작동하는지 더 잘 이해하려는 시도입니다. 문서와 소스 코드를 읽는 것만으로도 약간 지루할 수 있으므로 여기서 실습 접근 방식을 취하고 있습니다. until 파이썬에 대한 진술.

이 기사의 모든 코딩은 최첨단 PY3K 지점에 대해 수행되었습니다. 파이썬 수은 저장소 미러.

그만큼 until 성명

루비와 같은 일부 언어에는 다음과 같습니다 until 보완 된 진술 while (until num == 0 동일합니다 while num != 0). 루비에서는 다음을 쓸 수 있습니다.

num = 3
until num == 0 do
  puts num
  num -= 1
end

그리고 그것은 인쇄 할 것입니다 :

3
2
1

따라서 파이썬에 비슷한 기능을 추가하고 싶습니다. 즉, 쓸 수 있습니다.

num = 3
until num == 0:
  print(num)
  num -= 1

언어 advocacy worression

이 기사는 until 파이썬에 대한 진술. 비록 그러한 진술이 일부 코드를 더 명확하게 만들 것이라고 생각하지만,이 기사는 추가가 얼마나 쉬운지를 보여줍니다. 나는 Python의 미니멀리즘 철학을 완전히 존중합니다. 내가 여기서하려고하는 것은 실제로 파이썬의 내부 작업에 대한 통찰력을 얻는 것입니다.

문법 수정

Python은 이름이 지정된 커스텀 파서 생성기를 사용합니다 pgen. 이것은 Python 소스 코드를 구문 분석 트리로 변환하는 LL (1) 파서입니다. 파서 생성기에 대한 입력은 파일입니다 Grammar/Grammar[1]. 이것은 파이썬의 문법을 지정하는 간단한 텍스트 파일입니다.

[1]: 여기서부터 파이썬 소스의 파일에 대한 참조는 소스 트리의 루트에 비교적 주어지며, 이는 구성을 실행하고 파이썬을 빌드하는 디렉토리입니다.

문법 파일을 두 가지 수정해야합니다. 첫 번째는에 대한 정의를 추가하는 것입니다 until 성명. 나는 어디를 찾았다 while 진술이 정의되었습니다 (while_stmt)) 및 추가 until_stmt 아래에 [2]:

compound_stmt: if_stmt | while_stmt | until_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
while_stmt: 'while' test ':' suite ['else' ':' suite]
until_stmt: 'until' test ':' suite

[2]: 이것은 익숙하지 않은 소스 코드를 수정할 때 사용하는 일반적인 기술을 보여줍니다. 유사하게 작동합니다. 이 원칙은 모든 문제를 해결하지는 않지만 프로세스를 쉽게 완화 할 수 있습니다. 해야 할 모든 일이 있기 때문에 while 또한 완료해야합니다 until, 그것은 꽤 좋은 가이드 라인 역할을합니다.

내가 배제하기로 결정했습니다 else 내 정의에서 조항 until, 조금 다르게 만들기 위해 (그리고 솔직히 나는 나는 else 루프 조항은 파이썬의 선과 잘 어울린다고 생각하지 않습니다).

두 번째 변화는 규칙을 수정하는 것입니다 compound_stmt 포함하는 until_stmt, 위의 스 니펫에서 볼 수 있듯이. 직후입니다 while_stmt, 다시.

당신이 달릴 때 make 수정 후 Grammar/Grammar, pgen 프로그램은 재생으로 실행됩니다 Include/graminit.h 그리고 Python/graminit.c, 그런 다음 여러 파일이 다시 컴파일됩니다.

AST 생성 코드 수정

파이썬 파서가 구문 분석 트리를 만든 후에는이 나무가 ASTS이기 때문에 AST로 변환됩니다. 작업하기가 훨씬 간단합니다 컴파일 프로세스의 후속 단계에서.

그래서 우리는 방문 할 것입니다 Parser/Python.asdl Python의 AST의 구조를 정의하고 새로 AST 노드를 추가합니다. until 바로 아래의 진술 while:

| While(expr test, stmt* body, stmt* orelse)
| Until(expr test, stmt* body)

지금 달리면 make, 많은 파일을 컴파일하기 전에 Parser/asdl_c.py AST 정의 파일에서 C 코드를 생성하기 위해 실행됩니다. 이것은 (같은 Grammar/Grammar)는 프로그래밍을 단순화하기 위해 미니 언어 (즉, DSL)를 사용하는 Python 소스 코드의 또 다른 예입니다. 또한 그 이후로 주목하십시오 Parser/asdl_c.py 파이썬 스크립트입니다. 이것은 일종의 부트 스트랩 - 처음부터 파이썬을 만들려면 이미 Python을 사용할 수 있어야합니다.

하는 동안 Parser/asdl_c.py 새로 정의 된 AST 노드를 관리하기 위해 코드를 생성했습니다 (파일에 Include/Python-ast.h 그리고 Python/Python-ast.c), 우리는 여전히 관련 구문 분석 노드를 손으로 변환하는 코드를 작성해야합니다. 이것은 파일에서 수행됩니다 Python/ast.c. 거기에, 이름이 지정된 함수 ast_for_stmt 문장의 구문 분석 트리 노드를 AST 노드로 변환합니다. 다시, 우리의 오랜 친구의 인도 while, 우리는 바로 큰 사람으로 뛰어 들었습니다 switch 복합 진술을 처리하고 조항을 추가합니다 until_stmt:

case while_stmt:
    return ast_for_while_stmt(c, ch);
case until_stmt:
    return ast_for_until_stmt(c, ch);

이제 우리는 구현해야합니다 ast_for_until_stmt. 여기있어:

static stmt_ty
ast_for_until_stmt(struct compiling *c, const node *n)
{
    /* until_stmt: 'until' test ':' suite */
    REQ(n, until_stmt);

    if (NCH(n) == 4) {
        expr_ty expression;
        asdl_seq *suite_seq;

        expression = ast_for_expr(c, CHILD(n, 1));
        if (!expression)
            return NULL;
        suite_seq = ast_for_suite(c, CHILD(n, 3));
        if (!suite_seq)
            return NULL;
        return Until(expression, suite_seq, LINENO(n), n->n_col_offset, c->c_arena);
    }

    PyErr_Format(PyExc_SystemError,
                 "wrong number of tokens for 'until' statement: %d",
                 NCH(n));
    return NULL;
}

다시, 이것은 동등한 것을 자세히 보면서 코딩되었습니다. ast_for_while_stmt, 그 차이가 있습니다 until 나는 그것을지지하지 않기로 결정했다 else 절. 예상대로 AST는 다른 AST 생성 기능을 사용하여 재귀 적으로 만들어집니다. ast_for_expr 조건 표현 및 ast_for_suite 몸의 몸을 위해 until 성명. 마지막으로 새 노드가 명명되었습니다 Until 반환됩니다.

구문 분석 노드에 액세스합니다 n 같은 매크로를 사용합니다 NCH 그리고 CHILD. 이것들은 이해할 가치가 있습니다 - 그들의 코드는 있습니다 Include/node.h.

탈퇴 : AST 구성

나는 새로운 유형의 AST를 만들기로 선택했다. until 진술이지만 실제로 이것은 필요하지 않습니다. 기존 AST 노드의 구성을 사용하여 일부 작업을 저장하고 새로운 기능을 구현할 수있었습니다.

until condition:
   # do stuff

기능적으로 다음과 같습니다.

while not condition:
  # do stuff

창조하는 대신 Until 노드를 사용합니다 ast_for_until_stmt, 나는 만들 수 있었다 Not an이있는 노드 While 어린이로서 노드. AST 컴파일러는 이미 이러한 노드를 처리하는 방법을 알고 있으므로 프로세스의 다음 단계를 건너 뛸 수 있습니다.

ASTS를 바이트 코드로 컴파일합니다

다음 단계는 AST를 파이썬 바이트 코드로 컴파일하는 것입니다. 컴파일에는 CFG (Control Flow Graph) 인 중간 결과가 있지만 동일한 코드가 처리하므로 지금은이 세부 사항을 무시하고 다른 기사를 위해 남겨 둘 것입니다.

다음에 볼 코드는입니다 Python/compile.c. 의 리드에 따라 while, 우리는 기능을 찾습니다 compiler_visit_stmt, 진술을 바이트 코드로 컴파일하는 일을 담당합니다. 우리는 조항을 추가합니다 Until:

case While_kind:
    return compiler_while(c, s);
case Until_kind:
    return compiler_until(c, s);

당신이 무엇을 궁금해한다면 Until_kind 상수입니다 (실제로는 값입니다. _stmt_kind 열거)) AST 정의 파일에서 자동으로 생성됩니다. Include/Python-ast.h. 어쨌든 우리는 전화합니다 compiler_until 물론 여전히 존재하지 않습니다. 나는 그것을 잠시 이길 것이다.

당신이 나처럼 궁금하다면, 당신은 그것을 알게 될 것입니다 compiler_visit_stmt 독특합니다. 금액이 없습니다 grep-소스 트리를 파는 것은 그것이 호출되는 위치를 드러냅니다. 이 경우 하나의 옵션 만 남아 있습니다 -C Macro -Fu. 실제로, 짧은 조사는 우리를 VISIT 매크로가 정의되었습니다 Python/compile.c:

#define VISIT(C, TYPE, V) {\
    if (!compiler_visit_ ## TYPE((C), (V))) \
        return 0; \

호출하는 데 사용됩니다 compiler_visit_stmt 안에 compiler_body. 그러나 우리 사업으로 돌아가 ...

약속 한대로 여기 있습니다 compiler_until:

static int
compiler_until(struct compiler *c, stmt_ty s)
{
    basicblock *loop, *end, *anchor = NULL;
    int constant = expr_constant(s->v.Until.test);

    if (constant == 1) {
        return 1;
    }
    loop = compiler_new_block(c);
    end = compiler_new_block(c);
    if (constant == -1) {
        anchor = compiler_new_block(c);
        if (anchor == NULL)
            return 0;
    }
    if (loop == NULL || end == NULL)
        return 0;

    ADDOP_JREL(c, SETUP_LOOP, end);
    compiler_use_next_block(c, loop);
    if (!compiler_push_fblock(c, LOOP, loop))
        return 0;
    if (constant == -1) {
        VISIT(c, expr, s->v.Until.test);
        ADDOP_JABS(c, POP_JUMP_IF_TRUE, anchor);
    }
    VISIT_SEQ(c, stmt, s->v.Until.body);
    ADDOP_JABS(c, JUMP_ABSOLUTE, loop);

    if (constant == -1) {
        compiler_use_next_block(c, anchor);
        ADDOP(c, POP_BLOCK);
    }
    compiler_pop_fblock(c, LOOP, loop);
    compiler_use_next_block(c, end);

    return 1;
}

나는 고백 할 수있다 :이 코드는 파이썬 바이트 코드에 대한 깊은 이해를 바탕으로 작성되지 않았다. 기사의 나머지 부분과 마찬가지로, 그것은 친척의 모방으로 이루어졌습니다. compiler_while 기능. 그러나 신중하게 읽음으로써 Python VM은 스택 기반이라는 점을 명심하고 dis 모듈 파이썬 바이트 코드의 목록 설명을 통해 무슨 일이 일어나고 있는지 이해할 수 있습니다.

그게 다야, 우리는 끝났어 ... 그렇지 않니?

모든 변경을하고 실행 한 후 make, 우리는 새로 컴파일 된 파이썬을 실행하고 새로운 것을 시도 할 수 있습니다. until 성명:

>>> until num == 0:
...   print(num)
...   num -= 1
...
3
2
1

Voila, 그것은 작동합니다! 사용하여 새 문장을 위해 생성 된 바이트 코드를 보자. dis 다음과 같이 모듈 :

import dis

def myfoo(num):
    until num == 0:
        print(num)
        num -= 1

dis.dis(myfoo)

결과는 다음과 같습니다.

4           0 SETUP_LOOP              36 (to 39)
      >>    3 LOAD_FAST                0 (num)
            6 LOAD_CONST               1 (0)
            9 COMPARE_OP               2 (==)
           12 POP_JUMP_IF_TRUE        38

5          15 LOAD_NAME                0 (print)
           18 LOAD_FAST                0 (num)
           21 CALL_FUNCTION            1
           24 POP_TOP

6          25 LOAD_FAST                0 (num)
           28 LOAD_CONST               2 (1)
           31 INPLACE_SUBTRACT
           32 STORE_FAST               0 (num)
           35 JUMP_ABSOLUTE            3
      >>   38 POP_BLOCK
      >>   39 LOAD_CONST               0 (None)
           42 RETURN_VALUE

가장 흥미로운 작업은 12 번입니다. 조건이 참이면 루프 후에 점프합니다. 이것은 올바른 의미입니다 until. 점프가 실행되지 않으면 루프 본체는 작동 35의 상태로 돌아올 때까지 계속 작동합니다.

내 변화에 대해 기분이 좋았고, 나는 기능을 실행하려고 시도했다 (실행 myfoo(3)) 바이트 코드를 표시하는 대신. 결과는 격려가 적었습니다.

Traceback (most recent call last):
  File "zy.py", line 9, in
    myfoo(3)
  File "zy.py", line 5, in myfoo
    print(num)
SystemError: no locals when loading 'print'

우와 ... 이건 좋지 않아. 그래서 무엇이 잘못 되었습니까?

누락 된 심볼 테이블의 경우

AST를 컴파일 할 때 Python 컴파일러가 수행하는 단계 중 하나는 컴파일하는 코드의 심볼 테이블을 작성하는 것입니다. 전화 PySymtable_Build 안에 PyAST_Compile 기호 테이블 모듈로 호출 (Python/symtable.c), 코드 생성 함수와 유사한 방식으로 AST를 걷는다. 각 범위에 대한 기호 테이블이 있으면 컴파일러가 전역의 변수 및 범위에 따라 로컬인지와 같은 몇 가지 주요 정보를 파악할 수 있습니다.

문제를 해결하려면 symtable_visit_stmt 기능 Python/symtable.c, 처리를위한 코드 추가 until 비슷한 코드 후 진술 while 진술 [3]:

case While_kind:
    VISIT(st, expr, s->v.While.test);
    VISIT_SEQ(st, stmt, s->v.While.body);
    if (s->v.While.orelse)
        VISIT_SEQ(st, stmt, s->v.While.orelse);
    break;
case Until_kind:
    VISIT(st, expr, s->v.Until.test);
    VISIT_SEQ(st, stmt, s->v.Until.body);
    break;

[3]: 그건 그렇고,이 코드가 없으면 컴파일러 경고가 있습니다. Python/symtable.c. 컴파일러는 Until_kind 열거 값은 스위치 문에서 처리되지 않습니다 symtable_visit_stmt 그리고 불만. 컴파일러 경고를 확인하는 것이 항상 중요합니다!

그리고 지금 우리는 정말로 끝났습니다. 이 변경 후 소스를 컴파일하면 myfoo(3) 예상대로 작동합니다.

결론

이 기사에서는 Python에 새로운 진술을 추가하는 방법을 보여주었습니다. Python 컴파일러의 코드에 약간의 땜질이 필요하지만 비슷하고 기존의 진술을 가이드 라인으로 사용했기 때문에 변경은 구현하기가 어렵지 않았습니다.

Python 컴파일러는 정교한 소프트웨어 덩어리이며 전문가라고 주장하지 않습니다. 그러나 나는 파이썬의 내부, 특히 프론트 엔드에 정말로 관심이 있습니다. 따라서이 운동은 컴파일러의 원칙과 소스 코드에 대한 이론적 연구에 매우 유용한 동반자라는 것을 알았습니다. 컴파일러에 더 깊이 갈 수있는 미래의 기사의 기반이 될 것입니다.

참조

이 기사의 구성을 위해 몇 가지 훌륭한 참조를 사용했습니다. 여기에는 특별한 순서가 없습니다.

  • PEP 339 : CPYTHON 컴파일러 설계 - 아마도 가장 중요하고 포괄적 인 부분 일 것입니다 공식적인 파이썬 컴파일러에 대한 문서화. 매우 짧기 때문에 파이썬의 내부에 대한 좋은 문서화가 부족하다는 것을 고통스럽게 보여줍니다.
  • "Python Compiler Internals" - Thomas Lee의 기사
  • "Python : 디자인 및 구현" - Guido van Rossum의 프레젠테이션
  • Python (2.5) 가상 머신, 가이드 투어 - Peter Tröger의 프레젠테이션

원래 소스

다른 팁

이와 같은 일을하는 한 가지 방법은 소스를 전제로 처리하고 수정하여 추가 된 문을 Python으로 변환하는 것입니다. 이 접근법이 가져올 다양한 문제가 있으며, 일반적인 사용을 위해 권장하지는 않지만 언어 또는 특정 목적 메타 프로 그램을 실험하기 위해서는 때때로 유용 할 수 있습니다.

예를 들어, 화면에 인쇄하는 대신 특정 파일에 로그인하는 "MyPrint"문을 소개한다고 가정 해 봅시다. 즉:

myprint "This gets logged to file"

동등합니다

print >>open('/tmp/logfile.txt','a'), "This gets logged to file"

Regex 대체에서 AST 생성, AST 생성, 구문이 기존 Python과 얼마나 가까이 일치하는지에 따라 교체하는 방법에 대한 다양한 옵션이 있습니다. 좋은 중간 접근 방식은 토큰 화기 모듈을 사용하는 것입니다. 이를 통해 Python 통역사와 유사하게 소스를 해석하면서 새로운 키워드, 제어 구조 등을 추가 할 수 있으므로 Breakage Crude Regex 솔루션을 피하면 발생할 수 있습니다. 위의 "MyPrint"의 경우 다음 변환 코드를 작성할 수 있습니다.

import tokenize

LOGFILE = '/tmp/log.txt'
def translate(readline):
    for type, name,_,_,_ in tokenize.generate_tokens(readline):
        if type ==tokenize.NAME and name =='myprint':
            yield tokenize.NAME, 'print'
            yield tokenize.OP, '>>'
            yield tokenize.NAME, "open"
            yield tokenize.OP, "("
            yield tokenize.STRING, repr(LOGFILE)
            yield tokenize.OP, ","
            yield tokenize.STRING, "'a'"
            yield tokenize.OP, ")"
            yield tokenize.OP, ","
        else:
            yield type,name

(이것은 MyPrint가 효과적으로 키워드가되므로 다른 곳에서 변수로 사용하면 문제가 발생할 수 있습니다).

문제는 Python에서 코드를 사용할 수 있도록 사용하는 방법입니다. 한 가지 방법은 자신의 가져 오기 기능을 작성하고이를 사용하여 사용자 정의 언어로 작성된 코드를로드하는 것입니다. 즉:

import new
def myimport(filename):
    mod = new.module(filename)
    f=open(filename)
    data = tokenize.untokenize(translate(f.readline))
    exec data in mod.__dict__
    return mod

그러나 사용자 정의 코드는 일반적인 파이썬 모듈과 다르게 처리해야합니다. 즉 "some_mod = myimport("some_mod.py")"보다는"import some_mod"

상당히 깔끔한 (Hacky)의 또 다른 깔끔한 솔루션은 사용자 정의 인코딩을 만드는 것입니다 ( PEP 263) 처럼 이것 레시피가 보여줍니다. 당신은 이것을 다음과 같이 구현할 수 있습니다.

import codecs, cStringIO, encodings
from encodings import utf_8

class StreamReader(utf_8.StreamReader):
    def __init__(self, *args, **kwargs):
        codecs.StreamReader.__init__(self, *args, **kwargs)
        data = tokenize.untokenize(translate(self.stream.readline))
        self.stream = cStringIO.StringIO(data)

def search_function(s):
    if s!='mylang': return None
    utf8=encodings.search_function('utf8') # Assume utf8 encoding
    return codecs.CodecInfo(
        name='mylang',
        encode = utf8.encode,
        decode = utf8.decode,
        incrementalencoder=utf8.incrementalencoder,
        incrementaldecoder=utf8.incrementaldecoder,
        streamreader=StreamReader,
        streamwriter=utf8.streamwriter)

codecs.register(search_function)

이제이 코드가 실행되면 (예 : .pythonrc 또는 site.py에 배치 할 수 있음) 주석 "# 코딩 : mylang"으로 시작하는 모든 코드는 위의 전처리 단계를 통해 자동으로 번역됩니다. 예를 들어.

# coding: mylang
myprint "this gets logged to file"
for i in range(10):
    myprint "so does this : ", i, "times"
myprint ("works fine" "with arbitrary" + " syntax" 
  "and line continuations")

경고 :

C Preprocessor와 함께 일했다면 익숙해지기 때문에 전처리서 접근 방식에는 문제가 있습니다. 메인은 디버깅입니다. 모든 Python이 보는 전처리 파일은 스택 추적 등에 인쇄 된 텍스트가이를 참조 할 것임을 의미합니다. 상당한 번역을 수행 한 경우 소스 텍스트와 매우 다를 수 있습니다. 위의 예는 선 번호 등을 변경하지 않으므로 너무 다르지는 않지만 더 많이 변경할수록 파악하기가 더 어려워집니다.

예, 어느 정도는 가능합니다. 이있다 기준 치수 거기에서 사용합니다 sys.settrace() 구현 goto 그리고 comefrom "키워드":

from goto import goto, label
for i in range(1, 10):
  for j in range(1, 20):
    print i, j
    if j == 3:
      goto .end # breaking out from nested loop
label .end
print "Finished"

소스 코드를 변경하고 다시 컴파일하지 않음 ( ~이다 오픈 소스에서는 가능), 기본 언어 변경은 실제로 불가능합니다.

소스를 다시 컴파일하더라도 Python이 아니라 버그를 도입하지 않기 위해 매우 조심 해야하는 해킹 된 변경 버전 만 있습니다.

그러나 왜 당신이 원하는지 잘 모르겠습니다. Python의 객체 지향 기능은 언어와 비슷한 결과를 얻는 것이 매우 간단합니다.

일반 답변 : 소스 파일을 전처리해야합니다.

보다 구체적인 답변 : 설치 EasyExtend, 다음 단계를 거치게됩니다

i) 새로운 langlet 만들기 (확장 언어)

import EasyExtend
EasyExtend.new_langlet("mystmts", prompt = "my> ", source_ext = "mypy")

추가 사양이 없으면 Easyextend/Langlets/Mystmts/에서 많은 파일이 생성되어야합니다.

ii) mystmts/parsedef/grammar.ext를 열고 다음 줄을 추가하십시오.

small_stmt: (expr_stmt | print_stmt  | del_stmt | pass_stmt | flow_stmt |
             import_stmt | global_stmt | exec_stmt | assert_stmt | my_stmt )

my_stmt: 'mystatement' expr

이것은 새 진술의 구문을 정의하기에 충분합니다. small_stmt 비 터미널은 파이썬 문법의 일부이며 새로운 문장이 연결된 곳입니다. 파서는 이제 새로운 문 (즉, 포함 된 소스 파일)이 구문 분석 될 것입니다. 컴파일러는 여전히 유효한 파이썬으로 변환되어야하기 때문에이를 거부합니다.

iii) 이제 성명서의 의미론을 추가해야한다. 이를 위해서는 msytmts/langlet.py를 편집하고 my_stmt 노드 방문자를 추가해야합니다.

 def call_my_stmt(expression):
     "defines behaviour for my_stmt"
     print "my stmt called with", expression

 class LangletTransformer(Transformer):
       @transform
       def my_stmt(self, node):
           _expr = find_node(node, symbol.expr)
           return any_stmt(CST_CallFunc("call_my_stmt", [_expr]))

 __publish__ = ["call_my_stmt"]

iv) Langlets/mystmts 및 유형에 CD

python run_mystmts.py

이제 세션이 시작되고 새로 정의 된 진술을 사용할 수 있습니다.

__________________________________________________________________________________

 mystmts

 On Python 2.5.1 (r251:54863, Apr 18 2007, 08:51:08) [MSC v.1310 32 bit (Intel)]
 __________________________________________________________________________________

 my> mystatement 40+2
 my stmt called with 42

사소한 진술을하기위한 몇 가지 단계가 있습니다. 아직 문법을 신경 쓰지 않고 간단한 것을 정의 할 수있는 API는 없습니다. 그러나 EE는 매우 신뢰할 수있는 모듈로 일부 버그입니다. 따라서 프로그래머가 편리한 OO 프로그래밍을 사용하여 Infix 연산자 또는 작은 명령문과 같은 편리한 내용을 정의 할 수있는 API가 등장하는 것은 시간 문제 일뿐입니다. langlet을 구축하여 Python에 전체 언어를 포함시키는 것과 같은 더 복잡한 것들의 경우 전체 문법 접근 방식을 둘러 볼 방법이 없습니다.

다음은 새로운 진술을 추가하는 매우 간단하지만 엉터리 방법입니다. 해석 모드에서만. Sys.DisplayHook 만 사용하여 유전자 주석을 편집하기 위해 작은 1 글자 명령에 사용하고 있지만이 질문에 대답 할 수 있습니다. 후자는 정말 추악하여 읽기 라인 버퍼에서 원시 코드를 가져옵니다. 이점은 이런 식으로 새로운 진술을 추가하기가 쉽다는 것입니다.


jcomeau@intrepid:~/$ cat demo.py; ./demo.py
#!/usr/bin/python -i
'load everything needed under "package", such as package.common.normalize()'
import os, sys, readline, traceback
if __name__ == '__main__':
    class t:
        @staticmethod
        def localfunction(*args):
            print 'this is a test'
            if args:
                print 'ignoring %s' % repr(args)

    def displayhook(whatever):
        if hasattr(whatever, 'localfunction'):
            return whatever.localfunction()
        else:
            print whatever

    def excepthook(exctype, value, tb):
        if exctype is SyntaxError:
            index = readline.get_current_history_length()
            item = readline.get_history_item(index)
            command = item.split()
            print 'command:', command
            if len(command[0]) == 1:
                try:
                    eval(command[0]).localfunction(*command[1:])
                except:
                    traceback.print_exception(exctype, value, tb)
        else:
            traceback.print_exception(exctype, value, tb)

    sys.displayhook = displayhook
    sys.excepthook = excepthook
>>> t
this is a test
>>> t t
command: ['t', 't']
this is a test
ignoring ('t',)
>>> ^D

새로운 진술 추가에 대한 안내서를 찾았습니다.

https://troeger.eu/files/teaching/pythonvm08lab.pdf

기본적으로 새 진술을 추가하려면 편집해야합니다. Python/ast.c (무엇보다도) 파이썬 바이너리를 다시 컴파일하십시오.

가능하지만하지 마십시오. 기능과 클래스를 통해 거의 모든 것을 달성 할 수 있습니다 (사람들이 스크립트를 실행하기 위해 파이썬을 다시 컴파일해야합니다.)

가능 사용하여 이 작업을 수행하려면 EasyExtend:

EasyExtend(전자)의 전처리기 발전기와 메타 프로그래밍 레이로 순수한 Python 고 통합 CPython.메인 목적 EasyExtend 성 확장의 언어를 즉추가 사용자 정의 구문과 의미하는 파이썬.

통역사를 수정하지 않고는 아닙니다. 지난 몇 년 동안 많은 언어가 "확장 가능한"것으로 묘사되었지만 설명하는 방식은 아닙니다. 함수와 클래스를 추가하여 Python을 확장합니다.

Python이라는 언어가 있습니다 로 픽스 당신은 그런 일을 할 수 있습니다. 한동안 개발 중이 아니지만 요청한 기능은 일해 최신 버전으로.

데코레이터로 어떤 일을 할 수 있습니다. 예를 들어, 파이썬에는 없다고 가정합시다 with 성명. 그런 다음 이와 같은 비슷한 동작을 구현할 수 있습니다.

# ====== Implementation of "mywith" decorator ======

def mywith(stream):
    def decorator(function):
        try: function(stream)
        finally: stream.close()
    return decorator

# ====== Using the decorator ======

@mywith(open("test.py","r"))
def _(infile):
    for l in infile.readlines():
        print(">>", l.rstrip())

그러나 여기서는 꽤 부정한 해결책입니다. 특히 데코레이터가 기능을 호출하고 설정하는 동작 _ 에게 None 예상치 못한 일입니다. 설명을 위해 :이 데코레이터는 글쓰기와 같습니다

def _(infile): ...
_ = mywith(open(...))(_) # mywith returns None.

그리고 데코레이터는 일반적으로 기능을 실행하지 않고 수정해야합니다.

스크립트에서 여러 기능에 대한 작업 디렉토리를 일시적으로 설정 해야하는 스크립트에서 그러한 방법을 사용했습니다.

언어 구문에 새로운 진술을 정확히 추가하는 것은 아니지만 매크로는 강력한 도구입니다. https://github.com/lihaoyi/macropy

10 년 전 당신은 할 수 없었고, 그것이 바뀌 었다고 의심합니다. 그러나 파이썬을 다시 컴파일 할 준비가되어 있다면 구문을 수정하는 것은 그리 어렵지 않았으며, 그것이 바뀌 었다고 의심합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top