From 19d79a79141b213c56d52ff95296b3fee70d1849 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Mon, 24 Feb 2025 17:41:06 -0600 Subject: [PATCH] parser: initial statement/expression --- .gitignore | 2 + Makefile | 43 +++++++++++ assert_hosted.h | 2 +- ast.h | 12 +-- compiler.mk | 8 ++ lexer.c | 10 +++ lexer.h | 2 + malloc.h | 7 ++ minmax.h | 4 + parser.c | 81 ++++++++++++++++---- parser.h | 12 +++ print_class | Bin 0 -> 37420 bytes printf.c | 186 +++++++++++++++++++++++++++++++++++++++++++++ printf.h | 47 ++++++++++++ string_parse.c | 70 +++++++++++++++++ string_parse.h | 21 +++++ string_unparse.c | 194 +++++++++++++++++++++++++++++++++++++++++++++++ string_unparse.h | 19 +++++ 18 files changed, 697 insertions(+), 23 deletions(-) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 compiler.mk create mode 100644 malloc.h create mode 100644 minmax.h create mode 100644 parser.h create mode 100755 print_class create mode 100644 printf.c create mode 100644 printf.h create mode 100644 string_parse.c create mode 100644 string_parse.h create mode 100644 string_unparse.c create mode 100644 string_unparse.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6142305 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +*.d diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..16d8666 --- /dev/null +++ b/Makefile @@ -0,0 +1,43 @@ +%.csv: %.ods + libreoffice --headless --convert-to csv:"Text - txt - csv (StarCalc)":44,34,76,,,,true --outdir $(dir $@) $< + +include compiler.mk + +MAKEFILE_PATH := $(patsubst %/,%,$(dir $(abspath $(firstword $(MAKEFILE_LIST))))) +CC ?= gcc +ARCH = -m32 +CFLAGS += -Wall -Werror -Wfatal-errors -Wno-error=unused-variable -fstack-protector -std=c2x -g +CFLAGS += -I$(MAKEFILE_PATH)/ +CFLAGS += -I$(MAKEFILE_PATH)/c +CFLAGS += -DDEBUG +#CFLAGS += -DDEBUG_PRINT +LDFLAGS = -lm +OPT ?= -O0 +DEPFLAGS = -MMD -MP + +%.o: %.c + $(CC) $(ARCH) $(CFLAGS) $(OPT) $(DEPFLAGS) -MF ${<}.d -c $< -o $@ + +print_class: $(OBJ) $(PRINT_CLASS_OBJ) + $(CC) $(ARCH) $(LDFLAGS) $^ -o $@ + +main: $(OBJ) $(MAIN_HOSTED_OBJ) + $(CC) $(ARCH) $(LDFLAGS) $^ -o $@ + +clean: + rm -f main print_class *.elf *.bin + find -P \ + -regextype posix-egrep \ + -regex '.*\.(o|d|gch)$$' \ + -exec rm {} \; + +.SUFFIXES: +.INTERMEDIATE: +.SECONDARY: +.PHONY: all clean phony + +%: RCS/%,v +%: RCS/% +%: %,v +%: s.% +%: SCCS/s.% diff --git a/assert_hosted.h b/assert_hosted.h index 09dc5c0..599d08e 100644 --- a/assert_hosted.h +++ b/assert_hosted.h @@ -13,4 +13,4 @@ extern void __assert_fail (const char *__assertion, const char *__file, }) #define fail(expr) \ - (__assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION);) + (__assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION)) diff --git a/ast.h b/ast.h index 982c95d..e0150e2 100644 --- a/ast.h +++ b/ast.h @@ -3,12 +3,11 @@ struct token; struct expression { - struct token * constant; + struct token constant; }; enum statement_type { STATEMENT_RETURN, - STATEMENT_IF, STATEMENT_IF_ELSE, }; @@ -18,21 +17,16 @@ struct statement_return { struct expression * expression; }; -struct statement_if { - struct expression * expression; - struct statement * statement; -}; - struct statement_if_else { struct expression * expression; - struct statement * statement; + struct statement * statement_if; + struct statement * statement_else; }; struct statement { enum statement_type type; union { struct statement_return * statement_return; - struct statement_if * statement_if; struct statement_if_else * statement_if_else; }; }; diff --git a/compiler.mk b/compiler.mk new file mode 100644 index 0000000..65cb20d --- /dev/null +++ b/compiler.mk @@ -0,0 +1,8 @@ +OBJ = \ + lexer.o \ + parser.o \ + malloc.o \ + printf.o \ + string_parse.o \ + string_unparse.o \ + main_hosted.o diff --git a/lexer.c b/lexer.c index 2fe7774..3251349 100644 --- a/lexer.c +++ b/lexer.c @@ -40,6 +40,16 @@ static const struct keyword_desc keywords[] = { .length = 6, .token_type = TOKEN_RETURN, }, + { + .buf = (const uint8_t *)"if", + .length = 2, + .token_type = TOKEN_RETURN, + }, + { + .buf = (const uint8_t *)"else", + .length = 4, + .token_type = TOKEN_RETURN, + }, }; static inline bool keyword_equal(const uint8_t * buf, int start, int end, const struct keyword_desc * keyword) diff --git a/lexer.h b/lexer.h index d7cd6b3..62b5f94 100644 --- a/lexer.h +++ b/lexer.h @@ -10,6 +10,8 @@ enum token_type { TOKEN_INT, TOKEN_VOID, TOKEN_RETURN, + TOKEN_IF, + TOKEN_ELSE, TOKEN_LPAREN, TOKEN_RPAREN, TOKEN_LBRACE, diff --git a/malloc.h b/malloc.h new file mode 100644 index 0000000..dca962f --- /dev/null +++ b/malloc.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#define malloct(t) (malloc_class_arena((sizeof (t)))) + +void * malloc_class_arena(uint32_t size); diff --git a/minmax.h b/minmax.h new file mode 100644 index 0000000..df4c506 --- /dev/null +++ b/minmax.h @@ -0,0 +1,4 @@ +#pragma once + +#define min(a, b) ( (a < b) ? a : b ) +#define max(a, b) ( (a > b) ? a : b ) diff --git a/parser.c b/parser.c index 2de949d..72d528c 100644 --- a/parser.c +++ b/parser.c @@ -1,19 +1,14 @@ #include "parser.h" -#include "lexer.h" #include "assert.h" - -struct token_reader { - bool have_token; - struct token token; - struct lexer_state lexer_state; -}; +#include "printf.h" +#include "malloc.h" struct token peek(struct token_reader * reader) { if (reader->have_token) { return reader->token; } else { - reader->token = lexer_next_token(reader->lexer_state); + reader->token = lexer_next_token(&reader->lexer_state); reader->have_token = true; return reader->token; } @@ -23,13 +18,73 @@ struct token consume(struct token_reader * reader) { struct token token = peek(reader); reader->have_token = false; + return token; } -void expect_type(struct token_reader * reader, enum token_type token_type) +bool match_type(struct token_reader * reader, enum token_type token_type) { - struct token token = consume(reader); - if (!(token->type == token_type)) { - printf("token->type=%d token_type=%d\n", token->type, token_type); - fail(token->type == token_type); + struct token token = peek(reader); + if (token.type == token_type) { + consume(reader); + return true; + } else { + return false; } } + +struct token expect_type(struct token_reader * reader, enum token_type token_type) +{ + struct token token = consume(reader); + if (!(token.type == token_type)) { + printf("token.type=%d token_type=%d\n", token.type, token_type); + fail(token.type == token_type); + } + return token; +} + +struct expression * parse_expression(struct token_reader * reader) +{ + struct expression * expr = malloct(struct expression); + struct token token = expect_type(reader, TOKEN_CONSTANT); + expr->constant = token; + return expr; +} + +struct statement_return * parse_statement_return(struct token_reader * reader) +{ + struct expression * expr = parse_expression(reader); + struct statement_return * stmt = malloct(struct statement_return); + stmt->expression = expr; + return stmt; +} + +struct statement_if_else * parse_statement_if_else(struct token_reader * reader) +{ + struct statement_if_else * stmt = malloct(struct statement_if_else); + expect_type(reader, TOKEN_LPAREN); + struct expression * expr = parse_expression(reader); + expect_type(reader, TOKEN_RPAREN); + struct statement * statement_if = parse_statement(reader); + struct statement * statement_else = match_type(reader, TOKEN_ELSE) ? parse_statement(reader) : nullptr; + stmt->expression = expr; + stmt->statement_if = statement_if; + stmt->statement_else = statement_else; + return stmt; +} + +struct statement * parse_statement(struct token_reader * reader) +{ + struct statement * stmt = malloct(struct statement); + if (match_type(reader, TOKEN_IF)) { + stmt->type = STATEMENT_IF_ELSE; + stmt->statement_if_else = parse_statement_if_else(reader); + } else if (match_type(reader, TOKEN_RETURN)) { + stmt->type = STATEMENT_RETURN; + stmt->statement_return = parse_statement_return(reader); + } else { + printf("token_type=%d\n", peek(reader).type); + fail("expected statement"); + } + + return stmt; +} diff --git a/parser.h b/parser.h new file mode 100644 index 0000000..66df2e4 --- /dev/null +++ b/parser.h @@ -0,0 +1,12 @@ +#pragma once + +#include "lexer.h" +#include "ast.h" + +struct token_reader { + bool have_token; + struct token token; + struct lexer_state lexer_state; +}; + +struct statement * parse_statement(struct token_reader * reader); diff --git a/print_class b/print_class new file mode 100755 index 0000000000000000000000000000000000000000..7acecf966086a812dd8e863cb57c34a6fdf128bf GIT binary patch literal 37420 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 M +#include + +#include "string_parse.h" +#include "string_unparse.h" +#include "printf.h" +//#include "sh7091_scif.h" + +enum format_type { + FORMAT_BASE10_UNSIGNED, + FORMAT_BASE10, + FORMAT_BASE10_64, + FORMAT_POINTER, + FORMAT_BASE16, + FORMAT_STRING, + FORMAT_CHAR, + FORMAT_PERCENT, +}; + +struct format { + enum format_type type; + int pad_length; + char fill_char; +}; + +static const char * parse_escape(const char * format, struct format * ft); + +static const char * parse_fill_pad(const char * format, struct format * ft) +{ + if (*format == 0) + return format; + if (*format >= '1' || *format <= '9') + ft->fill_char = ' '; + else + ft->fill_char = *format++; + format = parse_base10(format, &ft->pad_length); + return parse_escape(format, ft); +} + +static const char * parse_escape(const char * format, struct format * ft) +{ + switch (*format) { + case 0: + return format; + case 'u': + ft->type = FORMAT_BASE10_UNSIGNED; + return format + 1; + case 'd': + ft->type = FORMAT_BASE10; + return format + 1; + case 'l': + ft->type = FORMAT_BASE10_64; + return format + 1; + case 'p': + ft->type = FORMAT_POINTER; + return format + 1; + case 'x': + ft->type = FORMAT_BASE16; + return format + 1; + case 's': + ft->type = FORMAT_STRING; + return format + 1; + case 'c': + ft->type = FORMAT_CHAR; + return format + 1; + case '%': + ft->type = FORMAT_PERCENT; + return format + 1; + default: + return parse_fill_pad(format, ft); + } +} + +void print_string(const char * s, int length) +{ + for (int i = 0; i < length; i++) { + print_char(s[i]); + } +} + +void print_bytes(const uint8_t * s, int length) +{ + for (int i = 0; i < length; i++) { + print_char(s[i]); + } +} + +void print_chars(const uint16_t * s, int length) +{ + for (int i = 0; i < length; i++) { + print_char(s[i]); + } +} + +void print_cstring(const char * s) +{ + while (*s != 0) { + print_char(*s++); + } +} + +void _printf(const char * format, ...) +{ + va_list args; + va_start(args, format); + + while (true) { + if (*format == 0) + break; + + switch (*format) { + case '%': + { + struct format ft = {0}; + format = parse_escape(format + 1, &ft); + switch (ft.type) { + case FORMAT_BASE10_UNSIGNED: + { + uint32_t num = va_arg(args, uint32_t); + char s[10]; + int offset = unparse_base10_unsigned(s, num, ft.pad_length, ft.fill_char); + print_string(s, offset); + } + break; + case FORMAT_BASE10: + { + int32_t num = va_arg(args, int32_t); + char s[10]; + int offset = unparse_base10(s, num, ft.pad_length, ft.fill_char); + print_string(s, offset); + } + break; + case FORMAT_BASE10_64: + { + int64_t num = va_arg(args, int64_t); + char s[20]; + int offset = unparse_base10_64(s, num, ft.pad_length, ft.fill_char); + print_string(s, offset); + } + break; + case FORMAT_POINTER: + { + print_char('0'); + print_char('x'); + } + /* fall through */; + case FORMAT_BASE16: + { + uint32_t num = va_arg(args, uint32_t); + char s[8]; + int offset = unparse_base16(s, num, ft.pad_length, ft.fill_char); + print_string(s, offset); + } + break; + case FORMAT_STRING: + { + const char * s = va_arg(args, const char *); + while (*s != 0) { + char c = *s++; + print_char(c); + } + } + break; + case FORMAT_CHAR: + { + const int c = va_arg(args, const int); + print_char((char)c); + } + break; + case FORMAT_PERCENT: + print_char('%'); + break; + } + } + break; + default: + { + char c = *format++; + print_char(c); + } + break; + } + } + + va_end(args); +} diff --git a/printf.h b/printf.h new file mode 100644 index 0000000..1713fba --- /dev/null +++ b/printf.h @@ -0,0 +1,47 @@ +#pragma once + +#if defined(__dreamcast__) +#include "sh7091_scif.h" +#else +#include +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static inline void print_char(char c) +{ +#if defined(__dreamcast__) + // scif_character(c); +#else + fputc(c, stderr); +#endif +} + +void print_string(const char * s, int length); +void print_bytes(const uint8_t * s, int length); +void print_chars(const uint16_t * s, int length); +void print_cstring(const char * s); + +void _printf(const char * format, ...); + +#define printf(...) _printf(__VA_ARGS__) +#define printc(c) print_char(c) +#define prints(s) print_cstring(s) + +#if defined(DEBUG_PRINT) +#define debugf(...) _printf(__VA_ARGS__) +#define debugc(c) print_char(c) +#define debugs(s) print_cstring(s) +#else +#define debugf(...) +#define debugc(c) +#define debugs(c) +#endif + +#ifdef __cplusplus +} +#endif diff --git a/string_parse.c b/string_parse.c new file mode 100644 index 0000000..05cb963 --- /dev/null +++ b/string_parse.c @@ -0,0 +1,70 @@ +#include + +#include "string_parse.h" + +int parse_base10_digit(char c) +{ + switch (c) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + default: return -1; + } +} + +const char * parse_base10(const char * s, int * n) +{ + *n = 0; + int sign = 1; + + if (*s == '-') { + sign = -1; + s++; + } + + while (true) { + int digit = parse_base10_digit(*s); + if (digit == -1) + break; + + *n *= 10; + *n += digit; + s++; + } + + *n *= sign; + + return s; +} + +const char * parse_base10_64(const char * s, int64_t * n) +{ + *n = 0; + int sign = 1; + + if (*s == '-') { + sign = -1; + s++; + } + + while (true) { + int digit = parse_base10_digit(*s); + if (digit == -1) + break; + + *n *= 10; + *n += digit; + s++; + } + + *n *= sign; + + return s; +} diff --git a/string_parse.h b/string_parse.h new file mode 100644 index 0000000..3e7f4e4 --- /dev/null +++ b/string_parse.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +const char * parse_skip(const char * s, char c); +const char * parse_find(const char * s, char c); +const char * parse_find_first_right(const char * s, int length, char c); +int parse_base10_digit(char c); +const char * parse_base10(const char * s, int * n); +const char * parse_base10_64(const char * s, int64_t * n); +const char * parse_match(const char * s, const char * m); +int parse_stride(const char * s, int length); +int parse_height(const char * s, int length); + +#ifdef __cplusplus +} +#endif diff --git a/string_unparse.c b/string_unparse.c new file mode 100644 index 0000000..45a78fa --- /dev/null +++ b/string_unparse.c @@ -0,0 +1,194 @@ +#include + +#include "minmax.h" +#include "string_unparse.h" + +int digits_base10(uint32_t n) +{ + if (n >= 1000000000ul) return 10; + if (n >= 100000000ul) return 9; + if (n >= 10000000ul) return 8; + if (n >= 1000000ul) return 7; + if (n >= 100000ul) return 6; + if (n >= 10000ul) return 5; + if (n >= 1000ul) return 4; + if (n >= 100ul) return 3; + if (n >= 10ul) return 2; + return 1; +} + +int unparse_base10_unsigned(char * s, uint32_t n, int len, char fill) +{ + int digits = 0; + digits += digits_base10(n); + len = max(digits, len); + int ret = len; + + while (len > digits) { + *s++ = fill; + --len; + } + + while (len > 0) { + const uint32_t digit = n % 10; + n = n / 10; + s[--len] = digit + 48; + } + + return ret; +} + +int unparse_base10(char * s, int32_t n, int len, char fill) +{ + bool negative = false; + int digits = 0; + if (n < 0) { + digits += 1; + n = -n; + negative = true; + } + + digits += digits_base10(n); + len = max(digits, len); + int ret = len; + + while (len > digits) { + *s++ = fill; + --len; + } + + if (negative) { + *s++ = '-'; + len--; + } + + while (len > 0) { + const uint32_t digit = n % 10; + n = n / 10; + s[--len] = digit + 48; + } + + return ret; +} + +int digits_base10_64(uint64_t n) +{ + if (n >= 10000000000000000000ull) return 20; + if (n >= 1000000000000000000ull) return 19; + if (n >= 100000000000000000ull) return 18; + if (n >= 10000000000000000ull) return 17; + if (n >= 1000000000000000ull) return 16; + if (n >= 100000000000000ull) return 15; + if (n >= 10000000000000ull) return 14; + if (n >= 1000000000000ull) return 13; + if (n >= 100000000000ull) return 12; + if (n >= 10000000000ull) return 11; + if (n >= 1000000000ull) return 10; + if (n >= 100000000ull) return 9; + if (n >= 10000000ull) return 8; + if (n >= 1000000ull) return 7; + if (n >= 100000ull) return 6; + if (n >= 10000ull) return 5; + if (n >= 1000ull) return 4; + if (n >= 100ull) return 3; + if (n >= 10ull) return 2; + return 1; +} + +int unparse_base10_64(char * s, int64_t n, int len, char fill) +{ + bool negative = false; + int digits = 0; + if (n < 0) { + digits += 1; + n = -n; + negative = true; + } + + digits += digits_base10_64(n); + len = max(digits, len); + int ret = len; + + while (len > digits) { + *s++ = fill; + --len; + } + + if (negative) { + *s++ = '-'; + len--; + } + + while (len > 0) { + const uint32_t digit = n % 10; + n = n / 10; + s[--len] = digit + 48; + } + + return ret; +} + +static int digits_base16(uint32_t n) +{ + if (n <= 0xf) return 1; + if (n <= 0xff) return 2; + if (n <= 0xfff) return 3; + if (n <= 0xffff) return 4; + if (n <= 0xfffff) return 5; + if (n <= 0xffffff) return 6; + if (n <= 0xfffffff) return 7; + return 8; +} + +int unparse_base16(char * s, uint32_t n, int len, char fill) +{ + int digits = digits_base16(n); + len = max(digits, len); + int ret = len; + + while (len > digits) { + *s++ = fill; + --len; + } + + while (len > 0) { + uint32_t nib = n & 0xf; + n = n >> 4; + if (nib > 9) { + nib += (97 - 10); + } else { + nib += (48 - 0); + } + + s[--len] = nib; + } + + return ret; +} + +#ifdef UNPARSE_TEST +#include + +int main() +{ + char s[1024]; + + { + int n = 124; + + int offset = unparse_base10(s, n, 6, ' '); + s[offset] = 0; + + printf("`%s`\n", s); + } + + { + int n = 0x5678; + + int offset = unparse_base16(s, n, 7, '0'); + s[offset] = 0; + + printf("`%s`\n", s); + } +} +#endif diff --git a/string_unparse.h b/string_unparse.h new file mode 100644 index 0000000..ba8ea99 --- /dev/null +++ b/string_unparse.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int unparse_base10_unsigned(char * s, uint32_t n, int len, char fill); +int unparse_base10(char * s, int32_t n, int len, char fill); +int unparse_base10_64(char * s, int64_t n, int len, char fill); +int unparse_base16(char * s, uint32_t n, int len, char fill); + +int digits_base_64(uint64_t n); +int digits_base10_64(uint64_t n); + +#ifdef __cplusplus +} +#endif