⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 prsnt.asm

📁 Dos6.0
💻 ASM
📖 第 1 页 / 共 2 页
字号:
	TITLE	PRSNT - Parser NonTerminal Functions

;==========================================================================
;
;  Module:  prsnt.asm - Parser NonTerminal Functions
;  Subsystem:  Parser
;  System:  Quick BASIC Interpreter
;  Copyright <C> 1985, Microsoft Corporation
;
;	RULES FOR WRITING NON-TERMINAL FUNCTIONS:
;	----- --- ------- --- -------- ---------
;
;	A 'TERMINAL' is a syntactic item which can be described by
;	   1 token.  Examples include reserved words, constants, variables.
;	A 'NON-TERMINAL' is a complicated syntactic item which is
;	   described by a sequence of TERMINALS and other NON-TERMINALS.
;	   Examples include statement (NtStatement), expressions (NtExp) etc.
;
;	Whenever possible, we try to parse non-terminals with the function
;	Parse(), which uses state-table information in prsstate.asm.
;	Non-Terminal parsing functions are used instead of state-table
;	driven non-terminal entries (in bnf.prs) for the following reasons:
;	-	When we need to look ahead (i.e. PeekNextTok()) to decide
;		which of several similar non-terminals the token stream is.
;	-	When we need to generate complicated pcode.
;	-	When we want to setup state for the code generator which
;		will eventually be called for this statement/intrinsic
;		(See NtACTIONxxx() for an example of this).
;
;	The functions in this module (and in module PrsExp.asm) are responsible
;	for parsing NON-TERMINALs which could not be parsed by Parse().
;	These non-terminal parsing functions must return 1 of 3 values:
;
;	-	PR_NotFound, meaning the non-terminal was not recognized, but
;		no tokens were consumed and no pcode was emitted.  This
;		allows the caller (which is almost always Parse()) to
;		try alternate non-terminals before generating a syntax error.
;
;	-	PR_GoodSyntax, meaning the non-terminal was successfully
;		recognized, tokens were consumed, and pcode was emitted.
;
;	-	PR_BadSyntax, meaning the non-terminal was not recognized, and
;		tokens were consumed and/or pcode was emitted.  An error
;		message must also be generated, like ("Expected <non-terminal>")
;		by calling PErrExpMsg() or similar routines.  This will
;		cause the caller to return all the way up to ParseLine()
;		with the generated syntax error.
;
;=========================================================================

	include version.inc
	PRSNT_ASM = ON
	includeOnce architec
	includeOnce context
	includeOnce opstmt
	includeOnce opid
	includeOnce parser
	includeOnce prsirw
	includeOnce prstab
	includeOnce psint
	includeOnce qbimsgs
	includeOnce rtinterp
	includeOnce rtps

	assumes	CS,CP
	assumes	DS,DGROUP
	assumes	ES,DGROUP
	assumes	SS,DGROUP

sBegin	DATA

	EXTRN	b$ErrInfo:byte		;extended error code

	PUBLIC	pStateCur, pStateLastScan, pStateLastGood
pStateCur	DW 0		;see comments below in NtParse
pStateLastScan	DW 0		;see comments below in NtParse
pStateLastGood	DW 0		;see comments below in NtParse

	PUBLIC	pCurStkMark, minStkMark, maxStkMark
	MARK_STACK_SIZE EQU 10
	;The size of this constant must exceed the maximum number
	;of MARK meta-commands that can occur for any statement
	;in the bnf (see bnf.prs).  Since MARK never appears within
	;a repeating construct (i.e. {id MARK(n)}), we can easily
	;compute the worst case number of MARKs in 1 statement.
	;It is currently 6 for OPEN, so 10 gives plenty of breathing
	;room.  It would be nice at some point to change buildprs
	;(the program that reads bnf.prs) to produce this constant.
	
pCurStkMark LABEL WORD
	DW	0
minStkMark LABEL WORD
	DW	MARK_STACK_SIZE DUP(?)
maxStkMark LABEL WORD


fNeedPrintEos DB 1			;used by NtEndPrint

sEnd	DATA


;*====================================================================
;*    P A R S E   S T A T E   T A B L E   I N T E R P R E T E R
;*
;*    This function make up the interpreter which emulates the
;*    push-down-automata for the recursive descent parser.
;*
;*====================================================================

sBegin	CP
assumes	CS,CP

PUBLIC	RtErrOmStack
RtErrOmStack PROC NEAR
	mov	[b$ErrInfo],OMErr_STK	;so user interface can distinguish
					; this from other types of Out-of-memory
					; when reporting the error
	mov	al,ER_OM		;"Out of memory"
	jmp	RtErrorNoSi
RtErrOmStack ENDP

;*********************************************************************
; PARSE_RESULT NEAR NtParse(ax:pState)
;
; Purpose:
;	Try to match (parse) the token stream which begins with 'pTokScan'
;	to the regular expression identified by pState.
;
; Entry:
;	ax=pState: pointer into tState[] for the syntax to be recognized
;
; Exit:
;	If no errors are encountered, on exit 'pTokScan' should be a valid
;	   syntax terminator.
;	   Return value is PR_GoodSyntax.
;	If the 1st token was not even consumed, the return value is
;	   PR_NotFound.
;	Otherwise, the error(s) are reported and 'pTokScan' is the next
;	   statement terminator.
;	   Return value is PR_BadSyntax.
;	Condition codes set based on value in al
;
; Example:
;	If the state tree pointed to by 'pState' looked like:
;
;           A
;          / \
;         B   C
;        / \   \
;       D   E   F
;
; Then the input token stream had better look like one of the following:
;  ACF or BE or D
;
; Tricky statements to report errors for (i.e. test these if you
; ever alter the error reporting logic):
;  KEY(1)="this"
;  CIRCLE (1,2)),3
;  DO STOP --> make sure error is expected WHILE or end-of-stmt
;  NEXT STOP --> make sure error is expected id or end-of-stmt
;
;********************************************************************
;Register usage:
;	si = pState

;in response to EMIT(opcode) directive, emit the opcode
GotEmitNode:
	lods	WORD PTR cs:[si]	;ax = EMIT's operand
	call	Emit16_AX
	jmp	SHORT ParseLoop

cProc	NtParse,<PUBLIC,NEAR>,<si,di>
	localW	nodeId
	localB	cTokFirst
cBegin	NtParse
	DbChkPsStk			;see if this is a new high-water stack
	cmp	sp,[stkChkParse]
	jbe	RtErrOmStack		;brif almost out of stack space
	xchg	si,ax			;si = pState
	mov	al,[cTokScan]
	mov	[cTokFirst],al		;cTokFirst = cTokScan
ParseLoop:
	;fetch state transition arc from encoded state tree 'tState'
	lods	BYTE PTR cs:[si]	;al = nodeId
	cmp	al,ND_EMIT
	ja	NotDirective		;brif not ACCEPT/REJECT/MARK/EMIT
	je	GotEmitNode		;branch if EMIT node
.errnz	ND_ACCEPT
	or	al,al
	je	J1_GotAcceptNode	;branch if ACCEPT node
	cmp	al,ND_MARK
	je	GotMarkNode		;branch if MARK node
	jmp	GotRejectNode		;else it has to be a REJECT node

J1_GotAcceptNode:
	jmp	GotAcceptNode

;In response to MARK(const) directive, push current
;pcode offset and const onto stack.  This information
;will be used by stmt or func code generator
;
GotMarkNode:
	mov	bx,[pCurStkMark]
	DbAssertRel bx,a,MIN_STK_MARK,CP,<NtParse: marker stack overflow>
	mov	ax,WORD PTR ps.PS_bdpDst.BDP_pbCur
	sub	ax,WORD PTR ps.PS_bdpDst.BDP_pb ;ax = cur offset into out buffer
	dec	bx			;push pcode offset onto MARK stack
	dec	bx			; for use by Code Generator function
	mov	[bx],ax
	dec	bx			;push MARK's operand as well
	dec	bx
	lods	BYTE PTR cs:[si]	;al = MARK's operand
	sub	ah,ah
	mov	[bx],ax
	mov	[pCurStkMark],bx
	jmp	SHORT ParseLoop

Got_STT_Accept:
	sub	ax,ax			;indicates unconditional acceptance
	jmp	SHORT PsHandleNode

;got a branch, token, or nonterminal - fetch its id
;al = 1st byte of nodeId
;
NotDirective:
	sub	ah,ah
	cmp	al,ENCODE1BYTE
	jb	PsOneByteNodeId
	;nodeId = ((nodeId-ENCODE1BYTE) << 8) + (*pState++) + ENCODE1BYTE
	mov	ah,al
	lods	BYTE PTR cs:[si]	;ax = nodeId * 256 + next byte
	sub	ax,255 * ENCODE1BYTE
;ax = nodeId
;node is followed by 1 or 2 byte branch operand, fetch it
;
PsOneByteNodeId:
	mov	[nodeId],ax
	lods	BYTE PTR cs:[si]	;al = 1st byte of operand
	cmp	al,ENCODE1BYTE
	jb	PsOneByteOperand
	cmp	al,255
	je	Got_STT_Accept
	;operand is 2 byte offset into tState, pick up 2nd byte
	;pStateTrue = &tState[((operand-ENCODE1BYTE)<<8) + *pState++]
	
	mov	ah,al
	lods	BYTE PTR cs:[si]	;al=2nd byte of operand
	add	ax,OFFSET CP:tState-256*ENCODE1BYTE
	jmp	SHORT PsHandleNode

;operand is 1 byte relative branch in state table
;al = 1st byte of node's operand
;
PsOneByteOperand:
	sub	ah,ah			;ax = operand
	mov	bl,al			;bl = operand
	add	ax,si
	cmp	bl,ENCODE1BYTE/2
	jbe	PsHandleNode		;brif positive relative branch
	sub	ax,ENCODE1BYTE		;negative relative branch

;ax = state to branch to if we recognize(consume) what we're expecting
PsHandleNode:
	mov	di,ax			;di = pStateTrue
	mov	[pStateCur],ax		;set static var for error reporting
	mov	ax,[nodeId]
	cmp	ax,ND_BRANCH
	je	GotBranch		;brif got unconditional branch
	;got a token or nonterminal node
	sub	ax,ND_BRANCH + 1
	cmp	ax,NUMNTINT
	jae	PsNotIntNt
	;we expect a non-terminal described by a state tree
	mov	bx,ax
	shl	bx,1
	mov	ax,WORD PTR cs:tIntNtDisp[bx]
	add	ax,OFFSET CP:tState	;ax = pState = &(tState[oState])
	call	NtParse			;result = Parse(tIntNtDisp[nodeId])
	jmp	SHORT CheckResult

;ax = node id - (ND_BRANCH + 1)
PsNotIntNt:
	sub	ax,NUMNTINT
	cmp	ax,NUMNTEXT
	jae	GotResWord		;brif expecting a reserved word token
	;we expect a non-terminal defined by a C function
	mov	bx,ax
	shl	bx,1
	call	cs:tExtNtDisp[bx]	;result = (*tExtNtDisp[nodeId])()

;al = PR_NotFound, PR_BadSyntax, or PR_GoodSyntax
CheckResult:
	or	al,al
	jg	RecognizedToken		;brif result was PR_GoodSyntax
	jl	ParseExit		;brif result was PR_BadSyntax
					; non-terminal already generated
					; complete error message
	jmp	ParseLoop		;else result == PR_NotFound, continue
					; scanning by taking FALSE branch from
					; current node

;Unconditional branch to a new parse-state
GotBranch:
	mov	si,di			;si = pStateTrue
					;unconditional branch to another state,
J1_ParseLoop:
	jmp	ParseLoop		;continue scanning

;Expect a reserved word
;ax = node id - (ND_BRANCH + 1) - NUMNTINT
GotResWord:
	sub	ax,NUMNTEXT		;ax=expected res word's IRW_xxx
	mov	bx,[pTokScan]		;bx -> current token
	cmp	WORD PTR [bx],CL_RESWORD
	jne	J1_ParseLoop		;brif token isn't a reserved word
	cmp	[bx.TOK_rw_iRw],ax	;ax = current token's IRW_xxx
	jne	J1_ParseLoop		;brif expected token was not found,
					; continue scanning by taking FALSE
					; branch from current node
	call	ScanTok			;skip recognized token
RecognizedToken:
	or	di,di			;test for pStateTrue for NULL
	je	GotAcceptNode		;brif got acceptance state transition
					;else take TRUE transition out of this
	mov	si,di			; state to another node
	mov	[pStateLastGood],di	;save for error reporting
J2_ParseLoop:
	jmp	ParseLoop

;Got to a leaf node of the parse tree which is not an accepting node,
; ie. we were unable to recognize what Parse() was called to recognize.
; If we didn't get what was expected, but didn't emit code (see note below)
; or consume any
; tokens, its not necessarily a syntax error yet.  If Parse() was called to
; consume some non-terminal, let the caller continue trying other options
; (if any), and when they are all exausted, the caller will generate an
; error msg.
;NOTE: NtParse does not actually test whether any pcode was emitted and so
;NOTE: will still return PR_NotFound even if some pcode has been emitted
;NOTE: as long as no tokens have been consumed. This is used to advantage
;NOTE: in the NonTerminal "AsClause" which will return PR_NotFound for
;NOTE: 		"AS REAL" even though it will emit an opcode.
;
GotRejectNode:
	mov	al,[cTokScan]
	cmp	[cTokFirst],al
	mov	al,PR_NotFound
	je	ParseExit
	call	PErrState		;Produce error message "Expected <a>
					; or <b> or ..."
					;al = PR_BadSyntax
	jmp	SHORT ParseExit

;We recognized parse tree described by initial pState
GotAcceptNode:
	mov	al,PR_GoodSyntax
ParseExit:
	or	al,al			;set condition codes for caller
cEnd	NtParse

;===========================================================================
;    M I S C    P A R S E R    R E C O G N I Z I N G    F U N C T I O N S
;
;    NOTE:  These functions are arranged alphabetically
;
;=======================================================================

;**********************************************************************
; PARSE_RESULT NEAR NtAssignment()
;
; Purpose:
;	Called for the 'LET "var=exp"' statement
;
;******************************************************************
PUBLIC	NtAssignment
NtAssignment PROC NEAR
	mov	bx,[pTokScan]		;bx points to current token
	cmp	[bx.TOK_class],CL_id
	jne	NtAssBadSyntax
	push	bx			;Tell NtImpliedLetOrCall that
					; it must be LET, not CALL
	call	NtImpliedLetOrCall	;al = result
NtAssExit:
	ret
NtAssignment ENDP

NtAssBadSyntax:
	sub	al,al			;al = PR_NotFound
	jmp	SHORT NtAssExit

;*******************************************************************************
; PARSE_RESULT NEAR NtCommaNoEos()
;
; Purpose:
;	Special nonterminal for a comma which is followed by optional arguments.
;	Rather than generate oodles of state table, this nonterminal is used to
;	make sure that the comma, if present, is NOT followed by end of line or
;	end of statement. Generates an opComma if comma found and all is okay.
;
; Returns:
;	PR_NotFound	- no comma found
;	PR_GoodSyntax	- comma found, followed by something.
;	PR_BadSyntax	- comma found, followed by end of statement.
;
;***************************************************************************
cProc	NtCommaNoEos <PUBLIC,NEAR>
cBegin
	mov	ax,IRW_Comma
	call	TestScan_AX
	jne	NoComma			;branch if current token isn't comma
	call	ScanTok			;consume the comma

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -