원패스 컴파일러
One-pass compiler![]() |
컴퓨터 프로그래밍에서 원패스 컴파일러는 각 컴파일 유닛의 부품을 한 번만 통과시켜 각 부품을 즉시 최종 머신 코드로 변환하는 컴파일러입니다.이는 프로그램을 소스 코드와 기계 코드 사이의 단계에서 하나 이상의 중간 표현으로 변환하고 각 시퀀셜 패스로 전체 컴파일 유닛을 재처리하는 멀티패스 컴파일러와는 대조적입니다.
이는 컴파일러의 논리적인 기능을 의미하며, 실제로 소스 파일을 한 번만 읽는 것을 의미하지 않습니다.예를 들어 소스 파일을 임시 저장소로 한 번 읽을 수 있지만 그 복사본은 여러 번 스캔될 수 있습니다.IBM 1130 Fortran 컴파일러는 소스를 메모리에 저장하고 많은 패스를 사용했습니다. 반면, 디스크 저장 장치가 없는 시스템에서는 어셈블러가 카드 리더/펀치에 소스 덱을 두 번 제시해야 했습니다.
특성.
![]() | 이 섹션은 확장해야 합니다.추가하시면 됩니다. (2018년 2월) |
원패스 컴파일러는 멀티패스 컴파일러보다 작고 빠릅니다.
원패스 컴파일러는 이용 가능한 정보의 범위가 한정되어 있기 때문에 멀티패스 컴파일러만큼 효율적인 프로그램을 생성할 수 없습니다.많은 효과적인 컴파일러 최적화에서는 기본 블록, 루프(특히 네스트 루프), 서브루틴 또는 모듈 전체를 여러 번 통과해야 합니다.프로그램 전체를 통과해야 하는 경우도 있습니다.일부 프로그래밍 언어는 설계상 한 번에 컴파일할 수 없습니다.예를 들어, PL/I는 데이터 선언을 프로그램 내 어디에나 배치할 수 있도록 합니다.특히 아직 선언되지 않은 항목에 대한 참조 후이므로 프로그램 전체가 스캔될 때까지 코드를 생성할 수 없습니다.언어 정의에는 컴파일할 소스 코드를 생성하는 pre-processor 문도 포함되어 있습니다.복수의 패스는 확실합니다.대조적으로 많은 프로그래밍 언어들은 원패스 컴파일러를 사용하여 컴파일되도록 특별히 설계되었으며 원패스 컴파일러를 가능하게 하는 특별한 구조를 포함하고 있습니다.
애로
기본적인 문제는 미래 참조에 관한 것이다.소스 파일의 특정 지점에서 기호의 올바른 해석은 소스 파일에서 더 멀리 있는 다른 기호의 유무에 따라 달라질 수 있으며, 이러한 기호가 발견될 때까지 현재 기호에 대한 올바른 코드를 생성할 수 없습니다.이것은 콘텍스트 의존성의 문제이며, 인접한 기호에서 임의의 양의 소스 텍스트까지 범위가 다양합니다.
로컬 컨텍스트
예를 들어 < 기호가 "보다 크다"가 아닌 "보다 작다" 비교 대상으로 인식된다고 가정합니다.문자 부호화 제한으로 인해 표준 부호화에서는 글리프 δ를 사용할 수 없을 수 있으므로 "<="라는 복합 표현이 허용된다.이 컨텍스트는 바로 다음 기호에 의해 결정되지만 "<"가 언제 발생하는지 알 수 없습니다.마찬가지로, "=" 기호가 복합 기호의 일부인 경우처럼 "="를 항상 의미하는 것은 아닙니다.특수 문자 "<"를 사용할 수 없는 경우 다른 복합 기호에는 ".lt"가 포함될 수 있습니다.그러나 문자 코드 "not"을 사용할 수 없는 또 다른 가능성은 "not"=" 또는 "not equal"에 대한 "<>"이다. 일부 시스템은 추가적인 변형으로 as에 대해 ~ 또는 !를 사용한다.한 가지 방법은 "<" 뒤에 스캔을 진행하고 "="와 마주쳤을 때 백트랙으로 스캔을 진행하는 것입니다.이는 물론 텍스트의 그 부분에 2개의 패스(pass)가 있음을 의미하며, 이는 피해야 합니다.이 경우 소스 파일은 카드 리더 등의 go-back-and-read 조작을 지원하지 않는 디바이스에서 가져올 수 있습니다.나중에 취소해야 할 수 있는 조기 결정을 내리는 대신, 사전 분석기는 양자 중첩의 개념처럼 다중 해석을 유지할 수 있으며, 나중에 결정 기호를 관찰할 때만 특정 선택으로 축소된다.특히, COBOL 컴파일러는 10진수 상수에 나타나는 완전중단과 문 끝에 나타나는 완전중단을 구별하기 위해 패스합니다.이러한 스킴은 싱글패스 컴파일러에서는 사용할 수 없습니다.
항목 이름에서도 비슷합니다.단일 문자 이름으로 제한되는 언어는 거의 없기 때문에 단일 문자 이름으로서의 문자 "x"는 "text"와 같은 이름의 문자 "x"와는 상당히 다릅니다. 이제 컨텍스트는 바로 인접한 문자 이상으로 확장됩니다.순차 소스 스트림의 항목을 언어의 토큰으로 분리하는 것은 어휘 분석기의 작업이다.단어뿐만 아니라 "<"와 "<="도 토큰이기 때문입니다.이름은 보통 문자로 시작하여 문자와 숫자로 이어지며 "_"와 같은 몇 개의 추가 기호도 있을 수 있습니다.숫자 지정에 허용되는 구문은 놀라울 정도로 복잡합니다. 예를 들어 +3.14159E+0이 유효할 수 있습니다.토큰 사이에 임의의 수의 공백 문자를 허용하는 것이 일반적이며, 포트란은 "GOTO"와 "GOTO"가 "< ="와 "< ="와 같도록 명백한 토큰 내에 공백을 허용(그리고 무시)하는 것이 일반적입니다.그러나 일부 시스템에서는 특정 토큰을 구분하기 위해 공백이 필요할 수 있으며, Python과 같은 다른 시스템에서는 선두 공백을 사용하여 프로그램 블록의 범위를 나타냅니다. 그렇지 않으면 시작...끝 또는 유사한 마커.
식 내의 컨텍스트
산술식을 사용할 수 있는 언어는 일반적으로 precedence 규칙이 있는 infix 표기 구문을 따릅니다.즉, 표현의 토큰이 소스 텍스트에서 파생되므로 표현식의 평가를 위한 코드 생성이 원활하게 진행되지 않습니다.예를 들어 x + y*(u - v) 식에서는 x가 y에 추가되지 않으므로 y를 더하십시오.스택 스킴을 산술에 사용하는 경우 코드는 로드 x로 시작할 수 있지만 다음 + 토큰에 대응하는 코드는 뒤따르지 않습니다.대신 (u - v)에 대한 코드가 생성되고 그 뒤에 y를 곱한 다음 x가 추가됩니다.산술식의 파서는 분석 중에 소스를 따라 앞뒤로 이동하지 않으며 우선 순위 규칙에 따라 실행되는 지연 연산의 로컬 스택을 사용합니다.이 춤은 산술식을 리버스 폴란드어 표기법 또는 이와 유사한 방법으로 표시하도록 요구함으로써 피할 수 있습니다. 위의 예에서는 u v - y * x +와 같이 엄격하게 왼쪽에서 오른쪽으로 스캔됩니다.
최적화 컴파일러는 산술식의 형태를 분석하여 반복을 식별하고 제거하거나 다른 잠재적 개선을 할 수 있다.고려하다
a*sin(x) + b*sin(x)
알골과 같은 몇몇 언어들은 산술 표현식 내에서 할당을 허용하기 때문에 프로그래머는 다음과 같은 것을 쓸 수 있었다.
a*(t:=sin(x) + b*t
그러나 그렇게 하기 위해 필요한 노력과는 별도로, 결과적인 진술의 형태는 혼란스럽고 더 이상 코드화된 수학 표현과 쉽게 비교되지 않을 것이다.실수하기 쉬워요.대신, 컴파일러는 (일반적으로 트리 구조를 사용하여) 표현식의 전체 형태를 나타내며, 그 구조를 분석 및 수정한 다음 개선된 형태를 위해 코드를 내보낼 수 있습니다.연속된 과제문 블록에 대한 확장이 있을 것입니다.이것은 원본 텍스트를 통과하는 두 번째 통과를 수반하지 않습니다.
중간 범위의 컨텍스트
어휘 분석기가 입력 스트림을 토큰의 스트림으로 분할했지만(그리고 주석도 폐기), 언어의 구문에 따른 이러한 토큰의 해석은 아직 상황에 따라 달라질 수 있다.fortran 의사 코드의 다음 문장을 고려합니다.
if (표현) = if (표현) label1, label2, label3 if (표현)일 경우
첫 번째는 "if"라고 불리는 1차원 배열의 요소에 산술식 값(등)을 할당하는 것입니다.Fortran은 예약된 단어를 포함하지 않는다는 점에서 특이합니다. 따라서 토큰 "write"가 반드시 write-statement가 진행 중임을 의미하지는 않습니다.다른 문장은 실제로 if-스테이트먼트입니다.-두 번째는 식 결과의 부호를 검사하고, 그 부호를 바탕으로 라벨 1, 2, 또는 3에 음수, 0 또는 양의 점프를 하는 산술문입니다.세 번째는 논리적인 if이며, 그 식 결과가 부울이어야 합니다.따라서 토큰의 올바른 해석은 "if표현식이 스캔될 때까지 어휘 분석기에서 나올 수 없으며 닫힘 괄호 뒤에 등호, 숫자(label1의 텍스트인 경우: fortran은 문자 스캔이 허용된 경우 쉼표 찾기에 의존해야 하는 것처럼 라벨로 정수만 사용) 또는 le로 시작하는 무언가가 나타납니다.tter(즉, "then"이어야 함)는 표현식이 임의적이기 때문에 컨텍스트는 임의의 양의 소스 텍스트에 걸쳐 있습니다.그러나 이 세 가지 경우 모두 컴파일러는 스캔이 진행됨에 따라 식을 평가하기 위한 코드를 생성할 수 있습니다.따라서 어휘 분석은 허용 구문의 변덕 때문에 방금 식별한 토큰의 의미를 항상 결정할 수 없기 때문에 역추적이 회피되는 경우 구문 분석은 가능한 상태의 중첩을 유지해야 합니다.
중첩된 상태의 안개 속에서 구문 분석이 표류하고 있는 경우 오류가 발생했을 경우(즉, 유효한 구문 프레임에 넣을 수 없는 토큰이 발견되었을 경우) 유용한 메시지를 생성하기가 어려울 수 있습니다.예를 들어 B6700 Algol 컴파일러는 "semicolon expected"와 같은 오류 메시지와 함께 소스 라인의 목록 및 문제의 위치를 나타내는 마커로 악명이 높았습니다(종종 세미콜론으로 표시됨).세미콜론이 없는 경우, 실제로 표시된 대로 배치된 경우, 재컴파일 시 세미콜론에 대한 "예상치 않은 세미콜론" 메시지가 나타납니다.후속 메시지가 잘못되었기 때문에 컴파일러의 첫 번째 오류 메시지만 주의할 필요가 있는 경우가 많습니다.현재 해석을 취소하고 다음 문을 시작할 때 검색을 재개하는 것은 소스 파일에 오류가 있을 때 어렵기 때문에 이후의 메시지는 도움이 되지 않습니다.더 이상의 코드 생성은 물론 포기됩니다.
예를 들어 "if", "then" 및 "else"는 항상 if-statement의 일부이며 변수의 이름이 될 수 없지만 의외로 많은 유용한 단어를 사용할 수 없게 될 수 있습니다.또 다른 접근법은 "스트립"으로, 예약된 단어들을 마침표 또는 알골의 일부 버전에서와 같은 특수 문자 사이에 배치함으로써 마크오프합니다.즉,'if'
그리고.if
다른 토큰입니다.후자는 일반적인 이름이지만, 이러한 모든 아포스트로피를 제공하는 것은 곧 귀찮아집니다.많은 언어에서 공백은 충분한 정보를 제공하지만 이는 복잡할 수 있습니다.일반적으로 토큰의 텍스트를 끝내는 것은 공백(또는 탭 등)이 아니라 문자 또는 숫자 이외의 문자입니다.위의 예에서 if-statement 표현은 괄호 안에 있어야 합니다.그러면 (""가 확실히 "if"의 식별을 종료하고 ""도 마찬가지로 "then"을 식별할 수 있습니다.복잡한 if-statement의 다른 부분은 "else"와 "end if"(또는 "endif") 및 "el if"라는 새로운 행에 표시되어야 합니다.반면 Algol 등의 경우 괄호는 필요하지 않으며 if 스테이트먼트의 모든 부분을 한 줄에 표시할 수 있습니다.파스칼에서는 a나 b 등입니다.는 유효하지만 a와 b가 식일 경우 괄호로 묶어야 합니다.
컴파일러에 의해 작성된 소스 파일 목록은 밑줄이나 굵은 글씨 또는 이탤릭체로 표시된 예약된 단어를 통해 읽기 쉽게 만들 수 있지만, "Algol은 이탤릭체와 일반 풀스톱을 구별하는 유일한 언어입니다."라는 비판이 제기되어 왔습니다.사실 이건 장난이 아니야.포트란에서는 다음과 같은 do-statement의 시작DO 12 I = 1,15
와 구별된다.DO 12 I = 1.15
(변수 1.15의 값 할당:DO12I
공백은 관계없음을 기억하십시오.) 쉼표와 마침표 사이의 차이만으로 인쇄된 목록의 글리프가 제대로 형성되지 않을 수 있습니다.
언어 설계에 세심한 주의를 기울이면 동작을 쉽게 이해할 수 있는 신뢰할 수 있는 컴파일러를 만들기 위해 표현의 명확성과 단순성을 향상시킬 수 있습니다.하지만 좋지 않은 선택은 흔하다.예를 들어, Matlab은 A'에서와 같이 예외적으로 수학적인 용법을 밀접하게 따르는 아포스트로피를 사용하여 행렬 전이를 나타낸다.좋습니다만, 텍스트 문자열의 구분자에서는 Matlab은 어떤 목적으로든 큰따옴표 기호가 나타내는 기회를 무시하고 이 경우에도 아포스트로피를 사용합니다.Octab은 텍스트 문자열에 큰 따옴표를 사용하지만 Matlab 문도 받아들이려고 하기 때문에 문제가 다른 시스템으로 확대됩니다.
프리프로세서 확장
프리프로세서 옵션이 동작하는 것은 이 단계에서 컴파일러가 착신 소스를 적절히 처리하기 전에 동작하기 때문입니다.이들은 어셈블러 시스템의 "매크로 확장" 옵션을 에코하며, 보다 우아한 구문을 사용합니다.가장 일반적인 배열은 에 대한 변형입니다.
한다면 조건. 그리고나서 본원 또 다른 다른 소스 fi
pl/i의 % 기호 또는 # 등으로 시작하는 문장과 같이 사전 사전 출처 문장과 "구체적인" 출처 문구를 구별하기 위한 몇 가지 약정을 사용하는 경우가 많다.또 다른 간단한 옵션은 다음과 같은 변형입니다.
이 =를 다음과 같이 정의합니다.
그러나 다음과 같이 주의해야 합니다.
SumXY = (x + y) 합 정의:=3* SumXY;
괄호가 없으면 결과는 다음과 같이 됩니다.=3*x + y; 마찬가지로 대체 텍스트의 경계와 결과 텍스트의 스캔 방법을 결정할 때 주의가 필요합니다.고려하다
#3점=3점;#1점=1점;x:=3점;
여기서 정의문은 세미콜론으로 끝나며 세미콜론 자체는 치환의 일부가 아닙니다.호출할 수 없습니다.x:=threepointone;
다른 이름이긴 하지만three point one
되지요3 . 1
후속 검색에서는 단일 토큰으로 간주할 수도 있고 그렇지 않을 수도 있습니다.
일부 시스템에서는 소스 텍스트 출력의 프리프로세서 프로시저를 컴파일 할 수 있습니다.또, 이러한 소스에서는, 한층 더 프리프로세서 항목을 정의할 수도 있습니다.이러한 옵션을 능숙하게 사용하면 실제 절차를 고안하는 대신 상수에 설명 이름을 부여하고, 알기 쉬운 기억법, 새로운 스테이트먼트 폼의 등장 및 일반적인 프로시저의 특정 용도(예: 정렬)에 대한 인라인 코드 생성을 할 수 있다.파라미터와 파라미터 타입이 증가함에 따라 필요한 조합의 수는 기하급수적으로 증가합니다.
또한, 사람의 이름, 닉네임, 애완견 이름 등을 사용하여 스토리 템플릿에서 스토리를 생성하는 것과 같은 다른 언어에도 동일한 프리프로세서 구문을 사용할 수 있으며, 소스 파일을 받아들여 프리프로세서 동작을 수행하는 프리프로세서 프로그램을 고안하는 것이 유혹이다.ns 및 다음 단계인 컴파일을 위해 준비된 결과를 출력합니다.단, 이는 소스를 통과하는 적어도1개의 추가 패스를 구성하기 때문에 싱글 패스 컴파일러에서는 이러한 솔루션을 사용할 수 없습니다.따라서 실제 입력 소스 파일을 통한 진행은 쉽게 진행되지만 여전히 단방향입니다.
롱 레인지 여부
컴파일러에 의한 코드 생성은 또한 Go to label과 같은 전송 참조의 문제에 직면하게 되는데, Go to label과 같이 가장 직접적으로 행선지 라벨이 소스 파일에서 알 수 없는 거리에 있는 경우, 따라서 해당 라벨의 위치에 도달하기 위한 점프 명령은 아직 생성되지 않은 코드 간의 알 수 없는 거리를 포함한다.GOTOs consible that harious(해로 간주되는 GOTOs)의 영향을 받은 일부 언어 설계에는 GOTO 문이 없지만, 프로그램에는 많은 암묵적인 GOTO 등가물이 존재하기 때문에 이 문제를 회피할 수 없습니다.고려하다
한다면 조건. 그리고나서 코드 True 또 다른 부호가 틀리다 fi
앞서 말한 바와 같이 상태를 평가하는 코드를 바로 생성할 수 있습니다.단, 토큰이 발견되면 JumpFalse 연산 코드를 배치해야 합니다.이 연산 코드는 행선지 주소가 false 스테이트먼트 코드 시작이며, 마찬가지로 다른 토큰이 발견되면 tha 코드인 GOTO 스타일의 점프 연산 뒤에 행선지가 GOTO 형식의 점프 동작이 이어져야 합니다.fi 토큰으로 표시된 if 스테이트먼트의 말미에 이어집니다.이러한 수신처는, 아직 스캔 되지 않은 소스에 대해서 임의의 양의 코드가 생성된 후에만 알 수 있습니다.케이스 스테이트먼트와 같이 임의의 양의 소스를 망라하는 부품이 있는 스테이트먼트에서도 같은 문제가 발생합니다.
recursive-descent 컴파일러는 if-statement와 같은 각 유형의 스테이트먼트에 대한 프로시저를 활성화하고, 그 스테이트먼트의 true 및 false 부분에 대한 코드를 생성하기 위한 적절한 프로시저를 차례로 호출하며, 그 구문에 따라 다른 스테이트먼트에 대해서도 유사합니다.로컬 스토리지에서는 불완전한 JumpFalse 동작의 주소 필드의 위치를 추적하고, 그 토큰을 발견하면 현재 알려진 주소가 배치되며, 마찬가지로 코드 True 코드 뒤에 필요한 점프용 fi 토큰을 발견했을 때 배치됩니다.GoTo 문은 점프할 코드가 스테이트먼트 양식 내에 없기 때문에 최종적으로 라벨이 발견되었을 때 사용되는 "fixups" 보조 테이블의 엔트리가 필요하다는 점에서 다릅니다.이 개념은 확장될 수 있습니다.모든 미지의 행선지 점프는 점프 테이블의 엔트리(행선지가 발견되면 나중에 주소가 입력됨)를 통해 실행할 수 있지만, 이 테이블의 필요한 크기는 컴파일이 끝날 때까지 알 수 없습니다.
이것에 대한 해결책 중 하나는 컴파일러가 어셈블러 소스(컴파일러가 생성한 라벨을 점프 대상으로 사용)를 내보내는 것이며 어셈블러는 실제 주소를 결정합니다.그러나 여기에는 소스 파일의 추가 패스스루(버전)가 필요합니다.따라서 싱글패스 컴파일러에서는 허용되지 않습니다.
불행한 결정
위의 설명은 나중에 수정될 특정 필드를 남겨두고 코드가 생성될 수 있다는 개념을 사용했지만, 이러한 코드 시퀀스의 크기가 안정적이라는 암묵적인 가정이 있었다.그렇지 않을 수도 있습니다.많은 컴퓨터에는 다양한 양의 스토리지를 점유하는 조작이 준비되어 있습니다.특히, 상대 어드레싱은, 행선지가 -128 또는 +127 의 어드레싱 스텝내에 있는 경우는, 8 비트의 주소 필드를 사용할 수 있습니다.그렇지 않은 경우는, 보다 큰 주소 필드가 필요합니다.따라서 코드가 희망적인 짧은 주소 필드로 생성된 경우 나중에 더 긴 필드를 사용하도록 코드를 조정해야 할 수 있습니다. 따라서 변경 후 이전 코드를 참조하는 위치도 조정해야 합니다.마찬가지로 나중에 변경 내용을 역방향으로 바꾸는 참조는 기존 주소에 있었던 참조도 수정해야 합니다.또한 수정 정보 자체를 올바르게 수정해야 합니다.한편, 근접성이 확실하지 않은 경우 모든 경우에 긴 주소를 사용할 수 있지만, 결과적으로 발생하는 코드가 더 이상 이상 이상 이상적이지 않습니다.
원패스 시퀀셜 입력, 불규칙한 시퀀스 출력
이미 언급한 바와 같이 단일 문장에서 최적화할 수 있는 몇 가지 가능성이 있습니다.여러 명세서에 걸쳐 최적화를 위해서는 그러한 명세서의 내용이 코드가 방출되기 전에 분석되고 조작될 수 있는 일종의 데이터 구조로 유지되어야 한다.이 경우 수정이 허용된 경우에도 임시 코드를 생성하는 것이 방해가 됩니다.제한에서 이는 컴파일러가 내부 형식으로 전체 프로그램을 나타내는 데이터 구조를 생성한다는 것을 의미하지만, 스트로가 잡힐 수 있으며 소스 파일의 실제 두 번째 패스는 처음부터 끝까지 존재하지 않는다는 주장이 제기되었습니다.컴파일러를 광고하는 홍보 문서에 있을 수 있습니다.
따라서 컴파일러는 소스의 각 부분이 읽히는 즉시 코드를 가차없이 단일 시퀀스로 생성할 수 없습니다.출력은 계속 순차적으로 쓸 수 있지만 섹션의 출력이 해당 섹션의 보류 중인 수정이 모두 완료될 때까지 지연되는 경우에만 가능합니다.
사용 전 신고
다양한 식에 대한 코드를 생성할 때 컴파일러는 피연산자의 특성을 알아야 합니다.예를 들어 A:=B; A와 B가 정수인지 부동소수점 변수인지(및 단일, 이중 또는 4배 정밀도인지) 또는 복소수, 배열, 문자열, 프로그래머 정의 유형 등에 따라 다소 다른 코드를 생성할 수 있다.이 경우, 간단한 방법은 적절한 수의 저장 단어를 전송하는 것이지만, 문자열의 경우 수신자가 공급업체보다 작을 수 있고 어떤 경우든 문자열의 일부만 사용할 수 있기 때문에 적합하지 않을 수 있습니다. 아마도 1,000자를 위한 공간이 있지만 현재 10자를 포함합니다.그리고 COBOL과 pl/i에 의해 제공되는 다음과 같은 보다 복잡한 구조가 있습니다.A:=B by name;
이 경우, A와 B는 집합체(또는 구조체)이며, A는 예를 들어 부품을 가지고 있다.A.x
,A.y
그리고.A.other
B가 부품을 가지고 있는 동안B.y
,B.c
그리고.B.x
, 그리고 그 순서로."이름별" 기능은 다음과 같은 기능을 의미합니다.A.y:=B.y; A.x:=B.x;
근데 왜냐하면B.c
A에는 대응책이 없습니다.A.other
는 B에 대응하지 않기 때문에 관여하지 않습니다.
이 모든 것은 항목을 사용하기 전에 선언해야 하는 요건에 의해 처리될 수 있습니다.일부 언어에서는 명시적인 선언이 필요하지 않으며, 새 이름을 처음 접했을 때 암묵적인 선언을 생성합니다.fortran 컴파일러가 I, J, ..., N 중 하나의 문자를 가진 이전에 알려지지 않은 이름을 발견하면 변수는 정수이며, 그렇지 않으면 부동소수점 변수가 됩니다.그래서 이름이DO12I
부동소수점 변수가 됩니다.이것은 편리하지만 이름을 잘못 입력한 경험이 몇 번 있으면 대부분의 프로그래머들은 컴파일러 옵션 "암묵적 없음"을 사용해야 한다는 데 동의합니다.
다른 시스템에서는 첫 번째 만남의 특성을 사용하여 문자열이나 배열 등의 유형을 결정합니다.통역 언어는 특히 유연하며, 다음과 같이 런타임에 결정을 내릴 수 있습니다.
조건일 경우 pi:="3.14" 또는 pi:=3.14 fi, pi 인쇄;
이러한 언어를 위한 컴파일러가 있는 경우, 변수 pi를 나타내는 복잡한 엔티티를 생성해야 하며, 이 엔티티는 현재 타입이 무엇인지에 대한 표시와 그러한 타입을 나타내기 위한 관련 스토리지를 포함해야 한다.이것은 확실히 유연하지만 A.x = b를 풀 때처럼 집중적인 계산에 도움이 되지 않을 수 있다. 여기서 A는 100의 행렬이며, 갑자기 A의 요소 중 하나가 다른 유형의 요소가 될 수 있다.
절차 및 기능
사용 전 선언은 절차 및 기능에 대해 충족하기 쉬운 요건이며, 이는 절차 내 절차의 중첩에도 적용됩니다.ALGOL, Pascal, PL/I 및 기타 많은 기능과 마찬가지로, MATLAB 및 (1995년 이후) Fortran은 함수(또는 프로시저)가 다른 함수(또는 프로시저)의 정의를 포함할 수 있도록 허용하며, 포함 함수 내에서만 볼 수 있지만, 이러한 시스템은 포함 프로시저의 종료 후에 정의되어야 합니다.
그러나 재귀가 허용되면 문제가 발생합니다.각각 다른 프로시저를 호출하는 두 가지 프로시저는 사용 전에 둘 다 선언할 수 없습니다.소스 파일의 선두에 1개가 있어야 합니다.불분명한 변수와의 조우처럼 컴파일러가 알 수 없는 프로시저의 호출에 적합한 코드를 생성할 수 있다는 것을 충분히 추론할 수 있다면, 물론 프로시저가 정의되었을 때 "fixup" 장치가 돌아와 수신처의 올바른 주소를 입력할 수 있습니다.n이 검출되었습니다.예를 들어 파라미터가 없는 프로시저의 경우입니다.함수 호출에서 반환되는 결과는 호출에서 식별 가능한 유형일 수 있지만 항상 올바른 것은 아닙니다. 함수는 부동소수점 결과를 반환하지만 그 값을 정수에 할당할 수 있습니다.
Pascal은 "사전 정리"를 요구함으로써 이 문제를 해결합니다.프로시저 또는 함수 선언 중 하나를 먼저 지정해야 하는데 프로시저 또는 함수 본문 대신 키워드 forward가 지정됩니다.그런 다음 다른 절차 또는 기능을 선언하고 본문을 정의할 수 있습니다.어느 시점에서 "전진" 절차 또는 기능이 기능의 본문과 함께 다시 선언됩니다.
파라미터에 의한 프로시저(또는 함수)의 호출의 경우, 그 타입은 알 수 있지만(사용전에 선언됩니다), 프로시저 호출에서의 사용법은 알 수 없습니다.예를 들어 Fortran은 모든 파라미터를 참조(즉, 주소별)로 전달하기 때문에 코드를 생성하는 데 즉각적인 어려움이 없지만(항상 실제 주소를 나중에 수정하는 경우), Pascal과 다른 언어들은 프로그래머의 선택에 따라(기준, 값, 또는 심지어 "n"에 의해 매개 변수를 전달할 수 있습니다.ame") 및 이는 절차 정의에서만 나타나며, 정의에 도달하기 전에는 알려지지 않았습니다.특히 Pascal의 경우 파라미터 지정에서 접두사 "Var"는 참조로 수신해야 함을 나타내며, 그 부재는 값으로 나타냅니다.첫 번째 경우 컴파일러는 파라미터의 주소를 통과하는 코드를 생성해야 하며, 두 번째 경우 값의 복사본을 통과하는 다른 코드를 생성해야 합니다(일반적으로 스택을 통해).항상 그렇듯이, 이 문제에 대처하기 위해서 「수정」메커니즘이 발동될 수 있지만, 매우 혼란스러울 것입니다.물론 멀티패스 컴파일러는 왕복하면서 필요한 모든 정보를 수집할 수 있지만 싱글패스 컴파일러는 그렇지 않습니다.필요한 엔티티에 도달할 때까지 스캔이 진행되는 동안(및 그 결과는 내부 스토리지에 보관) 코드 생성을 일시 중지할 수 있습니다. 코드 생성 단계가 곧 따라잡기 때문에 소스 통과를 두 번째 경로로 간주하지 않을 수 있습니다. 코드 생성은 잠시 중단되었을 뿐입니다.하지만 이것은 복잡할 것이다.대신 특별한 구조가 도입되어 파라미터 사용의 프로시저의 정의가 나중에 완전한 정의의 "전송"으로 선언되어 컴파일러가 필요에 따라 사용하기 전에 알 수 있게 됩니다.
First Fortran(1957년) 이후 프로그램 부분의 개별 컴파일이 가능해져 절차 및 기능의 라이브러리 작성을 지원하게 되었습니다.이러한 외부 컬렉션에서 함수를 호출하는 소스 파일 내의 프로시저는 결과를 찾기 위해 올바른 위치를 찾는 코드를 생성하는 경우에만 알 수 없는 함수에 의해 반환되는 결과의 유형을 알아야 합니다.원래 정수 및 부동소수점 변수만 존재했을 경우 암묵적 선언의 규칙에 따라 선택할 수 있었지만 크기와 유형이 증가함에 따라 호출 프로시저는 함수에 대한 유형 선언이 필요합니다.이는 절차 내에서 선언된 변수와 형식이 동일하기 때문에 특별하지 않습니다.
충족해야 할 요건은 싱글패스 컴파일의 현재 시점에서 엔티티에 대한 정보가 필요하며, 이를 위한 올바른 코드를 지금(나중에 주소 수정으로) 생성할 수 있도록 해야 한다는 것입니다.필요한 정보가 나중에 소스 파일에서 발견되든, 별도로 컴파일된 코드 파일에서 발견되든, 이 정보는 여기에서 일부 프로토콜에 의해 제공됩니다.
프로시저(또는 기능)의 모든 호출이 서로 호환성이 있는지 확인하고 그 정의를 확인할지는 별개의 문제입니다.Algol과 같은 영감에서 유래한 언어에서는 보통 이 체크는 엄격하지만 다른 시스템은 무관심할 수 있습니다.프로시저가 옵션 파라미터를 가질 수 있는 시스템은 제쳐두고 파라미터의 수와 타입이 틀리면 보통 프로그램이 크래시됩니다.나중에 함께 "연결"되는 전체 프로그램의 일부를 개별적으로 컴파일할 수 있는 시스템은 실수가 발생하기 더 쉽지만 종종 그렇지 않기 때문에 정확한 유형 및 매개변수 및 결과의 수를 확인해야 한다.일부 언어(Algol 등)는 "업그레이드" 또는 "와이드" 또는 "프로모션"의 공식 개념을 가지고 있으며, 여기서 이중 정밀도 매개변수를 단일 정밀도 변수로 호출할 수 있으며, 이 경우 컴파일러는 단일 정밀도 변수를 임시 이중 정밀도 변수로 저장하는 코드를 생성합니다.hich가 실제 파라미터가 됩니다.그러나 이 경우 파라미터 전달 메커니즘이 카피인, 카피아웃으로 변경되어 동작에 미묘한 차이가 발생할 수 있습니다.절차가 이중 정밀도 매개변수 또는 기타 크기 변동을 예상할 때 단일 정밀도 변수의 주소를 수신했을 때의 결과는 훨씬 덜 미묘하다.절차 내에서 파라미터 값을 읽으면 지정된 파라미터 값보다 더 많은 저장공간이 읽히고 결과 값이 개선되지 않을 수 있습니다.훨씬 더 나쁜 것은 절차에서 매개 변수의 값을 변경하는 경우입니다. 무언가가 손상되는 것이 확실합니다.이러한 실수를 발견하고 바로잡는 데는 많은 인내심이 필요할 수 있다.
파스칼의 예
그러한 구성의 예로는 파스칼의 전진 선언이 있습니다.Pascal은 사용하기 전에 절차를 선언하거나 완전히 정의해야 합니다.이것은 원패스 컴파일러의 타입 체크에 도움이 됩니다.어디에도 선언되지 않은 프로시저를 호출하는 것은 명백한 오류입니다.전송 선언은 사용 전 선언 규칙에도 불구하고 상호 재귀적 절차가 서로 직접 호출하는 데 도움이 됩니다.
기능. 이상한(n : 정수) : 부울; 시작한다. 한다면 n = 0 그리고나서 이상한 := 거짓의 또 다른 한다면 n < > 0 그리고나서 이상한 := 심지어.(n + 1) {컴파일러 오류: '짝수'가 정의되지 않았습니다.} 또 다른 이상한 := 심지어.(n - 1) 끝.; 기능. 심지어.(n : 정수) : 부울; 시작한다. 한다면 n = 0 그리고나서 심지어. := 진실의 또 다른 한다면 n < > 0 그리고나서 심지어. := 이상한(n + 1) 또 다른 심지어. := 이상한(n - 1) 끝.;
함수에 대한 순방향 선언을 추가함으로써even
행사 전에odd
원패스 컴파일러는 다음과 같은 정의가 있다고 합니다.even
프로그램 후반부에.
기능. 심지어.(n : 정수) : 부울; 앞으로; 기능. 이상한(n : 정수) : 부울; { et cetera }
함수 본문의 실제 선언이 이루어지면 파라미터가 생략되거나 원래의 순방향 선언과 완전히 동일해야 합니다.그렇지 않으면 에러가 플래그가 지정됩니다.
프리프로세서 재귀
복잡한 데이터 집약을 선언할 때 홀수 및 짝수 함수가 사용될 수 있습니다.데이터 집약 X의 스토리지 사이즈가 홀수인 경우, 홀수(Byte Size(X))의 테스트 제어 하에 1바이트 항목이 추가되어 짝수가 될 수 있습니다.위와 같이 홀수 선언과 짝수 선언이 동등한 경우 파라미터의 사용이 프리프로세서에 알려져 있기 때문에 "전송" 선언은 필요하지 않을 수 있습니다.이는 참조별 또는 값별 중 하나를 선택할 기회를 제공할 가능성이 낮기 때문입니다.그러나 호출의 결과를 알 필요가 있기 때문에 실제 정의가 끝날 때까지 소스 코드(정의 외)에 이러한 함수의 호출은 없을 수 있습니다.물론 프리프로세서가 소스 파일의 여러 패스에 관여하지 않는 한.
유해하다고 간주되는 전방 선언
대규모 프로그램에서 절차의 선언과 사용 및 루틴 라이브러리의 사용, 특히 변경이 진행 중인 프로그램 간에 일관성을 유지하려고 시도한 사람은 호출되었지만 현재 편집에서 정의되지 않은 절차에 대한 순방향 또는 유사한 추가 선언의 사용에 대해 어려움을 겪었을 것이다.특히 서로 다른 소스 파일에 걸쳐 광범위하게 분리된 위치 간의 동기화를 유지하려면 주의가 필요합니다.예약어를 사용한 선언은 쉽게 찾을 수 있지만, 도움이 되는 선언이 일반 선언과 구별되지 않으면 작업이 번거로워집니다.단순히 원패스 컴파일의 목표를 포기하는 것이 이러한 부과를 제거할 경우, 보다 빠른 컴파일의 이득은 불충분해 보일 수 있다.