프로그래밍 언어는 어떻게 함수 / 방법을 정의하고 저장합니까? Ruby로 해석 된 프로그래밍 언어를 만들고 함수 선언을 구현하는 방법을 알아 내려고 노력 중입니다.
첫 번째 아이디어는 선언의 내용을지도에 저장하는 것입니다. 예를 들어
def a() {
callSomething();
x += 5;
}
그런 다음 맵에 항목을 추가합니다.
{
'a' => 'callSomething(); x += 5;'
}
이것에 대한 문제 parse
는 문자열에서 내 메소드 를 호출해야하기 때문에 재귀 적으로 parse
발생 doSomething
한다는 것입니다.
그렇다면 해석 된 언어는 어떻게 이것을 처리합니까?
답변
“구문 분석”기능이 코드를 구문 분석 할뿐만 아니라 동시에 실행한다고 가정하면 정확합니까? 그렇게하려면 맵에 함수의 내용을 저장하는 대신 함수의 위치 를 저장하십시오 .
그러나 더 좋은 방법이 있습니다. 약간의 노력이 선행되지만 복잡성이 증가함에 따라 훨씬 더 나은 결과를 얻을 수 있습니다. 추상 구문 트리를 사용하십시오.
기본 아이디어는 코드를 한 번만 구문 분석하는 것입니다. 그런 다음 연산과 값을 나타내는 데이터 형식 집합이 있으며 다음과 같이 트리를 만듭니다.
def a() {
callSomething();
x += 5;
}
된다 :
Function Definition: [
Name: a
ParamList: []
Code:[
Call Operation: [
Routine: callSomething
ParamList: []
]
Increment Operation: [
Operand: x
Value: 5
]
]
]
(이것은 가상 AST의 구조를 텍스트로 표현한 것입니다. 실제 트리는 텍스트 형식이 아닐 것입니다.) 어쨌든 코드를 AST로 파싱 한 다음 AST를 통해 통역사를 직접 실행해야합니다. 또는 두 번째 ( “코드 생성”) 패스를 사용하여 AST를 일부 출력 형식으로 전환하십시오.
언어의 경우, 아마도 함수 이름 대신 함수 문자열에 함수 이름을 함수 AST에 매핑하는 맵이있을 것입니다.
답변
당신은 볼 때 구문 분석을 호출해서는 안됩니다 callSomething()
(나는 당신이 callSomething
아닌 의미한다고 가정합니다 doSomething
). 차이 a
및 callSomething
다른 메소드 호출 중에 하나 인 방법이 정의된다.
새 정의가 표시되면 해당 정의를 추가 할 수 있는지 확인하는 것과 관련된 검사를 수행해야합니다.
- 동일한 서명으로 기능이 존재하지 않는지 확인
- 메소드 선언이 올바른 범위에서 수행되고 있는지 확인하십시오 (즉, 메소드를 다른 메소드 선언 내에 선언 할 수 있습니까?).
이러한 검사가 통과되었다고 가정하면이를지도에 추가하고 해당 방법의 내용을 확인할 수 있습니다.
와 같은 메소드 호출을 찾으면 callSomething()
다음 점검을 수행해야합니다.
callSomething
지도에 존재 합니까 ?- 제대로 호출 되었습니까 (발견 된 서명과 일치하는 인수 수)?
- 인수가 유효합니까 (변수 이름을 사용하는 경우 선언 되었습니까?이 범위에서 액세스 할 수 있습니까?)?
- 어디에서 callSomething을 호출 할 수 있습니까 (비공개, 공개, 보호)?
만약 당신 callSomething()
이 괜찮다면,이 시점에서 당신이하고 싶은 것은 실제로 당신이 그것에 접근하고자하는 방법에 달려 있습니다. 엄밀히 말하면,이 시점에서 그러한 호출이 괜찮다는 것을 알고 나면 추가 세부 사항으로 가지 않고 메소드 이름과 인수 만 저장할 수 있습니다. 프로그램을 실행할 때 런타임에 있어야하는 인수를 사용하여 메소드를 호출합니다.
더 나아가고 싶다면 문자열뿐만 아니라 실제 방법에 대한 링크를 저장할 수 있습니다. 이것은 더 효율적이지만 메모리를 관리해야하는 경우 혼동 될 수 있습니다. 처음에는 끈을 붙잡고있는 것이 좋습니다. 나중에 최적화를 시도 할 수 있습니다.
참고이하는 모든 당신이 당신의 프로그램에 모든 토큰을 인식하고 그들이 알고있는 수단 프로그램, lexxed 한 가정입니다 됩니다 . 그것들이 아직 서로 이해가되는지 아는 것은 아닙니다. 파싱 단계입니다. 아직 토큰이 무엇인지 모르는 경우 먼저 해당 정보를 얻는 데 중점을 두십시오.
도움이 되길 바랍니다. 프로그래머 SE에 오신 것을 환영합니다!
답변
귀하의 게시물을 읽으면서 귀하의 질문에 두 가지 질문이 있음을 발견했습니다. 가장 중요한 것은 파싱하는 방법입니다. (명시 적 또는 암시 적) 문법으로 텍스트 프로그램을 “재귀 적으로”순회하기 위해 사용할 수 있는 많은 종류의 파서 (예 : 재귀 하강 파서 , LR 파서 , Packrat 파서 ) 및 파서 생성기 (예 : GNU bison , ANTLR )가 있습니다.
두 번째 질문은 함수의 저장 형식에 관한 것입니다. 구문 지향 변환을 수행하지 않을 때는 프로그램의 중간 표현을 작성하십시오.이 구문은 추가 구문 처리 (컴파일, 변환, 실행, 쓰기)를 위해 추상 구문 트리 또는 일부 사용자 정의 중간 언어 일 수 있습니다 . 파일 등).
답변
일반적인 관점에서 함수의 정의는 코드에서 레이블 또는 책갈피에 지나지 않습니다. 대부분의 다른 루프, 범위 및 조건 연산자는 비슷합니다. 하위 레벨의 추상화에서 기본 “점프”또는 “고토”명령을 나타냅니다. 함수 호출은 기본적으로 다음과 같은 저수준 컴퓨터 명령으로 요약됩니다.
- 모든 매개 변수의 데이터와 현재 함수의 다음 명령어에 대한 포인터를 “호출 스택 프레임”으로 알려진 구조로 연결합니다.
- 이 프레임을 호출 스택으로 밀어 넣습니다.
- 함수 코드의 첫 번째 줄의 메모리 오프셋으로 이동하십시오.
“반환”문구 또는 이와 유사한 문구는 다음을 수행합니다.
- 레지스터로 반환 할 값을로드하십시오.
- 호출자에 대한 포인터를 레지스터에로드하십시오.
- 현재 스택 프레임을 팝합니다.
- 발신자의 포인터로 이동합니다.
따라서 함수는 더 높은 수준의 언어 사양에서 추상화 된 것이므로 사람이 코드를보다 관리하기 쉽고 직관적으로 구성 할 수 있습니다. 어셈블리 또는 중간 언어 (JIL, MSIL, ILX)로 컴파일되고 기계 코드로 렌더링 될 때 거의 모든 추상화가 사라집니다.