Compare commits

...

2 Commits

Author SHA1 Message Date
wash de0cad11b2 meta: add vim plugin for syntax highlighting 2026-05-07 10:46:19 +01:00
wash 416d86ad63 bshell: add line-editor and file-based input support 2026-05-07 10:46:03 +01:00
35 changed files with 1945 additions and 0 deletions
+1
View File
@@ -143,3 +143,4 @@ tags
# End of https://www.toptal.com/developers/gitignore/api/c,vim,linux,macos,cmake
build/
.cache/
+7
View File
@@ -1,7 +1,13 @@
cmake_minimum_required(VERSION 3.31)
project(bshell C)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
find_package(Python COMPONENTS Interpreter REQUIRED)
find_package(FX REQUIRED COMPONENTS
fx.runtime
fx.collections
fx.term)
execute_process(
COMMAND ${Python_EXECUTABLE}
@@ -16,5 +22,6 @@ message(STATUS "B Shell version: ${bshell_version}")
add_executable(bshell ${bshell_sources})
target_link_libraries(bshell FX::Runtime FX::Collections FX::Term)
target_compile_definitions(bshell PUBLIC
BSHELL_VERSION="${bshell_version}")
View File
+1
View File
@@ -0,0 +1 @@
autocmd BufNewFile,BufRead *.bshell setfiletype bshell
+4
View File
@@ -0,0 +1,4 @@
setlocal tabstop=8
setlocal softtabstop=4
setlocal shiftwidth=4
setlocal expandtab
+126
View File
@@ -0,0 +1,126 @@
if exists('b:current_syntax')
finish
endif
let s:save_cpo = &cpoptions
set cpoptions&vim
setlocal iskeyword+=-
syn match bshellFunctionRef /\<[A-Za-z][A-Za-z0-9]*\(-[A-Za-z0-9][A-Za-z0-9]*\)\+\>/
syn match bshellVariable /\$[A-Za-z][A-Za-z0-9_]*/
syn match bshellArgFlag /\<-[A-Za-z][A-Za-z0-9]*\(-[A-Za-z0-9][A-Za-z0-9]*\)*\>/
syn keyword bshellKeyword func
syn keyword bshellTodo contained TODO FIXME XXX NOTE HACK TBD
syn match bshellLineComment /#.*$/ contains=bshellTodo
syn region bshellInterpolatedString matchgroup=bshellString start=+"+ end=+"+ extend contains=bshellVariable
syn region bshellLiteralString matchgroup=bshellString start=+\'+ end=+\'+ extend contains=bshellSpecialChar,bshellSpecialError,bshellUnicodeNumber,@Spell
hi def link bshellKeyword Statement
hi def link bshellArgFlag Tag
hi def link bshellVariable Identifier
hi def link bshellLineComment Comment
hi def link bshellFunctionRef Function
hi def link bshellString String
hi def link bshellInterpolatedString String
hi def link bshellLiteralString String
" The default highlighting.
" hi def link bshellUnspecifiedStatement Statement
" hi def link bshellUnsupportedStatement Statement
"
" hi def link bshellGlobalNamespaceAlias Include
"
" hi def link bshellType Type
"
" hi def link bshellStorage Keyword
" hi def link bshellIsAs Keyword
" hi def link bshellAccessor Keyword
" hi def link bshellBuiltinVar @variable.builtin
" hi def link bshellSelfVar @variable.builtin
"
" hi def link bshellStatement Statement
" hi def link bshellRepeat Repeat
" hi def link bshellConditional Conditional
" hi def link bshellSelectorLabel Tag
" hi def link bshellUnnamedLabel Comment
" hi def link bshellUnnamedVariable Comment
" hi def link bshellLambdaParameter @variable.builtin
" hi def link bshellException Exception
"
" hi def link bshellParens Delimiter
" hi def link bshellBraces Structure
" hi def link bshellControlSymbols Keyword
"
" hi def link bshellModifier StorageClass
" hi def link bshellAccessModifier bshellModifier
" hi def link bshellAsyncModifier bshellModifier
" hi def link bshellCheckedModifier bshellModifier
" hi def link bshellManagedModifier bshellModifier
" hi def link bshellUsingModifier bshellModifier
"
" hi def link bshellTodo Todo
" hi def link bshellComment Comment
" hi def link bshellLineComment bshellComment
" hi def link bshellBlockComment bshellComment
" hi def link bshellLineContinuation bshellComment
"
" hi def link bshellKeywordOperator Keyword
" hi def link bshellAsyncOperator bshellKeywordOperator
" hi def link bshellTypeOf bshellKeywordOperator
" hi def link bshellTypeOfOperand Typedef
" hi def link bshellTypeOfError Error
" hi def link bshellOpSymbols Operator
" hi def link bshellPackageAccessOperator Operator
" hi def link bshellOtherSymbols Structure
" hi def link bshellLogicSymbols Operator
" hi def link bshellWordOperator Operator
"
" hi def link bshellSpecialError Error
" hi def link bshellSpecialCharError Error
" hi def link bshellString String
" hi def link bshellQuote String
" hi def link bshellInterpolatedString String
" hi def link bshellVerbatimString String
" hi def link bshellInterVerbString String
" hi def link bshellVerbatimQuote SpecialChar
"
" hi def link bshellConstant Constant
" hi def link bshellNull Constant
" hi def link bshellBoolean Boolean
" hi def link bshellCharacter Character
" hi def link bshellSpecialChar SpecialChar
" hi def link bshellInteger Number
" hi def link bshellReal Float
" hi def link bshellWord Identifier
" hi def link bshellUnicodeNumber SpecialChar
" hi def link bshellUnicodeSpecifier SpecialChar
" hi def link bshellInterpolationDelimiter Delimiter
" hi def link bshellInterpolationAlignDel bshellInterpolationDelimiter
" hi def link bshellInterpolationFormat bshellInterpolationDelimiter
" hi def link bshellInterpolationFormatDel bshellInterpolationDelimiter
"
" hi def link bshellGenericBraces bshellBraces
"
" hi def link bshellAtomName Constant
"
" hi def link bshellComplexMessageName Function
" hi def link bshellUnaryMessageName Function
" hi def link bshellPropertyName @property
" hi def link bshellPropertySymbol Statement
"
" hi def link bshellStatementSeparator Comment
" hi def link bshellMessageTerminator @punctuation.special
"
" hi def link bshellPackageStmtIdentifier @string.special.url
" hi def link bshellUseStmtIdentifier @module
let b:current_syntax = 'bshell'
let &cpoptions = s:save_cpo
unlet s:save_cpo
" vim: vts=16,28
+116
View File
@@ -0,0 +1,116 @@
#include "file.h"
#include "line-source.h"
#include <errno.h>
#include <fx/collections/array.h>
#include <fx/string.h>
#include <stdlib.h>
#include <string.h>
static enum bshell_status get_name(
struct line_source *src,
char *buf,
size_t count,
size_t *nr_read)
{
struct file *f = (struct file *)src;
*nr_read = snprintf(buf, count, "%s", f->f_path);
return BSHELL_SUCCESS;
}
static enum bshell_status get_row(
struct line_source *src,
size_t row,
char *buf,
size_t count,
size_t *nr_read)
{
struct file *f = (struct file *)src;
size_t nr_rows = fx_array_size(f->f_lines);
if (row > nr_rows) {
return BSHELL_ERR_EOF;
}
fx_string *line = fx_array_at(f->f_lines, row - 1);
const char *line_str = fx_string_get_cstr(line);
size_t line_len = fx_string_get_size(line, FX_STRLEN_NORMAL);
size_t copy_len = fx_min(ulong, count, line_len);
memcpy(buf, line_str, copy_len);
buf[copy_len] = 0;
buf[strcspn(buf, "\n")] = 0;
*nr_read = copy_len;
return BSHELL_SUCCESS;
}
static enum bshell_status readline(
struct line_source *src,
fx_stringstream *out)
{
struct file *f = (struct file *)src;
fx_wchar c = FX_WCHAR_INVALID;
size_t nr_read = 0;
while (1) {
fx_status status = fx_stream_read_char(f->f_strp, &c);
if (!FX_OK(status)) {
break;
}
fx_stream_write_char(out, c);
nr_read++;
if (c == '\n') {
break;
}
}
if (nr_read == 0) {
return BSHELL_ERR_EOF;
}
return BSHELL_SUCCESS;
}
enum bshell_status file_open(const char *path, struct file **out)
{
FILE *fp = fopen(path, "r");
if (!fp) {
return bshell_status_from_errno(errno);
}
fx_stream *strp = fx_stream_open_fp(fp);
struct file *file = malloc(sizeof *file);
if (!file) {
fclose(fp);
return BSHELL_ERR_NO_MEMORY;
}
memset(file, 0x0, sizeof *file);
file->f_base.s_get_name = get_name;
file->f_base.s_get_row = get_row;
file->f_base.s_readline = readline;
file->f_fp = fp;
file->f_strp = strp;
file->f_path = fx_strdup(path);
file->f_lines = fx_array_create();
*out = file;
return BSHELL_SUCCESS;
}
void file_close(struct file *file)
{
fx_stream_unref(file->f_strp);
fx_array_unref(file->f_lines);
free(file->f_path);
fclose(file->f_fp);
free(file);
}
+20
View File
@@ -0,0 +1,20 @@
#ifndef FILE_H_
#define FILE_H_
#include "line-source.h"
#include <fx/collections/array.h>
#include <stdio.h>
struct file {
struct line_source f_base;
fx_array *f_lines;
char *f_path;
fx_stream *f_strp;
FILE *f_fp;
};
extern enum bshell_status file_open(const char *path, struct file **out);
extern void file_close(struct file *file);
#endif
+61
View File
@@ -0,0 +1,61 @@
#include "lex.h"
#include "token.h"
#define LEX_TOKEN_DEF(i, n) {.id = (i), .name = (n)}
static struct lex_token_def keywords[] = {
LEX_TOKEN_DEF(KW_FUNC, "func"),
};
static const size_t nr_keywords = sizeof keywords / sizeof keywords[0];
static struct lex_token_def symbols[] = {
LEX_TOKEN_DEF(SYM_SQUOTE, "'"),
LEX_TOKEN_DEF(SYM_DQUOTE, "\""),
LEX_TOKEN_DEF(SYM_LEFT_BRACE, "{"),
LEX_TOKEN_DEF(SYM_RIGHT_BRACE, "}"),
LEX_TOKEN_DEF(SYM_LEFT_BRACKET, "["),
LEX_TOKEN_DEF(SYM_RIGHT_BRACKET, "]"),
LEX_TOKEN_DEF(SYM_LEFT_PAREN, "("),
LEX_TOKEN_DEF(SYM_RIGHT_PAREN, ")"),
};
static const size_t nr_symbols = sizeof symbols / sizeof symbols[0];
enum bshell_status lex_ctx_init(struct lex_ctx *ctx, struct line_source *src)
{
memset(ctx, 0x0, sizeof *ctx);
ctx->lex_status = BSHELL_SUCCESS;
ctx->lex_src = src;
return BSHELL_SUCCESS;
}
enum bshell_status lex_ctx_cleanup(struct lex_ctx *ctx)
{
return BSHELL_SUCCESS;
}
static struct lex_token *dequeue_token(struct lex_ctx *ctx)
{
fx_queue_entry *entry = fx_queue_first(&ctx->lex_tokens);
return fx_unbox(struct lex_token, entry, tok_entry);
}
static enum bshell_status pump_tokens(struct lex_ctx *ctx)
{
return BSHELL_SUCCESS;
}
struct lex_token *lex_ctx_peek(struct lex_ctx *ctx)
{
return NULL;
}
void lex_ctx_advance(struct lex_ctx *ctx)
{
struct lex_token *tok = dequeue_token(ctx);
if (tok) {
lex_token_destroy(tok);
}
}
+31
View File
@@ -0,0 +1,31 @@
#ifndef LEX_H_
#define LEX_H_
#include "status.h"
#include <fx/queue.h>
struct lex_token;
struct line_source;
struct lex_token_def {
int id;
const char *name;
uint64_t name_hash;
};
struct lex_ctx {
fx_queue lex_tokens;
struct line_source *lex_src;
enum bshell_status lex_status;
};
extern enum bshell_status lex_ctx_init(
struct lex_ctx *ctx,
struct line_source *src);
extern enum bshell_status lex_ctx_cleanup(struct lex_ctx *ctx);
extern struct lex_token *lex_ctx_peek(struct lex_ctx *ctx);
extern void lex_ctx_advance(struct lex_ctx *ctx);
#endif
+40
View File
@@ -0,0 +1,40 @@
#include "buffer.h"
#include "line-ed.h"
const char *line_start(struct line_ed *ed, size_t y)
{
const char *line = ed->l_buf;
for (size_t i = 0; i < y; i++) {
line += strcspn(line, "\n");
if (*line == '\n') {
line++;
}
}
return line;
}
size_t line_length(struct line_ed *ed, size_t y)
{
const char *line = ed->l_buf;
for (size_t i = 0; i < y; i++) {
line += strcspn(line, "\n");
if (*line == '\n') {
line++;
}
}
if (*line == '\0') {
return 0;
}
size_t len = strcspn(line, "\n");
if (line[len] == '\n') {
len++;
}
return len;
}
+16
View File
@@ -0,0 +1,16 @@
#ifndef LINE_ED_BUFFER_H_
#define LINE_ED_BUFFER_H_
#include <stddef.h>
struct line_ed;
/* returns a pointer to the start of the line based on the given `y`
* coordinate */
extern const char *line_start(struct line_ed *ed, size_t y);
/* returns the length of the line based on the given `y` coordinate.
* for any line other than the last line in the buffer, this length
* INCLUDES the trailing linefeed. */
extern size_t line_length(struct line_ed *ed, size_t y);
#endif
+26
View File
@@ -0,0 +1,26 @@
#include "line-ed.h"
#include "cursor.h"
#include "prompt.h"
void line_ed_coords_to_physical_coords(
struct line_ed *ed, size_t x, size_t y, size_t *out_x, size_t *out_y)
{
size_t prompt_len = 0;
if (ed->l_cursor_y == 0) {
prompt_len = prompt_length(ed, PROMPT_MAIN);
} else if (ed->l_cursor_y <= ed->l_continuations) {
prompt_len = prompt_length(ed, PROMPT_CONT);
}
if (y == 0) {
x += prompt_len;
}
if (out_x) {
*out_x = x;
}
if (out_y) {
*out_y = y;
}
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef LINE_ED_CURSOR_H_
#define LINE_ED_CURSOR_H_
struct line_ed;
extern void line_ed_coords_to_physical_coords(
struct line_ed *ed, size_t x, size_t y, size_t *out_x, size_t *out_y);
#endif
+72
View File
@@ -0,0 +1,72 @@
#include "../misc.h"
#include "line-ed.h"
#include <fx/collections/array.h>
#include <fx/string.h>
void alloc_empty_history_entry(struct line_ed *ed)
{
fx_string *str = (fx_string *)fx_array_at(
ed->l_history,
fx_array_size(ed->l_history) - 1);
if (!str || fx_string_get_size(str, FX_STRLEN_NORMAL) > 0) {
str = fx_string_create();
fx_array_append(ed->l_history, (fx_object *)str);
}
ed->l_history_pos = fx_array_size(ed->l_history) - 1;
}
void save_buf_to_history(struct line_ed *ed)
{
fx_string *cur
= (fx_string *)fx_array_get(ed->l_history, ed->l_history_pos);
fx_string_replace_all(cur, ed->l_buf);
}
void append_buf_to_history(struct line_ed *ed)
{
fx_string *cur
= (fx_string *)fx_array_get(ed->l_history, ed->l_history_pos);
char s[] = {'\n', 0};
fx_string_append_cstr(cur, s);
fx_string_append_cstr(cur, ed->l_buf);
}
void load_buf_from_history(struct line_ed *ed)
{
fx_string *cur
= (fx_string *)fx_array_at(ed->l_history, ed->l_history_pos);
size_t len
= MIN((size_t)(ed->l_buf_end - ed->l_buf - 1),
fx_string_get_size(cur, FX_STRLEN_NORMAL));
memcpy(ed->l_buf, fx_string_get_cstr(cur), len);
ed->l_buf[len] = '\0';
unsigned int x = 0, y = 0;
for (size_t i = 0; ed->l_buf[i]; i++) {
if (ed->l_buf[i] == '\n') {
x = 0;
y++;
} else {
x++;
}
}
ed->l_buf_ptr = ed->l_buf + len;
ed->l_line_end = ed->l_buf_ptr;
ed->l_cursor_x = x;
ed->l_cursor_y = y;
}
const char *last_history_line(struct line_ed *ed)
{
size_t nlines = fx_array_size(ed->l_history);
if (nlines < 2) {
return NULL;
}
fx_string *last = (fx_string *)fx_array_at(ed->l_history, nlines - 2);
return fx_string_get_cstr(last);
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef LINE_ED_HISTORY_H_
#define LINE_ED_HISTORY_H_
struct line_ed;
extern void alloc_empty_history_entry(struct line_ed *ed);
extern void save_buf_to_history(struct line_ed *ed);
extern void append_buf_to_history(struct line_ed *ed);
extern void load_buf_from_history(struct line_ed *ed);
extern const char *last_history_line(struct line_ed *ed);
#endif
+41
View File
@@ -0,0 +1,41 @@
#include "hook.h"
#include "line-ed.h"
void line_ed_add_hook(struct line_ed *ed, struct line_ed_hook *hook)
{
fx_queue_push_back(&ed->l_hooks, &hook->hook_entry);
}
void line_ed_remove_hook(struct line_ed *ed, struct line_ed_hook *hook)
{
fx_queue_delete(&ed->l_hooks, &hook->hook_entry);
}
void hook_keypress(struct line_ed *ed, fx_keycode key)
{
fx_queue_entry *entry = fx_queue_first(&ed->l_hooks);
while (entry) {
struct line_ed_hook *hook
= fx_unbox(struct line_ed_hook, entry, hook_entry);
if (hook->hook_keypress) {
hook->hook_keypress(ed, hook, key);
}
entry = fx_queue_next(entry);
}
}
void hook_buffer_modified(struct line_ed *ed)
{
fx_queue_entry *entry = fx_queue_first(&ed->l_hooks);
while (entry) {
struct line_ed_hook *hook
= fx_unbox(struct line_ed_hook, entry, hook_entry);
if (hook->hook_buffer_modified) {
hook->hook_buffer_modified(ed, hook);
}
entry = fx_queue_next(entry);
}
}
+16
View File
@@ -0,0 +1,16 @@
#ifndef LINE_ED_HOOK_H_
#define LINE_ED_HOOK_H_
#include <fx/term/tty.h>
enum hook_id {
HOOK_KEYPRESS,
HOOK_BEFORE_PAINT,
};
struct line_ed;
extern void hook_keypress(struct line_ed *ed, fx_keycode key);
extern void hook_buffer_modified(struct line_ed *ed);
#endif
+218
View File
@@ -0,0 +1,218 @@
#include "buffer.h"
#include "cursor.h"
#include "history.h"
#include "hook.h"
#include "line-ed.h"
#include "prompt.h"
#include "refresh.h"
#include <stdio.h>
void put_char(struct line_ed *ed, char c)
{
if (ed->l_buf_ptr > ed->l_line_end + 1) {
return;
}
struct refresh_state state = {
.r_prev_cursor_x = ed->l_cursor_x,
.r_prev_cursor_y = ed->l_cursor_y,
};
size_t prev_cursor = ed->l_buf_ptr - ed->l_buf;
char *dest = ed->l_buf_ptr;
size_t len = ed->l_line_end - ed->l_buf_ptr + 1;
if (dest < ed->l_line_end) {
memmove(dest + 1, dest, len);
}
ed->l_cursor_x++;
ed->l_line_end++;
ed->l_buf_ptr++;
*dest = c;
if (ed->l_buf_ptr == ed->l_line_end) {
*ed->l_buf_ptr = '\0';
}
hook_buffer_modified(ed);
put_refresh(ed, &state);
}
static void backspace_simple(struct line_ed *ed)
{
if (ed->l_buf_ptr == ed->l_buf) {
return;
}
struct refresh_state state = {
.r_prev_cursor_x = ed->l_cursor_x,
.r_prev_cursor_y = ed->l_cursor_y,
};
size_t prev_cursor = ed->l_buf_ptr - ed->l_buf;
char *dest = ed->l_buf_ptr;
size_t len = ed->l_line_end - ed->l_buf_ptr + 1;
memmove(dest - 1, dest, len);
ed->l_cursor_x--;
ed->l_line_end--;
ed->l_buf_ptr--;
hook_buffer_modified(ed);
backspace_simple_refresh(ed, &state);
}
static void backspace_nl(struct line_ed *ed)
{
size_t prev_line_len = line_length(ed, ed->l_cursor_y - 1);
struct refresh_state state = {
.r_prev_cursor_x = ed->l_cursor_x,
.r_prev_cursor_y = ed->l_cursor_y,
.r_prev_line_len = prev_line_len,
};
char *dest = ed->l_buf_ptr;
size_t len = ed->l_line_end - ed->l_buf_ptr + 1;
memmove(dest - 1, dest, len);
ed->l_cursor_x = prev_line_len - 1;
ed->l_cursor_y--;
ed->l_buf_ptr--;
ed->l_line_end--;
hook_buffer_modified(ed);
backspace_nl_refresh(ed, &state);
}
void backspace(struct line_ed *ed)
{
if (ed->l_buf_ptr == ed->l_buf) {
return;
}
if (ed->l_cursor_x == 0 && ed->l_cursor_y <= ed->l_continuations) {
return;
}
if (ed->l_cursor_x == 0 && ed->l_cursor_y > 0) {
backspace_nl(ed);
} else {
backspace_simple(ed);
}
}
void cursor_left(struct line_ed *ed)
{
if (ed->l_cursor_x != 0) {
//fputs("\010", stdout);
fx_tty_move_cursor_x(ed->l_tty, FX_TTY_POS_CURSOR, -1);
fflush(stdout);
ed->l_cursor_x--;
ed->l_buf_ptr--;
return;
}
if (ed->l_cursor_y <= ed->l_continuations || ed->l_buf_ptr <= ed->l_buf) {
return;
}
ed->l_cursor_y--;
ed->l_buf_ptr--;
size_t prompt_len = 0;
if (ed->l_cursor_y == 0) {
prompt_len = prompt_length(ed, PROMPT_MAIN);
}
size_t len = line_length(ed, ed->l_cursor_y);
ed->l_cursor_x = len - 1;
//printf("\033[A\033[%dG", len + prompt_len);
fx_tty_move_cursor_y(ed->l_tty, FX_TTY_POS_CURSOR, -1);
fx_tty_move_cursor_x(ed->l_tty, FX_TTY_POS_START, (int)(len + prompt_len));
fflush(stdout);
}
void cursor_right(struct line_ed *ed)
{
if (ed->l_buf_ptr >= ed->l_line_end) {
return;
}
if (*ed->l_buf_ptr != '\n') {
ed->l_cursor_x++;
ed->l_buf_ptr++;
//fputs("\033[C", stdout);
fx_tty_move_cursor_x(ed->l_tty, FX_TTY_POS_CURSOR, 1);
fflush(stdout);
return;
}
if (ed->l_buf_ptr >= ed->l_line_end) {
return;
}
ed->l_cursor_y++;
ed->l_cursor_x = 0;
ed->l_buf_ptr++;
//printf("\033[B\033[G");
fx_tty_move_cursor_y(ed->l_tty, FX_TTY_POS_CURSOR, 1);
fx_tty_move_cursor_x(ed->l_tty, FX_TTY_POS_START, 0);
fflush(stdout);
}
void arrow_up(struct line_ed *ed)
{
if (ed->l_history_pos == 0) {
return;
}
if (ed->l_cursor_y > 0) {
//printf("\033[%uA", ed->l_cursor_y);
fx_tty_move_cursor_y(ed->l_tty, FX_TTY_POS_CURSOR, (long long)ed->l_cursor_y);
}
//printf("\033[%zuG\033[J", prompt_length(ed, PROMPT_MAIN) + 1);
fx_tty_move_cursor_x(
ed->l_tty, FX_TTY_POS_START, (long long)prompt_length(ed, PROMPT_MAIN));
fx_tty_clear(ed->l_tty, FX_TTY_CLEAR_SCREEN | FX_TTY_CLEAR_FROM_CURSOR);
save_buf_to_history(ed);
ed->l_history_pos--;
load_buf_from_history(ed);
print_buffer(ed);
fflush(stdout);
}
void arrow_down(struct line_ed *ed)
{
if (ed->l_history_pos == fx_array_size(ed->l_history) - 1) {
return;
}
if (ed->l_cursor_y > 0) {
//printf("\033[%uA", ed->l_cursor_y);
fx_tty_move_cursor_y(ed->l_tty, FX_TTY_POS_CURSOR, (int)ed->l_cursor_y);
}
//printf("\033[%zuG\033[J", prompt_length(ed, PROMPT_MAIN) + 1);
fx_tty_move_cursor_x(
ed->l_tty, FX_TTY_POS_START, prompt_length(ed, PROMPT_MAIN) + 1);
save_buf_to_history(ed);
ed->l_history_pos++;
load_buf_from_history(ed);
print_buffer(ed);
fflush(stdout);
}
+13
View File
@@ -0,0 +1,13 @@
#ifndef LINE_ED_INPUT_H_
#define LINE_ED_INPUT_H_
struct line_ed;
extern void put_char(struct line_ed *ed, char c);
extern void backspace(struct line_ed *ed);
extern void cursor_left(struct line_ed *ed);
extern void cursor_right(struct line_ed *ed);
extern void arrow_up(struct line_ed *ed);
extern void arrow_down(struct line_ed *ed);
#endif
+301
View File
@@ -0,0 +1,301 @@
#include "line-ed.h"
#include "history.h"
#include "hook.h"
#include "input.h"
#include "prompt.h"
#include <ctype.h>
#include <fx/term/tty.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <wctype.h>
#define LINE_ED_FROM_LEX_SOURCE(p) \
((struct line_ed *)((char *)p \
- offsetof(struct line_ed, l_line_source)))
static enum bshell_status readline(
struct line_source *src,
fx_stringstream *out)
{
struct line_ed *ed = LINE_ED_FROM_LEX_SOURCE(src);
long r = line_ed_readline(ed, out);
line_ed_set_scope_type(ed, NULL);
if (r < 0) {
return BSHELL_ERR_EOF;
}
return BSHELL_SUCCESS;
}
struct line_ed *line_ed_create(void)
{
struct line_ed *out = malloc(sizeof *out);
if (!out) {
return NULL;
}
memset(out, 0x0, sizeof *out);
out->l_buf = malloc(LINE_MAX);
if (!out->l_buf) {
free(out);
return NULL;
}
out->l_history = fx_array_create();
if (!out->l_history) {
free(out->l_buf);
free(out);
return NULL;
}
out->l_tty = fx_stdtty;
out->l_buf_end = out->l_buf + LINE_MAX;
out->l_buf_ptr = out->l_buf;
out->l_line_end = out->l_buf;
out->l_prompt[PROMPT_MAIN] = ">>> ";
out->l_prompt[PROMPT_CONT] = "> ";
out->l_line_source.s_readline = readline;
return out;
}
void line_ed_destroy(struct line_ed *ed)
{
fx_array_unref(ed->l_history);
free(ed->l_buf);
free(ed);
}
void line_ed_set_flags(struct line_ed *ed, enum line_ed_flags flags)
{
ed->l_flags |= flags;
}
void line_ed_set_scope_type(struct line_ed *ed, const char *scope_type)
{
ed->l_scope_type = scope_type;
}
static void clear_buffer(struct line_ed *ed)
{
memset(ed->l_buf, 0x0, ed->l_buf_end - ed->l_buf);
ed->l_buf_ptr = ed->l_buf;
ed->l_line_end = ed->l_buf;
ed->l_cursor_x = 0;
ed->l_cursor_y = 0;
ed->l_continuations = 0;
}
static void convert_continuation_feeds(char *s, size_t max)
{
char *end = s + max;
size_t len = strlen(s);
while (1) {
size_t r = strcspn(s, "\\");
if (s + r > end) {
break;
}
s += r;
len -= r;
if (*s == '\0') {
break;
}
if (*(s + 1) != '\n') {
s++;
continue;
}
memmove(s, s + 1, len);
s += 1;
}
}
static void remove_continuation_feeds(char *s, size_t max)
{
char *end = s + max;
size_t len = strlen(s);
while (1) {
size_t r = strcspn(s, "\\");
if (s + r > end) {
break;
}
s += r;
len -= r;
if (*s == '\0') {
break;
}
if (*(s + 1) != '\n') {
s++;
continue;
}
memmove(s, s + 2, len);
s += 2;
}
}
static bool input_is_empty(struct line_ed *ed)
{
const char *p = ed->l_buf;
while (p < ed->l_line_end) {
if (!isspace(*p)) {
return false;
}
}
return true;
}
size_t line_ed_readline(struct line_ed *ed, fx_stringstream *out)
{
clear_buffer(ed);
bool append_history = false;
size_t append_to_index = (size_t)-1;
if (!ed->l_scope_type) {
alloc_empty_history_entry(ed);
} else {
append_history = true;
append_to_index = ed->l_history_pos;
}
fx_tty *tty = ed->l_tty;
fx_tty_set_mode(tty, FX_TTY_RAW);
show_prompt(ed);
for (int i = 0; ed->l_buf[i]; i++) {
if (ed->l_buf[i] == '\n') {
fputc('\r', stdout);
}
fputc(ed->l_buf[i], stdout);
}
fflush(stdout);
bool end = false;
bool eof = false;
while (!end) {
fx_keycode key = fx_tty_read_key(tty);
hook_keypress(ed, key);
if (key == FX_TTY_CTRL_KEY('d')) {
if (!input_is_empty(ed)) {
continue;
}
eof = true;
break;
}
if (key & FX_MOD_CTRL) {
continue;
}
switch (key) {
case FX_KEY_RETURN:
fx_tty_reset_vmode(tty);
if (ed->l_line_end > ed->l_buf
&& *(ed->l_line_end - 1) != '\\') {
end = true;
break;
}
if (input_is_empty(ed)) {
fputc('\r', stdout);
fputc('\n', stdout);
clear_buffer(ed);
show_prompt(ed);
break;
}
*ed->l_line_end = '\n';
ed->l_line_end++;
ed->l_buf_ptr = ed->l_line_end;
ed->l_cursor_x = 0;
ed->l_cursor_y++;
ed->l_continuations++;
fputc('\r', stdout);
fputc('\n', stdout);
// fputs("\033[G\n", stdout);
show_prompt(ed);
break;
case FX_KEY_BACKSPACE:
backspace(ed);
break;
case FX_KEY_ARROW_LEFT:
cursor_left(ed);
break;
case FX_KEY_ARROW_RIGHT:
cursor_right(ed);
break;
case FX_KEY_ARROW_UP:
arrow_up(ed);
break;
case FX_KEY_ARROW_DOWN:
arrow_down(ed);
break;
default:
if (iswgraph(key) || key == ' ') {
put_char(ed, key);
}
break;
}
}
fx_tty_set_mode(tty, FX_TTY_CANONICAL);
fputc('\n', stdout);
if (*ed->l_buf == '\0') {
return eof ? -1 : 0;
}
if (append_history) {
ed->l_history_pos = append_to_index;
append_buf_to_history(ed);
} else {
ed->l_history_pos = fx_array_size(ed->l_history) - 1;
const char *last = last_history_line(ed);
if (!last || strcmp(last, ed->l_buf) != 0) {
save_buf_to_history(ed);
}
}
size_t sz = ed->l_line_end - ed->l_buf + 1;
if ((ed->l_flags & LINE_ED_REMOVE_CONTINUATIONS)
== LINE_ED_REMOVE_CONTINUATIONS) {
remove_continuation_feeds(ed->l_buf, sz);
} else if (
(ed->l_flags & LINE_ED_CONVERT_CONTINUATIONS)
== LINE_ED_CONVERT_CONTINUATIONS) {
convert_continuation_feeds(ed->l_buf, sz);
}
fx_stream_write_cstr(out, ed->l_buf, NULL);
fx_stream_write_char(out, '\n');
return sz;
}
+107
View File
@@ -0,0 +1,107 @@
#ifndef LINE_ED_H_
#define LINE_ED_H_
#define LINE_MAX 4096
#include "../line-source.h"
#include <fx/collections/array.h>
#include <fx/queue.h>
#include <fx/term/tty.h>
#include <stddef.h>
struct s_tty;
struct fx_tty_vmode;
struct line_ed;
struct line_ed_hook {
void (*hook_keypress)(
struct line_ed *,
struct line_ed_hook *,
fx_keycode);
void (*hook_buffer_modified)(struct line_ed *, struct line_ed_hook *);
fx_queue_entry hook_entry;
};
enum line_ed_flags {
/* always reprint an entire line when a character is added/deleted.
* without this flag, only the modified character any subsequent
* characters are reprinted. */
LINE_ED_FULL_REPRINT = 0x01u,
/* keep line continuation (backslash-newline) tokens in the output
* buffer (default behaviour) */
LINE_ED_KEEP_CONTINUATIONS = 0x00u,
/* convert line continuation tokens in the output buffer to simple
* linefeeds. */
LINE_ED_CONVERT_CONTINUATIONS = 0x02u,
/* remove line continuation tokens from the output buffer, so that all
* chars are on a single line */
LINE_ED_REMOVE_CONTINUATIONS = 0x06u,
};
struct line_ed {
enum line_ed_flags l_flags;
/* array of basic prompt strings */
const char *l_prompt[2];
/* input buffer, pointer to the buffer cell that corresponds to
* the current cursor position, and pointer to the byte AFTER the last
* usable byte in the buffer */
char *l_buf, *l_buf_ptr, *l_buf_end;
/* pointer to the byte AFTER the last byte of the user's input */
char *l_line_end;
/* 2-dimensional coordinates of the current cursor position.
* this does NOT include any prompts that are visible on the terminal */
size_t l_cursor_x, l_cursor_y;
/* the number of line continuations that have been inputted */
unsigned int l_continuations;
struct line_source l_line_source;
/* pointer to tty interface */
fx_tty *l_tty;
/* the lexical scope that we are currently in.
* this is provided by components further up the input pipeline,
* for example, when we are inside a string or if-statement. */
const char *l_scope_type;
/* array of previously entered commands */
fx_array *l_history;
/* index of the currently selected history entry */
size_t l_history_pos;
/* list of defined highlight ranges */
fx_queue l_hl_ranges;
/* list of installed hooks */
fx_queue l_hooks;
};
extern struct line_ed *line_ed_create(void);
extern void line_ed_destroy(struct line_ed *ed);
extern void line_ed_set_flags(struct line_ed *ed, enum line_ed_flags flags);
extern void line_ed_set_scope_type(struct line_ed *ed, const char *scope_type);
extern void line_ed_put_highlight(
struct line_ed *ed,
unsigned long start_x,
unsigned long start_y,
unsigned long end_x,
unsigned long end_y,
const struct fx_tty_vmode *vmode);
extern void line_ed_clear_highlights(struct line_ed *ed);
extern void line_ed_print_highlights(struct line_ed *ed);
extern void line_ed_add_hook(struct line_ed *ed, struct line_ed_hook *hook);
extern void line_ed_remove_hook(struct line_ed *ed, struct line_ed_hook *hook);
extern size_t line_ed_readline(struct line_ed *ed, fx_stringstream *out);
static inline struct line_source *line_ed_to_line_source(struct line_ed *ed)
{
return &ed->l_line_source;
}
#endif
+27
View File
@@ -0,0 +1,27 @@
#include <stdio.h>
#include "line-ed.h"
#include "prompt.h"
void show_prompt(struct line_ed *ed)
{
int type = PROMPT_MAIN;
if (ed->l_scope_type) {
type = PROMPT_CONT;
/* this is a temporary solution to show the current
* scope type, until prompts are implemented properly. */
fputs(ed->l_scope_type, stdout);
}
if (ed->l_continuations > 0) {
type = PROMPT_CONT;
}
fputs(ed->l_prompt[type], stdout);
fflush(stdout);
}
size_t prompt_length(struct line_ed *ed, int prompt_id)
{
return strlen(ed->l_prompt[prompt_id]);
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef LINE_ED_PROMPT_H_
#define LINE_ED_PROMPT_H_
#define PROMPT_MAIN 0
#define PROMPT_CONT 1
struct line_ed;
extern void show_prompt(struct line_ed *ed);
extern size_t prompt_length(struct line_ed *ed, int prompt_id);
#endif
+265
View File
@@ -0,0 +1,265 @@
#include "refresh.h"
#include "buffer.h"
#include "cursor.h"
#include "line-ed.h"
#include <fx/term/tty.h>
#include <stdio.h>
#include <stdlib.h>
/* prints the provided string to the terminal, applying any relevant highlight
* ranges. this function prints all characters in `s` until it encounters a null
* char (\0) or linefeed (\n).
*
* the (x, y) coordinates provided should be the data coordinates of the
* first character in `s`.
*/
void print_text(struct line_ed *ed, size_t x, size_t y, const char *s)
{
fx_tty *tty = ed->l_tty;
for (size_t i = 0; s[i] != '\n' && s[i] != '\0'; i++) {
fputc(s[i], stdout);
}
}
void print_buffer(struct line_ed *ed)
{
const char *line_buf = ed->l_buf;
size_t line_len = strcspn(line_buf, "\n");
unsigned int y = 0;
while (1) {
print_text(ed, 0, y, line_buf);
line_buf += line_len;
if (*line_buf == '\n') {
line_buf++;
}
if (*line_buf == '\0') {
break;
}
y++;
line_len = strcspn(line_buf, "\n");
fputc('\r', stdout);
fputc('\n', stdout);
}
}
/* this function is called after a character is inserted into the line_ed
*buffer. the function performs the following steps:
* 1. get a pointer to the start of the line that was modified.
* 2. determine the first character in the line that needs to be redrawn.
* this may result in an append, a partial reprint, or a full reprint.
* 3. re-print the relevant portion of the buffer:
* for an append (a character added to the end of the line):
* * write the inserted char to the terminal.
* * done.
* for a partial reprint:
* * clear all printed chars from the logical cursor position to
*the end of the line.
* * print the portion of the line corresponding to the cleared
*section.
* * move the physical (terminal) cursor backwards until its
*position matches the logical (line_ed) cursor. for a full reprint:
* * same as a partial reprint except that, rather than reprinting
* from the logical cursor position, the entire line is
*reprinted.
*/
void put_refresh(struct line_ed *ed, struct refresh_state *state)
{
/* get the data for the line that is being refreshed */
const char *line_buf = line_start(ed, ed->l_cursor_y);
size_t line_len = strcspn(line_buf, "\n");
/* the index of the first char in line_buf that needs to be reprinted */
size_t start_x = state->r_prev_cursor_x;
/* the distance between the first char to be reprinted and the end
* of the line.
* the physical cursor will be moved back by this amount after the
* line is reprinted. */
long cursor_rdelta = (long)(line_len - start_x);
if (ed->l_flags & LINE_ED_FULL_REPRINT) {
if (start_x) {
// fprintf(stdout, "\033[%uD", start_x);
fx_tty_move_cursor_x(
ed->l_tty,
FX_TTY_POS_CURSOR,
-(long long)start_x);
}
start_x = 0;
}
print_text(ed, start_x, ed->l_cursor_y, line_buf + start_x);
/* decrement the rdelta (move the cursor back one fewer cells),
* so that the physical cursor will be placed AFTER the character that
* was just inserted. */
cursor_rdelta--;
fx_tty_move_cursor_x(ed->l_tty, FX_TTY_POS_CURSOR, -cursor_rdelta);
#if 0
for (unsigned int i = 0; i < cursor_rdelta; i++) {
fputs("\010", stdout);
}
#endif
fflush(stdout);
}
/* this function is called after a character is removed from the line_ed buffer.
* IF the character was a linefeed.
*
* this is separate from backspace_simple_refresh because, in this situation,
* the cursor position depends on the length of the previous line before
* the linefeed was deleted, and we have to reprint every line following the
* two that were combined.
*/
void backspace_nl_refresh(struct line_ed *ed, struct refresh_state *state)
{
/* get the data for the line that is being refreshed.
* relative to where the cursor was before the linefeed was deleted,
* this is the PREVIOUS line. */
const char *line_buf = line_start(ed, ed->l_cursor_y);
size_t line_len = strcspn(line_buf, "\n");
/* the index of the first char in line_buf that needs to be reprinted.
* subtract one to account for the linefeed that has since been deleted.
*/
size_t start_x = state->r_prev_line_len - 1;
/* the column to move the physical cursor to after it has been moved
* to the previous line.
* NOTE that this number includes the length of the prompt!
* we add 1 to start_x to ensure that the cursor is moved to the cell
* AFTER the last char of the line. */
size_t new_x;
line_ed_coords_to_physical_coords(
ed,
start_x + 1,
ed->l_cursor_y,
&new_x,
NULL);
/* the physical cursor is currently at the beginning of the line that
* has just been moved up. from here, clear this line and the rest
* from the screen. */
// fputs("\033[J", stdout);
fx_tty_clear(ed->l_tty, FX_TTY_CLEAR_SCREEN | FX_TTY_CLEAR_FROM_CURSOR);
if (ed->l_flags & LINE_ED_FULL_REPRINT) {
/* next, move the physical cursor up and to the beginning of the
* previous line */
size_t tmp_x;
line_ed_coords_to_physical_coords(
ed,
0,
ed->l_cursor_y,
&tmp_x,
NULL);
fx_tty_move_cursor_y(ed->l_tty, FX_TTY_POS_CURSOR, -1);
fx_tty_move_cursor_x(ed->l_tty, FX_TTY_POS_START, tmp_x);
// fprintf(stdout, "\033[A\033[%uG", tmp_x + 1);
start_x = 0;
} else {
/* next, move the physical cursor up and to the end of the
* previous line */
// fprintf(stdout, "\033[A\033[%uG", new_x);
fx_tty_move_cursor_y(ed->l_tty, FX_TTY_POS_CURSOR, -1);
fx_tty_move_cursor_x(ed->l_tty, FX_TTY_POS_START, new_x);
}
/* now reprint all of the buffer lines, starting with the first of the
* two lines that were concatenated. */
size_t ydiff = 0;
while (1) {
print_text(
ed,
start_x,
ed->l_cursor_y + ydiff,
line_buf + start_x);
line_buf += line_len + 1;
line_len = strcspn(line_buf, "\n");
start_x = 0;
if (*line_buf == '\0') {
break;
}
fputc('\r', stdout);
fputc('\n', stdout);
ydiff++;
}
/* finally, move the cursor BACK to the point where the two lines
* were concatenated. */
if (ydiff) {
// fprintf(stdout, "\033[%uA", ydiff);
fx_tty_move_cursor_y(ed->l_tty, FX_TTY_POS_CURSOR, ydiff);
}
// fprintf(stdout, "\033[%uG", new_x);
fx_tty_move_cursor_x(ed->l_tty, FX_TTY_POS_START, new_x);
fflush(stdout);
}
/* this function is called after a character is removed from the line_ed buffer.
* IF the character was not a linefeed.
*/
void backspace_simple_refresh(struct line_ed *ed, struct refresh_state *state)
{
/* get the data for the line that is being refreshed */
const char *line_buf = line_start(ed, ed->l_cursor_y);
size_t line_len = strcspn(line_buf, "\n");
/* the index of the first char in line_buf that needs to be reprinted */
size_t start_x = ed->l_cursor_x;
// get_data_cursor_position(ed, &start_x, NULL);
/* the distance between the first char to be reprinted and the end
* of the line.
* the physical cursor will be moved back by this amount after the
* line is reprinted. */
long long cursor_rdelta = (long long)(line_len - start_x);
if (ed->l_flags & LINE_ED_FULL_REPRINT) {
if (start_x) {
// fprintf(stdout, "\033[%uD", start_x);
fx_tty_move_cursor_x(
ed->l_tty,
FX_TTY_POS_CURSOR,
-(long long)start_x);
}
start_x = 0;
}
// fputc('\010', stdout);
fx_tty_move_cursor_x(ed->l_tty, FX_TTY_POS_CURSOR, -1);
print_text(ed, start_x, ed->l_cursor_y, line_buf + start_x);
fputc(' ', stdout);
/* increment the rdelta (move the cursor back one more cell), so
* that the cursor will appear to move back one cell (to accord with
* the fact that backspace was just pressed) */
cursor_rdelta++;
fx_tty_move_cursor_x(ed->l_tty, FX_TTY_POS_CURSOR, -cursor_rdelta);
#if 0
for (unsigned int i = 0; i < cursor_rdelta; i++) {
fputs("\010", stdout);
}
#endif
fflush(stdout);
}
+28
View File
@@ -0,0 +1,28 @@
#ifndef LINE_ED_REFRESH_H_
#define LINE_ED_REFRESH_H_
#include <stddef.h>
struct line_ed;
struct refresh_state {
/* cursor position before the update was performed (excluding the
* prompt) */
size_t r_prev_cursor_x, r_prev_cursor_y;
/* when a backspace results in two separate lines being combined,
* this property contains the length of the first of the two combined
* lines BEFORE the concotenation was performed */
size_t r_prev_line_len;
};
extern void print_text(struct line_ed *ed, size_t x, size_t y, const char *s);
extern void print_buffer(struct line_ed *ed);
extern void put_refresh(struct line_ed *ed, struct refresh_state *state);
extern void backspace_nl_refresh(
struct line_ed *ed,
struct refresh_state *state);
extern void backspace_simple_refresh(
struct line_ed *ed,
struct refresh_state *state);
#endif
+39
View File
@@ -0,0 +1,39 @@
#include "line-source.h"
enum bshell_status line_source_get_name(
struct line_source *src,
char *buf,
size_t count,
size_t *nr_read)
{
if (src->s_get_name) {
return src->s_get_name(src, buf, count, nr_read);
}
return BSHELL_ERR_NOT_SUPPORTED;
}
enum bshell_status line_source_readline(
struct line_source *src,
fx_stringstream *out)
{
if (src->s_readline) {
return src->s_readline(src, out);
}
return BSHELL_ERR_NOT_SUPPORTED;
}
enum bshell_status line_source_get_row(
struct line_source *src,
size_t row,
char *buf,
size_t count,
size_t *nr_read)
{
if (src->s_get_row) {
return src->s_get_row(src, row, buf, count, nr_read);
}
return BSHELL_ERR_NOT_SUPPORTED;
}
+37
View File
@@ -0,0 +1,37 @@
#ifndef LINE_SOURCE_H_
#define LINE_SOURCE_H_
#include "status.h"
#include <fx/stringstream.h>
#include <stddef.h>
struct line_source {
enum bshell_status (
*s_get_name)(struct line_source *, char *, size_t, size_t *);
enum bshell_status (
*s_readline)(struct line_source *, fx_stringstream *);
enum bshell_status (*s_get_row)(
struct line_source *,
size_t,
char *,
size_t,
size_t *);
};
extern enum bshell_status line_source_get_name(
struct line_source *src,
char *buf,
size_t count,
size_t *nr_read);
extern enum bshell_status line_source_readline(
struct line_source *src,
fx_stringstream *out);
extern enum bshell_status line_source_get_row(
struct line_source *src,
size_t row,
char *buf,
size_t count,
size_t *nr_read);
#endif
+38
View File
@@ -0,0 +1,38 @@
#include "file.h"
#include "line-ed/line-ed.h"
#include <stdio.h>
int main(int argc, const char **argv)
{
printf("B Shell " BSHELL_VERSION "\n");
struct line_source *linesrc = NULL;
enum bshell_status status = BSHELL_SUCCESS;
if (argc > 1) {
struct file *file = NULL;
status = file_open(argv[1], &file);
linesrc = &file->f_base;
} else {
struct line_ed *ed = line_ed_create();
linesrc = line_ed_to_line_source(ed);
}
fx_stringstream *linebuf = fx_stringstream_create();
while (1) {
enum bshell_status status
= line_source_readline(linesrc, linebuf);
if (status != BSHELL_SUCCESS) {
break;
}
printf("%s", fx_stringstream_ptr(linebuf));
fx_stringstream_reset(linebuf);
}
return 0;
}
+7
View File
@@ -0,0 +1,7 @@
#ifndef MISC_H_
#define MISC_H_
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#endif
+20
View File
@@ -0,0 +1,20 @@
#include "status.h"
#include <errno.h>
enum bshell_status bshell_status_from_errno(int err)
{
switch (err) {
case 0:
return BSHELL_SUCCESS;
case EIO:
return BSHELL_ERR_IO_FAILURE;
case ENOENT:
return BSHELL_ERR_NO_ENTRY;
case EPERM:
case EACCES:
return BSHELL_ERR_ACCESS_DENIED;
default:
return BSHELL_ERR_INTERNAL_FAILURE;
}
}
+24
View File
@@ -0,0 +1,24 @@
#ifndef STATUS_H_
#define STATUS_H_
enum bshell_status {
BSHELL_SUCCESS = 0,
BSHELL_ERR_EOF,
BSHELL_ERR_BAD_SYNTAX,
BSHELL_ERR_BAD_FORMAT,
BSHELL_ERR_BAD_STATE,
BSHELL_ERR_INVALID_VALUE,
BSHELL_ERR_INVALID_ARGUMENT,
BSHELL_ERR_NO_MEMORY,
BSHELL_ERR_NO_ENTRY,
BSHELL_ERR_NO_DATA,
BSHELL_ERR_NAME_EXISTS,
BSHELL_ERR_NOT_SUPPORTED,
BSHELL_ERR_IO_FAILURE,
BSHELL_ERR_ACCESS_DENIED,
BSHELL_ERR_INTERNAL_FAILURE,
};
extern enum bshell_status bshell_status_from_errno(int err);
#endif
View File
+86
View File
@@ -0,0 +1,86 @@
#ifndef IVY_LANG_LEX_H_
#define IVY_LANG_LEX_H_
#include <fx/queue.h>
#include <stdbool.h>
struct char_cell {
unsigned long c_row, c_col;
};
enum token_type {
TOK_NONE = 0,
__TOK_INDEX_BASE = 100,
TOK_KEYWORD,
TOK_SYMBOL,
TOK_INT,
TOK_DOUBLE,
TOK_WORD,
TOK_ARG,
TOK_STRING,
TOK_STR_START,
TOK_STR_END,
TOK_LINEFEED,
__TOK_INDEX_LIMIT,
};
enum token_keyword {
KW_NONE = 0,
__KW_INDEX_BASE = 200,
KW_FUNC,
__KW_INDEX_LIMIT,
};
enum token_symbol {
SYM_NONE = 0,
__SYM_INDEX_BASE = 300,
SYM_SQUOTE,
SYM_DQUOTE,
SYM_LEFT_BRACE,
SYM_RIGHT_BRACE,
SYM_LEFT_BRACKET,
SYM_RIGHT_BRACKET,
SYM_LEFT_PAREN,
SYM_RIGHT_PAREN,
__SYM_INDEX_LIMIT,
};
struct lex_token {
enum token_type tok_type;
struct char_cell tok_start, tok_end;
fx_queue_entry tok_entry;
union {
enum token_keyword tok_keyword;
enum token_symbol tok_symbol;
long long tok_int;
double tok_double;
char *tok_str;
};
};
extern struct lex_token *lex_token_create_discard(void);
extern struct lex_token *lex_token_create_ident(const char *s);
extern void lex_token_destroy(struct lex_token *tok);
static inline bool lex_token_is_symbol(
struct lex_token *tok,
enum token_symbol sym)
{
return (tok->tok_type == TOK_SYMBOL && tok->tok_symbol == sym);
}
static inline bool lex_token_is_keyword(
struct lex_token *tok,
enum token_keyword kw)
{
return (tok->tok_type == TOK_KEYWORD && tok->tok_keyword == kw);
}
extern const char *lex_token_to_string(const struct lex_token *tok);
extern const char *bshell_lex_token_type_to_string(enum token_type type);
extern const char *token_keyword_to_string(enum token_keyword keyword);
extern const char *token_symbol_to_string(enum token_symbol sym);
#endif
+124
View File
@@ -0,0 +1,124 @@
#[=======================================================================[.rst:
FindFX
------------
Find the FX library and header directories
Imported Targets
^^^^^^^^^^^^^^^^
This module defines the following :prop_tgt:`IMPORTED` target:
``FX::FX``
The FX library, if found
Result Variables
^^^^^^^^^^^^^^^^
This module will set the following variables in your project:
``FX_FOUND``
true if the FX C headers and libraries were found
``FX_INCLUDE_DIR``
directories containing the FX C headers.
``FX_LIBRARY``
the C library to link against
Hints
^^^^^
The user may set the environment variable ``FX_PREFIX`` to the root
directory of a FX library installation.
#]=======================================================================]
set (FX_SEARCH_PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr/local/share
/usr
/sw # Fink
/opt/local # DarwinPorts
/opt/csw # Blastwave
/opt
${FX_PREFIX}
$ENV{FX_PREFIX})
if (FX_STATIC)
set(_lib_suffix "-s")
endif ()
set(assemblies ${FX_FIND_COMPONENTS})
set(required_vars)
if (NOT FX_INCLUDE_DIR)
find_path(FX_INCLUDE_DIR
NAMES fx/misc.h ${FX_FIND_ARGS}
PATH_SUFFIXES include
PATHS ${FX_SEARCH_PATHS})
endif ()
set(required_vars FX_INCLUDE_DIR)
foreach (assembly ${assemblies})
string(TOLOWER ${assembly} header_name)
string(REPLACE "." "_" macro_name ${assembly})
string(TOUPPER ${macro_name} macro_name)
set(lib_name ${assembly}${_lib_suffix})
if (NOT ${macro_name}_LIBRARY)
find_library(${macro_name}_LIBRARY
NAMES ${lib_name} ${FX_FIND_ARGS}
PATH_SUFFIXES lib
PATHS ${FX_SEARCH_PATHS})
else ()
# on Windows, ensure paths are in canonical format (forward slahes):
file(TO_CMAKE_PATH "${${macro_name}_LIBRARY}" ${macro_name}_LIBRARY)
endif()
list(APPEND required_vars ${macro_name}_LIBRARY)
endforeach (assembly)
unset(FX_FIND_ARGS)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(FX
REQUIRED_VARS ${required_vars})
if (FX_FOUND)
set(created_targets)
foreach (assembly ${assemblies})
set(target_name ${assembly})
string(REPLACE "fx." "" target_name ${target_name})
string(SUBSTRING ${target_name} 0 1 target_name_prefix)
string(TOUPPER ${target_name_prefix} target_name_prefix)
string(SUBSTRING ${target_name} 1 -1 target_name_suffix)
set(target_name ${target_name_prefix}${target_name_suffix})
string(TOLOWER ${assembly} header_name)
string(REPLACE "." "_" macro_name ${assembly})
string(REPLACE "." "_" macro_name ${assembly})
string(TOUPPER ${macro_name} macro_name)
set(lib_name ${assembly}${_lib_suffix})
if (NOT TARGET FX::${target_name})
add_library(FX::${target_name} UNKNOWN IMPORTED)
set_target_properties(FX::${target_name} PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${FX_INCLUDE_DIR}")
target_compile_definitions(FX::${target_name} INTERFACE _CRT_SECURE_NO_WARNINGS=1)
if (FX_STATIC)
target_compile_definitions(FX::${target_name} INTERFACE FX_STATIC=1)
endif ()
set_target_properties(FX::${target_name} PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
IMPORTED_LOCATION "${${macro_name}_LIBRARY}")
set(created_targets ${created_targets} ${assembly})
endif ()
endforeach (assembly)
endif()