diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2024-07-04 16:23:05 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2024-07-04 16:23:05 -0400 |
| commit | 9d9fe12987bc8e7c3266ec320d04a97a37c3fb1b (patch) | |
| tree | 8a79d864f60d81f08a0776b6f85dbab242ecc89a | |
| parent | d073c10137a1aa8a3d3f5e3a71b22cbb4725b47c (diff) | |
Add `!!` statement for printing text
| -rw-r--r-- | ast.c | 1 | ||||
| -rw-r--r-- | ast.h | 5 | ||||
| -rw-r--r-- | compile.c | 20 | ||||
| -rw-r--r-- | parse.c | 53 | ||||
| -rw-r--r-- | test/text.tm | 3 | ||||
| -rw-r--r-- | typecheck.c | 2 |
6 files changed, 80 insertions, 4 deletions
@@ -137,6 +137,7 @@ CORD ast_to_xml(ast_t *ast) optional_tagged("combination", data.combination), optional_tagged("fallback", data.fallback)) T(Skip, "<Skip>%r</Skip>", data.target) T(Stop, "<Stop>%r</Stop>", data.target) + T(PrintStatement, "<PrintStatement>%r</PrintStatement>", ast_list_to_xml(data.to_print)) T(Pass, "<Pass/>") T(Return, "<Return>%r</Return>", ast_to_xml(data.value)) T(Extern, "<Extern name=\"%s\">%r</Extern>", data.name, type_ast_to_xml(data.type)) @@ -99,7 +99,7 @@ typedef enum { Unknown = 0, Nil, Bool, Var, Int, Num, - TextLiteral, TextJoin, + TextLiteral, TextJoin, PrintStatement, Declare, Assign, BinaryOp, UpdateAssign, Length, Not, Negative, HeapAllocate, StackReference, @@ -152,6 +152,9 @@ struct ast_s { ast_list_t *children; } TextJoin; struct { + ast_list_t *to_print; + } PrintStatement; + struct { ast_t *var; ast_t *value; } Declare; @@ -17,6 +17,7 @@ static CORD compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bool allow_optional); static env_t *with_enum_scope(env_t *env, type_t *t); static CORD compile_math_method(env_t *env, ast_t *ast, binop_e op, ast_t *lhs, ast_t *rhs, type_t *required_type); +static CORD compile_string(env_t *env, ast_t *ast, CORD color); static bool promote(env_t *env, CORD *code, type_t *actual, type_t *needed) { @@ -679,6 +680,23 @@ CORD compile_statement(env_t *env, ast_t *ast) code_err(ast, "I couldn't figure out how to make this stop work!"); } case Pass: return ";"; + case PrintStatement: { + ast_list_t *to_print = Match(ast, PrintStatement)->to_print; + if (!to_print) + return CORD_EMPTY; + + CORD code = "say(CORD_all("; + for (ast_list_t *chunk = to_print; chunk; chunk = chunk->next) { + type_t *t = get_type(env, chunk->ast); + if (t->tag == TextType && !Match(t, TextType)->lang) { + code = CORD_cat(code, compile(env, chunk->ast)); + } else { + code = CORD_cat(code, compile_string(env, chunk->ast, "USE_COLOR")); + } + if (chunk->next) code = CORD_cat(code, ", "); + } + return CORD_cat(code, "));"); + } case Return: { if (!env->fn_ctx) code_err(ast, "This return statement is not inside any function"); auto ret = Match(ast, Return)->value; @@ -2023,7 +2041,7 @@ CORD compile(env_t *env, ast_t *ast) case Extern: code_err(ast, "Externs are not supported as expressions"); case TableEntry: code_err(ast, "Table entries should not be compiled directly"); case Declare: case Assign: case UpdateAssign: case For: case While: case StructDef: case LangDef: - case EnumDef: case FunctionDef: case Skip: case Stop: case Pass: case Return: case DocTest: + case EnumDef: case FunctionDef: case Skip: case Stop: case Pass: case Return: case DocTest: case PrintStatement: code_err(ast, "This is not a valid expression"); case Unknown: code_err(ast, "Unknown AST"); } @@ -97,6 +97,7 @@ static PARSER(parse_extern); static PARSER(parse_inline_c); static PARSER(parse_declaration); static PARSER(parse_doctest); +static PARSER(parse_say); static PARSER(parse_use); static PARSER(parse_import); static PARSER(parse_linker); @@ -1527,7 +1528,8 @@ PARSER(parse_assignment) { PARSER(parse_statement) { ast_t *stmt = NULL; if ((stmt=parse_declaration(ctx, pos)) - || (stmt=parse_doctest(ctx, pos))) + || (stmt=parse_doctest(ctx, pos)) + || (stmt=parse_say(ctx, pos))) return stmt; if (!(false @@ -2014,6 +2016,55 @@ PARSER(parse_doctest) { return NewAST(ctx->file, start, pos, DocTest, .expr=expr, .output=output); } +PARSER(parse_say) { + const char *start = pos; + if (!match(&pos, "!!")) return NULL; + spaces(&pos); + + ast_list_t *chunks = NULL; + CORD chunk = CORD_EMPTY; + const char *chunk_start = pos; + const char open_interp = '{', close_interp = '}'; + while (pos < ctx->file->text + ctx->file->len) { + if (*pos == open_interp) { // Interpolation + const char *interp_start = pos; + if (chunk) { + ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .cord=chunk); + chunks = new(ast_list_t, .ast=literal, .next=chunks); + chunk = NULL; + } + ++pos; + ast_t *interp; + if (close_interp) { + whitespace(&pos); + interp = expect(ctx, interp_start, &pos, parse_expr, "I expected an interpolation expression here"); + whitespace(&pos); + expect_closing(ctx, &pos, (char[]){close_interp, 0}, "I was expecting a '%c' to finish this interpolation", close_interp); + } else { + if (*pos == ' ' || *pos == '\t') + parser_err(ctx, pos, pos+1, "Whitespace is not allowed before an interpolation here"); + interp = expect(ctx, interp_start, &pos, parse_term, "I expected an interpolation term here"); + } + chunks = new(ast_list_t, .ast=interp, .next=chunks); + chunk_start = pos; + } else if (*pos == '\r' || *pos == '\n') { // Newline + break; + } else { // Plain character + chunk = CORD_cat_char(chunk, *pos); + ++pos; + } + } + + if (chunk) { + ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .cord=chunk); + chunks = new(ast_list_t, .ast=literal, .next=chunks); + chunk = NULL; + } + + REVERSE_LIST(chunks); + return NewAST(ctx->file, start, pos, PrintStatement, .to_print=chunks); +} + PARSER(parse_use) { const char *start = pos; if (!match_word(&pos, "use")) return NULL; diff --git a/test/text.tm b/test/text.tm index edc3b37b..c665d588 100644 --- a/test/text.tm +++ b/test/text.tm @@ -1,5 +1,7 @@ func main(): >> str := "Hello Amélie!" + !! Testing strings like {str} + >> str:upper() = "HELLO AMÉLIE!" >> str:lower() @@ -7,6 +9,7 @@ func main(): >> str:lower():title() = "Hello Amélie!" + >> \UE9 = "é" diff --git a/typecheck.c b/typecheck.c index d21d28ad..8a601c69 100644 --- a/typecheck.c +++ b/typecheck.c @@ -735,7 +735,7 @@ type_t *get_type(env_t *env, ast_t *ast) case Use: { return Type(ModuleType, Match(ast, Use)->name); } - case Return: case Stop: case Skip: { + case Return: case Stop: case Skip: case PrintStatement: { return Type(AbortType); } case Pass: return Type(VoidType); |
