feat: implement M1 Ferro frontend
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
CC ?= cc
|
||||
CFLAGS ?= -O2 -Wall -Wextra -std=c89
|
||||
CPPFLAGS ?= -Isrc
|
||||
SRC = src/arena.c src/diag.c src/lexer.c src/ast.c src/parser.c src/driver.c
|
||||
OBJ = $(SRC:.c=.o)
|
||||
|
||||
.PHONY: all clean test dos-build
|
||||
all: fec
|
||||
|
||||
fec: $(OBJ)
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(OBJ)
|
||||
|
||||
src/%.o: src/%.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
|
||||
|
||||
test: fec
|
||||
@./tests/run-tests.sh
|
||||
|
||||
dos-build:
|
||||
@echo "Run build-dos.bat inside FreeDOS/Open Watcom."
|
||||
|
||||
clean:
|
||||
$(RM) $(OBJ) fec
|
||||
@@ -0,0 +1,29 @@
|
||||
@echo off
|
||||
rem Open Watcom C89 build. TEST-DOS.BAT runs this from C:\FEC.
|
||||
C:
|
||||
cd \FEC
|
||||
if exist BUILD.OK del BUILD.OK
|
||||
if exist BUILD.FAIL del BUILD.FAIL
|
||||
if exist fec.exe del fec.exe
|
||||
if exist __wcl__.lnk del __wcl__.lnk
|
||||
if exist arena.obj del arena.obj
|
||||
if exist diag.obj del diag.obj
|
||||
if exist lexer.obj del lexer.obj
|
||||
if exist ast.obj del ast.obj
|
||||
if exist parser.obj del parser.obj
|
||||
if exist driver.obj del driver.obj
|
||||
|
||||
if "%WATCOM%"=="" set WATCOM=C:\DEVEL\WATCOMC
|
||||
if not exist %WATCOM%\BINW\WCL.EXE goto build_fail
|
||||
set PATH=%WATCOM%\BINW;%WATCOM%\BINP;%PATH%
|
||||
wcl -q -za -wx -bt=dos -k32768 -fe=fec.exe src\arena.c src\diag.c src\lexer.c src\ast.c src\parser.c src\driver.c
|
||||
if errorlevel 1 goto build_fail
|
||||
if not exist fec.exe goto build_fail
|
||||
echo OK>BUILD.OK
|
||||
cd C:\FEC
|
||||
goto build_done
|
||||
|
||||
:build_fail
|
||||
echo FAIL>BUILD.FAIL
|
||||
|
||||
:build_done
|
||||
@@ -0,0 +1,56 @@
|
||||
#include "arena.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct FeArenaBlock {
|
||||
FeArenaBlock *next;
|
||||
size_t used;
|
||||
size_t size;
|
||||
unsigned char data[1];
|
||||
};
|
||||
|
||||
void fe_arena_init(FeArena *a, size_t block_size)
|
||||
{
|
||||
a->blocks = 0;
|
||||
a->block_size = block_size ? block_size : 16384;
|
||||
}
|
||||
|
||||
void fe_arena_destroy(FeArena *a)
|
||||
{
|
||||
FeArenaBlock *b = a->blocks;
|
||||
while (b) {
|
||||
FeArenaBlock *n = b->next;
|
||||
free(b);
|
||||
b = n;
|
||||
}
|
||||
a->blocks = 0;
|
||||
}
|
||||
|
||||
void *fe_arena_alloc(FeArena *a, size_t size)
|
||||
{
|
||||
FeArenaBlock *b;
|
||||
size_t need;
|
||||
if (size == 0) size = 1;
|
||||
need = (size + 7u) & ~(size_t)7u;
|
||||
b = a->blocks;
|
||||
if (!b || b->used + need > b->size) {
|
||||
size_t bs = a->block_size > need ? a->block_size : need;
|
||||
b = (FeArenaBlock *)malloc(sizeof(FeArenaBlock) + bs - 1);
|
||||
if (!b) return 0;
|
||||
b->next = a->blocks;
|
||||
b->used = 0;
|
||||
b->size = bs;
|
||||
a->blocks = b;
|
||||
}
|
||||
b->used += need;
|
||||
return b->data + b->used - need;
|
||||
}
|
||||
|
||||
char *fe_arena_strdup(FeArena *a, const char *s, size_t n)
|
||||
{
|
||||
char *p = (char *)fe_arena_alloc(a, n + 1);
|
||||
if (!p) return 0;
|
||||
if (n) memcpy(p, s, n);
|
||||
p[n] = '\0';
|
||||
return p;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef FE_ARENA_H
|
||||
#define FE_ARENA_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct FeArenaBlock FeArenaBlock;
|
||||
typedef struct FeArena {
|
||||
FeArenaBlock *blocks;
|
||||
size_t block_size;
|
||||
} FeArena;
|
||||
|
||||
void fe_arena_init(FeArena *a, size_t block_size);
|
||||
void fe_arena_destroy(FeArena *a);
|
||||
void *fe_arena_alloc(FeArena *a, size_t size);
|
||||
char *fe_arena_strdup(FeArena *a, const char *s, size_t n);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "ast.h"
|
||||
#include <stdio.h>
|
||||
|
||||
void fe_ast_init(FeAst *a) { fe_arena_init(&a->arena, 32768); a->root=0; }
|
||||
void fe_ast_destroy(FeAst *a) { fe_arena_destroy(&a->arena); a->root=0; }
|
||||
FeNode *fe_node(FeAst *a, FeNodeKind k, FeLoc loc, const char *text, unsigned long len)
|
||||
{
|
||||
FeNode *n=(FeNode *)fe_arena_alloc(&a->arena,sizeof(FeNode));
|
||||
if (!n) return 0;
|
||||
n->kind=k; n->loc=loc; n->text=text?fe_arena_strdup(&a->arena,text,len):0;
|
||||
n->a=n->b=n->c=n->children=n->next=0; return n;
|
||||
}
|
||||
void fe_node_add(FeNode *parent, FeNode *child)
|
||||
{
|
||||
FeNode *p;
|
||||
if (!child) return;
|
||||
if (!parent->children) { parent->children=child; return; }
|
||||
p=parent->children; while(p->next) p=p->next; p->next=child;
|
||||
}
|
||||
static void spaces(int n, FILE *out) { while(n-->0) fputc(' ',out); }
|
||||
void fe_ast_dump(const FeNode *n, int indent, FILE *out)
|
||||
{
|
||||
const FeNode *c;
|
||||
if (!n) return;
|
||||
spaces(indent,out); fprintf(out,"(%s",fe_node_name(n->kind));
|
||||
if (n->text) fprintf(out," %s",n->text);
|
||||
fputc('\n',out);
|
||||
if (n->a) fe_ast_dump(n->a,indent+2,out);
|
||||
if (n->b) fe_ast_dump(n->b,indent+2,out);
|
||||
if (n->c) fe_ast_dump(n->c,indent+2,out);
|
||||
for(c=n->children;c;c=c->next) fe_ast_dump(c,indent+2,out);
|
||||
spaces(indent,out); fputc(')',out); fputc('\n',out);
|
||||
}
|
||||
const char *fe_node_name(FeNodeKind k)
|
||||
{
|
||||
static const char *names[] = {"unit","import","fn","struct","enum","error","const","global","field","param","variant","block","let","var","expr-stmt","assign","if","while","for","match","arm","return","break","continue","defer","unsafe","asm","type","expr","binary","unary","call","index","member","literal","ident","struct-init","error"};
|
||||
if ((unsigned)k >= sizeof(names)/sizeof(names[0])) return "node";
|
||||
return names[k];
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef FE_AST_H
|
||||
#define FE_AST_H
|
||||
|
||||
#include "arena.h"
|
||||
#include "lexer.h"
|
||||
#include <stdio.h>
|
||||
|
||||
typedef enum FeNodeKind {
|
||||
FE_N_UNIT, FE_N_IMPORT, FE_N_FN, FE_N_STRUCT, FE_N_ENUM, FE_N_ERROR_DECL, FE_N_CONST,
|
||||
FE_N_GLOBAL, FE_N_FIELD, FE_N_PARAM, FE_N_VARIANT, FE_N_BLOCK, FE_N_LET, FE_N_VAR,
|
||||
FE_N_EXPR_STMT, FE_N_ASSIGN, FE_N_IF, FE_N_WHILE, FE_N_FOR, FE_N_MATCH, FE_N_ARM,
|
||||
FE_N_RETURN, FE_N_BREAK, FE_N_CONTINUE, FE_N_DEFER, FE_N_UNSAFE, FE_N_ASM,
|
||||
FE_N_TYPE, FE_N_EXPR, FE_N_BINARY, FE_N_UNARY, FE_N_CALL, FE_N_INDEX, FE_N_MEMBER,
|
||||
FE_N_LITERAL, FE_N_IDENT, FE_N_STRUCT_INIT, FE_N_ERROR_NODE
|
||||
} FeNodeKind;
|
||||
|
||||
typedef struct FeNode FeNode;
|
||||
struct FeNode {
|
||||
FeNodeKind kind;
|
||||
FeLoc loc;
|
||||
char *text;
|
||||
FeNode *a;
|
||||
FeNode *b;
|
||||
FeNode *c;
|
||||
FeNode *children;
|
||||
FeNode *next;
|
||||
};
|
||||
|
||||
typedef struct FeAst {
|
||||
FeArena arena;
|
||||
FeNode *root;
|
||||
} FeAst;
|
||||
|
||||
void fe_ast_init(FeAst *a);
|
||||
void fe_ast_destroy(FeAst *a);
|
||||
FeNode *fe_node(FeAst *a, FeNodeKind k, FeLoc loc, const char *text, unsigned long len);
|
||||
void fe_node_add(FeNode *parent, FeNode *child);
|
||||
void fe_ast_dump(const FeNode *n, int indent, FILE *out);
|
||||
const char *fe_node_name(FeNodeKind k);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "diag.h"
|
||||
|
||||
void fe_diag_error(FeDiags *d, FeLoc loc, const char *msg)
|
||||
{
|
||||
d->errors++;
|
||||
fprintf(stderr, "%s:%lu:%lu: error: %s\n", loc.file ? loc.file : "<source>", loc.line, loc.col, msg);
|
||||
}
|
||||
|
||||
void fe_diag_errorf(FeDiags *d, FeLoc loc, const char *msg, const char *arg)
|
||||
{
|
||||
d->errors++;
|
||||
fprintf(stderr, "%s:%lu:%lu: error: ", loc.file ? loc.file : "<source>", loc.line, loc.col);
|
||||
fprintf(stderr, msg, arg);
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
|
||||
void fe_diag_note(FeLoc loc, const char *msg)
|
||||
{
|
||||
fprintf(stderr, "%s:%lu:%lu: note: %s\n", loc.file ? loc.file : "<source>", loc.line, loc.col, msg);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef FE_DIAG_H
|
||||
#define FE_DIAG_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct FeLoc {
|
||||
const char *file;
|
||||
unsigned long line;
|
||||
unsigned long col;
|
||||
} FeLoc;
|
||||
|
||||
typedef struct FeDiags {
|
||||
unsigned long errors;
|
||||
unsigned long warnings;
|
||||
} FeDiags;
|
||||
|
||||
void fe_diag_error(FeDiags *d, FeLoc loc, const char *msg);
|
||||
void fe_diag_errorf(FeDiags *d, FeLoc loc, const char *msg, const char *arg);
|
||||
void fe_diag_note(FeLoc loc, const char *msg);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
#include "parser.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static char *read_file(const char *name, unsigned long *size)
|
||||
{
|
||||
FILE *f; long n; char *p;
|
||||
f=fopen(name,"rb"); if(!f){fprintf(stderr,"fec: cannot open %s\n",name);return 0;}
|
||||
if(fseek(f,0L,SEEK_END)!=0){fclose(f);return 0;} n=ftell(f); if(n<0){fclose(f);return 0;} rewind(f);
|
||||
p=(char *)malloc((unsigned long)n+1); if(!p){fclose(f);return 0;}
|
||||
if(n && fread(p,1,(size_t)n,f)!=(size_t)n){free(p);fclose(f);return 0;} fclose(f);p[n]='\0';*size=(unsigned long)n;return p;
|
||||
}
|
||||
static void usage(void)
|
||||
{ puts("usage: fec [--dump-ast] file.fe [--target=bits16|bits32] [--model=small|large]"); }
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i,dump=0; const char *file=0; unsigned long n; char *src; FeDiags d; FeAst ast; FeParser p;
|
||||
if(argc<2){usage();return 2;}
|
||||
for(i=1;i<argc;i++) { if(strcmp(argv[i],"--dump-ast")==0) dump=1; else if(argv[i][0]!='-') file=argv[i]; else if(strncmp(argv[i],"--target=",9)==0 || strncmp(argv[i],"--model=",8)==0 || strcmp(argv[i],"--no-checks")==0 || strcmp(argv[i],"--emit-c")==0 || strcmp(argv[i],"--strip-error-names")==0) { } else if(strcmp(argv[i],"--help")==0){usage();return 0;} else {fprintf(stderr,"fec: unknown option %s\n",argv[i]);return 2;} }
|
||||
if(!file){fprintf(stderr,"fec: no input file\n");return 2;}
|
||||
src=read_file(file,&n);if(!src)return 2;d.errors=0;d.warnings=0;fe_ast_init(&ast);fe_parser_init(&p,&ast,src,n,file,&d);ast.root=fe_parse_unit(&p);
|
||||
if(dump) fe_ast_dump(ast.root,0,stdout);
|
||||
fe_ast_destroy(&ast); free(src); return d.errors?1:0;
|
||||
}
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
#include "lexer.h"
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct FeKw { const char *s; FeTokKind k; } FeKw;
|
||||
static const FeKw keywords[] = {
|
||||
{"unit",FE_TOK_UNIT},{"import",FE_TOK_IMPORT},{"pub",FE_TOK_PUB},{"fn",FE_TOK_FN},
|
||||
{"struct",FE_TOK_STRUCT},{"enum",FE_TOK_ENUM},{"error",FE_TOK_ERROR_KW},{"const",FE_TOK_CONST},
|
||||
{"static",FE_TOK_STATIC},{"var",FE_TOK_VAR},{"let",FE_TOK_LET},{"mut",FE_TOK_MUT},
|
||||
{"if",FE_TOK_IF},{"else",FE_TOK_ELSE},{"while",FE_TOK_WHILE},{"for",FE_TOK_FOR},{"in",FE_TOK_IN},
|
||||
{"match",FE_TOK_MATCH},{"return",FE_TOK_RETURN},{"break",FE_TOK_BREAK},{"continue",FE_TOK_CONTINUE},
|
||||
{"defer",FE_TOK_DEFER},{"unsafe",FE_TOK_UNSAFE},{"comptime",FE_TOK_COMPTIME},{"asm",FE_TOK_ASM},
|
||||
{"try",FE_TOK_TRY},{"catch",FE_TOK_CATCH},{"as",FE_TOK_AS},{"extern",FE_TOK_EXTERN},
|
||||
{"interrupt",FE_TOK_INTERRUPT},{"interrupt_safe",FE_TOK_INTERRUPT_SAFE},{"far",FE_TOK_FAR},
|
||||
{"true",FE_TOK_TRUE},{"false",FE_TOK_FALSE},{"null",FE_TOK_NULL},{"undefined",FE_TOK_UNDEFINED},
|
||||
{"shared",FE_TOK_SHARED},{"atomic",FE_TOK_ATOMIC},{"critical",FE_TOK_CRITICAL},
|
||||
{"self",FE_TOK_SELF},{"Self",FE_TOK_SELFTYPE},{"type",FE_TOK_TYPE},
|
||||
{"packed",FE_TOK_PACKED},{"orelse",FE_TOK_ORELSE},{"and",FE_TOK_AND_KW},{"or",FE_TOK_OR_KW},{"not",FE_TOK_NOT},
|
||||
{0,FE_TOK_UNKNOWN}
|
||||
};
|
||||
|
||||
static int at(FeLexer *l, unsigned long n, char c) { return l->pos + n < l->length && l->src[l->pos+n] == c; }
|
||||
static FeLoc here(FeLexer *l, unsigned long line, unsigned long col)
|
||||
{ FeLoc x; x.file=l->file; x.line=line; x.col=col; return x; }
|
||||
static char cur(FeLexer *l) { return l->pos < l->length ? l->src[l->pos] : '\0'; }
|
||||
static void advance(FeLexer *l)
|
||||
{
|
||||
if (l->pos >= l->length) return;
|
||||
if (l->src[l->pos] == '\n') { l->line++; l->col = 1; }
|
||||
else l->col++;
|
||||
l->pos++;
|
||||
}
|
||||
static void skip_space(FeLexer *l)
|
||||
{
|
||||
for (;;) {
|
||||
while (isspace((unsigned char)cur(l))) advance(l);
|
||||
if (at(l,0,'/') && at(l,1,'/')) {
|
||||
while (cur(l) && cur(l) != '\n') advance(l);
|
||||
continue;
|
||||
}
|
||||
if (at(l,0,'/') && at(l,1,'*')) {
|
||||
unsigned long depth = 0;
|
||||
advance(l); advance(l); depth = 1;
|
||||
while (depth && cur(l)) {
|
||||
if (at(l,0,'/') && at(l,1,'*')) { advance(l); advance(l); depth++; }
|
||||
else if (at(l,0,'*') && at(l,1,'/')) { advance(l); advance(l); depth--; }
|
||||
else advance(l);
|
||||
}
|
||||
if (depth) fe_diag_error(l->diags, here(l,l->line,l->col), "unterminated block comment");
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void fe_lexer_init(FeLexer *l, const char *src, unsigned long length, const char *file, FeDiags *d)
|
||||
{
|
||||
l->src=src; l->length=length; l->pos=0; l->line=1; l->col=1; l->file=file; l->diags=d;
|
||||
}
|
||||
|
||||
static FeTokKind keyword(const char *s, unsigned long n)
|
||||
{
|
||||
unsigned long i;
|
||||
for (i=0; keywords[i].s; i++) {
|
||||
if (strlen(keywords[i].s)==n && memcmp(keywords[i].s,s,n)==0) return keywords[i].k;
|
||||
}
|
||||
return FE_TOK_IDENT;
|
||||
}
|
||||
static FeToken tok(FeLexer *l, FeTokKind k, unsigned long start, unsigned long line, unsigned long col)
|
||||
{
|
||||
FeToken t; t.kind=k; t.begin=l->src+start; t.length=l->pos-start; t.loc.file=l->file; t.loc.line=line; t.loc.col=col; return t;
|
||||
}
|
||||
static int digit_for_base(char c, int base)
|
||||
{
|
||||
int d;
|
||||
if (c >= '0' && c <= '9') d=c-'0';
|
||||
else if (c >= 'a' && c <= 'f') d=c-'a'+10;
|
||||
else if (c >= 'A' && c <= 'F') d=c-'A'+10;
|
||||
else return 0;
|
||||
return d < base;
|
||||
}
|
||||
|
||||
FeToken fe_lexer_next(FeLexer *l)
|
||||
{
|
||||
unsigned long start, line, col;
|
||||
char c;
|
||||
skip_space(l);
|
||||
start=l->pos; line=l->line; col=l->col; c=cur(l);
|
||||
if (!c) return tok(l,FE_TOK_EOF,start,line,col);
|
||||
if (isalpha((unsigned char)c) || c=='_') {
|
||||
advance(l);
|
||||
while (isalnum((unsigned char)cur(l)) || cur(l)=='_') advance(l);
|
||||
return tok(l,keyword(l->src+start,l->pos-start),start,line,col);
|
||||
}
|
||||
if (isdigit((unsigned char)c)) {
|
||||
int base=10, had_digit=0;
|
||||
if (c=='0' && (at(l,1,'x') || at(l,1,'X'))) { advance(l); advance(l); base=16; }
|
||||
else if (c=='0' && (at(l,1,'b') || at(l,1,'B'))) { advance(l); advance(l); base=2; }
|
||||
else if (c=='0' && (at(l,1,'o') || at(l,1,'O'))) { advance(l); advance(l); base=8; }
|
||||
while (cur(l)=='_' || digit_for_base(cur(l),base)) { if(cur(l)!='_') had_digit=1; advance(l); }
|
||||
if (!had_digit) fe_diag_error(l->diags,here(l,line,col),"integer literal has no digits");
|
||||
if (isalnum((unsigned char)cur(l))) {
|
||||
fe_diag_error(l->diags,here(l,line,col),"invalid digit in integer literal");
|
||||
while (isalnum((unsigned char)cur(l)) || cur(l)=='_') advance(l);
|
||||
}
|
||||
return tok(l,FE_TOK_INT,start,line,col);
|
||||
}
|
||||
if (c=='\'' || c=='"') {
|
||||
char quote=c; int bad=0, units=0; advance(l);
|
||||
while (cur(l) && cur(l)!=quote) {
|
||||
if (cur(l)=='\n' || cur(l)=='\r') { bad=1; break; }
|
||||
units++;
|
||||
if (cur(l)=='\\') {
|
||||
advance(l);
|
||||
if (!cur(l)) { bad=1; break; }
|
||||
if (cur(l)=='x') { int i; advance(l); for(i=0;i<2;i++) { if(!digit_for_base(cur(l),16)) bad=1; else advance(l); } }
|
||||
else if (cur(l)=='u') { int i; advance(l); for(i=0;i<4;i++) { if(!digit_for_base(cur(l),16)) bad=1; else advance(l); } }
|
||||
else if (strchr("nrt\\'\"0",cur(l))) advance(l);
|
||||
else { bad=1; advance(l); }
|
||||
} else advance(l);
|
||||
}
|
||||
if (cur(l)==quote) advance(l); else bad=1;
|
||||
if (quote=='\'' && units != 1) bad=1;
|
||||
if (bad) fe_diag_error(l->diags,here(l,line,col),quote=='\''?"invalid character literal":"unterminated or invalid string literal");
|
||||
return tok(l,quote=='\''?FE_TOK_CHAR:FE_TOK_STRING,start,line,col);
|
||||
}
|
||||
advance(l);
|
||||
switch(c) {
|
||||
case '(': return tok(l,FE_TOK_LPAREN,start,line,col); case ')': return tok(l,FE_TOK_RPAREN,start,line,col);
|
||||
case '{': return tok(l,FE_TOK_LBRACE,start,line,col); case '}': return tok(l,FE_TOK_RBRACE,start,line,col);
|
||||
case '[': return tok(l,FE_TOK_LBRACKET,start,line,col); case ']': return tok(l,FE_TOK_RBRACKET,start,line,col);
|
||||
case ',': return tok(l,FE_TOK_COMMA,start,line,col); case ';': return tok(l,FE_TOK_SEMI,start,line,col);
|
||||
case ':': return tok(l,FE_TOK_COLON,start,line,col); case '@': return tok(l,FE_TOK_AT,start,line,col);
|
||||
case '?': return tok(l,FE_TOK_QUESTION,start,line,col);
|
||||
case '.': if (cur(l)=='.') { advance(l); return tok(l,FE_TOK_DOTDOT,start,line,col); } return tok(l,FE_TOK_DOT,start,line,col);
|
||||
case '+': if(cur(l)=='='){advance(l);return tok(l,FE_TOK_PLUS_EQ,start,line,col);} if(cur(l)=='%'){advance(l);return tok(l,FE_TOK_PLUS_WRAP,start,line,col);} return tok(l,FE_TOK_PLUS,start,line,col);
|
||||
case '-': if(cur(l)=='>'){advance(l);return tok(l,FE_TOK_ARROW,start,line,col);} if(cur(l)=='='){advance(l);return tok(l,FE_TOK_MINUS_EQ,start,line,col);} if(cur(l)=='%'){advance(l);return tok(l,FE_TOK_MINUS_WRAP,start,line,col);} return tok(l,FE_TOK_MINUS,start,line,col);
|
||||
case '*': if(cur(l)=='='){advance(l);return tok(l,FE_TOK_STAR_EQ,start,line,col);} if(cur(l)=='%'){advance(l);return tok(l,FE_TOK_STAR_WRAP,start,line,col);} return tok(l,FE_TOK_STAR,start,line,col);
|
||||
case '/': if(cur(l)=='='){advance(l);return tok(l,FE_TOK_SLASH_EQ,start,line,col);} return tok(l,FE_TOK_SLASH,start,line,col);
|
||||
case '%': if(cur(l)=='='){advance(l);return tok(l,FE_TOK_PERCENT_EQ,start,line,col);} return tok(l,FE_TOK_PERCENT,start,line,col);
|
||||
case '=': if(cur(l)=='='){advance(l);return tok(l,FE_TOK_EQEQ,start,line,col);} if(cur(l)=='>'){advance(l);return tok(l,FE_TOK_FATARROW,start,line,col);} return tok(l,FE_TOK_EQ,start,line,col);
|
||||
case '!': if(cur(l)=='='){advance(l);return tok(l,FE_TOK_NE,start,line,col);} return tok(l,FE_TOK_BANG,start,line,col);
|
||||
case '<': if(cur(l)=='='){advance(l);return tok(l,FE_TOK_LE,start,line,col);} if(cur(l)=='<'){advance(l);if(cur(l)=='='){advance(l);return tok(l,FE_TOK_SHL_EQ,start,line,col);}return tok(l,FE_TOK_SHL,start,line,col);} return tok(l,FE_TOK_LT,start,line,col);
|
||||
case '>': if(cur(l)=='='){advance(l);return tok(l,FE_TOK_GE,start,line,col);} if(cur(l)=='>'){advance(l);if(cur(l)=='='){advance(l);return tok(l,FE_TOK_SHR_EQ,start,line,col);}return tok(l,FE_TOK_SHR,start,line,col);} return tok(l,FE_TOK_GT,start,line,col);
|
||||
case '&': if(cur(l)=='&'){advance(l);fe_diag_error(l->diags,here(l,line,col),"&& is not a Ferro logical operator; use 'and'");return tok(l,FE_TOK_UNKNOWN,start,line,col);} if(cur(l)=='='){advance(l);return tok(l,FE_TOK_AND_EQ,start,line,col);} return tok(l,FE_TOK_AND,start,line,col);
|
||||
case '|': if(cur(l)=='|'){advance(l);fe_diag_error(l->diags,here(l,line,col),"|| is not a Ferro logical operator; use 'or'");return tok(l,FE_TOK_UNKNOWN,start,line,col);} if(cur(l)=='='){advance(l);return tok(l,FE_TOK_OR_EQ,start,line,col);} return tok(l,FE_TOK_OR,start,line,col);
|
||||
case '^': if(cur(l)=='='){advance(l);return tok(l,FE_TOK_XOR_EQ,start,line,col);} return tok(l,FE_TOK_XOR,start,line,col);
|
||||
default: fe_diag_error(l->diags,here(l,line,col),"unknown character"); return tok(l,FE_TOK_UNKNOWN,start,line,col);
|
||||
}
|
||||
}
|
||||
|
||||
const char *fe_token_name(FeTokKind k)
|
||||
{
|
||||
switch(k) {
|
||||
case FE_TOK_EOF:return "eof"; case FE_TOK_IDENT:return "identifier"; case FE_TOK_INT:return "integer";
|
||||
case FE_TOK_CHAR:return "character"; case FE_TOK_STRING:return "string"; case FE_TOK_UNIT:return "unit";
|
||||
case FE_TOK_FN:return "fn"; case FE_TOK_STRUCT:return "struct"; case FE_TOK_ENUM:return "enum";
|
||||
case FE_TOK_ERROR_KW:return "error"; case FE_TOK_CONST:return "const"; case FE_TOK_LET:return "let";
|
||||
case FE_TOK_VAR:return "var"; case FE_TOK_IF:return "if"; case FE_TOK_ELSE:return "else";
|
||||
case FE_TOK_WHILE:return "while"; case FE_TOK_FOR:return "for"; case FE_TOK_MATCH:return "match";
|
||||
case FE_TOK_RETURN:return "return"; case FE_TOK_BREAK:return "break"; case FE_TOK_CONTINUE:return "continue";
|
||||
case FE_TOK_TRUE:return "true"; case FE_TOK_FALSE:return "false"; case FE_TOK_NULL:return "null";
|
||||
case FE_TOK_UNDEFINED:return "undefined"; case FE_TOK_AND_KW:return "and"; case FE_TOK_OR_KW:return "or";
|
||||
case FE_TOK_NOT:return "not"; case FE_TOK_BANG:return "!";
|
||||
case FE_TOK_LBRACE:return "{"; case FE_TOK_RBRACE:return "}"; case FE_TOK_LPAREN:return "("; case FE_TOK_RPAREN:return ")";
|
||||
case FE_TOK_SEMI:return ";"; case FE_TOK_COLON:return ":"; case FE_TOK_COMMA:return ",";
|
||||
case FE_TOK_EQ:return "="; case FE_TOK_ARROW:return "->"; case FE_TOK_FATARROW:return "=>";
|
||||
default:return "token";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifndef FE_LEXER_H
|
||||
#define FE_LEXER_H
|
||||
|
||||
#include "diag.h"
|
||||
#include "arena.h"
|
||||
|
||||
typedef enum FeTokKind {
|
||||
FE_TOK_EOF, FE_TOK_ERROR, FE_TOK_IDENT, FE_TOK_INT, FE_TOK_CHAR, FE_TOK_STRING,
|
||||
FE_TOK_UNIT, FE_TOK_IMPORT, FE_TOK_PUB, FE_TOK_FN, FE_TOK_STRUCT, FE_TOK_ENUM,
|
||||
FE_TOK_ERROR_KW, FE_TOK_CONST, FE_TOK_STATIC, FE_TOK_VAR, FE_TOK_LET, FE_TOK_MUT,
|
||||
FE_TOK_IF, FE_TOK_ELSE, FE_TOK_WHILE, FE_TOK_FOR, FE_TOK_IN, FE_TOK_MATCH,
|
||||
FE_TOK_RETURN, FE_TOK_BREAK, FE_TOK_CONTINUE, FE_TOK_DEFER, FE_TOK_UNSAFE,
|
||||
FE_TOK_COMPTIME, FE_TOK_ASM, FE_TOK_TRY, FE_TOK_CATCH, FE_TOK_AS, FE_TOK_EXTERN,
|
||||
FE_TOK_INTERRUPT, FE_TOK_INTERRUPT_SAFE, FE_TOK_FAR, FE_TOK_TRUE, FE_TOK_FALSE, FE_TOK_NULL,
|
||||
FE_TOK_UNDEFINED, FE_TOK_SHARED, FE_TOK_ATOMIC, FE_TOK_CRITICAL, FE_TOK_SELF,
|
||||
FE_TOK_SELFTYPE, FE_TOK_TYPE, FE_TOK_PACKED, FE_TOK_ORELSE,
|
||||
FE_TOK_LPAREN, FE_TOK_RPAREN, FE_TOK_LBRACE, FE_TOK_RBRACE, FE_TOK_LBRACKET, FE_TOK_RBRACKET,
|
||||
FE_TOK_COMMA, FE_TOK_SEMI, FE_TOK_COLON, FE_TOK_DOT, FE_TOK_DOTDOT,
|
||||
FE_TOK_PLUS, FE_TOK_MINUS, FE_TOK_STAR, FE_TOK_SLASH, FE_TOK_PERCENT,
|
||||
FE_TOK_PLUS_EQ, FE_TOK_MINUS_EQ, FE_TOK_STAR_EQ, FE_TOK_SLASH_EQ, FE_TOK_PERCENT_EQ,
|
||||
FE_TOK_PLUS_WRAP, FE_TOK_MINUS_WRAP, FE_TOK_STAR_WRAP,
|
||||
FE_TOK_EQ, FE_TOK_EQEQ, FE_TOK_NE, FE_TOK_LT, FE_TOK_LE, FE_TOK_GT, FE_TOK_GE,
|
||||
FE_TOK_AND, FE_TOK_OR, FE_TOK_AND_KW, FE_TOK_OR_KW, FE_TOK_XOR, FE_TOK_NOT, FE_TOK_BANG, FE_TOK_SHL, FE_TOK_SHR,
|
||||
FE_TOK_AND_EQ, FE_TOK_OR_EQ, FE_TOK_XOR_EQ, FE_TOK_SHL_EQ, FE_TOK_SHR_EQ,
|
||||
FE_TOK_ANDAND, FE_TOK_OROR, FE_TOK_ARROW, FE_TOK_FATARROW, FE_TOK_AT,
|
||||
FE_TOK_QUESTION, FE_TOK_UNKNOWN
|
||||
} FeTokKind;
|
||||
|
||||
typedef struct FeToken {
|
||||
FeTokKind kind;
|
||||
const char *begin;
|
||||
unsigned long length;
|
||||
FeLoc loc;
|
||||
} FeToken;
|
||||
|
||||
typedef struct FeLexer {
|
||||
const char *src;
|
||||
unsigned long length;
|
||||
unsigned long pos;
|
||||
unsigned long line;
|
||||
unsigned long col;
|
||||
const char *file;
|
||||
FeDiags *diags;
|
||||
} FeLexer;
|
||||
|
||||
void fe_lexer_init(FeLexer *l, const char *src, unsigned long length, const char *file, FeDiags *d);
|
||||
FeToken fe_lexer_next(FeLexer *l);
|
||||
const char *fe_token_name(FeTokKind k);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,201 @@
|
||||
#include "parser.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static FeToken next(FeParser *p) { p->previous=p->current; p->current=fe_lexer_next(&p->lexer); return p->current; }
|
||||
static int is(FeParser *p, FeTokKind k) { return p->current.kind==k; }
|
||||
static int eat(FeParser *p, FeTokKind k) { if(is(p,k)){next(p);return 1;}return 0; }
|
||||
static FeNode *toknode(FeParser *p, FeNodeKind k, FeToken t) { return fe_node(p->ast,k,t.loc,t.begin,t.length); }
|
||||
static void error(FeParser *p, const char *s) { fe_diag_error(p->diags,p->current.loc,s); }
|
||||
static int want(FeParser *p, FeTokKind k, const char *what)
|
||||
{ if(eat(p,k)) return 1; error(p,what); return 0; }
|
||||
static int is_name(FeParser *p) { return is(p,FE_TOK_IDENT)||is(p,FE_TOK_SELF)||is(p,FE_TOK_SELFTYPE); }
|
||||
static FeNode *expr(FeParser *p, int minprec);
|
||||
static FeNode *type(FeParser *p);
|
||||
static FeNode *statement(FeParser *p);
|
||||
static FeNode *block(FeParser *p);
|
||||
|
||||
void fe_parser_init(FeParser *p, FeAst *ast, const char *src, unsigned long length, const char *file, FeDiags *d)
|
||||
{
|
||||
p->ast=ast; p->diags=d; fe_lexer_init(&p->lexer,src,length,file,d);
|
||||
p->previous=p->current=fe_lexer_next(&p->lexer);
|
||||
}
|
||||
|
||||
static void recover(FeParser *p)
|
||||
{
|
||||
while(!is(p,FE_TOK_EOF) && !is(p,FE_TOK_SEMI) && !is(p,FE_TOK_RBRACE)) next(p);
|
||||
if(is(p,FE_TOK_SEMI)) next(p);
|
||||
}
|
||||
|
||||
static FeNode *type_prefix(FeParser *p, FeTokKind k, FeToken op)
|
||||
{
|
||||
FeNode *n;
|
||||
(void)k;
|
||||
n=toknode(p,FE_N_TYPE,op); n->a=type(p); return n;
|
||||
}
|
||||
static FeNode *type(FeParser *p)
|
||||
{
|
||||
FeToken t=p->current; FeNode *n;
|
||||
if (is(p,FE_TOK_QUESTION)||is(p,FE_TOK_BANG)||is(p,FE_TOK_STAR)||is(p,FE_TOK_XOR)) {
|
||||
next(p); return type_prefix(p,t.kind,t);
|
||||
}
|
||||
if (is(p,FE_TOK_AND)) {
|
||||
next(p); n=toknode(p,FE_N_TYPE,t); if(eat(p,FE_TOK_MUT)) n->text=fe_arena_strdup(&p->ast->arena,"&mut",4); n->a=type(p); return n;
|
||||
}
|
||||
if (is(p,FE_TOK_FAR)) {
|
||||
next(p); n=toknode(p,FE_N_TYPE,t); if(is(p,FE_TOK_STAR)||is(p,FE_TOK_XOR)||is(p,FE_TOK_AND)) next(p); n->a=type(p); return n;
|
||||
}
|
||||
if (is(p,FE_TOK_LBRACKET)) {
|
||||
next(p); n=toknode(p,FE_N_TYPE,t);
|
||||
if(!eat(p,FE_TOK_RBRACKET)) { n->a=expr(p,0); want(p,FE_TOK_RBRACKET,"expected ']' in array type"); }
|
||||
n->b=type(p); return n;
|
||||
}
|
||||
if (is(p,FE_TOK_FN)) {
|
||||
next(p); n=toknode(p,FE_N_TYPE,t); want(p,FE_TOK_LPAREN,"expected '(' in function type");
|
||||
while(!is(p,FE_TOK_RPAREN)&&!is(p,FE_TOK_EOF)) { fe_node_add(n,type(p)); if(!eat(p,FE_TOK_COMMA)) break; }
|
||||
want(p,FE_TOK_RPAREN,"expected ')' in function type"); if(eat(p,FE_TOK_ARROW)) n->a=type(p); return n;
|
||||
}
|
||||
if (is_name(p) || is(p,FE_TOK_TYPE)) {
|
||||
next(p); n=toknode(p,FE_N_TYPE,t);
|
||||
if (eat(p,FE_TOK_DOT)) { if(is_name(p)){FeNode *m=toknode(p,FE_N_IDENT,p->previous); n->a=m; next(p);} else error(p,"expected type name after '.'"); }
|
||||
if (eat(p,FE_TOK_BANG)) { FeNode *e=toknode(p,FE_N_TYPE,p->previous); e->a=n; e->b=type(p); return e; }
|
||||
if (eat(p,FE_TOK_LPAREN)) { while(!is(p,FE_TOK_RPAREN)&&!is(p,FE_TOK_EOF)){fe_node_add(n,type(p));if(!eat(p,FE_TOK_COMMA))break;} want(p,FE_TOK_RPAREN,"expected ')' in generic type"); }
|
||||
return n;
|
||||
}
|
||||
error(p,"expected type"); next(p); return fe_node(p->ast,FE_N_TYPE,t.loc,"error",5);
|
||||
}
|
||||
|
||||
static int precedence(FeTokKind k)
|
||||
{
|
||||
switch(k) {
|
||||
case FE_TOK_ORELSE: case FE_TOK_CATCH:return 1;
|
||||
case FE_TOK_OR_KW:return 2; case FE_TOK_AND_KW:return 3;
|
||||
case FE_TOK_EQEQ: case FE_TOK_NE: case FE_TOK_LT: case FE_TOK_LE: case FE_TOK_GT: case FE_TOK_GE:return 4;
|
||||
case FE_TOK_OR:return 5; case FE_TOK_XOR:return 6; case FE_TOK_AND:return 7;
|
||||
case FE_TOK_SHL: case FE_TOK_SHR:return 8;
|
||||
case FE_TOK_PLUS: case FE_TOK_MINUS: case FE_TOK_PLUS_WRAP: case FE_TOK_MINUS_WRAP:return 9;
|
||||
case FE_TOK_STAR: case FE_TOK_SLASH: case FE_TOK_PERCENT: case FE_TOK_STAR_WRAP:return 10;
|
||||
default:return 0;
|
||||
}
|
||||
}
|
||||
static FeNode *primary(FeParser *p)
|
||||
{
|
||||
FeToken t=p->current; FeNode *n;
|
||||
if(is(p,FE_TOK_INT)||is(p,FE_TOK_CHAR)||is(p,FE_TOK_STRING)||is(p,FE_TOK_TRUE)||is(p,FE_TOK_FALSE)||is(p,FE_TOK_NULL)||is(p,FE_TOK_UNDEFINED)) {next(p);return toknode(p,FE_N_LITERAL,t);}
|
||||
if(is_name(p) || is(p,FE_TOK_ERROR_KW)) {
|
||||
next(p); n=toknode(p,FE_N_IDENT,t);
|
||||
if(is(p,FE_TOK_LBRACE)) {
|
||||
FeNode *s=toknode(p,FE_N_STRUCT_INIT,t); next(p);
|
||||
while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF)) { FeNode *f;
|
||||
if(!is_name(p)){error(p,"expected field name");recover(p);break;} f=toknode(p,FE_N_FIELD,p->current);next(p);want(p,FE_TOK_COLON,"expected ':' after field");f->a=expr(p,0);fe_node_add(s,f);if(!eat(p,FE_TOK_COMMA))break;
|
||||
} want(p,FE_TOK_RBRACE,"expected '}' in struct literal"); return s;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
if(eat(p,FE_TOK_LPAREN)) { n=expr(p,0); want(p,FE_TOK_RPAREN,"expected ')'"); return n; }
|
||||
if(eat(p,FE_TOK_AT)) {
|
||||
FeToken name=p->current; if(!is_name(p)){error(p,"expected builtin name after '@'");return fe_node(p->ast,FE_N_ERROR_NODE,t.loc,"builtin",7);} next(p);
|
||||
n=toknode(p,FE_N_CALL,name); n->text=fe_arena_strdup(&p->ast->arena,name.begin-1,name.length+1);
|
||||
if(eat(p,FE_TOK_LPAREN)){while(!is(p,FE_TOK_RPAREN)&&!is(p,FE_TOK_EOF)){fe_node_add(n,expr(p,0));if(!eat(p,FE_TOK_COMMA))break;}want(p,FE_TOK_RPAREN,"expected ')' after builtin");}
|
||||
return n;
|
||||
}
|
||||
error(p,"expected expression"); next(p); return fe_node(p->ast,FE_N_ERROR_NODE,t.loc,"expression",10);
|
||||
}
|
||||
static FeNode *postfix(FeParser *p)
|
||||
{
|
||||
FeNode *n=primary(p);
|
||||
for(;;) {
|
||||
FeToken t=p->current; FeNode *m;
|
||||
if(eat(p,FE_TOK_LPAREN)) { m=toknode(p,FE_N_CALL,t); m->a=n; while(!is(p,FE_TOK_RPAREN)&&!is(p,FE_TOK_EOF)){fe_node_add(m,expr(p,0));if(!eat(p,FE_TOK_COMMA))break;} want(p,FE_TOK_RPAREN,"expected ')' after call"); n=m; }
|
||||
else if(eat(p,FE_TOK_LBRACKET)) { m=toknode(p,FE_N_INDEX,t);m->a=n;m->b=expr(p,0);if(eat(p,FE_TOK_DOTDOT)){m->c=expr(p,0);}want(p,FE_TOK_RBRACKET,"expected ']' after index");n=m; }
|
||||
else if(eat(p,FE_TOK_DOT)) { m=toknode(p,FE_N_MEMBER,t);m->a=n;if(is_name(p)){m->b=toknode(p,FE_N_IDENT,p->current);next(p);}else if(eat(p,FE_TOK_QUESTION)){m->text=fe_arena_strdup(&p->ast->arena,".?",2);}else error(p,"expected member name");n=m; }
|
||||
else if(eat(p,FE_TOK_AS)) { m=toknode(p,FE_N_TYPE,t);m->a=n;m->b=type(p);n=m; }
|
||||
else break;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
static FeNode *expr(FeParser *p, int minprec)
|
||||
{
|
||||
FeToken t=p->current; FeNode *left,*n; int prec;
|
||||
if(is(p,FE_TOK_MINUS)||is(p,FE_TOK_NOT)||is(p,FE_TOK_XOR)||is(p,FE_TOK_AND)||is(p,FE_TOK_STAR)||is(p,FE_TOK_TRY)) { next(p); n=toknode(p,FE_N_UNARY,t); n->a=expr(p,11); left=n; }
|
||||
else left=postfix(p);
|
||||
for(;;) { t=p->current;prec=precedence(t.kind);if(prec<=minprec)break;next(p);n=toknode(p,FE_N_BINARY,t);n->a=left;if(t.kind==FE_TOK_CATCH && eat(p,FE_TOK_OR)){if(is_name(p))n->b=toknode(p,FE_N_IDENT,p->current),next(p);else error(p,"expected catch binding");want(p,FE_TOK_OR,"expected '|' after catch binding");n->c=block(p);}else n->b=expr(p,prec);left=n; }
|
||||
return left;
|
||||
}
|
||||
|
||||
static FeNode *params(FeParser *p)
|
||||
{
|
||||
FeNode *list=fe_node(p->ast,FE_N_BLOCK,p->current.loc,"params",6);
|
||||
want(p,FE_TOK_LPAREN,"expected '(' after function name");
|
||||
while(!is(p,FE_TOK_RPAREN)&&!is(p,FE_TOK_EOF)) { FeToken t=p->current; FeNode *q;
|
||||
if(eat(p,FE_TOK_COMPTIME)) t=p->previous;
|
||||
if(!is_name(p)){error(p,"expected parameter name");recover(p);break;} q=toknode(p,FE_N_PARAM,t);next(p);want(p,FE_TOK_COLON,"expected ':' in parameter");q->a=type(p);fe_node_add(list,q);if(!eat(p,FE_TOK_COMMA))break;
|
||||
}
|
||||
want(p,FE_TOK_RPAREN,"expected ')' after parameters"); return list;
|
||||
}
|
||||
static FeNode *fn_decl(FeParser *p, int pub, int external, int interrupt, int interrupt_safe)
|
||||
{
|
||||
FeToken t=p->current, name; FeNode *n;
|
||||
(void)pub; (void)external; (void)interrupt; (void)interrupt_safe;
|
||||
want(p,FE_TOK_FN,"expected 'fn'"); if(!is_name(p)){error(p,"expected function name");return fe_node(p->ast,FE_N_ERROR_NODE,t.loc,"fn",2);}
|
||||
name=p->current; n=toknode(p,FE_N_FN,t); n->text=fe_arena_strdup(&p->ast->arena,name.begin,name.length); next(p); n->a=params(p); if(eat(p,FE_TOK_ARROW)) n->b=type(p); if(eat(p,FE_TOK_SEMI)) return n; n->c=block(p); return n;
|
||||
}
|
||||
static FeNode *field(FeParser *p)
|
||||
{
|
||||
FeToken t=p->current; FeNode *n;
|
||||
if(!is_name(p)){error(p,"expected field name");recover(p);return 0;} next(p);n=toknode(p,FE_N_FIELD,t);want(p,FE_TOK_COLON,"expected ':' after field");n->a=type(p);if(!eat(p,FE_TOK_COMMA) && !is(p,FE_TOK_RBRACE)) error(p,"expected ',' after field");return n;
|
||||
}
|
||||
static FeNode *decl(FeParser *p)
|
||||
{
|
||||
int pub=0, external=0, interrupt=0, interrupt_safe=0, shared=0, atomic=0; FeToken t=p->current; FeNode *n;
|
||||
(void)shared; (void)atomic;
|
||||
if(eat(p,FE_TOK_PUB)) pub=1;
|
||||
if(eat(p,FE_TOK_EXTERN)) { external=1; if(is(p,FE_TOK_STRING)) next(p); }
|
||||
if(eat(p,FE_TOK_INTERRUPT)) interrupt=1;
|
||||
if(eat(p,FE_TOK_INTERRUPT_SAFE)) interrupt_safe=1;
|
||||
if(!is(p,FE_TOK_PACKED)) t=p->current;
|
||||
if(is(p,FE_TOK_FN)) return fn_decl(p,pub,external,interrupt,interrupt_safe);
|
||||
if(eat(p,FE_TOK_PACKED)) t=p->previous;
|
||||
if(eat(p,FE_TOK_STRUCT)) { n=toknode(p,FE_N_STRUCT,t);if(!is_name(p)){error(p,"expected struct name");return n;}next(p);n->text=fe_arena_strdup(&p->ast->arena,p->previous.begin,p->previous.length);if(eat(p,FE_TOK_LPAREN)){while(!is(p,FE_TOK_RPAREN)&&!is(p,FE_TOK_EOF)){fe_node_add(n,type(p));if(!eat(p,FE_TOK_COMMA))break;}want(p,FE_TOK_RPAREN,"expected ')' after generic parameters");}want(p,FE_TOK_LBRACE,"expected '{' in struct");while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF)){if(is(p,FE_TOK_PUB))next(p);if(is(p,FE_TOK_FN))fe_node_add(n,fn_decl(p,0,0,0,0));else fe_node_add(n,field(p));}want(p,FE_TOK_RBRACE,"expected '}' after struct");return n; }
|
||||
if(eat(p,FE_TOK_ENUM)) { n=toknode(p,FE_N_ENUM,t);if(is_name(p)){next(p);n->text=fe_arena_strdup(&p->ast->arena,p->previous.begin,p->previous.length);}else error(p,"expected enum name");want(p,FE_TOK_LBRACE,"expected '{' in enum");while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF)){FeNode *v=toknode(p,FE_N_VARIANT,p->current);if(is_name(p))next(p);else{error(p,"expected variant name");recover(p);break;}if(eat(p,FE_TOK_LPAREN)){v->a=type(p);want(p,FE_TOK_RPAREN,"expected ')' in variant");}else if(eat(p,FE_TOK_LBRACE)){while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF))fe_node_add(v,field(p));want(p,FE_TOK_RBRACE,"expected '}' in variant");}fe_node_add(n,v);if(!eat(p,FE_TOK_COMMA))break;}want(p,FE_TOK_RBRACE,"expected '}' after enum");return n; }
|
||||
if(eat(p,FE_TOK_ERROR_KW)) { n=toknode(p,FE_N_ERROR_DECL,t);if(is_name(p)){next(p);n->text=fe_arena_strdup(&p->ast->arena,p->previous.begin,p->previous.length);}else error(p,"expected error name");want(p,FE_TOK_LBRACE,"expected '{' in error declaration");while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF)){FeNode *v=toknode(p,FE_N_VARIANT,p->current);if(is_name(p))next(p);else{error(p,"expected error member");recover(p);break;}want(p,FE_TOK_EQ,"expected '=' in error member");v->a=expr(p,0);want(p,FE_TOK_COMMA,"expected ',' in error declaration");fe_node_add(n,v);}want(p,FE_TOK_RBRACE,"expected '}' after error");return n; }
|
||||
if(eat(p,FE_TOK_SHARED)) { shared=1; if(eat(p,FE_TOK_ATOMIC)) atomic=1; if(!is(p,FE_TOK_VAR)) error(p,"expected 'var' after shared"); }
|
||||
if(is(p,FE_TOK_CONST)||is(p,FE_TOK_STATIC)||is(p,FE_TOK_VAR)) { FeTokKind kk=p->current.kind;next(p);n=toknode(p,kk==FE_TOK_CONST?FE_N_CONST:FE_N_GLOBAL,t);if(is_name(p)){next(p);n->text=fe_arena_strdup(&p->ast->arena,p->previous.begin,p->previous.length);}else error(p,"expected declaration name");if(eat(p,FE_TOK_COLON))n->a=type(p);want(p,FE_TOK_EQ,"expected '=' in declaration");n->b=expr(p,0);want(p,FE_TOK_SEMI,"expected ';' after declaration");return n; }
|
||||
error(p,"expected declaration"); recover(p); return 0;
|
||||
}
|
||||
|
||||
static FeNode *block(FeParser *p)
|
||||
{
|
||||
FeToken t=p->current; FeNode *n=toknode(p,FE_N_BLOCK,t);want(p,FE_TOK_LBRACE,"expected '{'");while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF)){FeNode *s=statement(p);if(s)fe_node_add(n,s);}want(p,FE_TOK_RBRACE,"expected '}'");return n;
|
||||
}
|
||||
static FeNode *statement(FeParser *p)
|
||||
{
|
||||
FeToken t=p->current; FeNode *n,*e;
|
||||
if(is(p,FE_TOK_LBRACE)) return block(p);
|
||||
if(eat(p,FE_TOK_LET)) { n=toknode(p,FE_N_LET,t);if(is_name(p))next(p);else error(p,"expected variable name");if(eat(p,FE_TOK_COLON))n->a=type(p);want(p,FE_TOK_EQ,"expected '=' in let");n->b=expr(p,0);want(p,FE_TOK_SEMI,"expected ';'");return n; }
|
||||
if(eat(p,FE_TOK_VAR)) { n=toknode(p,FE_N_VAR,t);if(is_name(p))next(p);else error(p,"expected variable name");if(eat(p,FE_TOK_COLON))n->a=type(p);if(eat(p,FE_TOK_EQ))n->b=expr(p,0);want(p,FE_TOK_SEMI,"expected ';'");return n; }
|
||||
if(eat(p,FE_TOK_CONST)) { n=toknode(p,FE_N_CONST,t);if(is_name(p))next(p);else error(p,"expected constant name");if(eat(p,FE_TOK_COLON))n->a=type(p);want(p,FE_TOK_EQ,"expected '=' in const");n->b=expr(p,0);want(p,FE_TOK_SEMI,"expected ';'");return n; }
|
||||
if(eat(p,FE_TOK_IF)) { n=toknode(p,FE_N_IF,t);if(eat(p,FE_TOK_LET)){n->text=fe_arena_strdup(&p->ast->arena,"if let",6);if(is_name(p))next(p);if(eat(p,FE_TOK_LPAREN)){if(is_name(p))next(p);want(p,FE_TOK_RPAREN,"expected ')' in if let pattern");}want(p,FE_TOK_EQ,"expected '=' in if let");}n->a=expr(p,0);n->b=block(p);if(eat(p,FE_TOK_ELSE))n->c=is(p,FE_TOK_IF)?statement(p):block(p);return n; }
|
||||
if(eat(p,FE_TOK_COMPTIME)) { n=toknode(p,FE_N_IF,t);want(p,FE_TOK_IF,"expected 'if' after comptime");n->text=fe_arena_strdup(&p->ast->arena,"comptime if",11);n->a=expr(p,0);n->b=block(p);if(eat(p,FE_TOK_ELSE))n->c=is(p,FE_TOK_IF)?statement(p):block(p);return n; }
|
||||
if(eat(p,FE_TOK_WHILE)) {n=toknode(p,FE_N_WHILE,t);n->a=expr(p,0);n->b=block(p);return n;}
|
||||
if(eat(p,FE_TOK_FOR)) {n=toknode(p,FE_N_FOR,t);if(is_name(p))next(p);else error(p,"expected loop variable");if(eat(p,FE_TOK_COMMA)){if(is_name(p))next(p);else error(p,"expected second loop variable");}want(p,FE_TOK_IN,"expected 'in' in for");n->a=expr(p,0);if(eat(p,FE_TOK_DOTDOT))n->c=expr(p,0);n->b=block(p);return n;}
|
||||
if(eat(p,FE_TOK_MATCH)) { n=toknode(p,FE_N_MATCH,t);n->a=expr(p,0);want(p,FE_TOK_LBRACE,"expected '{' after match expression");while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF)){FeNode *arm=toknode(p,FE_N_ARM,p->current);if(is_name(p)||is(p,FE_TOK_INT)||is(p,FE_TOK_CHAR)||is(p,FE_TOK_NULL)||is(p,FE_TOK_TRUE)||is(p,FE_TOK_FALSE)||is(p,FE_TOK_IDENT)){arm->text=fe_arena_strdup(&p->ast->arena,p->current.begin,p->current.length);next(p);}else{error(p,"expected match pattern");recover(p);continue;}while(is(p,FE_TOK_LPAREN)||is(p,FE_TOK_LBRACE)){FeTokKind close=is(p,FE_TOK_LPAREN)?FE_TOK_RPAREN:FE_TOK_RBRACE;next(p);while(!is(p,close)&&!is(p,FE_TOK_EOF))next(p);want(p,close,"expected end of match pattern");}want(p,FE_TOK_FATARROW,"expected '=>' in match arm");if(is(p,FE_TOK_LBRACE))arm->a=block(p);else{arm->a=expr(p,0);want(p,FE_TOK_SEMI,"expected ';' in match arm");}fe_node_add(n,arm);}want(p,FE_TOK_RBRACE,"expected '}' after match");return n;}
|
||||
if(eat(p,FE_TOK_RETURN)) {n=toknode(p,FE_N_RETURN,t);if(!is(p,FE_TOK_SEMI))n->a=expr(p,0);want(p,FE_TOK_SEMI,"expected ';' after return");return n;}
|
||||
if(eat(p,FE_TOK_BREAK)){n=toknode(p,FE_N_BREAK,t);want(p,FE_TOK_SEMI,"expected ';'");return n;}
|
||||
if(eat(p,FE_TOK_CONTINUE)){n=toknode(p,FE_N_CONTINUE,t);want(p,FE_TOK_SEMI,"expected ';'");return n;}
|
||||
if(eat(p,FE_TOK_DEFER)){n=toknode(p,FE_N_DEFER,t);n->a=block(p);return n;}
|
||||
if(eat(p,FE_TOK_UNSAFE)){n=toknode(p,FE_N_UNSAFE,t);n->a=block(p);return n;}
|
||||
if(eat(p,FE_TOK_CRITICAL)){n=toknode(p,FE_N_UNSAFE,t);n->text=fe_arena_strdup(&p->ast->arena,"critical",8);n->a=block(p);return n;}
|
||||
if(eat(p,FE_TOK_ASM)){n=toknode(p,FE_N_ASM,t);want(p,FE_TOK_LBRACE,"expected '{' after asm");while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF))next(p);want(p,FE_TOK_RBRACE,"expected '}' after asm");return n;}
|
||||
e=expr(p,0); if(is(p,FE_TOK_EQ)||is(p,FE_TOK_PLUS_EQ)||is(p,FE_TOK_MINUS_EQ)||is(p,FE_TOK_STAR_EQ)||is(p,FE_TOK_SLASH_EQ)||is(p,FE_TOK_PERCENT_EQ)||is(p,FE_TOK_AND_EQ)||is(p,FE_TOK_OR_EQ)||is(p,FE_TOK_XOR_EQ)||is(p,FE_TOK_SHL_EQ)||is(p,FE_TOK_SHR_EQ)){n=toknode(p,FE_N_ASSIGN,p->current);n->a=e;next(p);n->b=expr(p,0);}else{n=toknode(p,FE_N_EXPR_STMT,t);n->a=e;}want(p,FE_TOK_SEMI,"expected ';' after statement");return n;
|
||||
}
|
||||
|
||||
FeNode *fe_parse_unit(FeParser *p)
|
||||
{
|
||||
FeToken t=p->current, name; FeNode *root;
|
||||
if(!eat(p,FE_TOK_UNIT)){error(p,"source must start with 'unit'");return fe_node(p->ast,FE_N_ERROR_NODE,t.loc,"unit",4);}
|
||||
root=toknode(p,FE_N_UNIT,t);if(is_name(p)){name=p->current;root->text=fe_arena_strdup(&p->ast->arena,name.begin,name.length);next(p);}else error(p,"expected unit name");want(p,FE_TOK_SEMI,"expected ';' after unit name");
|
||||
while(eat(p,FE_TOK_IMPORT)){FeToken it=p->previous;FeNode *i=toknode(p,FE_N_IMPORT,it);if(is_name(p)){next(p);i->text=fe_arena_strdup(&p->ast->arena,p->previous.begin,p->previous.length);}else error(p,"expected import name");want(p,FE_TOK_SEMI,"expected ';' after import");fe_node_add(root,i);}
|
||||
while(!is(p,FE_TOK_EOF)){FeNode *d=decl(p);if(d)fe_node_add(root,d);}
|
||||
return root;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef FE_PARSER_H
|
||||
#define FE_PARSER_H
|
||||
|
||||
#include "ast.h"
|
||||
|
||||
typedef struct FeParser {
|
||||
FeLexer lexer;
|
||||
FeToken current;
|
||||
FeToken previous;
|
||||
FeAst *ast;
|
||||
FeDiags *diags;
|
||||
} FeParser;
|
||||
|
||||
void fe_parser_init(FeParser *p, FeAst *ast, const char *src, unsigned long length, const char *file, FeDiags *d);
|
||||
FeNode *fe_parse_unit(FeParser *p);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,5 @@
|
||||
unit core;
|
||||
|
||||
pub error Error { Invalid = 1, Io = 2, }
|
||||
pub fn panic(msg: str, file: str, line: u32) { }
|
||||
pub fn assert(ok: bool) { }
|
||||
@@ -0,0 +1,4 @@
|
||||
unit fmt;
|
||||
pub fn write_str(w: &mut io.Writer, s: str) -> !void;
|
||||
pub fn write_int_i32(w: &mut io.Writer, v: i32) -> !void;
|
||||
pub fn write_bool(w: &mut io.Writer, v: bool) -> !void;
|
||||
@@ -0,0 +1,9 @@
|
||||
unit io;
|
||||
pub struct Writer {
|
||||
ctx: *void,
|
||||
write_fn: fn(*void, []u8) -> !usize,
|
||||
}
|
||||
pub struct File {
|
||||
handle: u16,
|
||||
pub fn close(self: &mut Self) { }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
unit list;
|
||||
pub struct List(T) {
|
||||
items: ^[]T,
|
||||
len: usize,
|
||||
pub fn at(self: &Self, i: usize) -> &T;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
unit map;
|
||||
pub struct Map(K, V) {
|
||||
len: usize,
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
unit mem;
|
||||
|
||||
pub fn create(T: type) -> !^T;
|
||||
pub fn destroy(p: *void);
|
||||
pub fn copy(dst: []u8, src: []u8);
|
||||
pub struct Arena {
|
||||
ptr: *void,
|
||||
pub fn init() -> Arena { return Arena{ ptr: null }; }
|
||||
pub fn reset(self: &mut Self) { }
|
||||
pub fn drop(self: &mut Self) { }
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
unit str;
|
||||
pub fn eq(a: str, b: str) -> bool;
|
||||
pub fn trim(s: str) -> str;
|
||||
@@ -0,0 +1,2 @@
|
||||
unit sys;
|
||||
pub fn exit(code: u16);
|
||||
@@ -0,0 +1,51 @@
|
||||
@echo off
|
||||
rem FreeDOS smoke tests. All work happens on the writable C: drive.
|
||||
C:
|
||||
cd \FEC
|
||||
if exist TEST.OK del TEST.OK
|
||||
if exist TEST.FAIL del TEST.FAIL
|
||||
call C:\FEC\BUILD.BAT
|
||||
if not exist BUILD.OK goto test_fail
|
||||
|
||||
fec.exe --dump-ast TESTS\PASS\BASIC.FE > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
fec.exe --dump-ast TESTS\PASS\LITERALS.FE > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
fec.exe --dump-ast TESTS\PASS\KEYWOR.FE > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
fec.exe --dump-ast TESTS\PASS\V012-F.FE > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
|
||||
fec.exe --dump-ast STD\CORE.FE > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
fec.exe --dump-ast STD\FMT.FE > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
fec.exe --dump-ast STD\IO.FE > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
fec.exe --dump-ast STD\LIST.FE > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
fec.exe --dump-ast STD\MAP.FE > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
fec.exe --dump-ast STD\MEM.FE > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
fec.exe --dump-ast STD\STR.FE > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
fec.exe --dump-ast STD\SYS.FE > nul
|
||||
if errorlevel 1 goto test_fail
|
||||
|
||||
fec.exe --dump-ast TESTS\FAIL\MISSIN.FE > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
fec.exe --dump-ast TESTS\FAIL\UNCLOS.FE > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
fec.exe --dump-ast TESTS\FAIL\LOGICA.FE > nul
|
||||
if not errorlevel 1 goto test_fail
|
||||
|
||||
echo OK>TEST.OK
|
||||
cd C:\FEC
|
||||
goto test_done
|
||||
|
||||
:test_fail
|
||||
echo FAIL>TEST.FAIL
|
||||
verify other 2>nul
|
||||
|
||||
:test_done
|
||||
@@ -0,0 +1,3 @@
|
||||
// ERROR:logical operator
|
||||
unit old_logic;
|
||||
fn main() { let x = true && false; }
|
||||
@@ -0,0 +1,3 @@
|
||||
// ERROR:expected ';'
|
||||
unit broken;
|
||||
fn main() { let x: i32 = 1 }
|
||||
@@ -0,0 +1,3 @@
|
||||
// ERROR:unterminated block comment
|
||||
unit broken;
|
||||
/* no ending delimiter
|
||||
@@ -0,0 +1,25 @@
|
||||
unit basic;
|
||||
import core;
|
||||
|
||||
/* outer comment /* nested comment */ still active */
|
||||
const LIMIT: u16 = 1_000;
|
||||
pub struct Point {
|
||||
pub x: i32,
|
||||
y: i32,
|
||||
pub fn new(x: i32, y: i32) -> Point { return Point{ x: x, y: y }; }
|
||||
pub fn shift(self: &mut Self, dx: i32) { self.x += dx; }
|
||||
}
|
||||
pub enum Shape {
|
||||
Empty,
|
||||
Circle(i32),
|
||||
Rect{ w: i32, h: i32 },
|
||||
}
|
||||
pub error IoError { NotFound = 1, Denied = 2, }
|
||||
|
||||
pub fn main() -> !void {
|
||||
let p: Point = Point{ x: 1, y: 2 };
|
||||
if p.x > 0 and true { p.shift(1); } else { p.x = 0; }
|
||||
while p.x < 10 { p.x += 1; if p.x == 5 { continue; } }
|
||||
for i, x in p.x..10 { let _n: usize = i; let _q = x; }
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
unit keywords_and_builtins;
|
||||
|
||||
pub fn demo() {
|
||||
let a = true and not false;
|
||||
let b = a or false;
|
||||
@print("selected branch");
|
||||
let p = @as_far_fn(handler);
|
||||
@call_far(p);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
unit literals;
|
||||
const A: u32 = 0xFF;
|
||||
const B: u16 = 0b1010;
|
||||
const C: u16 = 0o17;
|
||||
const D: u32 = 1_000_000;
|
||||
pub fn strings() {
|
||||
let a = "hello\\nworld";
|
||||
let b = '\x41';
|
||||
let c = true;
|
||||
let d = null;
|
||||
let e = a orelse "fallback";
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
unit v012_forms;
|
||||
|
||||
shared atomic var ticks: u16 = 0;
|
||||
packed struct Packet {
|
||||
tag: u8,
|
||||
value: u16,
|
||||
}
|
||||
interrupt_safe fn poll() { }
|
||||
interrupt fn timer() { }
|
||||
fn invoke(p: far fn()) { }
|
||||
|
||||
pub fn demo() {
|
||||
var count = undefined;
|
||||
count = 1;
|
||||
critical { ticks += 1; }
|
||||
let x = true and not false or false;
|
||||
let y = x orelse true;
|
||||
let e = error.NotFound;
|
||||
@call_far(@as_far_fn(timer));
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
|
||||
ok=0
|
||||
for f in "$root"/std/*.fe; do
|
||||
[ -f "$f" ] || continue
|
||||
"$root"/fec --dump-ast "$f" >/dev/null || { echo "FAIL: $f"; exit 1; }
|
||||
ok=$((ok+1))
|
||||
done
|
||||
for f in "$root"/tests/pass/*.fe; do
|
||||
[ -f "$f" ] || continue
|
||||
"$root"/fec --dump-ast "$f" >/dev/null || { echo "FAIL: $f"; exit 1; }
|
||||
ok=$((ok+1))
|
||||
done
|
||||
for f in "$root"/tests/fail/*.fe; do
|
||||
[ -f "$f" ] || continue
|
||||
if "$root"/fec --dump-ast "$f" >/dev/null 2>/dev/null; then echo "FAIL (accepted): $f"; exit 1; fi
|
||||
ok=$((ok+1))
|
||||
done
|
||||
echo "M1 tests: $ok cases passed"
|
||||
@@ -0,0 +1,86 @@
|
||||
@echo off
|
||||
rem D: is the read-only exchange volume. Stage everything before running DOS tools.
|
||||
if not exist C:\FEC md C:\FEC
|
||||
if not exist C:\FEC\SRC md C:\FEC\SRC
|
||||
if not exist C:\FEC\STD md C:\FEC\STD
|
||||
if not exist C:\FEC\TESTS md C:\FEC\TESTS
|
||||
if not exist C:\FEC\TESTS\PASS md C:\FEC\TESTS\PASS
|
||||
if not exist C:\FEC\TESTS\FAIL md C:\FEC\TESTS\FAIL
|
||||
if exist C:\FEC\VM.FAIL del C:\FEC\VM.FAIL
|
||||
|
||||
copy D:\FEC\BUILD-~1.BAT C:\FEC\BUILD.BAT > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\TEST-DOS.BAT C:\FEC\TEST-DOS.BAT > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
|
||||
copy D:\FEC\SRC\ARENA.C C:\FEC\SRC\ARENA.C > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\SRC\ARENA.H C:\FEC\SRC\ARENA.H > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\SRC\DIAG.C C:\FEC\SRC\DIAG.C > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\SRC\DIAG.H C:\FEC\SRC\DIAG.H > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\SRC\LEXER.C C:\FEC\SRC\LEXER.C > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\SRC\LEXER.H C:\FEC\SRC\LEXER.H > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\SRC\AST.C C:\FEC\SRC\AST.C > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\SRC\AST.H C:\FEC\SRC\AST.H > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\SRC\PARSER.C C:\FEC\SRC\PARSER.C > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\SRC\PARSER.H C:\FEC\SRC\PARSER.H > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\SRC\DRIVER.C C:\FEC\SRC\DRIVER.C > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
|
||||
copy D:\FEC\STD\CORE.FE C:\FEC\STD\CORE.FE > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\STD\FMT.FE C:\FEC\STD\FMT.FE > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\STD\IO.FE C:\FEC\STD\IO.FE > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\STD\LIST.FE C:\FEC\STD\LIST.FE > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\STD\MAP.FE C:\FEC\STD\MAP.FE > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\STD\MEM.FE C:\FEC\STD\MEM.FE > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\STD\STR.FE C:\FEC\STD\STR.FE > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\STD\SYS.FE C:\FEC\STD\SYS.FE > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
|
||||
copy D:\FEC\TESTS\PASS\BASIC.FE C:\FEC\TESTS\PASS\BASIC.FE > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\TESTS\PASS\LITERALS.FE C:\FEC\TESTS\PASS\LITERALS.FE > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\TESTS\PASS\KEYWOR~1.FE C:\FEC\TESTS\PASS\KEYWOR.FE > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\TESTS\PASS\V012-F~1.FE C:\FEC\TESTS\PASS\V012-F.FE > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\TESTS\FAIL\MISSIN~1.FE C:\FEC\TESTS\FAIL\MISSIN.FE > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\TESTS\FAIL\UNCLOS~1.FE C:\FEC\TESTS\FAIL\UNCLOS.FE > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
copy D:\FEC\TESTS\FAIL\LOGICA~1.FE C:\FEC\TESTS\FAIL\LOGICA.FE > nul
|
||||
if errorlevel 1 goto stage_fail
|
||||
|
||||
call C:\FEC\TEST-DOS.BAT
|
||||
if exist C:\FEC\TEST.OK goto vm_success
|
||||
echo FAIL>C:\FEC\VM.FAIL
|
||||
verify other 2>nul
|
||||
goto stage_done
|
||||
|
||||
:vm_success
|
||||
cd C:\FEC
|
||||
goto stage_done
|
||||
|
||||
:stage_fail
|
||||
echo FAIL>C:\FEC\STAGE.FAIL
|
||||
echo FAIL>C:\FEC\VM.FAIL
|
||||
verify other 2>nul
|
||||
|
||||
:stage_done
|
||||
Reference in New Issue
Block a user