Fixed bug with wrong line numbers in error messages. Fixed permissions on examples. (0.39.14)
This commit is contained in:
parent
4956e44e60
commit
53ccdbda10
26 changed files with 274 additions and 116 deletions
|
|
@ -1,5 +1,5 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
project(fun VERSION 0.39.13 LANGUAGES C)
|
||||
project(fun VERSION 0.39.14 LANGUAGES C)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
|
|
@ -401,6 +401,28 @@ if(BUILD_TESTING)
|
|||
fun_add_example_test(crypto_crc32 examples/crypto/crc32_example.fun)
|
||||
fun_add_example_test(crypto_crc32c examples/crypto/crc32c_example.fun)
|
||||
fun_add_example_test(crypto_aes256 examples/crypto/aes256.fun)
|
||||
|
||||
# Include-line mapping regression test:
|
||||
# This script intentionally triggers a runtime error inside an included file.
|
||||
# The VM augments the error with a precise (file:line) from the included file.
|
||||
# We assert that stderr/stdout contain the correct mapped location and that the
|
||||
# program exits with a non-zero status (WILL_FAIL).
|
||||
set(_inc_map_script "${CMAKE_SOURCE_DIR}/examples/error/include_line_mapping_test.fun")
|
||||
if(EXISTS "${_inc_map_script}")
|
||||
add_test(NAME include_line_mapping
|
||||
COMMAND $<TARGET_FILE:fun> "${_inc_map_script}"
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
# Ensure the interpreter resolves <...> includes to the repo lib/
|
||||
set_tests_properties(include_line_mapping PROPERTIES
|
||||
ENVIRONMENT "FUN_LIB_DIR=${CMAKE_SOURCE_DIR}/lib"
|
||||
# The runtime error message should contain the mapped file path and line 7
|
||||
PASS_REGULAR_EXPRESSION "lib/test/include_divzero.fun:7"
|
||||
WILL_FAIL TRUE
|
||||
)
|
||||
else()
|
||||
message(WARNING "include_line_mapping_test.fun not found; skipping include_line_mapping CTest")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Install rules
|
||||
|
|
|
|||
0
examples/arrays/arrays.fun
Normal file → Executable file
0
examples/arrays/arrays.fun
Normal file → Executable file
0
examples/arrays/arrays_advanced.fun
Normal file → Executable file
0
examples/arrays/arrays_advanced.fun
Normal file → Executable file
0
examples/arrays/arrays_iter.fun
Normal file → Executable file
0
examples/arrays/arrays_iter.fun
Normal file → Executable file
0
examples/basics/boolean_decl.fun
Normal file → Executable file
0
examples/basics/boolean_decl.fun
Normal file → Executable file
0
examples/basics/booleans.fun
Normal file → Executable file
0
examples/basics/booleans.fun
Normal file → Executable file
0
examples/basics/builtins_conversions.fun
Normal file → Executable file
0
examples/basics/builtins_conversions.fun
Normal file → Executable file
0
examples/cli/cli_parse_example.fun
Normal file → Executable file
0
examples/cli/cli_parse_example.fun
Normal file → Executable file
0
examples/crypto/libressl_md5.fun
Normal file → Executable file
0
examples/crypto/libressl_md5.fun
Normal file → Executable file
0
examples/crypto/libressl_ripemd160.fun
Normal file → Executable file
0
examples/crypto/libressl_ripemd160.fun
Normal file → Executable file
0
examples/crypto/libressl_sha256.fun
Normal file → Executable file
0
examples/crypto/libressl_sha256.fun
Normal file → Executable file
0
examples/crypto/libressl_sha512.fun
Normal file → Executable file
0
examples/crypto/libressl_sha512.fun
Normal file → Executable file
34
examples/error/include_line_mapping_test.fun
Executable file
34
examples/error/include_line_mapping_test.fun
Executable file
|
|
@ -0,0 +1,34 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
* Added: 2026-03-30
|
||||
*/
|
||||
|
||||
/*
|
||||
Test: error location in included files shows correct file:line
|
||||
|
||||
How to run:
|
||||
- From repo root: ./build_debug/fun examples/error/include_line_mapping_test.fun
|
||||
|
||||
Expected: Runtime error message suffix contains the included file path
|
||||
(lib/test/include_divzero.fun) and the exact line number of
|
||||
the failing operation inside that file (see lib file below).
|
||||
*/
|
||||
|
||||
// Use angle-bracket include so resolution tries FUN_LIB_DIR, DEFAULT_LIB_DIR,
|
||||
// and finally the project-local lib/ directory. This makes the example work
|
||||
// from the repository without extra setup.
|
||||
#include <test/include_divzero.fun>
|
||||
|
||||
fun main()
|
||||
// Trigger the division by zero inside the included file
|
||||
boom()
|
||||
|
||||
main()
|
||||
0
examples/extra/notcurses_menu.fun
Normal file → Executable file
0
examples/extra/notcurses_menu.fun
Normal file → Executable file
0
examples/extra/notcurses_progress.fun
Normal file → Executable file
0
examples/extra/notcurses_progress.fun
Normal file → Executable file
11
examples/io/await_http_client.fun
Normal file → Executable file
11
examples/io/await_http_client.fun
Normal file → Executable file
|
|
@ -1,5 +1,16 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
* Added: 2026-03-29
|
||||
*/
|
||||
|
||||
/*
|
||||
* Async-style HTTP GET using lib/async/scheduler.fun helpers
|
||||
*
|
||||
|
|
|
|||
0
examples/io/file_io.fun
Normal file → Executable file
0
examples/io/file_io.fun
Normal file → Executable file
0
examples/snippets/include_namespace.fun
Normal file → Executable file
0
examples/snippets/include_namespace.fun
Normal file → Executable file
0
examples/snippets/maps_iterate.fun
Normal file → Executable file
0
examples/snippets/maps_iterate.fun
Normal file → Executable file
0
examples/strings/base64_demo.fun
Normal file → Executable file
0
examples/strings/base64_demo.fun
Normal file → Executable file
|
|
@ -1,3 +1,14 @@
|
|||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
* Added: 2026-03-30
|
||||
*/
|
||||
|
||||
/*
|
||||
* Cooperative asyncio helpers (library-level) for Fun
|
||||
*
|
||||
|
|
|
|||
20
lib/test/include_divzero.fun
Normal file
20
lib/test/include_divzero.fun
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
* Added: 2026-03-30
|
||||
*/
|
||||
|
||||
// Minimal test library to trigger a runtime error at a precise line
|
||||
|
||||
fun boom()
|
||||
// The division by zero MUST be on this exact line (line 14)
|
||||
x = 1 / 0
|
||||
return x
|
||||
|
||||
fun ok()
|
||||
return 42
|
||||
22
src/parser.c
22
src/parser.c
|
|
@ -6035,7 +6035,7 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
|||
static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos, int current_indent) {
|
||||
while (*pos < len) {
|
||||
if (g_has_error) return;
|
||||
size_t line_start = *pos;
|
||||
size_t line_start = *pos; /* remember raw line start for dedent backtracking */
|
||||
int indent = 0;
|
||||
if (!read_line_start(src, len, pos, &indent)) {
|
||||
/* EOF or error */
|
||||
|
|
@ -6053,10 +6053,12 @@ static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos,
|
|||
}
|
||||
|
||||
/* at same indent -> parse statement */
|
||||
/* Insert a line marker for better runtime error reporting */
|
||||
/* Insert a line marker for better runtime error reporting. Use the current
|
||||
* position (start of the actual statement) returned by read_line_start(),
|
||||
* not the pre-scan position which may point to a comment/blank line. */
|
||||
{
|
||||
int stmt_line = 1, stmt_col = 1;
|
||||
calc_line_col(src, len, line_start, &stmt_line, &stmt_col);
|
||||
calc_line_col(src, len, *pos, &stmt_line, &stmt_col);
|
||||
bytecode_add_instruction(bc, OP_LINE, stmt_line);
|
||||
}
|
||||
|
||||
|
|
@ -7643,6 +7645,20 @@ Bytecode *parse_file_to_bytecode(const char *path) {
|
|||
}
|
||||
|
||||
if (inner_line > 0 && inc_path[0] != '\0') {
|
||||
/* Adjust for shebang stripping in included file: if it starts with '#!' we
|
||||
* removed that entire first line during preprocessing, so add +1 to map
|
||||
* back to the physical file line number. */
|
||||
{
|
||||
FILE *sf = fopen(inc_path, "rb");
|
||||
if (sf) {
|
||||
int c1 = fgetc(sf);
|
||||
int c2 = fgetc(sf);
|
||||
if (c1 == '#' && c2 == '!') {
|
||||
inner_line += 1;
|
||||
}
|
||||
fclose(sf);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Parse error %s:%d:%d: %s (in %s:%d)\n",
|
||||
path ? path : "<input>", line, col, g_err_msg, inc_path, inner_line);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -661,17 +661,6 @@ static char *preprocess_includes_internal(const char *src, int depth) {
|
|||
free(inc);
|
||||
free(inc_clean);
|
||||
if (exp) {
|
||||
/* If alias requested, initialize namespace map before including content */
|
||||
if (ns[0] != '\0') {
|
||||
/* Announce alias so the parser can treat dot-call without implicit 'this' */
|
||||
sb_append(&out, "// __ns_alias__: ");
|
||||
sb_append(&out, ns);
|
||||
sb_append(&out, "\n");
|
||||
|
||||
sb_append(&out, ns);
|
||||
sb_append(&out, " = {}\n");
|
||||
}
|
||||
|
||||
/* mark file origin for better error messages */
|
||||
sb_append(&out, "// __include_begin__: ");
|
||||
sb_append(&out, resolved);
|
||||
|
|
@ -686,22 +675,6 @@ static char *preprocess_includes_internal(const char *src, int depth) {
|
|||
/* ensure included chunk ends with newline to preserve line structure */
|
||||
if (out.len == 0 || out.buf[out.len - 1] != '\n') sb_append_ch(&out, '\n');
|
||||
|
||||
/* If alias is present, export top-level fun/class into alias map */
|
||||
if (ns[0] != '\0') {
|
||||
NameList nl;
|
||||
nl_init(&nl);
|
||||
collect_exports_top_level(exp, &nl);
|
||||
for (int ei = 0; ei < nl.count; ++ei) {
|
||||
sb_append(&out, ns);
|
||||
sb_append(&out, ".");
|
||||
sb_append(&out, nl.names[ei]);
|
||||
sb_append(&out, " = ");
|
||||
sb_append(&out, nl.names[ei]);
|
||||
sb_append(&out, "\n");
|
||||
}
|
||||
nl_free(&nl);
|
||||
}
|
||||
|
||||
free(exp);
|
||||
}
|
||||
}
|
||||
|
|
@ -822,6 +795,150 @@ char *preprocess_includes(const char *src) {
|
|||
return preprocess_includes_internal(src, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Public helper: map a line number in the include-expanded top-level source file
|
||||
* back to the original included file path and inner line number, using the
|
||||
* `// __include_begin__: <path>[ as <alias>]` markers injected by the
|
||||
* preprocessor. Returns 1 on success and fills out_path/out_line; 0 otherwise.
|
||||
*/
|
||||
int map_expanded_line_to_include_path(const char *path, int line,
|
||||
char *out_path, size_t out_path_cap,
|
||||
int *out_line) {
|
||||
if (!path || line <= 0 || !out_path || out_path_cap == 0 || !out_line) return 0;
|
||||
out_path[0] = '\0';
|
||||
*out_line = line;
|
||||
|
||||
/* read original top-level file */
|
||||
size_t fsz = 0;
|
||||
char *orig = read_file_all(path, &fsz);
|
||||
if (!orig) return 0;
|
||||
|
||||
char *prep = preprocess_includes_internal(orig, 0);
|
||||
free(orig);
|
||||
if (!prep) return 0;
|
||||
|
||||
/* find start offset of requested 1-based line */
|
||||
size_t len = strlen(prep);
|
||||
size_t pos = 0;
|
||||
int cur = 1;
|
||||
while (pos < len && cur < line) {
|
||||
if (prep[pos] == '\n') cur++;
|
||||
pos++;
|
||||
}
|
||||
if (cur != line) { free(prep); return 0; }
|
||||
|
||||
/* scan backward to find last include marker line */
|
||||
const char *marker = "// __include_begin__: ";
|
||||
const char *end_marker = "// __include_end__: ";
|
||||
size_t mlen = strlen(marker);
|
||||
size_t elen = strlen(end_marker);
|
||||
size_t scan = pos;
|
||||
while (scan > 0) {
|
||||
/* find start of current line */
|
||||
size_t ls = scan;
|
||||
while (ls > 0 && prep[ls - 1] != '\n') ls--;
|
||||
/* check if this line starts with marker */
|
||||
if (ls + mlen <= len && strncmp(prep + ls, marker, mlen) == 0) {
|
||||
/* extract included path until EOL or " as " */
|
||||
size_t p = ls + mlen;
|
||||
size_t pe = p;
|
||||
while (pe < len && prep[pe] != '\n' && !(prep[pe] == ' ' && pe + 3 < len && strncmp(prep + pe, " as ", 4) == 0)) pe++;
|
||||
size_t copy = (pe - p) < (out_path_cap - 1) ? (pe - p) : (out_path_cap - 1);
|
||||
memcpy(out_path, prep + p, copy);
|
||||
out_path[copy] = '\0';
|
||||
|
||||
/* determine the span end by scanning forward with nesting to match the
|
||||
* corresponding __include_end__ marker. Lines between begin and its
|
||||
* matching end belong to this include. */
|
||||
size_t q = pe; /* end of path segment on the begin line */
|
||||
/* move to first content line after begin marker */
|
||||
while (q < len && prep[q] != '\n') q++;
|
||||
if (q < len && prep[q] == '\n') q++;
|
||||
|
||||
/* forward scan to find matching end marker accounting for nested includes */
|
||||
size_t span_start = q;
|
||||
size_t span_end = len; /* default to EOF if no end marker is found */
|
||||
int depth = 0;
|
||||
size_t fwd = q;
|
||||
while (fwd < len) {
|
||||
/* find start of current line */
|
||||
size_t ls2 = fwd;
|
||||
if (ls2 > 0) {
|
||||
while (ls2 > 0 && prep[ls2 - 1] != '\n') ls2--;
|
||||
}
|
||||
if (ls2 + mlen <= len && strncmp(prep + ls2, marker, mlen) == 0) {
|
||||
/* nested begin */
|
||||
depth++;
|
||||
} else if (ls2 + elen <= len && strncmp(prep + ls2, end_marker, elen) == 0) {
|
||||
if (depth == 0) {
|
||||
/* matching end for our begin */
|
||||
span_end = ls2; /* end before this line */
|
||||
break;
|
||||
} else {
|
||||
depth--;
|
||||
}
|
||||
}
|
||||
/* advance to next line */
|
||||
while (fwd < len && prep[fwd] != '\n') fwd++;
|
||||
if (fwd < len && prep[fwd] == '\n') fwd++;
|
||||
}
|
||||
|
||||
/* If the requested position is not within [span_start, span_end), this
|
||||
* begin marker does not apply; continue scanning backward. */
|
||||
if (!(pos >= span_start && pos < span_end)) {
|
||||
/* not inside this include span */
|
||||
goto next_scan_back;
|
||||
}
|
||||
|
||||
/* compute inner line as number of newlines from span_start to current pos */
|
||||
int inner = 1;
|
||||
size_t cnt = span_start;
|
||||
while (cnt < pos) { if (prep[cnt] == '\n') inner++; cnt++; }
|
||||
|
||||
/* Adjust for stripped shebang in included file: if the physical file
|
||||
* starts with "#!" (with or without a UTF-8 BOM), add +1 so the reported
|
||||
* line matches what the user sees in the editor. */
|
||||
do {
|
||||
size_t inc_sz = 0;
|
||||
char *inc_src = read_file_all(out_path, &inc_sz);
|
||||
if (!inc_src) break;
|
||||
const unsigned char *u = (const unsigned char *)inc_src;
|
||||
size_t off = 0;
|
||||
if (inc_sz >= 3 && u[0] == 0xEF && u[1] == 0xBB && u[2] == 0xBF) off = 3; /* UTF-8 BOM */
|
||||
if (inc_sz >= off + 2 && inc_src[off] == '#' && inc_src[off + 1] == '!') {
|
||||
inner += 1;
|
||||
}
|
||||
free(inc_src);
|
||||
} while (0);
|
||||
|
||||
/* Adjust for shebang stripping: if the real included file starts with '#!',
|
||||
* preprocessing removed that entire first line, so the visible "inner" line
|
||||
* number in the expanded text is one less than the actual file line. */
|
||||
int adjusted = inner;
|
||||
if (copy > 2) {
|
||||
FILE *sf = fopen(out_path, "rb");
|
||||
if (sf) {
|
||||
int c1 = fgetc(sf);
|
||||
int c2 = fgetc(sf);
|
||||
if (c1 == '#' && c2 == '!') {
|
||||
adjusted += 1;
|
||||
}
|
||||
fclose(sf);
|
||||
}
|
||||
}
|
||||
*out_line = adjusted;
|
||||
free(prep);
|
||||
return 1;
|
||||
}
|
||||
if (ls == 0) break;
|
||||
next_scan_back:
|
||||
scan = (ls > 0) ? (ls - 1) : 0;
|
||||
}
|
||||
|
||||
free(prep);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Float literal parser: supports decimal and scientific notation. Returns parsed double and advances pos on success. */
|
||||
static double parse_float_literal_value(const char *src, size_t len, size_t *pos, int *ok) {
|
||||
size_t p = *pos;
|
||||
|
|
|
|||
10
src/repl.c
10
src/repl.c
|
|
@ -20,6 +20,9 @@
|
|||
#include "value.h"
|
||||
#include "vm.h"
|
||||
|
||||
/* include mapping helper from parser_utils.c */
|
||||
extern int map_expanded_line_to_include_path(const char *path, int line, char *out_path, size_t out_path_cap, int *out_line);
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
@ -1662,6 +1665,13 @@ int fun_run_repl(VM *vm) {
|
|||
if (ins.op == OP_LINE) line = ins.operand;
|
||||
}
|
||||
const char *path = f->fn->source_file;
|
||||
/* Map to included file if the current line belongs to an included chunk */
|
||||
char mapped_path[1024];
|
||||
int mapped_line = line;
|
||||
if (map_expanded_line_to_include_path(path, line, mapped_path, sizeof(mapped_path), &mapped_line)) {
|
||||
path = mapped_path;
|
||||
line = mapped_line;
|
||||
}
|
||||
size_t flen = 0;
|
||||
char *src = read_entire_file(path, &flen);
|
||||
if (!src) {
|
||||
|
|
|
|||
87
src/vm.c
87
src/vm.c
|
|
@ -81,7 +81,7 @@
|
|||
|
||||
/* forward declarations for include mapping used in error reporting */
|
||||
extern char *preprocess_includes(const char *src);
|
||||
static int map_expanded_line_to_include(const char *path, int line, char *out_path, size_t out_path_cap, int *out_line);
|
||||
extern int map_expanded_line_to_include_path(const char *path, int line, char *out_path, size_t out_path_cap, int *out_line);
|
||||
|
||||
/* Threading internals (registry and platform glue) */
|
||||
#include "vm/os/thread_common.c"
|
||||
|
|
@ -127,7 +127,7 @@ static int fun_vm_vfprintf(FILE *stream, const char *fmt, va_list ap) {
|
|||
if (sfile && line > 0) {
|
||||
char mapped_path[1024];
|
||||
int mapped_line = line;
|
||||
if (map_expanded_line_to_include(sfile, line, mapped_path, sizeof(mapped_path), &mapped_line)) {
|
||||
if (map_expanded_line_to_include_path(sfile, line, mapped_path, sizeof(mapped_path), &mapped_line)) {
|
||||
sfile = strdup(mapped_path); /* leak on purpose for simplicity; this is rare */
|
||||
line = mapped_line;
|
||||
}
|
||||
|
|
@ -153,89 +153,6 @@ static int fun_vm_fprintf(FILE *stream, const char *fmt, ...) {
|
|||
return r;
|
||||
}
|
||||
|
||||
/* Helper: try to map expanded source line to included file:line using preprocessor markers */
|
||||
static int map_expanded_line_to_include(const char *path, int line, char *out_path, size_t out_path_cap, int *out_line) {
|
||||
if (!path || line <= 0 || !out_path || out_path_cap == 0 || !out_line) return 0;
|
||||
out_path[0] = '\0';
|
||||
*out_line = line;
|
||||
|
||||
/* read original file */
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) return 0;
|
||||
fseek(f, 0, SEEK_END);
|
||||
long sz = ftell(f);
|
||||
if (sz < 0) {
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
rewind(f);
|
||||
char *buf = (char *)malloc((size_t)sz + 1);
|
||||
if (!buf) {
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
size_t n = fread(buf, 1, (size_t)sz, f);
|
||||
fclose(f);
|
||||
buf[n] = '\0';
|
||||
|
||||
char *prep = preprocess_includes(buf);
|
||||
free(buf);
|
||||
if (!prep) return 0;
|
||||
|
||||
/* find start offset of requested 1-based line */
|
||||
size_t len = strlen(prep);
|
||||
size_t pos = 0;
|
||||
int cur = 1;
|
||||
while (pos < len && cur < line) {
|
||||
if (prep[pos] == '\n') cur++;
|
||||
pos++;
|
||||
}
|
||||
if (cur != line) {
|
||||
free(prep);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* scan backward to find last include marker line */
|
||||
const char *marker = "// __include_begin__: ";
|
||||
size_t mlen = strlen(marker);
|
||||
size_t scan = pos;
|
||||
while (scan > 0) {
|
||||
/* find start of this line */
|
||||
size_t ls = scan;
|
||||
while (ls > 0 && prep[ls - 1] != '\n')
|
||||
ls--;
|
||||
/* check marker */
|
||||
if (ls + mlen <= len && strncmp(prep + ls, marker, mlen) == 0) {
|
||||
/* extract included path up to EOL or ' as ' */
|
||||
size_t p = ls + mlen;
|
||||
size_t pe = p;
|
||||
while (pe < len && prep[pe] != '\n' && !(prep[pe] == ' ' && pe + 3 < len && strncmp(prep + pe, " as ", 4) == 0))
|
||||
pe++;
|
||||
size_t copy = (pe - p) < (out_path_cap - 1) ? (pe - p) : (out_path_cap - 1);
|
||||
memcpy(out_path, prep + p, copy);
|
||||
out_path[copy] = '\0';
|
||||
/* compute inner line as number of newlines from end of marker line to current pos */
|
||||
int inner = 1;
|
||||
size_t q = pe;
|
||||
/* skip to next line start */
|
||||
while (q < len && prep[q] != '\n')
|
||||
q++;
|
||||
if (q < len && prep[q] == '\n') q++;
|
||||
while (q < pos) {
|
||||
if (prep[q] == '\n') inner++;
|
||||
q++;
|
||||
}
|
||||
*out_line = inner;
|
||||
free(prep);
|
||||
return 1;
|
||||
}
|
||||
if (ls == 0) break;
|
||||
scan = (ls > 0) ? (ls - 1) : 0;
|
||||
}
|
||||
|
||||
free(prep);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Redirect fprintf within this translation unit so opcode handlers use our wrapper */
|
||||
#define fprintf fun_vm_fprintf
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue