diff --git a/expression.c b/expression.c index 1ba2db9..dcaac04 100644 --- a/expression.c +++ b/expression.c @@ -767,7 +767,7 @@ void ExpressionInfixOperator(struct ParseState *Parser, struct ExpressionStack * else if (Op == TokenAssign && TopInt == 0) { /* assign a NULL pointer */ - HeapUnpopStack(sizeof(struct Value)); /* XXX - possible bug if lvalue is a temp value and takes more than sizeof(struct Value) */ + HeapUnpopStack(sizeof(struct Value)); ExpressionAssign(Parser, BottomValue, TopValue, FALSE, NULL, 0, FALSE); ExpressionStackPushValueNode(Parser, StackTop, BottomValue); } @@ -785,7 +785,7 @@ void ExpressionInfixOperator(struct ParseState *Parser, struct ExpressionStack * else Pointer = (void *)((char *)Pointer - TopInt * Size); - HeapUnpopStack(sizeof(struct Value)); /* XXX - possible bug if lvalue is a temp value and takes more than sizeof(struct Value) */ + HeapUnpopStack(sizeof(struct Value)); BottomValue->Val->Pointer = Pointer; ExpressionStackPushValueNode(Parser, StackTop, BottomValue); } @@ -963,7 +963,7 @@ void ExpressionGetStructElement(struct ParseState *Parser, struct ExpressionStac if (StructType->Base != TypeStruct && StructType->Base != TypeUnion) ProgramFail(Parser, "can't use '%s' on something that's not a struct or union %s : it's a %t", (Token == TokenDot) ? "." : "->", (Token == TokenArrow) ? "pointer" : "", ParamVal->Typ); - if (!TableGet(StructType->Members, Ident->Val->Identifier, &MemberValue)) + if (!TableGet(StructType->Members, Ident->Val->Identifier, &MemberValue, NULL, NULL)) ProgramFail(Parser, "doesn't have a member called '%s'", Ident->Val->Identifier); /* pop the value - assume it'll still be there until we're done */ diff --git a/lex.c b/lex.c index 56bca25..23ca606 100644 --- a/lex.c +++ b/lex.c @@ -755,7 +755,7 @@ void LexHashIfdef(struct ParseState *Parser, int IfNot) ProgramFail(Parser, "identifier expected"); /* is the identifier defined? */ - IsDefined = TableGet(&GlobalTable, IdentValue->Val->Identifier, &SavedValue); + IsDefined = TableGet(&GlobalTable, IdentValue->Val->Identifier, &SavedValue, NULL, NULL); if (Parser->HashIfEvaluateToLevel == Parser->HashIfLevel && ( (IsDefined && !IfNot) || (!IsDefined && IfNot)) ) { /* #if is active, evaluate to this new level */ @@ -777,7 +777,7 @@ void LexHashIf(struct ParseState *Parser) if (Token == TokenIdentifier) { /* look up a value from a macro definition */ - if (!TableGet(&GlobalTable, IdentValue->Val->Identifier, &SavedValue)) + if (!TableGet(&GlobalTable, IdentValue->Val->Identifier, &SavedValue, NULL, NULL)) ProgramFail(Parser, "'%s' is undefined", IdentValue->Val->Identifier); if (SavedValue->Typ->Base != TypeMacro) diff --git a/parse.c b/parse.c index 921786c..03bf89d 100644 --- a/parse.c +++ b/parse.c @@ -128,7 +128,7 @@ struct Value *ParseFunctionDefinition(struct ParseState *Parser, struct ValueTyp FuncValue->Val->FuncDef.Body.Pos = LexCopyTokens(&FuncBody, Parser); /* is this function already in the global table? */ - if (TableGet(&GlobalTable, Identifier, &OldFuncValue)) + if (TableGet(&GlobalTable, Identifier, &OldFuncValue, NULL, NULL)) { if (OldFuncValue->Val->FuncDef.Body.Pos == NULL) TableDelete(&GlobalTable, Identifier); /* override an old function prototype */ @@ -137,7 +137,7 @@ struct Value *ParseFunctionDefinition(struct ParseState *Parser, struct ValueTyp } } - if (!TableSet(&GlobalTable, Identifier, FuncValue)) + if (!TableSet(&GlobalTable, Identifier, FuncValue, Parser->Line, Parser->CharacterPos)) ProgramFail(Parser, "'%s' is already defined", Identifier); return FuncValue; @@ -227,7 +227,7 @@ int ParseDeclaration(struct ParseState *Parser, enum LexToken Token) ProgramFail(Parser, "can't define a void variable"); if (Parser->Mode == RunModeRun) - NewVariable = VariableDefine(Parser, Identifier, NULL, Typ, TRUE); + NewVariable = VariableDefineButIgnoreIdentical(Parser, Identifier, Typ); if (LexGetToken(Parser, NULL, FALSE) == TokenAssign) { @@ -304,7 +304,7 @@ void ParseMacroDefinition(struct ParseState *Parser) LexToEndOfLine(Parser); MacroValue->Val->MacroDef.Body.Pos = LexCopyTokens(&MacroValue->Val->MacroDef.Body, Parser); - if (!TableSet(&GlobalTable, MacroNameStr, MacroValue)) + if (!TableSet(&GlobalTable, MacroNameStr, MacroValue, Parser->Line, Parser->CharacterPos)) ProgramFail(Parser, "'%s' is already defined", MacroNameStr); } diff --git a/picoc.h b/picoc.h index 909c9e9..fbb275d 100644 --- a/picoc.h +++ b/picoc.h @@ -208,6 +208,9 @@ struct Value struct TableEntry { struct TableEntry *Next; /* next item in this hash chain */ + unsigned short DeclLine; /* where the variable was declared */ + unsigned short DeclColumn; + union TableEntryPayload { struct ValueEntry @@ -321,8 +324,8 @@ void TableInit(); char *TableStrRegister(const char *Str); char *TableStrRegister2(const char *Str, int Len); void TableInitTable(struct Table *Tbl, struct TableEntry **HashTable, int Size, int OnHeap); -int TableSet(struct Table *Tbl, char *Key, struct Value *Val); -int TableGet(struct Table *Tbl, const char *Key, struct Value **Val); +int TableSet(struct Table *Tbl, char *Key, struct Value *Val, int DeclLine, int DeclColumn); +int TableGet(struct Table *Tbl, const char *Key, struct Value **Val, int *DeclLine, int *DeclColumn); struct Value *TableDelete(struct Table *Tbl, const char *Key); char *TableSetIdentifier(struct Table *Tbl, const char *Ident, int IdentLen); void TableStrFree(); @@ -394,6 +397,7 @@ struct Value *VariableAllocValueFromType(struct ParseState *Parser, struct Value struct Value *VariableAllocValueFromExistingData(struct ParseState *Parser, struct ValueType *Typ, union AnyValue *FromValue, int IsLValue, struct Value *LValueFrom); struct Value *VariableAllocValueShared(struct ParseState *Parser, struct Value *FromValue); struct Value *VariableDefine(struct ParseState *Parser, char *Ident, struct Value *InitValue, struct ValueType *Typ, int MakeWritable); +struct Value *VariableDefineButIgnoreIdentical(struct ParseState *Parser, char *Ident, struct ValueType *Typ); int VariableDefined(const char *Ident); void VariableGet(struct ParseState *Parser, const char *Ident, struct Value **LVal); void VariableDefinePlatformVar(struct ParseState *Parser, char *Ident, struct ValueType *Typ, union AnyValue *FromValue, int IsWritable); diff --git a/table.c b/table.c index d0e89a2..8ed2658 100644 --- a/table.c +++ b/table.c @@ -56,7 +56,7 @@ static struct TableEntry *TableSearch(struct Table *Tbl, const char *Key, int *A /* set an identifier to a value. returns FALSE if it already exists. * Key must be a shared string from TableStrRegister() */ -int TableSet(struct Table *Tbl, char *Key, struct Value *Val) +int TableSet(struct Table *Tbl, char *Key, struct Value *Val, int DeclLine, int DeclColumn) { int AddAt; struct TableEntry *FoundEntry = TableSearch(Tbl, Key, &AddAt); @@ -64,6 +64,8 @@ int TableSet(struct Table *Tbl, char *Key, struct Value *Val) if (FoundEntry == NULL) { /* add it to the table */ struct TableEntry *NewEntry = VariableAlloc(NULL, sizeof(struct TableEntry), Tbl->OnHeap); + NewEntry->DeclLine = DeclLine; + NewEntry->DeclColumn = DeclColumn; NewEntry->p.v.Key = Key; NewEntry->p.v.Val = Val; NewEntry->Next = Tbl->HashTable[AddAt]; @@ -76,7 +78,7 @@ int TableSet(struct Table *Tbl, char *Key, struct Value *Val) /* find a value in a table. returns FALSE if not found. * Key must be a shared string from TableStrRegister() */ -int TableGet(struct Table *Tbl, const char *Key, struct Value **Val) +int TableGet(struct Table *Tbl, const char *Key, struct Value **Val, int *DeclLine, int *DeclColumn) { int AddAt; struct TableEntry *FoundEntry = TableSearch(Tbl, Key, &AddAt); @@ -84,6 +86,13 @@ int TableGet(struct Table *Tbl, const char *Key, struct Value **Val) return FALSE; *Val = FoundEntry->p.v.Val; + + if (DeclLine != NULL) + { + *DeclLine = FoundEntry->DeclLine; + *DeclColumn = FoundEntry->DeclColumn; + } + return TRUE; } @@ -132,7 +141,7 @@ char *TableSetIdentifier(struct Table *Tbl, const char *Ident, int IdentLen) return &FoundEntry->p.Key[0]; else { /* add it to the table - we economise by not allocating the whole structure here */ - struct TableEntry *NewEntry = HeapAllocMem(sizeof(struct TableEntry *) + IdentLen + 1); + struct TableEntry *NewEntry = HeapAllocMem(sizeof(struct TableEntry) - sizeof(union TableEntryPayload) + IdentLen + 1); if (NewEntry == NULL) ProgramFail(NULL, "out of memory"); diff --git a/tests/44_scoped_declarations.c b/tests/44_scoped_declarations.c new file mode 100644 index 0000000..5cbed65 --- /dev/null +++ b/tests/44_scoped_declarations.c @@ -0,0 +1,10 @@ +#include + +int a; + +for (a = 0; a < 2; a++) +{ + int b = a; +} + +printf("it's all good\n"); diff --git a/tests/44_scoped_declarations.expect b/tests/44_scoped_declarations.expect new file mode 100644 index 0000000..231ccc0 --- /dev/null +++ b/tests/44_scoped_declarations.expect @@ -0,0 +1 @@ +it's all good diff --git a/tests/Makefile b/tests/Makefile index abf4025..8357f11 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -38,7 +38,8 @@ TESTS= 00_assignment.test \ 39_typedef.test \ 40_stdio.test \ 41_hashif.test \ - 43_void_param.test + 43_void_param.test \ + 44_scoped_declarations.test %.test: %.expect %.c @echo Test: $*... diff --git a/type.c b/type.c index 4aad6a3..f27c493 100644 --- a/type.c +++ b/type.c @@ -233,7 +233,7 @@ void TypeParseStruct(struct ParseState *Parser, struct ValueType **Typ, int IsSt (*Typ)->Sizeof = TypeSizeValue(MemberValue); } - if (!TableSet((*Typ)->Members, MemberIdentifier, MemberValue)) + if (!TableSet((*Typ)->Members, MemberIdentifier, MemberValue, Parser->Line, Parser->CharacterPos)) ProgramFail(Parser, "member '%s' already defined", &MemberIdentifier); if (LexGetToken(Parser, NULL, TRUE) != TokenSemicolon) diff --git a/variable.c b/variable.c index 37d93bd..a131914 100644 --- a/variable.c +++ b/variable.c @@ -161,20 +161,34 @@ struct Value *VariableDefine(struct ParseState *Parser, char *Ident, struct Valu AssignValue->IsLValue = MakeWritable; - if (!TableSet((TopStackFrame == NULL) ? &GlobalTable : &TopStackFrame->LocalTable, Ident, AssignValue)) + if (!TableSet((TopStackFrame == NULL) ? &GlobalTable : &TopStackFrame->LocalTable, Ident, AssignValue, Parser ? Parser->Line : 0, Parser ? Parser->CharacterPos : 0)) ProgramFail(Parser, "'%s' is already defined", Ident); return AssignValue; } +/* define a variable. Ident must be registered. If it's a redefinition from the same declaration don't throw an error */ +struct Value *VariableDefineButIgnoreIdentical(struct ParseState *Parser, char *Ident, struct ValueType *Typ) +{ + struct Value *ExistingValue; + int DeclLine; + int DeclColumn; + + if (Parser->Line != 0 && TableGet((TopStackFrame == NULL) ? &GlobalTable : &TopStackFrame->LocalTable, Ident, &ExistingValue, &DeclLine, &DeclColumn) + && DeclLine == Parser->Line && DeclColumn == Parser->CharacterPos) + return ExistingValue; + else + return VariableDefine(Parser, Ident, NULL, Typ, TRUE); +} + /* check if a variable with a given name is defined. Ident must be registered */ int VariableDefined(const char *Ident) { struct Value *FoundValue; - if (TopStackFrame == NULL || !TableGet(&TopStackFrame->LocalTable, Ident, &FoundValue)) + if (TopStackFrame == NULL || !TableGet(&TopStackFrame->LocalTable, Ident, &FoundValue, NULL, NULL)) { - if (!TableGet(&GlobalTable, Ident, &FoundValue)) + if (!TableGet(&GlobalTable, Ident, &FoundValue, NULL, NULL)) return FALSE; } @@ -184,9 +198,9 @@ int VariableDefined(const char *Ident) /* get the value of a variable. must be defined. Ident must be registered */ void VariableGet(struct ParseState *Parser, const char *Ident, struct Value **LVal) { - if (TopStackFrame == NULL || !TableGet(&TopStackFrame->LocalTable, Ident, LVal)) + if (TopStackFrame == NULL || !TableGet(&TopStackFrame->LocalTable, Ident, LVal, NULL, NULL)) { - if (!TableGet(&GlobalTable, Ident, LVal)) + if (!TableGet(&GlobalTable, Ident, LVal, NULL, NULL)) ProgramFail(Parser, "'%s' is undefined", Ident); } } @@ -198,7 +212,7 @@ void VariableDefinePlatformVar(struct ParseState *Parser, char *Ident, struct Va SomeValue->Typ = Typ; SomeValue->Val = FromValue; - if (!TableSet((TopStackFrame == NULL) ? &GlobalTable : &TopStackFrame->LocalTable, TableStrRegister(Ident), SomeValue)) + if (!TableSet((TopStackFrame == NULL) ? &GlobalTable : &TopStackFrame->LocalTable, TableStrRegister(Ident), SomeValue, Parser ? Parser->Line : 0, Parser ? Parser->CharacterPos : 0)) ProgramFail(Parser, "'%s' is already defined", Ident); } @@ -261,7 +275,7 @@ struct Value *VariableStringLiteralGet(char *Ident) { struct Value *LVal = NULL; - if (TableGet(&StringLiteralTable, Ident, &LVal)) + if (TableGet(&StringLiteralTable, Ident, &LVal, NULL, NULL)) return LVal; else return NULL; @@ -270,7 +284,7 @@ struct Value *VariableStringLiteralGet(char *Ident) /* define a string literal. assumes that Ident is already registered */ void VariableStringLiteralDefine(char *Ident, struct Value *Val) { - TableSet(&StringLiteralTable, Ident, Val); + TableSet(&StringLiteralTable, Ident, Val, 0, 0); } /* check a pointer for validity and dereference it for use */