diff --git a/Makefile b/Makefile index 9baf5ca..ac845a0 100644 --- a/Makefile +++ b/Makefile @@ -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) *~ diff --git a/intrinsic.c b/intrinsic.c index 4a26847..cf43313 100644 --- a/intrinsic.c +++ b/intrinsic.c @@ -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) diff --git a/lex.c b/lex.c index 8ff44ef..cff7111 100644 --- a/lex.c +++ b/lex.c @@ -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++; +} + diff --git a/parse.c b/parse.c index 18ac4b6..bf2dbb5 100644 --- a/parse.c +++ b/parse.c @@ -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"); diff --git a/picoc.h b/picoc.h index 877255a..5ce004f 100644 --- a/picoc.h +++ b/picoc.h @@ -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); diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..66cc68d --- /dev/null +++ b/test/Makefile @@ -0,0 +1,11 @@ +TESTS= assignment.test \ + comment.test \ + printf.test + +%.test: %.t + @./picotest.sh $< + +all: test + +test: $(TESTS) + @echo "test passed" diff --git a/test/assignment.t b/test/assignment.t new file mode 100644 index 0000000..d458661 --- /dev/null +++ b/test/assignment.t @@ -0,0 +1,5 @@ +int a; +a = 42; +printint(a); +%% +42 diff --git a/test/picotest.sh b/test/picotest.sh new file mode 100755 index 0000000..b88da81 --- /dev/null +++ b/test/picotest.sh @@ -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 + diff --git a/test/printf.t b/test/printf.t new file mode 100644 index 0000000..b4871b9 --- /dev/null +++ b/test/printf.t @@ -0,0 +1,3 @@ +default +%% +Hello