#include "asl.h" // // Globals // UCHAR Paran; // parathises level UCHAR Brace; // brace level UCHAR ParseState; // current parser state UCHAR NextParseState; // next parser state #define INITIAL 0x00 // the states #define NEXT_CHAR 0x01 #define COMMENT 0x02 #define WHITE_SPACE 0x03 #define BAD_CHAR 0x04 #define RETURN_CHAR 0x05 #define NEWLINE_CHAR 0x06 #define LITERIAL 0x07 #define FIRST_QUOTE_CHAR 0x08 #define FIRST_COMMENT_CHAR 0x09 #define COMMA 0x0A #define OPEN_PARAN 0x0B #define CLOSE_PARAN 0x0C #define OPEN_BRACE 0x0D #define CLOSE_BRACE 0x0E #define FIRST_TOKEN_CHAR 0x40 #define NEXT_TOKEN_CHAR 0x41 #define QUOTE 0x80 #define SPECIAL_ALL (0x80 | 0x40) // this one is a bit masked onto parse state #define SPECIAL_TOKEN (0x40) UCHAR cvt[256]; // char states & attributes for parsing UCHAR Token[200]; // token being built by parser UCHAR TokenLen; // length of token BOOLEAN TokenNum; BOOLEAN TokenBreak; // // Internal prototypes // VOID HandleToken (BOOLEAN); VOID IncludeSource ( IN PUCHAR Filename ) { PASL_SOURCE NewSource; OFSTRUCT OpenBuf; VPRINT(7, "Open include '%s'\n", Filename); NewSource = AllocZMem(sizeof(ASL_SOURCE)); NewSource->Name = StrDup(Filename); // // Open file & get it's size // NewSource->FileHandle = OpenFile(Filename, &OpenBuf, OF_READ); if (NewSource->FileHandle == HFILE_ERROR) { ErrorW32("File not opened '%s'", Filename); } NewSource->FileSize = GetFileSize(NewSource->FileHandle, NULL); // // Map it // NewSource->MapHandle = CreateFileMapping( NewSource->FileHandle, NULL, PAGE_READONLY, 0, NewSource->FileSize, NULL ); if (!NewSource->MapHandle) { ErrorW32("Could not map file '%'", Filename); } NewSource->Image = MapViewOfFile ( NewSource->MapHandle, FILE_MAP_READ, 0, 0, NewSource->FileSize ); if (!NewSource->Image) { ErrorW32("Could not map view of image '%s'", Filename); } // // Make current source // NewSource->EndOfData = NewSource->Image + NewSource->FileSize; NewSource->Position = NewSource->Image; NewSource->Previous = Source; NewSource->BraceLevel = Brace; NewSource->LineNo = 1; Source = NewSource; } VOID CloseSource ( VOID ) { PASL_SOURCE OldSource; VPRINT(7, "Close include '%s'\n", Source->Name); // // Verify end of image // EASSERT (Paran == 0, "end of file unexpected. open paran '('"); EASSERT (Brace == Source->BraceLevel, "end of file unexpected. mis-matched brace '{'"); ParseState = INITIAL; // // Go to previous source // OldSource = Source; Source = OldSource->Previous; if (OldSource->Name) { CloseHandle (OldSource->MapHandle); CloseHandle (OldSource->FileHandle); } OldSource->MapHandle = HFILE_ERROR; OldSource->MapHandle = HFILE_ERROR; OldSource->Previous = NULL; OldSource->Position = NULL; OldSource->Image = NULL; OldSource->EndOfData = NULL; SourceLines += OldSource->LineNo; } InitParse () { UCHAR c; // mark illegal chars for (c=127; c < ' '; c++) { cvt[c] = SPECIAL_ALL | BAD_CHAR; } // mark white space chars cvt[' '] = WHITE_SPACE; cvt['\t'] = WHITE_SPACE; // mark special chars cvt['\r'] = SPECIAL_ALL | RETURN_CHAR; cvt['\n'] = SPECIAL_ALL | NEWLINE_CHAR; cvt['\"'] = SPECIAL_ALL | FIRST_QUOTE_CHAR; cvt['/'] = SPECIAL_TOKEN | FIRST_COMMENT_CHAR; cvt[','] = SPECIAL_TOKEN | COMMA; cvt['('] = SPECIAL_TOKEN | OPEN_PARAN; cvt[')'] = SPECIAL_TOKEN | CLOSE_PARAN; cvt['{'] = SPECIAL_TOKEN | OPEN_BRACE; cvt['}'] = SPECIAL_TOKEN | CLOSE_BRACE; //cvt['\\'] = SPECIAL_TOKEN | LITERIAL; } VOID ParseSource ( ) { UCHAR c, cv; BOOLEAN literial; PAL_DATA Al; literial = FALSE; for (; ;) { switch (ParseState) { case INITIAL: NextParseState = FIRST_TOKEN_CHAR; case NEXT_CHAR: // // Get next char // if (Source->Position >= Source->EndOfData) { if (literial) { AERROR("end of file unexpected. literal override"); } switch (NextParseState) { case FIRST_TOKEN_CHAR: case COMMENT: // these two are expected break; case NEXT_TOKEN_CHAR: AERROR("end of file unexpected. incomplete token"); break; case QUOTE: AERROR("end of file unexpected. incomplete string"); break; default: AERROR("end of file unexpected. (1)"); break; } return; } c = *Source->Position; Source->Position += 1; // count newlines if (c == '\n') { Source->LineNo += 1; } // move to next parse state ParseState = NextParseState; if (literial) { // // Treat this char as a literal // cv = 0; // no char meaning switch (c) { case 'b': c = '\b'; break; case 'r': c = '\r'; break; case 'n': c = '\n'; break; case 't': c = '\t'; break; } } else { cv = cvt[c]; if (cv & (ParseState & SPECIAL_ALL) ) { // // Char has some meaning in this state, // perform that before NextState // ParseState = cv & ~SPECIAL_ALL; } } break; case RETURN_CHAR: // ignore these ParseState = NEXT_CHAR; break; case LITERIAL: // treat next char as literia; literial = TRUE; ParseState = NEXT_CHAR; break; case FIRST_TOKEN_CHAR: ParseState = NEXT_CHAR; // ignore white space if (cv == WHITE_SPACE) { break; } // put first char in token Token[0] = c; TokenLen = 1; TokenNum = (c >= '0' && c <= '9'); TokenBreak = TRUE; NextParseState = NEXT_TOKEN_CHAR; break; case NEXT_TOKEN_CHAR: ParseState = NEXT_CHAR; // ignore white space if (cv == WHITE_SPACE) { break; } Token[TokenLen] = c; TokenLen += 1; if (TokenLen > sizeof(Token)-2) { AERROR ("token too large"); NextParseState = COMMENT; TokenLen = 0; } break; case NEWLINE_CHAR: ParseState = NEXT_CHAR; switch (NextParseState) { case QUOTE: AERROR("Newline in string"); ParseState = INITIAL; break; } break; case FIRST_QUOTE_CHAR: if (TokenNum) { AERROR ("Can not mix numeric and string"); } ParseState = NEXT_CHAR; if (NextParseState == QUOTE) { NextParseState = NEXT_TOKEN_CHAR; } else { // start of quote NextParseState = QUOTE; } break; case QUOTE: ParseState = NEXT_CHAR; Token[TokenLen] = c; TokenLen += 1; if (TokenLen > sizeof(Token)-2) { AERROR ("string too large"); } break; case FIRST_COMMENT_CHAR: if (Source->Position <= Source->EndOfData && *Source->Position == '/') { // // Is a comment // ParseState = NEXT_CHAR; NextParseState = COMMENT; } else { // // Not a comment // ParseState = NextParseState; } break; case COMMENT: // in comment, wait for new line ParseState = c == '\n' ? INITIAL : NEXT_CHAR; break; case COMMA: // serves only to break tokens HandleToken(TokenBreak); break; case OPEN_PARAN: TokenBreak = TRUE; HandleToken(FALSE); // // Get last term added to CurLoc // if (AlLoc->u1.VariableList.Blink) { Al = CONTAINING_RECORD(AlLoc->u1.VariableList.Blink, AL_DATA, Link); } else if (AlLoc->FixedList.Blink) { Al = CONTAINING_RECORD(AlLoc->FixedList.Blink, AL_DATA, Link); } else { // // AlLoc doesn't have a last term. AlLoc is the new term // Al = AlLoc; } if (Al->FixedList.Flink) { // either no prior term, or prior term // already has a list AERROR ("Unexpected '('"); break; } // // Start filling in the fixed list on Al // AlLoc = Al; AlLoc->Flags |= F_PFIXED; InitializeListHead(&AlLoc->FixedList); Paran += 1; break; case CLOSE_PARAN: TokenBreak = FALSE; HandleToken(FALSE); if (!AlLoc->Flags & F_PFIXED) { // fixed list not in progress AERROR ("Unexpected ')'"); } else { Paran -= 1; AlLoc->Flags &= ~F_PFIXED; RetireTerm(); } break; case OPEN_BRACE: TokenBreak = TRUE; HandleToken(FALSE); if (!(AlLoc->Flags & F_AMLPACKAGE) || (AlLoc->Flags & (F_PVARIABLE | F_PFIXED))) { // either not a package, or in the middle parans AERROR ("Unexpectped '{'"); } else { if (AlLoc->u1.VariableList.Flink) { // package already built AERROR ("Unexpected '{'"); } else { Brace += 1; AlLoc->Flags |= F_PVARIABLE; InitializeListHead(&AlLoc->u1.VariableList); // // If no fixed list appeared, then build empty one // if (!AlLoc->FixedList.Flink) { InitializeListHead(&AlLoc->FixedList); } } } break; case CLOSE_BRACE: TokenBreak = FALSE; HandleToken(FALSE); if (!AlLoc->Flags & F_PVARIABLE) { // package not in progress AERROR ("Unexpected '}'"); } else { Brace -= 1; AlLoc->Flags &= ~F_PVARIABLE; RetireTerm(); } break; case BAD_CHAR: AERROR ("Bad charactor in source - 0x%x", c); Terminate(); default: AERROR ("Internal error - unkown parse state %d", ParseState); Terminate(); } } } VOID HandleToken ( BOOLEAN AllowNullTerm ) { PASL_TERM Term; DATATYPE DataType; PAL_DATA NewAl; ULONG Len; BOOLEAN MakeCurrent, IsNum; // // token is complete, reset parse state for next token // ParseState = INITIAL; Len = TokenLen; IsNum = TokenNum; TokenLen = 0; TokenNum = FALSE; // // If there's no token, done // if (!Len && !AllowNullTerm) { return; } // // All tokens are stored in an AL_DATA one way or another // NewAl = AllocAl(); // // Check token to see if it's a global token // MakeCurrent = FALSE; Token[Len] = 0; Term = GlobalToken(Token, Len, &DataType); if (Term) { // // Found a hit in as a global name. For now, we are // only supported ASL terms this way. (later we can // add defines) // ASSERT (DataType == TypeTerm, "Unsupported global name type"); // // Initialize New Term Al // NewAl->Term = Term; if (Term->Flags & T_VARIABLE) { NewAl->Flags |= F_AMLPACKAGE; } if (Term->Op1 || Term->Parse == IsZeroOp) { NewAl->Flags |= F_AMLENCODE; NewAl->u.Data.Length = 1; NewAl->u.Data.Data[0] = Term->Op1; if (Term->Op2) { NewAl->u.Data.Length = 2; NewAl->u.Data.Data[1] = Term->Op2; } } VPRINT(9, "Adding token '%s' ", Term->Name); // // If this term has a variable list, then make it the // current one // if (Term->Flags & T_VARIABLE) { MakeCurrent = TRUE; } } else { // // This is not a global name, store data verbatum // VPRINT(9, "Adding data '%s' ", Token); if (IsNum) { NewAl->Flags |= F_ISNUMERIC; } if (Len < MAX_AML_DATA_LEN) { NewAl->Flags |= F_AMLENCODE; NewAl->u.Data.Length = Len; memcpy (NewAl->u.Data.Data, Token, Len+1); } else { NewAl->Flags |= F_AMLIENCODE; NewAl->u.IData.Length = Len; NewAl->u.IData.MaxLength = Len+16; NewAl->u.IData.Data = AllocMem(Len+16); memcpy (NewAl->u.IData.Data, Token, Len+1); } } // // Link NewAl in as either variable or fixed onto the // current al // if (AlLoc->Flags & F_PFIXED) { VPRINT(9, "to fixed"); InsertTailList (&AlLoc->FixedList, &NewAl->Link); AlLoc->FLCount += 1; } else if (AlLoc->Flags & F_PVARIABLE) { VPRINT(9, "to variable '%s'", AlLoc->Term->Name); InsertTailList (&AlLoc->u1.VariableList, &NewAl->Link); } else { AERROR ("Term is outside of scope"); Terminate(); } if (MakeCurrent) { VPRINT(9, " (drop)"); AlLoc = NewAl; } VPRINT(9, "\n"); } RetireTerm() { PASL_TERM Term; PAL_DATA NextAl; // // If the varibiale portion of the term is complete, or // if the fixed portion is complete & there's no variable // portion, then // if (!AlLoc->Term) { // // Not an ASL term. // if (AlLoc->u1.VariableList.Flink) { ERRORAL (AlLoc, "Syntax error. Not an ASL term or Method"); } else if (AlLoc->FixedList.Flink) { // // Looks like a method // ParseMethodReference(); } else { ERRORAL (AlLoc, "Syntax error. Not an ASL term or Method"); } return ; } ASSERT ((AlLoc->Flags & (F_PFIXED | F_PVARIABLE)) == 0, "Term in progress"); ASSERT (AlLoc->FixedList.Flink != NULL, "No fixed list"); if ((AlLoc->Flags & F_AMLPACKAGE) && AlLoc->u1.VariableList.Flink == NULL) { // // Term requires a package and does not have one. // This must be a close paran. See if it wants to parse // on Fixed list completion // if (AlLoc->Term->Flags & T_PARSEARGS) { VPRINT(9, "Arg parse for '%s'\n", AlLoc->Term->Name); AlLoc->Term->Parse(); } // // Term still needs Package. Do not close this scope // return; } // // Term is complete. Pickup parent in case parse removes current. // NextAl = AlLoc->Parent; // // Parse it // if (AlLoc->Term->Flags & T_PARSECOMPLETE) { VPRINT(9, "Complete parse for '%s'\n", AlLoc->Term->Name); AlLoc->Term->Parse(); } // // Close this scope. move to parent // AlLoc = NextAl; }