parsing and environment infrastructure
. add env struct and parameter, . add lookup() and append() functions . add `q` and `add` in eval,
This commit is contained in:
parent
3ba49003f3
commit
ca33a1096c
243
tokenize.l
243
tokenize.l
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/* llm - litle lisp machine:
|
||||||
* flex tokenize.l
|
* Author: Christian Barthel <bch@online.de>
|
||||||
* cc lex.yy.c -lfl
|
* Compile: flex tokenize.l
|
||||||
|
* cc lex.yy.c -lfl
|
||||||
*/
|
*/
|
||||||
%{
|
%{
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
@ -11,26 +12,34 @@ struct token;
|
||||||
struct token {
|
struct token {
|
||||||
int type;
|
int type;
|
||||||
union {
|
union {
|
||||||
char *str;
|
char *str;
|
||||||
int num;
|
int num;
|
||||||
} v;
|
} v;
|
||||||
struct token *next;
|
struct token *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ast;
|
struct ast;
|
||||||
struct ast {
|
struct ast {
|
||||||
int type;
|
int type;
|
||||||
union {
|
union {
|
||||||
struct token *token;
|
struct token *token;
|
||||||
struct ast *list;
|
struct ast *list;
|
||||||
} v;
|
} v;
|
||||||
struct ast *next;
|
struct ast *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct env;
|
||||||
|
struct env {
|
||||||
|
char *name;
|
||||||
|
struct ast *a;
|
||||||
|
struct env *parent;
|
||||||
|
struct env *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
enum asttype {
|
enum asttype {
|
||||||
AST_TOK = 400,
|
AST_TOK = 400,
|
||||||
AST_LIST = 401
|
AST_LIST = 401,
|
||||||
|
AST_QUOTE = 402
|
||||||
};
|
};
|
||||||
enum yytokentype {
|
enum yytokentype {
|
||||||
NUM = 258,
|
NUM = 258,
|
||||||
|
@ -38,7 +47,8 @@ enum yytokentype {
|
||||||
RPAR = 260,
|
RPAR = 260,
|
||||||
SYM = 261,
|
SYM = 261,
|
||||||
STR = 262,
|
STR = 262,
|
||||||
EOL = 263
|
EOL = 263,
|
||||||
|
QUOTE = 264
|
||||||
};
|
};
|
||||||
|
|
||||||
struct token *make_token(enum yytokentype, int, char*);
|
struct token *make_token(enum yytokentype, int, char*);
|
||||||
|
@ -47,32 +57,62 @@ char *yystr;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
%%
|
%%
|
||||||
"(" { return LPAR; }
|
"(" { return LPAR; }
|
||||||
")" { return RPAR; }
|
")" { return RPAR; }
|
||||||
[0-9]+ { yylval = atoi(yytext); return NUM; }
|
[0-9]+ { yylval = atoi(yytext); return NUM; }
|
||||||
\n { return EOL; }
|
\n { return EOL; }
|
||||||
[ \t\n] { /* ignore white space */ }
|
[ \t\n] { /* ignore white space */ }
|
||||||
\".*\" { return STR; }
|
\".*\" { return STR; }
|
||||||
[a-zA-Z][a-zA-Z0-9]* { return SYM; }
|
[a-zA-Z][a-zA-Z0-9]* { return SYM; }
|
||||||
. { err(1, "invalid symbol: %s\n", yytext); }
|
. { err(1, "invalid symbol: %s\n", yytext); }
|
||||||
%%
|
%%
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Goal: write LISP read-eval-print Loop and support
|
* Goal: write LISP read-eval-print Loop and support
|
||||||
* @ Function
|
* @ Function
|
||||||
* 0,1 boolean (nand 0 1) -> boolean
|
* 0,1 boolean (nand 0 1) -> boolean
|
||||||
* (eq 0 0) -> boolean
|
* (eq 0 0) -> boolean
|
||||||
* "str" Strings (streq str1 str2) -> boolean
|
* "str" Strings (streq str1 str2) -> boolean
|
||||||
* abc Symboles (symeq s1 s2) -> boolean
|
* abc Symboles (symeq s1 s2) -> boolean
|
||||||
* 1234 Number/Integer [add,sub,div,mul,mod] -> number
|
* 1234 Number/Integer [add,sub,div,mul,mod] -> num
|
||||||
* [lt, numeq] -> boolean
|
* [lt, numeq] -> boolean
|
||||||
* [num2bool] -> boolean
|
* [num2bool] -> boolean
|
||||||
*
|
*
|
||||||
* (def <var> <symbol,number,#function)
|
* (def <var> <symbol,number,#function)
|
||||||
* (lambda (<symbol> ..) (<sym..> ))
|
* (lambda (<symbol> ..) (<sym..> ))
|
||||||
* (if <bool> <body> <else>)
|
* (if <bool> <body> <else>)
|
||||||
|
* (quote a) -> a
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
debug_token(struct token *x, int indent) {
|
||||||
|
for (int i = 0; i < indent; i++) putc(' ', stderr);
|
||||||
|
fprintf(stderr, "%d: (%p)", x->type, x);
|
||||||
|
if (x->type == NUM) fprintf(stderr, "%d @ %p", x->v.num, x);
|
||||||
|
if (x->type == STR || x->type == SYM)
|
||||||
|
fprintf(stderr, "%s", x->v.str);
|
||||||
|
putc('\n', stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
debug_ast(struct ast *a, int indent) {
|
||||||
|
while (a) {
|
||||||
|
for (int i = 0; i < indent; i++) putc(' ', stderr);
|
||||||
|
fprintf(stderr, "AST: %s,a=%p,list=%p,token=%p,next=%p", (a->type == AST_TOK) ? "AST_TOK" :
|
||||||
|
"AST_LIST", a, a->v.list, a->v.token, a->next);
|
||||||
|
putc('\n', stderr);
|
||||||
|
if (a->type == AST_LIST) {
|
||||||
|
fprintf(stderr, "enter %p\n", a->v.list);
|
||||||
|
fprintf(stderr, "==\n");
|
||||||
|
debug_ast(a->v.list, indent+4);
|
||||||
|
fprintf(stderr, "==\n");
|
||||||
|
}
|
||||||
|
else if(a->type == AST_TOK)
|
||||||
|
debug_token(a->v.token, indent);
|
||||||
|
a = a->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct token*
|
struct token*
|
||||||
make_token(enum yytokentype type, int num, char* str)
|
make_token(enum yytokentype type, int num, char* str)
|
||||||
{
|
{
|
||||||
|
@ -91,7 +131,8 @@ make_token(enum yytokentype type, int num, char* str)
|
||||||
struct ast*
|
struct ast*
|
||||||
make_ast(enum asttype type, struct token *t)
|
make_ast(enum asttype type, struct token *t)
|
||||||
{
|
{
|
||||||
struct ast *a = (struct ast*) calloc(1, sizeof(struct ast));
|
struct ast *a =
|
||||||
|
(struct ast*) calloc(1, sizeof(struct ast));
|
||||||
if (a == NULL)
|
if (a == NULL)
|
||||||
err(1, "malloc failed");
|
err(1, "malloc failed");
|
||||||
a->type = type;
|
a->type = type;
|
||||||
|
@ -102,6 +143,25 @@ make_ast(enum asttype type, struct token *t)
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct env*
|
||||||
|
make_env(char *name, struct ast *a)
|
||||||
|
{
|
||||||
|
struct env *e =
|
||||||
|
(struct env*) calloc(1, sizeof(struct env));
|
||||||
|
if (e == NULL)
|
||||||
|
err(1, "malloc failed");
|
||||||
|
e->name = name;
|
||||||
|
e->a = a;
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* upon parsing a list of tokens like:
|
||||||
|
* "(" -> "def" -> "(" -> "b" -> ")" -> "c" -> ")"
|
||||||
|
* care must be taken when entering a new sub-list (b),
|
||||||
|
* because the processing in the upper level must continue
|
||||||
|
* where the sub-list ended, i.e. "c" must be the next
|
||||||
|
* token on this level.
|
||||||
|
*/
|
||||||
struct token *next;
|
struct token *next;
|
||||||
struct ast*
|
struct ast*
|
||||||
parse(struct token *t)
|
parse(struct token *t)
|
||||||
|
@ -118,85 +178,113 @@ parse(struct token *t)
|
||||||
a = parse(t);
|
a = parse(t);
|
||||||
if (a == NULL)
|
if (a == NULL)
|
||||||
err(1, "syntax error");
|
err(1, "syntax error");
|
||||||
if (head->v.list == NULL) {
|
if (head->v.list == NULL)
|
||||||
p = head->v.list = a;
|
p = head->v.list = a;
|
||||||
} else {
|
else
|
||||||
p->next = a;
|
p->next = a;
|
||||||
}
|
|
||||||
p = a;
|
p = a;
|
||||||
t = next; /* skip all tokens that are processed */
|
t = next; /* skip all tokens that are processed */
|
||||||
assert(t != NULL);
|
assert(t != NULL);
|
||||||
}
|
}
|
||||||
fprintf(stderr, "a=%p, t=%p\n", a, t);
|
|
||||||
assert(t->type == RPAR);
|
assert(t->type == RPAR);
|
||||||
t = t->next;
|
t = t->next;
|
||||||
next = t;
|
next = t;
|
||||||
return head;
|
return head;
|
||||||
} else if (t->type == NUM) {
|
} else if (t->type == NUM) {
|
||||||
printf("is a number %d\n", t->v.num);
|
|
||||||
next = t->next;
|
next = t->next;
|
||||||
return make_ast(AST_TOK, t);
|
return make_ast(AST_TOK, t);
|
||||||
} else if (t->type == SYM) {
|
} else if (t->type == SYM) {
|
||||||
printf("is a sym: %s (%p)\n", t->v.str, t);
|
|
||||||
next = t->next;
|
next = t->next;
|
||||||
return make_ast(AST_TOK, t);
|
return make_ast(AST_TOK, t);
|
||||||
} else if (t->type == STR) {
|
} else if (t->type == STR) {
|
||||||
next = t->next;
|
next = t->next;
|
||||||
printf("is a str: %s\n", t->v.str);
|
|
||||||
return make_ast(AST_TOK, t);
|
return make_ast(AST_TOK, t);
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
debug_token(struct token *x, indent) {
|
append(struct env *e, struct env *a)
|
||||||
for (int i = 0; i < indent; i++) putc(' ', stderr);
|
{
|
||||||
fprintf(stderr, "%d: (%p)", x->type, x);
|
if (e->next == NULL) {
|
||||||
if (x->type == NUM) fprintf(stderr, "%d @ %p", x->v.num, x);
|
e->next = a;
|
||||||
if (x->type == STR || x->type == SYM)
|
return;
|
||||||
fprintf(stderr, "%s", x->v.str);
|
} else
|
||||||
putc('\n', stderr);
|
append(e->next, a);
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
debug_ast(struct ast *a, indent) {
|
|
||||||
while (a) {
|
|
||||||
for (int i = 0; i < indent; i++) putc(' ', stderr);
|
|
||||||
fprintf(stderr, "AST: %s,a=%p,list=%p,token=%p,next=%p", (a->type == AST_TOK) ? "AST_TOK" :
|
|
||||||
"AST_LIST", a, a->v.list, a->v.token, a->next);
|
|
||||||
putc('\n', stderr);
|
|
||||||
if (a->type == AST_LIST) {
|
|
||||||
fprintf(stderr, "enter %p\n", a->v.list);
|
|
||||||
fprintf(stderr, "==\n");
|
|
||||||
debug_ast(a->v.list, indent+4);
|
|
||||||
fprintf(stderr, "==\n");
|
|
||||||
}
|
|
||||||
else if(a->type == AST_TOK)
|
|
||||||
debug_token(a->v.token, indent);
|
|
||||||
a = a->next;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ast *
|
struct ast *
|
||||||
eval(struct ast *a)
|
lookup(struct env *e, char *name)
|
||||||
{
|
{
|
||||||
|
if (e == NULL)
|
||||||
|
return NULL;
|
||||||
|
for (struct env *t = e; t != NULL; t = t->next)
|
||||||
|
if (strcmp(t->name, name) == 0)
|
||||||
|
return t->a;
|
||||||
|
return lookup(e->parent, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ast *
|
||||||
|
eval(struct ast *a, struct env *e)
|
||||||
|
{
|
||||||
|
if (a == NULL) return NULL;
|
||||||
|
|
||||||
if (a->type == AST_TOK) {
|
if (a->type == AST_TOK) {
|
||||||
if (a->v.token->type == NUM) {
|
if (a->v.token->type == NUM) {
|
||||||
return make_ast(AST_TOK, a->v.token);
|
return make_ast(AST_TOK, a->v.token);
|
||||||
}
|
}
|
||||||
else if (a->v.token->type == SYM) {
|
else if (a->v.token->type == SYM) {
|
||||||
|
fprintf(stderr, "process: %s\n", a->v.token->v.str);
|
||||||
if (strcmp(a->v.token->v.str, "def") == 0) {
|
if (strcmp(a->v.token->v.str, "def") == 0) {
|
||||||
printf("define: %s\n", a->next->v.token->v.str);
|
a = a->next;
|
||||||
/* x = eval a->next->next */
|
assert((a->next->type == AST_TOK &&
|
||||||
/* install: var:=x */
|
a->next->v.token->type== STR) ||
|
||||||
|
(a->next->type == AST_TOK &&
|
||||||
|
a->next->v.token->type == NUM) ||
|
||||||
|
(a->next->type == AST_LIST) ||
|
||||||
|
(a->next->type == AST_TOK &&
|
||||||
|
a->next->v.token->type== SYM));
|
||||||
|
append(e, make_env(a->v.token->v.str, eval(a->next, e)));
|
||||||
|
return NULL;
|
||||||
|
} else if (strcmp(a->v.token->v.str, "lm") == 0) {
|
||||||
|
} else if (strcmp(a->v.token->v.str, "if") == 0) {
|
||||||
|
} else if (strcmp(a->v.token->v.str, "add") == 0) {
|
||||||
|
assert (a != NULL &&
|
||||||
|
a->next != NULL &&
|
||||||
|
a->next->next != NULL &&
|
||||||
|
a->next->next->next == NULL);
|
||||||
|
|
||||||
|
struct ast *op1 = eval(a->next, e);
|
||||||
|
struct ast *op2 = eval(a->next->next, e);
|
||||||
|
|
||||||
|
assert (op1 != NULL &&
|
||||||
|
op1->type == AST_TOK &&
|
||||||
|
op1->v.token->type == NUM &&
|
||||||
|
op2 != NULL &&
|
||||||
|
op2->type == AST_TOK &&
|
||||||
|
op2->v.token->type == NUM);
|
||||||
|
return make_ast(AST_TOK,
|
||||||
|
make_token(NUM,
|
||||||
|
op1->v.token->v.num +
|
||||||
|
op2->v.token->v.num,
|
||||||
|
NULL));
|
||||||
|
} else if (strcmp(a->v.token->v.str, "q") == 0) {
|
||||||
|
//return make_ast(AST_QUOTE, a->next);
|
||||||
|
return a->next;
|
||||||
|
} else {
|
||||||
|
// fprintf(stderr, "Lookup: %s\n", a->v.token->v.str);
|
||||||
|
struct ast *var = lookup(e, a->v.token->v.str);
|
||||||
|
assert(var != NULL); /* variable found? */
|
||||||
|
return var;
|
||||||
}
|
}
|
||||||
|
/* XXX: nand, eq, streq, symeq, add, sub, div, mul,
|
||||||
|
* mod, lt, numeq, num2bool
|
||||||
|
*/
|
||||||
|
|
||||||
} else if (a->v.token->type == STR)
|
} else if (a->v.token->type == STR)
|
||||||
fprintf(stderr, "str\n");
|
return make_ast(AST_TOK, a->v.token);
|
||||||
} else if (a->type == AST_LIST) {
|
} else if (a->type == AST_LIST) {
|
||||||
eval(a->v.list);
|
return eval(a->v.list, e);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "%d\n", a->type);
|
fprintf(stderr, "%d\n", a->type);
|
||||||
assert(0);
|
assert(0);
|
||||||
|
@ -207,10 +295,18 @@ eval(struct ast *a)
|
||||||
void
|
void
|
||||||
pr(struct ast *a)
|
pr(struct ast *a)
|
||||||
{
|
{
|
||||||
|
if (a == NULL) {
|
||||||
|
fprintf(stderr, "null %p\n", a);
|
||||||
|
return;
|
||||||
|
}
|
||||||
switch (a->type) {
|
switch (a->type) {
|
||||||
case AST_TOK:
|
case AST_TOK:
|
||||||
if (a->v.token->type == NUM)
|
if (a->v.token->type == NUM)
|
||||||
printf("%d\n", a->v.token->v.num);
|
printf("%d\n", a->v.token->v.num);
|
||||||
|
else if (a->v.token->type == STR)
|
||||||
|
printf("%s\n", a->v.token->v.str);
|
||||||
|
else if (a->v.token->type == SYM)
|
||||||
|
printf("%s\n", a->v.token->v.str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,8 +314,11 @@ int main(void)
|
||||||
{
|
{
|
||||||
int tok;
|
int tok;
|
||||||
struct token *t, *u = NULL, *start = NULL;
|
struct token *t, *u = NULL, *start = NULL;
|
||||||
|
struct env default_env = { 0 }, *env;
|
||||||
|
env = &default_env;
|
||||||
|
env->name = "";
|
||||||
|
|
||||||
while(tok = yylex()) {
|
while((tok = yylex())) {
|
||||||
/* read token and create list */
|
/* read token and create list */
|
||||||
t = make_token(tok, yylval, yytext);
|
t = make_token(tok, yylval, yytext);
|
||||||
if (start == NULL) start = t;
|
if (start == NULL) start = t;
|
||||||
|
@ -234,7 +333,7 @@ int main(void)
|
||||||
|
|
||||||
/* struct ast *tl = parse(start); */
|
/* struct ast *tl = parse(start); */
|
||||||
/* eval(tl); */
|
/* eval(tl); */
|
||||||
eval(parse(start));
|
pr(eval(parse(start), env));
|
||||||
//debug_ast(tl, 1);
|
//debug_ast(tl, 1);
|
||||||
|
|
||||||
/* start anew: */
|
/* start anew: */
|
||||||
|
|
Loading…
Reference in New Issue