Added test framework.

Implemented preprocessor-style directives (not finished yet).


git-svn-id: http://picoc.googlecode.com/svn/trunk@26 21eae674-98b7-11dd-bd71-f92a316d2d60
This commit is contained in:
zik.saleeba 2009-01-04 04:08:49 +00:00
parent 939c68cd4d
commit 995266ce9c
9 changed files with 184 additions and 69 deletions

View file

@ -11,6 +11,9 @@ all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $(TARGET) $(OBJS) $(LIBS)
test: picoc
(cd test; make test)
clean:
rm -f $(TARGET) $(OBJS) *~

View file

@ -4,6 +4,11 @@
Str IntrinsicFilename = { 9, "intrinsic" };
void IntrinsicPrintInt(void)
{
printf("%d\n", Parameter[0].Val.Integer);
}
void IntrinsicPrintf(void)
{
printf("IntrinsicPrintf\n");
@ -22,6 +27,7 @@ struct IntrinsicFunction
{
{ IntrinsicSayHello, "void sayhello()" }, /* -1 */
{ IntrinsicPrintf, "void printf()" }, /* -2 */
{ IntrinsicPrintInt, "void printint(int)" }, /* -3 */
};
void IntrinsicInit(struct Table *GlobalTable)

44
lex.c
View file

@ -4,13 +4,12 @@
#include "picoc.h"
#define isCidstart(c) (isalpha(c) || (c)=='_')
#define isCidstart(c) (isalpha(c) || (c)=='_' || (c)=='#')
#define isCident(c) (isalnum(c) || (c)=='_')
#define LEXINC Lexer->Pos++
#define NEXTIS(c,x,y) { if (NextChar == (c)) { LEXINC; return (x); } else return (y); }
#define NEXTIS3(c,x,d,y,z) { if (NextChar == (c)) { LEXINC; return (x); } else NEXTIS(d,y,z) }
#define NEXTIS4(c,x,d,y,e,z,a) { if (NextChar == (c)) { LEXINC; return (x); } else NEXTIS3(d,y,e,z,a) }
#define NEXTIS(c,x,y) { if (NextChar == (c)) { Lexer->Pos++; return (x); } else return (y); }
#define NEXTIS3(c,x,d,y,z) { if (NextChar == (c)) { Lexer->Pos++; return (x); } else NEXTIS(d,y,z) }
#define NEXTIS4(c,x,d,y,e,z,a) { if (NextChar == (c)) { Lexer->Pos++; return (x); } else NEXTIS3(d,y,e,z,a) }
struct ReservedWord
@ -21,6 +20,8 @@ struct ReservedWord
static struct ReservedWord ReservedWords[] =
{
{ "#define", TokenHashDefine },
{ "#include", TokenHashInclude },
{ "break", TokenBreak },
{ "case", TokenCase },
{ "char", TokenCharType },
@ -138,6 +139,26 @@ enum LexToken LexGetCharacterConstant(struct LexState *Lexer, union AnyValue *Va
return TokenCharacterConstant;
}
enum LexToken LexGetComment(struct LexState *Lexer, char NextChar, union AnyValue *Value)
{
Lexer->Pos++;
if (NextChar == '*')
{ /* conventional C comment */
while (Lexer->Pos != Lexer->End && (*(Lexer->Pos-1) != '*' || *Lexer->Pos != '/'))
Lexer->Pos++;
if (Lexer->Pos != Lexer->End)
Lexer->Pos++;
}
else
{ /* C++ style comment */
while (Lexer->Pos != Lexer->End && *Lexer->Pos != '\n')
Lexer->Pos++;
}
return LexGetToken(Lexer, Value);
}
enum LexToken LexGetToken(struct LexState *Lexer, union AnyValue *Value)
{
char ThisChar;
@ -147,7 +168,7 @@ enum LexToken LexGetToken(struct LexState *Lexer, union AnyValue *Value)
{
if (*Lexer->Pos == '\n')
Lexer->Line++;
Lexer->Pos++;
}
@ -162,7 +183,7 @@ enum LexToken LexGetToken(struct LexState *Lexer, union AnyValue *Value)
return LexGetNumber(Lexer, Value);
NextChar = (Lexer->Pos+1 != Lexer->End) ? *(Lexer->Pos+1) : 0;
LEXINC;
Lexer->Pos++;
switch (ThisChar)
{
case '"': return LexGetStringConstant(Lexer, Value);
@ -173,7 +194,7 @@ enum LexToken LexGetToken(struct LexState *Lexer, union AnyValue *Value)
case '+': NEXTIS3('=', TokenAddAssign, '+', TokenIncrement, TokenPlus);
case '-': NEXTIS4('=', TokenSubtractAssign, '>', TokenArrow, '-', TokenDecrement, TokenMinus);
case '*': return TokenAsterisk;
case '/': return TokenSlash;
case '/': if (NextChar == '/' || NextChar == '*') return LexGetComment(Lexer, NextChar, Value); else return TokenSlash;
case '<': NEXTIS('=', TokenLessEqual, TokenLessThan);
case '>': NEXTIS('=', TokenGreaterEqual, TokenGreaterThan);
case ';': return TokenSemicolon;
@ -214,3 +235,10 @@ enum LexToken LexPeekPlainToken(struct LexState *Lexer)
return LexGetToken(&LocalState, &Value);
}
/* skip everything up to the end of the line */
void LexToEndOfLine(struct LexState *Lexer)
{
while (Lexer->Pos != Lexer->End && *Lexer->Pos != '\n')
Lexer->Pos++;
}

140
parse.c
View file

@ -149,7 +149,9 @@ void ParseFunctionCall(struct LexState *Lexer, struct Value *Result, Str *FuncNa
VariableGet(Lexer, FuncName, Result, &LValue);
if (Result->Typ != TypeFunction)
ProgramFail(Lexer, "not a function - can't call");
// XXX - handle macros here too
StackFrameAdd(Lexer);
if (Result->Val.Integer >= 0)
FuncLexer = FunctionStore[Result->Val.Integer];
@ -219,7 +221,11 @@ int ParseValue(struct LexState *Lexer, struct Value *Result, struct Value **LVal
else
{
if (RunIt)
{
VariableGet(Lexer, &Result->Val.String, Result, LValue);
if (!ISVALUETYPE(Result->Typ))
ProgramFail(Lexer, "bad variable type");
}
}
break;
@ -382,10 +388,60 @@ void ParseFunctionDefinition(struct LexState *Lexer, Str *Identifier, struct Lex
ProgramFail(Lexer, "'%S' is already defined", Identifier);
}
void ParseFor(struct LexState *Lexer, struct Value *Result, int RunIt)
{
struct Value Conditional;
struct LexState PreConditional;
struct LexState PreIncrement;
struct LexState PreStatement;
struct LexState After;
if (LexGetPlainToken(Lexer) != TokenOpenBracket)
ProgramFail(Lexer, "'(' expected");
if (!ParseStatement(Lexer, RunIt))
ProgramFail(Lexer, "statement expected");
PreConditional = *Lexer;
ParseIntExpression(Lexer, &Conditional, RunIt);
if (LexGetPlainToken(Lexer) != TokenSemicolon)
ProgramFail(Lexer, "';' expected");
PreIncrement = *Lexer;
ParseStatement(Lexer, FALSE);
if (LexGetPlainToken(Lexer) != TokenCloseBracket)
ProgramFail(Lexer, "')' expected");
PreStatement = *Lexer;
if (!ParseStatement(Lexer, RunIt && Conditional.Val.Integer))
ProgramFail(Lexer, "statement expected");
After = *Lexer;
while (Conditional.Val.Integer && RunIt)
{
*Lexer = PreIncrement;
ParseStatement(Lexer, TRUE);
*Lexer = PreConditional;
ParseIntExpression(Lexer, &Conditional, RunIt);
if (Conditional.Val.Integer)
{
*Lexer = PreStatement;
ParseStatement(Lexer, TRUE);
}
}
*Lexer = After;
}
/* parse a statement */
int ParseStatement(struct LexState *Lexer, int RunIt)
{
struct Value Conditional;
struct Value CValue;
struct LexState PreState = *Lexer;
union AnyValue LexerValue;
enum ValueType Typ;
@ -398,7 +454,7 @@ int ParseStatement(struct LexState *Lexer, int RunIt)
case TokenIdentifier:
*Lexer = PreState;
ParseExpression(Lexer, &Conditional, RunIt);
ParseExpression(Lexer, &CValue, RunIt);
break;
case TokenLeftBrace:
@ -410,15 +466,15 @@ int ParseStatement(struct LexState *Lexer, int RunIt)
break;
case TokenIf:
ParseIntExpression(Lexer, &Conditional, RunIt);
ParseIntExpression(Lexer, &CValue, RunIt);
if (!ParseStatement(Lexer, RunIt && Conditional.Val.Integer))
if (!ParseStatement(Lexer, RunIt && CValue.Val.Integer))
ProgramFail(Lexer, "statement expected");
if (LexPeekToken(Lexer, &LexerValue) == TokenElse)
{
LexGetToken(Lexer, &LexerValue);
if (!ParseStatement(Lexer, RunIt && !Conditional.Val.Integer))
if (!ParseStatement(Lexer, RunIt && !CValue.Val.Integer))
ProgramFail(Lexer, "statement expected");
}
break;
@ -429,12 +485,12 @@ int ParseStatement(struct LexState *Lexer, int RunIt)
do
{
*Lexer = PreConditional;
ParseIntExpression(Lexer, &Conditional, RunIt);
ParseIntExpression(Lexer, &CValue, RunIt);
if (!ParseStatement(Lexer, RunIt && Conditional.Val.Integer))
if (!ParseStatement(Lexer, RunIt && CValue.Val.Integer))
ProgramFail(Lexer, "statement expected");
} while (RunIt && Conditional.Val.Integer);
} while (RunIt && CValue.Val.Integer);
}
break;
@ -447,60 +503,13 @@ int ParseStatement(struct LexState *Lexer, int RunIt)
if (!ParseStatement(Lexer, RunIt))
ProgramFail(Lexer, "statement expected");
ParseIntExpression(Lexer, &Conditional, RunIt);
ParseIntExpression(Lexer, &CValue, RunIt);
} while (Conditional.Val.Integer && RunIt);
} while (CValue.Val.Integer && RunIt);
}
break;
case TokenFor:
{
struct LexState PreConditional;
struct LexState PreIncrement;
struct LexState PreStatement;
struct LexState After;
if (LexGetToken(Lexer, &LexerValue) != TokenOpenBracket)
ProgramFail(Lexer, "'(' expected");
if (!ParseStatement(Lexer, RunIt))
ProgramFail(Lexer, "statement expected");
PreConditional = *Lexer;
ParseIntExpression(Lexer, &Conditional, RunIt);
if (LexGetToken(Lexer, &LexerValue) != TokenSemicolon)
ProgramFail(Lexer, "';' expected");
PreIncrement = *Lexer;
ParseStatement(Lexer, FALSE);
if (LexGetToken(Lexer, &LexerValue) != TokenCloseBracket)
ProgramFail(Lexer, "')' expected");
PreStatement = *Lexer;
if (!ParseStatement(Lexer, RunIt && Conditional.Val.Integer))
ProgramFail(Lexer, "statement expected");
After = *Lexer;
while (Conditional.Val.Integer && RunIt)
{
*Lexer = PreIncrement;
ParseStatement(Lexer, TRUE);
*Lexer = PreConditional;
ParseIntExpression(Lexer, &Conditional, RunIt);
if (Conditional.Val.Integer)
{
*Lexer = PreStatement;
ParseStatement(Lexer, TRUE);
}
}
*Lexer = After;
}
break;
case TokenSemicolon: break;
@ -531,6 +540,21 @@ int ParseStatement(struct LexState *Lexer, int RunIt)
}
break;
case TokenHashDefine:
if (LexGetToken(Lexer, &LexerValue) != TokenIdentifier)
ProgramFail(Lexer, "identifier expected");
CValue.Val.String.Str = Lexer->Pos;
LexToEndOfLine(Lexer);
CValue.Val.String.Len = Lexer->Pos - CValue.Val.String.Str;
CValue.Typ = TypeMacro;
VariableDefine(Lexer, &LexerValue.String, &CValue);
break;
case TokenHashInclude:
ProgramFail(Lexer, "not implemented");
break;
case TokenDefault:
if (RunIt)
printf("Hello\n");

16
picoc.h
View file

@ -32,6 +32,8 @@
#define PATH_MAX 1024
#endif
#define ISVALUETYPE(t) (((t) == TypeInt) || ((t) == TypeFP) || ((t) == TypeString))
/* lexical tokens */
enum LexToken
{
@ -88,7 +90,10 @@ enum LexToken
TokenSwitch,
TokenCase,
TokenDefault,
TokenReturn
TokenReturn,
TokenHashDefine,
TokenHashInclude,
TokenEndOfLine
};
/* string type so we can use source file strings */
@ -113,7 +118,8 @@ enum ValueType
TypeInt,
TypeFP,
TypeString,
TypeFunction
TypeFunction,
TypeMacro
};
union AnyValue
@ -160,7 +166,10 @@ struct StackFrame
};
/* globals */
extern struct Table GlobalTable;
struct Table GlobalTable;
extern struct Value Parameter[PARAMETER_MAX];
extern int ParameterUsed;
extern struct Value ReturnValue;
/* str.c */
void StrToC(char *Dest, int DestSize, const Str *Source);
@ -186,6 +195,7 @@ enum LexToken LexGetToken(struct LexState *Lexer, union AnyValue *Value);
enum LexToken LexGetPlainToken(struct LexState *Lexer);
enum LexToken LexPeekToken(struct LexState *Lexer, union AnyValue *Value);
enum LexToken LexPeekPlainToken(struct LexState *Lexer);
void LexToEndOfLine(struct LexState *Lexer);
/* parse.c */
void ParseInit(void);

11
test/Makefile Normal file
View file

@ -0,0 +1,11 @@
TESTS= assignment.test \
comment.test \
printf.test
%.test: %.t
@./picotest.sh $<
all: test
test: $(TESTS)
@echo "test passed"

5
test/assignment.t Normal file
View file

@ -0,0 +1,5 @@
int a;
a = 42;
printint(a);
%%
42

25
test/picotest.sh Executable file
View file

@ -0,0 +1,25 @@
#!/bin/sh
if [ ! -f $1 ]
then
echo "no test file $1"
exit 1
fi
TESTNAME="`echo $1 | sed 's/\.t$//'`"
echo $TESTNAME...
awk '{ if ($1 == "%%") hide = 1; if (hide != 1) print; }' <$TESTNAME.t >$TESTNAME.c
awk '{ if (show == 1) print; if ($1 == "%%") show = 1; }' <$TESTNAME.t >$TESTNAME.expect
../picoc $TESTNAME.c 2>&1 >$TESTNAME.out
if [ "x`diff -q $TESTNAME.expect $TESTNAME.out`" != "x" ]
then
echo "error in test $TESTNAME"
diff -u $TESTNAME.expect $TESTNAME.out
rm -f $TESTNAME.c $TESTNAME.expect $TESTNAME.out
exit 1
fi
rm -f $TESTNAME.c $TESTNAME.expect $TESTNAME.out

3
test/printf.t Normal file
View file

@ -0,0 +1,3 @@
default
%%
Hello