Add a format implementation
This commit is contained in:
parent
be2e915d0d
commit
603ff46d8c
@ -1,10 +1,11 @@
|
|||||||
set(SOURCES
|
set(SOURCES
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/string.cpp
|
src/luna/String.cpp
|
||||||
src/Framebuffer.cpp
|
src/Framebuffer.cpp
|
||||||
src/MemoryManager.cpp
|
src/MemoryManager.cpp
|
||||||
src/Init.cpp
|
src/Init.cpp
|
||||||
src/arch/Serial.cpp
|
src/arch/Serial.cpp
|
||||||
|
src/luna/Format.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# x86-64 specific
|
# x86-64 specific
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "arch/Serial.h"
|
#include "arch/Serial.h"
|
||||||
|
#include <Format.h>
|
||||||
|
|
||||||
namespace Serial
|
namespace Serial
|
||||||
{
|
{
|
||||||
@ -17,4 +18,19 @@ namespace Serial
|
|||||||
print(str);
|
print(str);
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int printf(const char* format, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
isize rc = cstyle_format(
|
||||||
|
format,
|
||||||
|
[](char c, void*) -> int {
|
||||||
|
putchar(c);
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
nullptr, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return (int)rc;
|
||||||
|
}
|
||||||
}
|
}
|
@ -7,4 +7,5 @@ namespace Serial
|
|||||||
void write(const char* str, usize size);
|
void write(const char* str, usize size);
|
||||||
void print(const char* str);
|
void print(const char* str);
|
||||||
void println(const char* str);
|
void println(const char* str);
|
||||||
|
int printf(const char* str, ...);
|
||||||
}
|
}
|
3
kernel/src/luna/Format.cpp
Normal file
3
kernel/src/luna/Format.cpp
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/* This file instantiates the functions from Luna's Format.h into a translation unit, and serves no other purpose. */
|
||||||
|
#define _LUNA_IMPLEMENTATION
|
||||||
|
#include <Format.h>
|
3
kernel/src/luna/String.cpp
Normal file
3
kernel/src/luna/String.cpp
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/* This file instantiates the functions from Luna's String.h into a translation unit, and serves no other purpose. */
|
||||||
|
#define _LUNA_IMPLEMENTATION
|
||||||
|
#include <String.h>
|
@ -14,6 +14,8 @@ extern "C" [[noreturn]] void _start()
|
|||||||
|
|
||||||
Serial::println("Hello, world!");
|
Serial::println("Hello, world!");
|
||||||
|
|
||||||
|
Serial::printf("Current platform: %s\n", CPU::platform_string());
|
||||||
|
|
||||||
Framebuffer::rect(0, 0, 200, 200, 0xFF00FF00);
|
Framebuffer::rect(0, 0, 200, 200, 0xFF00FF00);
|
||||||
|
|
||||||
auto cpu_name_or_error = CPU::identify();
|
auto cpu_name_or_error = CPU::identify();
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
#define _LUNA_IMPLEMENTATION
|
|
||||||
#include <String.h>
|
|
290
luna/Format.h
Normal file
290
luna/Format.h
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <CType.h>
|
||||||
|
#include <Types.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
typedef int (*callback_t)(char, void*);
|
||||||
|
|
||||||
|
#ifdef _LUNA_IMPLEMENTATION
|
||||||
|
#include <NumberParsing.h>
|
||||||
|
|
||||||
|
extern "C" usize strlen(const char*);
|
||||||
|
|
||||||
|
typedef usize flags_t;
|
||||||
|
#define FLAG_ZERO_PAD 1 << 0
|
||||||
|
#define FLAG_LEFT_ALIGN 1 << 1
|
||||||
|
#define FLAG_BLANK_SIGNED 1 << 2
|
||||||
|
#define FLAG_ALTERNATE 1 << 3
|
||||||
|
#define FLAG_SIGN 1 << 4
|
||||||
|
#define FLAG_USE_PRECISION 1 << 5
|
||||||
|
#define FLAG_LONG 1 << 6
|
||||||
|
#define FLAG_LONG_LONG 1 << 7
|
||||||
|
#define FLAG_SHORT 1 << 8
|
||||||
|
#define FLAG_CHAR 1 << 9
|
||||||
|
|
||||||
|
struct format_state
|
||||||
|
{
|
||||||
|
usize count;
|
||||||
|
callback_t callback;
|
||||||
|
void* arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct conv_state
|
||||||
|
{
|
||||||
|
flags_t flags;
|
||||||
|
usize width;
|
||||||
|
usize precision;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline int cstyle_format_putchar(char c, format_state& state)
|
||||||
|
{
|
||||||
|
state.count++;
|
||||||
|
return state.callback(c, state.arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int cstyle_format_puts(const char* s, format_state& state)
|
||||||
|
{
|
||||||
|
while (*s)
|
||||||
|
{
|
||||||
|
if (cstyle_format_putchar(*s, state)) return -1;
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TRY_PUTCHAR(c, state) \
|
||||||
|
if (cstyle_format_putchar(c, state)) return -1;
|
||||||
|
|
||||||
|
#define TRY_PUTS(s, state) \
|
||||||
|
if (cstyle_format_puts(s, state)) return -1;
|
||||||
|
|
||||||
|
static int start_pad(const conv_state& vstate, format_state& state, usize start)
|
||||||
|
{
|
||||||
|
if (!(vstate.flags & FLAG_LEFT_ALIGN))
|
||||||
|
{
|
||||||
|
while (start++ < vstate.width) TRY_PUTCHAR(' ', state);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TRY_START_PAD(vstate, state, start) \
|
||||||
|
if (start_pad(vstate, state, start)) return -1;
|
||||||
|
|
||||||
|
static int end_pad(const conv_state& vstate, format_state& state, usize start)
|
||||||
|
{
|
||||||
|
if (vstate.flags & FLAG_LEFT_ALIGN)
|
||||||
|
{
|
||||||
|
while (start++ < vstate.width) TRY_PUTCHAR(' ', state);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TRY_END_PAD(vstate, state, start) \
|
||||||
|
if (end_pad(vstate, state, start)) return -1;
|
||||||
|
|
||||||
|
static flags_t parse_flags(const char** format)
|
||||||
|
{
|
||||||
|
flags_t result = 0;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
switch (**format)
|
||||||
|
{
|
||||||
|
case '#':
|
||||||
|
result |= FLAG_ALTERNATE;
|
||||||
|
(*format)++;
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
result |= FLAG_ZERO_PAD;
|
||||||
|
(*format)++;
|
||||||
|
break;
|
||||||
|
case ' ':
|
||||||
|
result |= FLAG_BLANK_SIGNED;
|
||||||
|
(*format)++;
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
result |= FLAG_LEFT_ALIGN;
|
||||||
|
(*format)++;
|
||||||
|
break;
|
||||||
|
case '+':
|
||||||
|
result |= FLAG_SIGN;
|
||||||
|
(*format)++;
|
||||||
|
break;
|
||||||
|
default: return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static usize parse_width(const char** format, flags_t& flags, va_list ap)
|
||||||
|
{
|
||||||
|
usize result = 0;
|
||||||
|
|
||||||
|
if (_isdigit(**format)) result = _atou(format);
|
||||||
|
else if (**format == '*')
|
||||||
|
{
|
||||||
|
const int width = va_arg(ap, int);
|
||||||
|
if (width >= 0) result = (usize)width;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
flags |= FLAG_LEFT_ALIGN;
|
||||||
|
result = (usize)-width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static usize parse_precision(const char** format, flags_t& flags, va_list ap)
|
||||||
|
{
|
||||||
|
usize result = 0;
|
||||||
|
|
||||||
|
if (**format == '.')
|
||||||
|
{
|
||||||
|
(*format)++;
|
||||||
|
|
||||||
|
flags |= FLAG_USE_PRECISION;
|
||||||
|
|
||||||
|
if (_isdigit(**format)) result = _atou(format);
|
||||||
|
else if (**format == '*')
|
||||||
|
{
|
||||||
|
const int precision = va_arg(ap, int);
|
||||||
|
if (precision >= 0) result = (usize)precision;
|
||||||
|
else
|
||||||
|
result = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_length(const char** format, flags_t& flags)
|
||||||
|
{
|
||||||
|
// FIXME: Support %j (intmax_t/uintmax_t)
|
||||||
|
switch (**format)
|
||||||
|
{
|
||||||
|
case 'h':
|
||||||
|
flags |= FLAG_SHORT;
|
||||||
|
(*format)++;
|
||||||
|
if (**format == 'h')
|
||||||
|
{
|
||||||
|
flags |= FLAG_CHAR;
|
||||||
|
(*format)++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
flags |= FLAG_LONG;
|
||||||
|
(*format)++;
|
||||||
|
if (**format == 'l')
|
||||||
|
{
|
||||||
|
flags |= FLAG_LONG_LONG;
|
||||||
|
(*format)++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
flags |= (sizeof(ptrdiff_t) == sizeof(long)) ? FLAG_LONG : FLAG_LONG_LONG;
|
||||||
|
(*format)++;
|
||||||
|
break;
|
||||||
|
case 'z':
|
||||||
|
flags |= (sizeof(size_t) == sizeof(long)) ? FLAG_LONG : FLAG_LONG_LONG;
|
||||||
|
(*format)++;
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_integer_format_specifier(char c)
|
||||||
|
{
|
||||||
|
return (c == 'd') || (c == 'i') || (c == 'u') || (c == 'x') || (c == 'X') || (c == 'o') || (c == 'b');
|
||||||
|
}
|
||||||
|
|
||||||
|
static int output_integer(char, const conv_state&, format_state&, va_list)
|
||||||
|
{
|
||||||
|
return -1; // FIXME: Implement this.
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
isize cstyle_format(const char* format, callback_t callback, void* arg, va_list ap)
|
||||||
|
#ifdef _LUNA_IMPLEMENTATION
|
||||||
|
{
|
||||||
|
format_state state;
|
||||||
|
state.callback = callback;
|
||||||
|
state.arg = arg;
|
||||||
|
state.count = 0;
|
||||||
|
|
||||||
|
while (*format)
|
||||||
|
{
|
||||||
|
if (*format != '%')
|
||||||
|
{
|
||||||
|
TRY_PUTCHAR(*format, state);
|
||||||
|
format++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
format++;
|
||||||
|
|
||||||
|
// %[flags][width][.precision][length]conversion
|
||||||
|
|
||||||
|
flags_t flags = parse_flags(&format);
|
||||||
|
usize width = parse_width(&format, flags, ap);
|
||||||
|
usize precision = parse_precision(&format, flags, ap);
|
||||||
|
parse_length(&format, flags);
|
||||||
|
|
||||||
|
conv_state vstate = {flags, width, precision};
|
||||||
|
|
||||||
|
char specifier = *format;
|
||||||
|
format++;
|
||||||
|
|
||||||
|
if (is_integer_format_specifier(specifier))
|
||||||
|
{
|
||||||
|
if (output_integer(specifier, vstate, state, ap)) return -1;
|
||||||
|
}
|
||||||
|
else if (specifier == 'c')
|
||||||
|
{
|
||||||
|
const char c = (char)va_arg(ap, int);
|
||||||
|
|
||||||
|
TRY_START_PAD(vstate, state, 1);
|
||||||
|
TRY_PUTCHAR(c, state);
|
||||||
|
TRY_END_PAD(vstate, state, 1);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (specifier == 's')
|
||||||
|
{
|
||||||
|
const char* str = va_arg(ap, const char*);
|
||||||
|
if (str == nullptr)
|
||||||
|
{
|
||||||
|
TRY_START_PAD(vstate, state, 6);
|
||||||
|
TRY_PUTS("(null)", state);
|
||||||
|
TRY_END_PAD(vstate, state, 6);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
usize len = strlen(str);
|
||||||
|
|
||||||
|
bool use_precision = (flags & FLAG_USE_PRECISION);
|
||||||
|
if (use_precision && len > precision) len = precision;
|
||||||
|
|
||||||
|
TRY_START_PAD(vstate, state, len);
|
||||||
|
while (*str && (!use_precision || precision))
|
||||||
|
{
|
||||||
|
TRY_PUTCHAR(*str, state);
|
||||||
|
precision--;
|
||||||
|
str++;
|
||||||
|
}
|
||||||
|
TRY_END_PAD(vstate, state, len);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else { continue; }
|
||||||
|
}
|
||||||
|
|
||||||
|
return (isize)state.count;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
;
|
||||||
|
#endif
|
40
luna/NumberParsing.h
Normal file
40
luna/NumberParsing.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <CType.h>
|
||||||
|
#include <Types.h>
|
||||||
|
|
||||||
|
inline usize _atou(const char** str)
|
||||||
|
{
|
||||||
|
usize val = 0;
|
||||||
|
|
||||||
|
while (_isdigit(**str))
|
||||||
|
{
|
||||||
|
val = (10 * val) + (**str - '0');
|
||||||
|
(*str)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline isize _atos(const char** str)
|
||||||
|
{
|
||||||
|
bool neg = false;
|
||||||
|
isize val = 0;
|
||||||
|
|
||||||
|
switch (**str)
|
||||||
|
{
|
||||||
|
case '-':
|
||||||
|
neg = true;
|
||||||
|
(*str)++;
|
||||||
|
break;
|
||||||
|
case '+': (*str)++; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (_isdigit(**str))
|
||||||
|
{
|
||||||
|
val = (10 * val) + (**str - '0');
|
||||||
|
(*str)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return neg ? -val : val;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user