mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
700 lines
18 KiB
700 lines
18 KiB
#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;
|
|
}
|