Compare commits

..

29 commits

Author SHA1 Message Date
Igor M 118de2a37e fixed int typ in positive overflow detection 2024-03-18 15:05:58 +02:00
Igor M f556bb56ac remove unused variable 2024-03-18 14:57:45 +02:00
Igor M 1ded3630f4 Add hello-name example, fix backslash offset 2024-03-18 14:27:09 +02:00
Igor M 68cb60e342 comment hello world 2024-03-17 22:02:56 +02:00
m1el 3393d19cac Merge pull request #11 from m1el/example-hello
added hello example
2024-03-17 21:32:27 +02:00
Igor M 23729438a2 added hello example 2024-03-17 21:31:42 +02:00
m1el b745c4621c Merge pull request #10 from m1el/db-align
Implement db, align, fix some bugs
2024-03-17 19:26:21 +02:00
Igor M b01ed56ce7 handle malloc fail at start 2024-03-17 19:19:25 +02:00
Igor M 35828b75be format 2024-03-17 18:20:10 +02:00
Igor M 80c38fa747 use wildcard 2024-03-17 18:18:41 +02:00
Igor M fe985ca781 fixed error reporting 2024-03-17 18:05:09 +02:00
Igor M 4beaee5dab better align example 2024-03-17 14:11:43 +02:00
Igor M dc06b1b6d8 More specific error messages 2024-03-17 14:10:36 +02:00
Igor M 7b098ff98c more align examples 2024-03-17 14:05:00 +02:00
Igor M f8ea125d0f fixed offset calculation for escape syntax 2024-03-17 13:15:37 +02:00
Igor M 6f30327420 Updated example with hex escape 2024-03-17 13:10:32 +02:00
Igor M 220043a895 updated todo 2024-03-17 13:00:54 +02:00
Igor M 0847660293 push int as separate file 2024-03-17 12:59:05 +02:00
Igor M 757fb71b9a format 2024-03-17 12:42:44 +02:00
Igor M 4dfd6f2fc0 implemented .db .dw .dd .dq .align 2024-03-17 12:42:11 +02:00
m1el 81c505cd75 Merge pull request #9 from m1el/some-refactor
Some refactor
2024-03-13 22:06:37 +02:00
m1el aa45a8eba4 Merge pull request #8 from m1el/clang-format
added clang format
2024-03-11 16:01:52 +02:00
m1el f64528736b Merge pull request #7 from m1el/m1el-patch-2
Update c-cpp.yml, upgrade checkout action
2024-03-11 14:01:42 +02:00
m1el 5dec93f774 Update c-cpp.yml, upgrade checkout action 2024-03-11 14:01:15 +02:00
m1el 3aef796bd5 Merge pull request #6 from m1el/m1el-patch-1
Create gitgub workflow
2024-03-11 13:46:07 +02:00
m1el c57f615e01 Update Makefile -- fix path to binary 2024-03-11 13:45:44 +02:00
m1el 94c5192c92 Create git workflow 2024-03-11 13:42:46 +02:00
m1el a9c0cbeb8f Merge pull request #5 from m1el/fix-build
Fix build: change paths, add missing error
2024-03-11 13:41:34 +02:00
m1el 642ba58eeb Merge pull request #4 from m1el/int_comments_label_deref 2024-03-11 13:22:55 +02:00
14 changed files with 454 additions and 93 deletions

19
.github/workflows/c-cpp.yml vendored Normal file
View file

@ -0,0 +1,19 @@
name: Cee-lang CI
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: make
run: make
- name: example
run: make example

View file

@ -7,6 +7,7 @@ CLANG_FORMAT_STYLE = '{ BasedOnStyle: Google, IndentWidth: 4 }'
hbas: build/hbas
example: build/example.hbf
hello: build/hello.hbf
format:
clang-format --style=${CLANG_FORMAT_STYLE} -i src/*
@ -17,13 +18,17 @@ check-format:
build:
mkdir -p build
build/hbas: build src/hbas.c
build/hbas: build $(wildcard src/*.h src/*.c)
${CC} ${CFLAGS} ${CFLAGS_EXTRA} src/hbas.c -o build/hbas
build/example.hbf: build build/hbas examples/example.S
./build/hbas < examples/example.S > build/example.hbf
xxd build/example.hbf
build/hello.hbf: build build/hbas examples/hello.S
./build/hbas < examples/hello.S > build/hello.hbf
xxd build/hello.hbf
clean:
rm -rf build

View file

@ -2,16 +2,25 @@
; https://git.ablecorp.us/AbleOS/holey-bytes/src/branch/trunk/spec.md
; TODO:
; .origin 0x1000
; .align 0x100
; .db "hello"
; 'c' char literals
; .struct
start:
jmp end
un
; .db "hello world\n"
add16 r1, r2, r255
addi8 r1, r2, -128
lra r1, r0, start
jmp start
end:
tx
hello_string:
.db "Hello, w\x6frld\n", 0
hello_string_end:
.db "hi"
; TODO .db 'H', 'e', 'l', 'l', 'o', '\0'
.align 2
.dw 0x4546
.align 4
.dd 0x4748494a
.align 8
.dq 0x5051525354555657

103
examples/hello-name.S Normal file
View file

@ -0,0 +1,103 @@
jmp entry
puts:
; Write string to console
; r2: [IN] *const u8 String pointer
; r3: [IN] usize String length
li8 r1, 0x1 ; Write syscall
brc r2, r3, 2 ; Copy parameters
li8 r2, 0x1 ; STDOUT
eca
jal r0, r31, 0
gets:
; Read string until end of buffer or LF
; r2: [IN] *mut u8 Buffer
; r3: [IN] usize Buffer length
; Register allocations:
; r33: *mut u8 Buffer end
; r34: u8 Immediate char
; r35: u8 Const [0x0A = LF]
li8 r35, 0x0A
add64 r33, r2, r3
; Setup syscall
li8 r2, 0x1 ; Stdin
cp r3, r2
li8 r4, 0x1 ; Read one char
jeq r3, r33, end
loop:
li8 r1, 0x1 ; Read syscall
eca
addi64 r3, r3, 1
ld r34, r3, 0, 1
jeq r34, r35, end
jne r3, r33, loop
end:
; Set copied amount
sub64 r1, r33, r3
addi64 r1, r1, -1
jal r0, r31, 0
alloc_pages:
; Allocate pages
; r1: [OUT] *mut u8 Pointer to page
; r2: [IN] u16 Page count
muli16 r3, r2, 4096 ; page count
li8 r1, 0x9 ; mmap syscall
li8 r2, 0x0 ; no address set, kernel chosen
li8 r4, 0x2 ; PROT_WRITE
li8 r5, 0x20 ; MAP_ANONYMOUS
li64 r6, -1 ; Doesn't map file
li8 r7, 0x0 ; Doesn't map file
eca
jal r0, r31, 0
entry:
; Program entrypoint
; Register allocations:
; r32: *mut u8 Buffer
; r36: usize Read buffer length
; Allocate one page (4096 KiB)
li8 r2, 1
jal r31, r0, alloc_pages
cp r32, r1
; Print message
lra16 r2, r0, enter_your_name
li8 r3, 17
jal r31, r0, puts
; Read name
cp r2, r32
li16 r3, 4096
jal r31, r0, gets
cp r36, r1
; Print your name is
lra16 r2, r0, your_name_is
li8 r3, 15
jal r31, r0, puts
; And now print the name
cp r2, r32
cp r3, r36
jal r31, r0, puts
tx
enter_your_name:
.db "Enter your name: "
your_name_is:
.db "\nYour name is: "

8
examples/hello.S Normal file
View file

@ -0,0 +1,8 @@
li8 r1, 1 ; 1->sys::write
li8 r2, 1 ; fildes=stdout
lra16 r3, r0, hello_string ; buf=hello_string
li8 r4, 0x11 ; nbyte=0x11
eca ; sys::write(stdout, hello_string, 0x11)
tx
hello_string:
.db "Hello, AbleCorp!\n"

View file

@ -56,8 +56,7 @@ const char *TYPE_STR[] = {
const size_t NARGS = sizeof(ARGS) / sizeof(ARGS[0]);
static
ArgMeta arg_meta(char arg) {
static ArgMeta arg_meta(char arg) {
for (size_t ii = 0; ii < NARGS; ii += 1) {
ArgMeta meta = ARGS[ii];
if (meta.chr == arg) {

View file

@ -6,8 +6,7 @@ typedef struct ByteVec_s {
size_t len;
} ByteVec;
static
AsmError ensure_push(ByteVec *vec, size_t el_size, size_t extra) {
static AsmError ensure_push(ByteVec *vec, size_t el_size, size_t extra) {
if (vec->len + extra < vec->len) {
return ErrOutOfMemory;
}

138
src/directive.c Normal file
View file

@ -0,0 +1,138 @@
AsmError push_string(char *buf, char *input, size_t len) {
size_t ndata = 0;
for (size_t pos = 0; pos < len; pos += 1) {
char chr = input[pos];
if (chr == '\\') {
if (pos + 1 >= len) {
return ErrDanglingEscape;
}
pos += 1;
chr = input[pos];
switch (chr) {
case '\\':
chr = '\\';
break;
case '"':
chr = '"';
break;
case 'r':
chr = '\r';
break;
case 'n':
chr = '\n';
break;
case '0':
chr = '\0';
break;
case 't':
chr = '\t';
break;
case 'x':
if (pos + 2 >= len) {
return ErrDanglingEscape;
}
char high = get_hex(input[pos + 1]);
char low = get_hex(input[pos + 2]);
if (high > 15 || low > 15) {
return ErrStringBadHex;
}
pos += 2;
chr = high << 4 | low;
break;
default:
return ErrBadStringEscape;
}
}
buf[ndata] = chr;
ndata += 1;
}
return ErrOk;
}
static AsmError push_data(char *input, size_t len, ByteVec *out, Token *tok,
size_t word_size) {
while (1) {
*tok = token(input, len, tok->start + tok->len);
if (tok->kind == TokNumber) {
if (ensure_push(out, 1, word_size) != 0) {
return ErrOutOfMemory;
}
push_int_le(&out->buf[out->len], tok->num, word_size, 3);
out->len += word_size;
} else if (tok->kind == TokString) {
if (word_size != 1) {
return ErrStringDataNotByte;
}
if (ensure_push(out, 1, tok->num) != 0) {
return ErrOutOfMemory;
}
char *str = &input[tok->start + 1];
AsmError err = push_string(&out->buf[out->len], str, tok->len - 2);
if (err != ErrOk) {
return err;
}
out->len += tok->num;
} else {
return ErrNeedsDataLiteral;
}
*tok = token(input, len, tok->start + tok->len);
if (tok->kind == TokNewline || tok->kind == TokEOF) {
return ErrOk;
}
if (tok->kind == TokComma) {
continue;
}
return ErrNeedCommaOrNewline;
}
}
AsmError assemble_directive(char *input, size_t len, ByteVec *out, Token *tok) {
if (tok->len < 2) {
return ErrInvalidDirective;
}
size_t pos = tok->start;
char byte0 = input[pos];
char byte1 = input[pos + 1];
if (tok->len == 2 && byte0 == 'd') {
size_t word_size;
switch (byte1) {
case 'b':
word_size = 1;
break;
case 'w':
word_size = 2;
break;
case 'd':
word_size = 4;
break;
case 'q':
word_size = 8;
break;
default:
return ErrInvalidDirective;
}
return push_data(input, len, out, tok, word_size);
}
if (tok->len == 5 && strncmp("align", &input[pos], 5) == 0) {
*tok = token(input, len, tok->start + tok->len);
if (tok->kind != TokNumber) {
return ErrAlignNeedsNumber;
}
size_t mask = tok->num - 1;
if (tok->num == 0 || (tok->num & mask) != 0) {
return ErrAlignNeedsPow2;
}
if ((~(size_t)0) - mask < out->len) {
return ErrOutOfMemory;
}
size_t aligned = (out->len + mask) & ~mask;
if (ensure_push(out, 1, aligned - out->len) != 0) {
return ErrOutOfMemory;
}
// TODO: zero-fill?
out->len = aligned;
return ErrOk;
}
return ErrInvalidDirective;
}

View file

@ -18,6 +18,16 @@ typedef enum AsmError_e {
ErrDirectiveNotImplemented,
ErrUnexpectedToken,
ErrTriedNegateNonNumber,
ErrInvalidDirective,
ErrStringNewLine,
ErrDanglingEscape,
ErrStringBadHex,
ErrBadStringEscape,
ErrStringDataNotByte,
ErrAlignNeedsNumber,
ErrAlignNeedsPow2,
ErrNeedCommaOrNewline,
ErrNeedsDataLiteral,
} AsmError;
char *ERRORS[] = {
"Success",
@ -39,4 +49,14 @@ char *ERRORS[] = {
"Directive is not implemented",
"Unexpected token",
"Negation only works on numbers",
"Invalid directive",
"String contains a raw newline (did you forget to close the quote?)",
"Dangling escape in string literal",
"Bad hex in string literal",
"Bad escape sequence in string literal",
"String literals can be used only in .db directive",
".align requires a number",
".align requires a power of two as an argument",
"Need comma or newline after data literal",
"Data literal expects a number or a string",
};

View file

@ -5,8 +5,7 @@ typedef struct InstHtNode_s {
} InstHtNode;
typedef InstHtNode *InstHt;
static
uint32_t inst_hash(const char *s, size_t len) {
static uint32_t inst_hash(const char *s, size_t len) {
uint32_t hash = 0;
uint32_t mul = 75;
for (size_t ii = 0; ii < len; ii += 1) {
@ -16,8 +15,7 @@ uint32_t inst_hash(const char *s, size_t len) {
return hash;
}
static
InstHt build_lookup(void) {
static InstHt build_lookup(void) {
const size_t size = 256;
InstHt table = (InstHt)malloc(size * sizeof(InstHtNode));
if (table == NULL) {
@ -42,8 +40,7 @@ InstHt build_lookup(void) {
return table;
}
static
size_t inst_lookup(InstHt ht, const char *s, size_t len) {
static size_t inst_lookup(InstHt ht, const char *s, size_t len) {
uint32_t hash = inst_hash(s, len);
uint8_t *node = (uint8_t *)&ht[(size_t)(hash & 0xff)];
for (size_t ii = 0; ii < 2; ii += 1) {

View file

@ -20,8 +20,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdint.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -35,15 +35,16 @@ SOFTWARE.
//
#include "hash.c"
//
#include "push_int.c"
#include "register.c"
#include "token.c"
//
#include "directive.c"
#include "einfo.h"
// Print space-separated hex dump of each byte, 16 bytes per line.
// Can be reversed with `xxd -p -r`.
static
void hex_dump(char *data, size_t len) {
static void hex_dump(char *data, size_t len) {
char buf[48];
const char *alphabet = "0123456789abcdef";
for (size_t ii = 0; ii < len; ii += 1) {
@ -61,8 +62,7 @@ void hex_dump(char *data, size_t len) {
#define MIN_SIZE 4096
static
int slurp(FILE *fd, ByteVec *out) {
static int slurp(FILE *fd, ByteVec *out) {
ByteVec rv = {malloc(MIN_SIZE), MIN_SIZE, 0};
size_t bread = 1;
int err = 0;
@ -109,8 +109,7 @@ typedef struct LabelVec_s {
size_t len;
} LabelVec;
static
size_t label_lookup(LabelVec *labels, char *name, size_t len) {
static size_t label_lookup(LabelVec *labels, char *name, size_t len) {
size_t nlabels = labels->len;
Label *buf = labels->buf;
for (size_t ii = 0; ii < nlabels; ii += 1) {
@ -122,65 +121,8 @@ size_t label_lookup(LabelVec *labels, char *name, size_t len) {
return INVALID;
}
static
bool check_valid_int(uint64_t val, size_t size, uint8_t sign) {
// All 64-bit values are considered valid.
if (size == 8) {
return true;
}
// Unsigned integers must have all upper bits set to zero. To check this,
// we shift the value right by the integer size and verify it equals zero.
int valid_uint = (val >> (size * 8)) == 0;
// For signed integers, the sign-extended high bits must match the sign bit.
// By shifting right by one less than the total bit size (size * 8 - 1),
// we isolate the sign bit and any sign-extended bits. For a value fitting
// in the signed range, this operation results in either 0 (for non-negative
// values) or -1 (for negative values due to sign extension).
int64_t int_shifted = ((int64_t)val) >> (size * 8 - 1);
// To unify the check for both positive and negative cases, we adjust
// non-zero values (-1) by incrementing by 1. This turns -1 into 0,
// enabling a single check for 0 to validate both cases. This adjustment
// simplifies the validation logic, allowing us to use a single condition to
// check for proper sign extension or zero extension in the original value.
int_shifted += int_shifted != 0;
// A valid signed integer will have `int_shifted` equal to 0
// after adjustment, indicating proper sign extension.
int valid_int = int_shifted == 0;
// Validity bitmask to represents whether the value
// fits as signed, unsigned, or both.
int validity = valid_int | (valid_uint << 1);
// If the value's validity doesn't match the `sign` requirements,
// we report an overflow.
return (validity & sign) != 0;
}
// safety: assumes the buffer has enough place for specified integer size.
// `sign` is a bitset, where bit `1` indicates that value accepts a signed int,
// and bit `2` indicates that value accepts an unsigned int.
static
AsmError push_int_le(char *buf, uint64_t val, size_t size, uint8_t sign) {
if (!check_valid_int(val, size, sign)) {
return ErrImmediateOverflow;
}
// Write out the bytes of the integer to the buffer in little-endian order,
// starting with the lowest byte first.
for (size_t ii = 0; ii < size; ii += 1) {
buf[ii] = val & 0xff;
val >>= 8;
}
return ErrOk;
}
static
AsmError assemble_instr(InstHt ht, char *input, size_t len, Token *tok,
ByteVec *rv, HoleVec *holes) {
static AsmError assemble_instr(InstHt ht, char *input, size_t len, Token *tok,
ByteVec *rv, HoleVec *holes) {
const InstDesc *inst;
const char *type_str;
size_t nargs;
@ -265,6 +207,8 @@ AsmError assemble_instr(InstHt ht, char *input, size_t len, Token *tok,
return ErrBadNumOverflow;
}
num_to_write = (uint64_t)tmp;
} else if (meta.sign == 2 && (int64_t)num_to_write < 0) {
return ErrBadNumOverflow;
}
AsmError err = push_int_le(&rv->buf[rv->len], num_to_write,
meta.size, meta.sign);
@ -283,6 +227,9 @@ AsmError assemble(InstHt ht, char *input, size_t len, ByteVec *out,
ByteVec rv = {malloc(MIN_SIZE), MIN_SIZE, 0};
HoleVec holes = {malloc(MIN_SIZE * sizeof(Hole)), MIN_SIZE, 0};
LabelVec labels = {malloc(MIN_SIZE * sizeof(Label)), MIN_SIZE, 0};
if (rv.buf == NULL || holes.buf == NULL || labels.buf == NULL) {
return ErrOutOfMemory;
}
size_t line = 0;
size_t line_start = 0;
size_t pos = 0;
@ -317,13 +264,17 @@ AsmError assemble(InstHt ht, char *input, size_t len, ByteVec *out,
}
if (tok.kind == TokDot) {
Token next = token(input, len, pos);
if (next.kind == TokIdent) {
err = ErrDirectiveNotImplemented;
goto end;
} else {
einfo->token = next;
if (next.kind != TokIdent) {
err = ErrNeedDirectiveAfterDot;
goto end;
}
err = assemble_directive(input, len, &rv, &next);
pos = next.start + next.len;
einfo->token = next;
if (err != ErrOk) {
goto end;
}
continue;
}
if (tok.kind == TokIdent) {

55
src/push_int.c Normal file
View file

@ -0,0 +1,55 @@
static bool check_valid_int(uint64_t val, size_t size, uint8_t sign) {
// All 64-bit values are considered valid.
if (size == 8) {
return true;
}
// Unsigned integers must have all upper bits set to zero. To check this,
// we shift the value right by the integer size and verify it equals zero.
int valid_uint = (val >> (size * 8)) == 0;
// For signed integers, the sign-extended high bits must match the sign bit.
// By shifting right by one less than the total bit size (size * 8 - 1),
// we isolate the sign bit and any sign-extended bits. For a value fitting
// in the signed range, this operation results in either 0 (for non-negative
// values) or -1 (for negative values due to sign extension).
int64_t int_shifted = ((int64_t)val) >> (size * 8 - 1);
// To unify the check for both positive and negative cases, we adjust
// non-zero values (-1) by incrementing by 1. This turns -1 into 0,
// enabling a single check for 0 to validate both cases. This adjustment
// simplifies the validation logic, allowing us to use a single condition to
// check for proper sign extension or zero extension in the original value.
int_shifted += int_shifted != 0;
// A valid signed integer will have `int_shifted` equal to 0
// after adjustment, indicating proper sign extension.
int valid_int = int_shifted == 0;
// Validity bitmask to represents whether the value
// fits as signed, unsigned, or both.
int validity = valid_int | (valid_uint << 1);
// If the value's validity doesn't match the `sign` requirements,
// we report an overflow.
return (validity & sign) != 0;
}
// safety: assumes the buffer has enough place for specified integer size.
// `sign` is a bitset, where bit `1` indicates that value accepts a signed int,
// and bit `2` indicates that value accepts an unsigned int.
static AsmError push_int_le(char *buf, uint64_t val, size_t size,
uint8_t sign) {
if (!check_valid_int(val, size, sign)) {
return ErrImmediateOverflow;
}
// Write out the bytes of the integer to the buffer in little-endian order,
// starting with the lowest byte first.
for (size_t ii = 0; ii < size; ii += 1) {
buf[ii] = val & 0xff;
val >>= 8;
}
return ErrOk;
}

View file

@ -1,5 +1,4 @@
static
int parse_register(char *name, size_t len) {
static int parse_register(char *name, size_t len) {
if (name[0] != 'r') {
return 256; // Register name should start with 'r'
}

View file

@ -10,6 +10,7 @@ typedef enum TokenKind_e {
TokColon = ':',
TokComment = ';',
TokNewline = 'n',
TokString = 's',
} TokenKind;
typedef struct Token_s {
TokenKind kind;
@ -18,8 +19,7 @@ typedef struct Token_s {
uint64_t num;
} Token;
static
Token token_ident(char *input, size_t len, size_t pos) {
static Token token_ident(char *input, size_t len, size_t pos) {
size_t start = pos;
while (pos < len) {
char chr = input[pos];
@ -34,8 +34,7 @@ Token token_ident(char *input, size_t len, size_t pos) {
return (Token){TokIdent, start, pos - start, 0};
}
static
Token token_number(char *input, size_t len, size_t pos) {
static Token token_number(char *input, size_t len, size_t pos) {
char *ptr = &input[pos];
char next = '\0';
size_t start = pos;
@ -111,8 +110,65 @@ Token token_number(char *input, size_t len, size_t pos) {
}
}
static
Token token(char *input, size_t len, size_t pos) {
static char get_hex(char chr) {
char chru = chr & ~0x20;
if (chr >= '0' && chr <= '9') {
return chr - '0';
}
if (chru >= 'A' && chru <= 'F') {
return chru - ('A' - 10);
}
return 16;
}
static Token token_string(char *input, size_t len, size_t pos) {
size_t start = pos;
size_t ndata = 0;
for (pos += 1; pos < len; pos += 1) {
if (input[pos] == '"') {
return (Token){TokString, start, pos + 1 - start, ndata};
}
if (input[pos] == '\n' || input[pos] == '\r') {
return (Token){TokInvalid, start, pos + 1 - start,
ErrStringNewLine};
}
if (input[pos] == '\\') {
if (pos + 1 >= len) {
return (Token){TokInvalid, start, pos - start,
ErrDanglingEscape};
}
pos += 1;
switch (input[pos]) {
case '\\':
case '"':
case 'r':
case 'n':
case '0':
case 't':
break;
case 'x':
if (pos + 2 >= len) {
return (Token){TokInvalid, start, pos - start,
ErrDanglingEscape};
}
if (get_hex(input[pos + 1]) > 15 ||
get_hex(input[pos + 2]) > 15) {
return (Token){TokInvalid, start, pos - start,
ErrStringBadHex};
}
pos += 2;
break;
default:
return (Token){TokInvalid, start, pos - start,
ErrBadStringEscape};
}
}
ndata += 1;
}
return (Token){TokString, start, pos - start, ndata};
}
static Token token(char *input, size_t len, size_t pos) {
char chr, chru;
char *ptr = &input[pos];
while (pos < len && (input[pos] == ' ' || input[pos] == '\t')) {
@ -142,6 +198,9 @@ Token token(char *input, size_t len, size_t pos) {
}
return (Token){TokComment, pos, clen, 0};
}
if (chr == '"') {
return token_string(input, len, pos);
}
if (chr >= '0' && chr <= '9') {
return token_number(input, len, pos);
}