📄 prsnt.asm
字号:
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 + -