aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexis Lockwood2021-07-18 10:38:56 -0600
committerAlexis Lockwood2021-07-18 10:38:56 -0600
commitda12c0d48a33d46d0a9ea648d21c526953bf2da8 (patch)
tree03277eb185fc68fb28ce02c4db13bddcb3432c9e
parent0fc8a1562e17aa38873a57250d3e4a227a87f681 (diff)
initial floating point support
-rw-r--r--Makefile1
-rw-r--r--README.md2
-rwxr-xr-xgen_kws.py4
-rw-r--r--include/ls.h17
-rw-r--r--lib/ls_expr.c438
-rw-r--r--lib/ls_internal.c32
-rw-r--r--lib/ls_internal.h17
-rw-r--r--lib/ls_kw_impl.c2
-rw-r--r--lib/ls_kw_impl_LOOPS.c10
-rw-r--r--lib/ls_kw_impl_PRINT.c12
-rw-r--r--lib/ls_lex.c46
-rw-r--r--lib/ls_types.h46
-rw-r--r--ls_minify.c26
13 files changed, 459 insertions, 194 deletions
diff --git a/Makefile b/Makefile
index c23898d..8c9664d 100644
--- a/Makefile
+++ b/Makefile
@@ -20,6 +20,7 @@ CFLAGS := \
-std=c99 -D_DEFAULT_SOURCE \
-Wall -Wextra -Wpedantic -Wshadow -pedantic \
-Iinclude -Ilib \
+ -DLS_HAVE_FLOAT=1 -DLS_FLOAT_IS_DOUBLE=1 \
${EXTRA_FLAGS}
.PHONY: all clean
diff --git a/README.md b/README.md
index 1073460..9dbe8ec 100644
--- a/README.md
+++ b/README.md
@@ -121,6 +121,8 @@ The following things use pool entries:
(this probably requires ON ERROR implemented so they can intentionally fail)
- I kinda like the idea of "cursor" instead of "pc"
- Scanning Statement is a bad name, these should be Block Statements
+- A lot of small internal functions are bloating the code size by requiring a
+ self argument for error-throwing. They should return errors instead.
## Coding style, contributions
diff --git a/gen_kws.py b/gen_kws.py
index c607eaf..5d6c2ed 100755
--- a/gen_kws.py
+++ b/gen_kws.py
@@ -107,7 +107,9 @@ KEYWORDS = [
Kw("WEND", 0xB9, scanning=True),
Kw("WHILE", 0xBA, scanning=True),
Kw("WRITE", 0xBB),
- Kw("XOR", 0xBC, op="LS_OP_XOR", noexec=True),
+ Kw("XOR", 0xBC, op="LS_OP_XOR", noexec=True),
+ Kw("FLOAT", 0xBD, op="LS_OP_FLOAT", noexec=True),
+ Kw("INT", 0xBE, op="LS_OP_INT", noexec=True),
]
if len(sys.argv) == 2 and sys.argv[1] == "source":
diff --git a/include/ls.h b/include/ls.h
index 53e282d..4d4ddcc 100644
--- a/include/ls.h
+++ b/include/ls.h
@@ -87,7 +87,22 @@ typedef struct ls_s {
/// Value of the last read token
union {
- char word[LS_IDENT_OR_KW_LEN + 1];
+ // .word is also a scan buffer for tokens that need to be
+ // scanned before they can be parsed. This includes:
+ //
+ // ident 7 - LS_IDENT_LEN (6) + 1
+ // keyword 11 - 10 + 1
+ // float 17 - 16 + 1 \ just one of these
+ // double 25 - 24 + 1 / depending on build flags
+#if defined(LS_USE_FLOAT) && defined(LS_FLOAT_IS_DOUBLE)
+ char word[25];
+ ls_float_t float_;
+#elif defined(LS_USE_FLOAT)
+ char word[17];
+ ls_float_t float_;
+#else
+ char word[11];
+#endif
ls_int_t number;
ls_addr_t string[2];
} _token;
diff --git a/lib/ls_expr.c b/lib/ls_expr.c
index 4d77907..1a7b22b 100644
--- a/lib/ls_expr.c
+++ b/lib/ls_expr.c
@@ -16,6 +16,7 @@
#include <inttypes.h>
#include <string.h>
#include <ctype.h>
+#include <math.h>
// --- PRIVATE MACROS ----------------------------------------------------------
@@ -29,8 +30,8 @@ typedef struct {
uint8_t precedence : 6;
bool unary : 1;
bool right : 1; // associativity
- /// Function for two ints, or for one int (ignore lhs, for unary)
- ls_int_t (*fun_int_int)(ls_int_t lhs, ls_int_t rhs);
+
+ ls_error_t (*fun)(ls_value_t * lhs, ls_value_t * rhs, ls_op_t op);
} ls_opdef_t;
typedef struct {
@@ -61,11 +62,16 @@ static void _push(ls_shuntingyard_t * yard, ls_op_t op);
/// Handle a LS_TOK_WORD. If a word is found, it is transformed into a NUMBER
/// and should be passed along to _handle_number(). (If it is not found, an
/// error is thrown)
-static void _handle_word(ls_shuntingyard_t * yard);
+static ls_token_t _handle_word(ls_shuntingyard_t * yard);
/// Handle a LS_TOK_NUMBER
static void _handle_number(ls_shuntingyard_t * yard);
+#ifdef LS_HAVE_FLOAT
+/// Handle a LS_TOK_FLOAT
+static void _handle_float(ls_shuntingyard_t * yard);
+#endif
+
/// Handle a LS_TOK_KEYWORD(). Some keywords need to be transformed into
/// operators, others halt parsing.
///
@@ -85,63 +91,53 @@ static bool _handle_other(ls_shuntingyard_t * yard, ls_token_t tok);
/// Pop one operator off the operator stack and apply it to the output stack.
static void _pop_oper_and_apply(ls_shuntingyard_t * yard);
-static ls_int_t _add_int_int(ls_int_t lhs, ls_int_t rhs);
-static ls_int_t _sub_int_int(ls_int_t lhs, ls_int_t rhs);
-static ls_int_t _mul_int_int(ls_int_t lhs, ls_int_t rhs);
-static ls_int_t _div_int_int(ls_int_t lhs, ls_int_t rhs);
-static ls_int_t _mod_int_int(ls_int_t lhs, ls_int_t rhs);
-static ls_int_t _shl_int_int(ls_int_t lhs, ls_int_t rhs);
-static ls_int_t _shr_int_int(ls_int_t lhs, ls_int_t rhs);
-static ls_int_t _abs_int(ls_int_t lhs, ls_int_t rhs);
-static ls_int_t _neg_int(ls_int_t lhs, ls_int_t rhs);
-
-// Logic
-static ls_int_t _not_int(ls_int_t lhs, ls_int_t rhs);
-static ls_int_t _and_int_int(ls_int_t lhs, ls_int_t rhs);
-static ls_int_t _or_int_int(ls_int_t lhs, ls_int_t rhs);
-static ls_int_t _xor_int_int(ls_int_t lhs, ls_int_t rhs);
-static ls_int_t _eqv_int_int(ls_int_t lhs, ls_int_t rhs);
-static ls_int_t _imp_int_int(ls_int_t lhs, ls_int_t rhs);
-
-// Relational
-static ls_int_t _eq_int_int(ls_int_t lhs, ls_int_t rhs);
-static ls_int_t _neq_int_int(ls_int_t lhs, ls_int_t rhs);
-static ls_int_t _lt_int_int(ls_int_t lhs, ls_int_t rhs);
-static ls_int_t _gt_int_int(ls_int_t lhs, ls_int_t rhs);
-static ls_int_t _leq_int_int(ls_int_t lhs, ls_int_t rhs);
-static ls_int_t _geq_int_int(ls_int_t lhs, ls_int_t rhs);
-
-// --- PUBLIC VARIABLES --------------------------------------------------------
-// --- PRIVATE VARIABLES -------------------------------------------------------
-
-static const ls_opdef_t _ops[] = {
- [LS_OP_ABS] = { 13, true, true, &_abs_int },
- [LS_OP_NOT] = { 13, true, true, &_not_int },
- [LS_OP_NEG] = { 13, true, true, &_neg_int },
+ls_error_t (*fun)(ls_value_t * lhs, ls_value_t * rhs, ls_op_t op);
- [LS_OP_MUL] = { 12, false, false, &_mul_int_int },
- [LS_OP_DIV] = { 12, false, false, &_div_int_int },
- [LS_OP_MOD] = { 12, false, false, &_mod_int_int },
+/// Basic arith operators rolled into one: add sub mul div mod abs neg
+static ls_error_t _arithop(ls_value_t * lhs, ls_value_t * rhs, ls_op_t op);
- [LS_OP_ADD] = { 11, false, false, &_add_int_int },
- [LS_OP_SUB] = { 11, false, false, &_sub_int_int },
+/// Integer binary operators: shl shr not and or xor eqv imp
+static ls_error_t _logicop(ls_value_t * lhs, ls_value_t * rhs, ls_op_t op);
- [LS_OP_SHL] = { 10, false, false, &_shl_int_int },
- [LS_OP_SHR] = { 10, false, false, &_shr_int_int },
+/// Relationals
+static ls_error_t _relop(ls_value_t * lhs, ls_value_t * rhs, ls_op_t op);
- [LS_OP_LT] = { 9, false, false, &_lt_int_int },
- [LS_OP_GT] = { 9, false, false, &_gt_int_int },
- [LS_OP_LEQ] = { 9, false, false, &_leq_int_int },
- [LS_OP_GEQ] = { 9, false, false, &_geq_int_int },
+/// Casts
+static ls_error_t _castop(ls_value_t * lhs, ls_value_t * rhs, ls_op_t op);
- [LS_OP_EQ] = { 8, false, false, &_eq_int_int },
- [LS_OP_NEQ] = { 8, false, false, &_neq_int_int },
+// --- PUBLIC VARIABLES --------------------------------------------------------
+// --- PRIVATE VARIABLES -------------------------------------------------------
- [LS_OP_AND] = { 7, false, false, &_and_int_int },
- [LS_OP_XOR] = { 7, false, false, &_xor_int_int },
- [LS_OP_OR] = { 7, false, false, &_or_int_int },
- [LS_OP_EQV] = { 7, false, false, &_eqv_int_int },
- [LS_OP_IMP] = { 7, false, false, &_imp_int_int },
+static const ls_opdef_t _ops[] = {
+ [LS_OP_ABS] = { 13, true, true, &_arithop },
+ [LS_OP_NOT] = { 13, true, true, &_logicop },
+ [LS_OP_NEG] = { 13, true, true, &_arithop },
+ [LS_OP_FLOAT] = { 13, true, true, &_castop },
+ [LS_OP_INT] = { 13, true, true, &_castop },
+
+ [LS_OP_MUL] = { 12, false, false, &_arithop },
+ [LS_OP_DIV] = { 12, false, false, &_arithop },
+ [LS_OP_MOD] = { 12, false, false, &_arithop },
+
+ [LS_OP_ADD] = { 11, false, false, &_arithop },
+ [LS_OP_SUB] = { 11, false, false, &_arithop },
+
+ [LS_OP_SHL] = { 10, false, false, &_logicop },
+ [LS_OP_SHR] = { 10, false, false, &_logicop },
+
+ [LS_OP_LT] = { 9, false, false, &_relop },
+ [LS_OP_GT] = { 9, false, false, &_relop },
+ [LS_OP_LEQ] = { 9, false, false, &_relop },
+ [LS_OP_GEQ] = { 9, false, false, &_relop },
+
+ [LS_OP_EQ] = { 8, false, false, &_relop },
+ [LS_OP_NEQ] = { 8, false, false, &_relop },
+
+ [LS_OP_AND] = { 7, false, false, &_logicop },
+ [LS_OP_XOR] = { 7, false, false, &_logicop },
+ [LS_OP_OR] = { 7, false, false, &_logicop },
+ [LS_OP_EQV] = { 7, false, false, &_logicop },
+ [LS_OP_IMP] = { 7, false, false, &_logicop },
};
// --- PUBLIC FUNCTIONS --------------------------------------------------------
@@ -168,14 +164,27 @@ ls_eval_expr(ls_t * self, ls_value_t * val, ls_token_t firsttok)
switch (tok)
{
case LS_TOK_WORD:
- _handle_word(&yard);
- // becomes number
+ tok = _handle_word(&yard);
+ if (tok == LS_TOK_NUMBER)
+ goto _is_number;
+#ifdef LS_HAVE_FLOAT
+ else if (tok == LS_TOK_FLOAT)
+ goto _is_float;
+#endif
// fall through
case LS_TOK_NUMBER:
+ _is_number:
_handle_number(&yard);
break;
+#ifdef LS_HAVE_FLOAT
+ case LS_TOK_FLOAT:
+ _is_float:
+ _handle_float(&yard);
+ break;
+#endif
+
default:
if (LS_TOK_KEYWORD(tok))
{
@@ -271,11 +280,7 @@ _pop_oper_and_apply(ls_shuntingyard_t * yard)
rhs->next = lhs->next;
}
- // TODO: types
- rhs->body.integer.value = p_op->fun_int_int(
- lhs ? lhs->body.integer.value : 0,
- rhs->body.integer.value
- );
+ LS_TRY_THROW(yard->self, p_op->fun(lhs, rhs, op));
if (!p_op->unary)
ls_free(yard->self, lhs);
@@ -294,6 +299,18 @@ _handle_number(ls_shuntingyard_t * yard)
}
static void
+_handle_float(ls_shuntingyard_t * yard)
+{
+ ls_value_t * v = ls_alloc(yard->self);
+ v->ty = LS_TY_FLOAT;
+ v->body.float_.value = yard->self->_token.float_;
+ v->prev = NULL;
+ v->next = yard->outstack;
+ yard->outstack = v;
+ yard->allow_unary = false;
+}
+
+static ls_token_t
_handle_word(ls_shuntingyard_t * yard)
{
ls_value_t * var = ls_find_var(yard->self, yard->self->_token.word,
@@ -302,9 +319,23 @@ _handle_word(ls_shuntingyard_t * yard)
ls_value_t val;
ls_read_scalar_var(yard->self, var, &val);
- // TODO: types
- ls_coerce_int(yard->self, &val);
- yard->self->_token.number = val.body.integer.value;
+ // TODO: stuffing this back into the token isn't really the cleanest
+ // way to do this...
+ switch (val.ty)
+ {
+ case LS_TY_INT:
+ yard->self->_token.number = val.body.integer.value;
+ return LS_TOK_NUMBER;
+
+#ifdef LS_HAVE_FLOAT
+ case LS_TY_FLOAT:
+ yard->self->_token.float_ = val.body.float_.value;
+ return LS_TOK_FLOAT;
+#endif
+
+ default:
+ ls_throw_err(yard->self, LS_TYPE_MISMATCH);
+ }
}
static ls_token_t
@@ -401,141 +432,198 @@ _handle_other(ls_shuntingyard_t * yard, ls_token_t tok)
return false;
}
-static ls_int_t
-_add_int_int(ls_int_t lhs, ls_int_t rhs)
+/// Basic arith operators rolled into one: add sub mul div mod abs neg
+static ls_error_t
+_arithop(ls_value_t * lhs, ls_value_t * rhs, ls_op_t op)
{
- return lhs + rhs;
-}
-
-static ls_int_t
-_sub_int_int(ls_int_t lhs, ls_int_t rhs)
-{
- return lhs - rhs;
-}
+ if (lhs)
+ LS_TRY_RETURN(ls_coerce_vals(lhs, rhs));
-static ls_int_t
-_mul_int_int(ls_int_t lhs, ls_int_t rhs)
-{
- return lhs * rhs;
-}
+ switch (rhs->ty)
+ {
+ case LS_TY_INT: {
+ ls_int_t const * lhsi = lhs ? &lhs->body.integer.value : NULL;
+ ls_int_t * rhsi = &rhs->body.integer.value;
+ switch (op)
+ {
+ case LS_OP_ABS: *rhsi = (*rhsi < 0) ? -*rhsi : *rhsi; break;
+ case LS_OP_NEG: *rhsi = -*rhsi; break;
+ case LS_OP_MUL: *rhsi = *lhsi * *rhsi; break;
+ case LS_OP_DIV: *rhsi = *lhsi / *rhsi; break;
+ case LS_OP_MOD: *rhsi = *lhsi % *rhsi; break;
+ case LS_OP_ADD: *rhsi = *lhsi + *rhsi; break;
+ case LS_OP_SUB: *rhsi = *lhsi - *rhsi; break;
+ default: return LS_INTERNAL_ERROR;
+ }
+ } break;
-static ls_int_t
-_div_int_int(ls_int_t lhs, ls_int_t rhs)
-{
- return lhs / rhs;
-}
+#ifdef LS_HAVE_FLOAT
+ case LS_TY_FLOAT: {
+ ls_float_t const * lhsi = lhs ? &lhs->body.float_.value : NULL;
+ ls_float_t * rhsi = &rhs->body.float_.value;
+ switch (op)
+ {
+#ifdef LS_FLOAT_IS_DOUBLE
+ case LS_OP_ABS: *rhsi = fabs(*rhsi); break;
+#else
+ case LS_OP_ABS: *rhsi = fabsf(*rhsi); break;
+#endif
+ case LS_OP_NEG: *rhsi = -*rhsi; break;
+ case LS_OP_MUL: *rhsi = *lhsi * *rhsi; break;
+ case LS_OP_DIV: *rhsi = *lhsi / *rhsi; break;
+ // TODO can we do better?
+ case LS_OP_MOD: return LS_TYPE_MISMATCH;
+ case LS_OP_ADD: *rhsi = *lhsi + *rhsi; break;
+ case LS_OP_SUB: *rhsi = *lhsi - *rhsi; break;
+ default: return LS_INTERNAL_ERROR;
+ }
+ } break;
+#endif // LS_HAVE_FLOAT
-static ls_int_t
-_mod_int_int(ls_int_t lhs, ls_int_t rhs)
-{
- return lhs % rhs;
-}
+ default:
+ return LS_TYPE_MISMATCH;
+ }
-static ls_int_t
-_shl_int_int(ls_int_t lhs, ls_int_t rhs)
-{
- if (rhs < 0)
- return _shr_int_int(lhs, -rhs);
- else if (rhs > 32)
- return 0;
- else
- return lhs << rhs;
+ return LS_OK;
}
-static ls_int_t
-_shr_int_int(ls_int_t lhs, ls_int_t rhs)
+/// Integer binary operators: shl shr not and or xor eqv imp
+static ls_error_t
+_logicop(ls_value_t * lhs, ls_value_t * rhs, ls_op_t op)
{
- if (rhs < 0)
- return _shl_int_int(lhs, -rhs);
- else if (rhs > 32)
- return 0;
- else
- return lhs >> rhs;
-}
+ if (lhs)
+ LS_TRY_RETURN(ls_coerce_int(lhs));
+ LS_TRY_RETURN(ls_coerce_int(rhs));
-static ls_int_t
-_abs_int(ls_int_t lhs, ls_int_t rhs)
-{
- (void) lhs;
- return rhs < 0 ? -rhs : rhs;
-}
+ ls_int_t const * lhsi = lhs ? &lhs->body.integer.value : NULL;
+ ls_int_t * rhsi = &rhs->body.integer.value;
-static ls_int_t
-_neg_int(ls_int_t lhs, ls_int_t rhs)
-{
- (void) lhs;
- return -rhs;
-}
+ // Range check shl/shr first
+ if (op == LS_OP_SHL || op == LS_OP_SHR)
+ {
+ if (*rhsi < 0 && *rhsi >= -32)
+ {
+ op = (op == LS_OP_SHL) ? LS_OP_SHR : LS_OP_SHL;
+ *rhsi = -*rhsi;
+ }
+ else if (*rhsi > 32 || *rhsi < -32)
+ {
+ *rhsi = 0;
+ return LS_OK;
+ }
+ }
-static ls_int_t
-_not_int(ls_int_t lhs, ls_int_t rhs)
-{
- (void) lhs;
- return ~rhs;
-}
+ switch (op)
+ {
+ case LS_OP_NOT: *rhsi = ~*rhsi; break;
+ case LS_OP_SHL: *rhsi = *lhsi << *rhsi; break;
+ case LS_OP_SHR: *rhsi = *lhsi >> *rhsi; break;
+ case LS_OP_AND: *rhsi = *lhsi & *rhsi; break;
+ case LS_OP_XOR: *rhsi = *lhsi ^ *rhsi; break;
+ case LS_OP_OR: *rhsi = *lhsi | *rhsi; break;
+ case LS_OP_EQV: *rhsi = ~(*lhsi ^ *rhsi); break;
+ case LS_OP_IMP: *rhsi = ~(*lhsi & ~*rhsi); break;
+ default: return LS_INTERNAL_ERROR;
+ }
-static ls_int_t
-_and_int_int(ls_int_t lhs, ls_int_t rhs)
-{
- return lhs & rhs;
+ return LS_OK;
}
-static ls_int_t
-_or_int_int(ls_int_t lhs, ls_int_t rhs)
+/// Relationals
+static ls_error_t
+_relop(ls_value_t * lhs, ls_value_t * rhs, ls_op_t op)
{
- return lhs | rhs;
-}
+ if (lhs)
+ LS_TRY_RETURN(ls_coerce_vals(lhs, rhs));
-static ls_int_t
-_xor_int_int(ls_int_t lhs, ls_int_t rhs)
-{
- return lhs ^ rhs;
-}
+ switch (rhs->ty)
+ {
+ case LS_TY_INT: {
+ ls_int_t const * lhsi = lhs ? &lhs->body.integer.value : NULL;
+ ls_int_t * rhsi = &rhs->body.integer.value;
+ switch (op)
+ {
+ case LS_OP_LT: *rhsi = *lhsi < *rhsi; break;
+ case LS_OP_GT: *rhsi = *lhsi > *rhsi; break;
+ case LS_OP_LEQ: *rhsi = *lhsi <= *rhsi; break;
+ case LS_OP_GEQ: *rhsi = *lhsi >= *rhsi; break;
+ case LS_OP_EQ: *rhsi = *lhsi == *rhsi; break;
+ case LS_OP_NEQ: *rhsi = *lhsi != *rhsi; break;
+ default: return LS_INTERNAL_ERROR;
+ }
+ } break;
+
+#ifdef LS_HAVE_FLOAT
+ case LS_TY_FLOAT: {
+ int result = 0;
+ ls_float_t const * lhsi = lhs ? &lhs->body.float_.value : NULL;
+ ls_float_t * rhsi = &rhs->body.float_.value;
+ switch (op)
+ {
+ case LS_OP_LT: result = *lhsi < *rhsi; break;
+ case LS_OP_GT: result = *lhsi > *rhsi; break;
+ case LS_OP_LEQ: result = *lhsi <= *rhsi; break;
+ case LS_OP_GEQ: result = *lhsi >= *rhsi; break;
+ case LS_OP_EQ: result = *lhsi == *rhsi; break;
+ case LS_OP_NEQ: result = *lhsi != *rhsi; break;
+ default: return LS_INTERNAL_ERROR;
+ }
-static ls_int_t
-_eqv_int_int(ls_int_t lhs, ls_int_t rhs)
-{
- return ~(lhs ^ rhs);
-}
+ rhs->ty = LS_TY_INT;
+ rhs->body.integer.value = result;
+ } break;
+#endif // LS_HAVE_FLOAT
-static ls_int_t
-_imp_int_int(ls_int_t lhs, ls_int_t rhs)
-{
- return ~(lhs & ~rhs);
-}
+ default:
+ return LS_TYPE_MISMATCH;
+ }
-static ls_int_t
-_eq_int_int(ls_int_t lhs, ls_int_t rhs)
-{
- return lhs == rhs;
+ return LS_OK;
}
-static ls_int_t
-_neq_int_int(ls_int_t lhs, ls_int_t rhs)
+/// Casts
+static ls_error_t
+_castop(ls_value_t * lhs, ls_value_t * rhs, ls_op_t op)
{
- return lhs != rhs;
-}
+ (void) lhs;
-static ls_int_t
-_lt_int_int(ls_int_t lhs, ls_int_t rhs)
-{
- return lhs < rhs;
-}
+ switch (op)
+ {
+#ifdef LS_HAVE_FLOAT
+ case LS_OP_FLOAT:
+ switch (rhs->ty)
+ {
+ case LS_TY_INT:
+ rhs->body.float_.value = rhs->body.integer.value;
+ rhs->ty = LS_TY_FLOAT;
+ break;
+ case LS_TY_FLOAT:
+ break;
+ default:
+ return LS_TYPE_MISMATCH;
+ }
+ break;
+#endif // LS_HAVE_FLOAT
-static ls_int_t
-_gt_int_int(ls_int_t lhs, ls_int_t rhs)
-{
- return lhs > rhs;
-}
+ case LS_OP_INT:
+ switch (rhs->ty)
+ {
+#ifdef LS_HAVE_FLOAT
+ case LS_TY_FLOAT:
+ rhs->body.integer.value = rhs->body.float_.value;
+ rhs->ty = LS_TY_INT;
+ break;
+#endif // LS_HAVE_FLOAT
+ case LS_TY_INT:
+ break;
+ default:
+ return LS_TYPE_MISMATCH;
+ }
+ break;
-static ls_int_t
-_leq_int_int(ls_int_t lhs, ls_int_t rhs)
-{
- return lhs <= rhs;
-}
+ default:
+ return LS_INTERNAL_ERROR;
+ }
-static ls_int_t
-_geq_int_int(ls_int_t lhs, ls_int_t rhs)
-{
- return lhs >= rhs;
+ return LS_OK;
}
diff --git a/lib/ls_internal.c b/lib/ls_internal.c
index 5098f75..b86b279 100644
--- a/lib/ls_internal.c
+++ b/lib/ls_internal.c
@@ -72,6 +72,8 @@ static LS_PROGMEM const char _s_ls_while_wend_mismatch[]
= "WHILE/WEND mismatch";
static LS_PROGMEM const char _s_ls_scanning_stmt_in_if[]
= "scanning stmt in IF";
+static LS_PROGMEM const char _s_ls_dont_have_float[]
+ = "don't have float";
static LS_PROGMEM const char _s_ls_no_more_program[]
= "no more program";
static LS_PROGMEM const char _s_ls_unknown[]
@@ -100,6 +102,7 @@ static LS_PROGMEM const char * _error_s_table[] = {
_s_ls_undefined_variable,
_s_ls_while_wend_mismatch,
_s_ls_scanning_stmt_in_if,
+ _s_ls_dont_have_float,
_s_ls_no_more_program,
_s_ls_unknown,
};
@@ -365,17 +368,38 @@ ls_write_scalar_var(ls_t * self, ls_value_t * var, ls_value_t * const val)
}
}
-void
-ls_coerce_int(ls_t * self, ls_value_t * value)
+ls_error_t
+ls_coerce_int(ls_value_t * value)
{
switch (value->ty)
{
case LS_TY_INT:
- return;
+ return LS_OK;
default:
- ls_throw_err(self, LS_TYPE_MISMATCH);
+ return LS_TYPE_MISMATCH;
+ }
+}
+
+ls_error_t
+ls_coerce_vals(ls_value_t * v1, ls_value_t * v2)
+{
+ if (v1->ty == v2->ty)
+ return LS_OK;
+
+ if (v1->ty == LS_TY_INT)
+ {
+ v1->body.float_.value = (ls_float_t) v1->body.integer.value;
+ v1->ty = LS_TY_FLOAT;
+ }
+
+ if (v2->ty == LS_TY_INT)
+ {
+ v2->body.float_.value = (ls_float_t) v2->body.integer.value;
+ v2->ty = LS_TY_FLOAT;
}
+
+ return LS_OK;
}
bool
diff --git a/lib/ls_internal.h b/lib/ls_internal.h
index b2daf86..a28ce59 100644
--- a/lib/ls_internal.h
+++ b/lib/ls_internal.h
@@ -50,9 +50,14 @@
#define LS_KW_HASH_FINALIZE(h) ((h) & 0x1F)
/// Bail if the expression is an error, returning that error
-#define LS_TRY(e) \
+#define LS_TRY_RETURN(e) \
do { ls_error_t __err = (e); if (__err) return __err; } while (0)
+/// Bail if the expression is an error, throwing that error
+#define LS_TRY_THROW(self_, e) \
+ do { ls_error_t __err = (e); if (__err) ls_throw_err((self_), __err); \
+ } while (0)
+
/// Throw an error. In normal builds, this just calls through to ls_throw_err_f.
/// In debug builds it reports the file and line as well.
#ifdef NDEBUG
@@ -196,7 +201,15 @@ void ls_read_scalar_var(ls_t * self, ls_value_t const * var, ls_value_t * val);
void ls_write_scalar_var(ls_t * self, ls_value_t * var, ls_value_t * const val);
/// Coerce a value into an LS_TY_INT. If not possible, throw LS_TYPE_MISMATCH.
-void ls_coerce_int(ls_t * self, ls_value_t * value);
+///
+/// TODO to ls_math.h
+ls_error_t ls_coerce_int(ls_value_t * value);
+
+/// Coerce two values into compatible types. If not possible, throw
+/// LS_TYPE_MISMATCH.
+///
+/// TODO to ls_math.h
+ls_error_t ls_coerce_vals(ls_value_t * v1, ls_value_t * v2);
/// Execute one line.
///
diff --git a/lib/ls_kw_impl.c b/lib/ls_kw_impl.c
index a9c1cb7..d4d0c71 100644
--- a/lib/ls_kw_impl.c
+++ b/lib/ls_kw_impl.c
@@ -52,7 +52,7 @@ ls_kw_fun_IF(ls_t * self)
ls_value_t cond;
ls_eval_expr(self, &cond, LS_TOK_NONE);
- ls_coerce_int(self, &cond);
+ LS_TRY_THROW(self, ls_coerce_int(&cond));
if (cond.body.integer.value == 0)
{
diff --git a/lib/ls_kw_impl_LOOPS.c b/lib/ls_kw_impl_LOOPS.c
index 0b9119a..ce2828f 100644
--- a/lib/ls_kw_impl_LOOPS.c
+++ b/lib/ls_kw_impl_LOOPS.c
@@ -61,14 +61,14 @@ ls_kw_fun_FOR(ls_t * self)
// FOR ident = <<a TO b>> STEP c
ls_value_t val;
ls_eval_expr(self, &val, LS_TOK_NONE);
- ls_coerce_int(self, &val);
+ LS_TRY_THROW(self, ls_coerce_int(&val));
iterator->body.int_var.value = val.body.integer.value;
if (ls_lex(self) != LS_KW_TO)
goto syntax_err;
ls_eval_expr(self, &val, LS_TOK_NONE);
- ls_coerce_int(self, &val);
+ LS_TRY_THROW(self, ls_coerce_int(&val));
ctx->body.sctx_for.term = val.body.integer.value;
// FOR ident = a TO b <<STEP c>>
@@ -77,7 +77,7 @@ ls_kw_fun_FOR(ls_t * self)
if (tok == LS_KW_STEP)
{
ls_eval_expr(self, &val, LS_TOK_NONE);
- ls_coerce_int(self, &val);
+ LS_TRY_THROW(self, ls_coerce_int(&val));
if (val.body.integer.value > INT16_MAX ||
val.body.integer.value < INT16_MIN)
@@ -145,7 +145,7 @@ ls_kw_fun_WHILE(ls_t * self)
ls_value_t cond;
ls_eval_expr(self, &cond, LS_TOK_NONE);
- ls_coerce_int(self, &cond);
+ LS_TRY_THROW(self, ls_coerce_int(&cond));
if (cond.body.integer.value != 0)
{
@@ -211,7 +211,7 @@ ls_kw_fun_WEND(ls_t * self)
ls_value_t cond;
ls_eval_expr(self, &cond, LS_TOK_NONE);
- ls_coerce_int(self, &cond);
+ LS_TRY_THROW(self, ls_coerce_int(&cond));
if (cond.body.integer.value == 0)
{
diff --git a/lib/ls_kw_impl_PRINT.c b/lib/ls_kw_impl_PRINT.c
index 6803edf..75a69c6 100644
--- a/lib/ls_kw_impl_PRINT.c
+++ b/lib/ls_kw_impl_PRINT.c
@@ -61,11 +61,12 @@ ls_kw_fun_PRINT(ls_t * self)
goto done;
default:
- if (LS_TOK_KEYWORD(tok))
- goto done;
-
print_lf = true;
ls_eval_expr(self, &val, tok);
+
+ if (val.ty == LS_TY_NULL)
+ goto done;
+
_print_value(file, &val);
break;
}
@@ -86,6 +87,11 @@ _print_value(FILE * f, ls_value_t const * value)
case LS_TY_INT:
fprintf(f, "%"PRId32, value->body.integer.value);
break;
+#ifdef LS_HAVE_FLOAT
+ case LS_TY_FLOAT:
+ fprintf(f, "%f", (double) value->body.float_.value);
+ break;
+#endif
case LS_TY_STR:
// TODO
fprintf(f, "<str>");
diff --git a/lib/ls_lex.c b/lib/ls_lex.c
index 4de0318..4cca9a4 100644
--- a/lib/ls_lex.c
+++ b/lib/ls_lex.c
@@ -13,6 +13,7 @@
// Standard headers
#include <stdbool.h>
#include <stddef.h>
+#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include <ctype.h>
@@ -317,15 +318,36 @@ _lex_num(ls_t * self, ls_uchar ch[2])
self->_pc++;
}
- for (;;)
+ size_t i;
+ bool have_float = false;
+ bool allow_sign = false;
+
+ memset(self->_token.word, 0, sizeof(self->_token.word));
+
+ for (i = 0;; i++)
{
+ if (i < sizeof(self->_token.word) - 1 && ch[0] <= CHAR_MAX)
+ self->_token.word[i] = (char) ch[0];
+
int8_t digit = 0;
ch[0] = (ls_uchar) toupper(ch[0]);
if (ch[0] >= '0' && ch[0] <= '9')
digit = (int8_t)(ch[0] - '0');
else if (ch[0] >= 'A' && ch[0] <= 'F')
+ {
digit = (int8_t)(ch[0] - 'A' + 10);
+ }
+ else if (ch[0] == '.')
+ have_float = true;
+ else if (ch[0] == '+' || ch[0] == '-')
+ {
+ if (!allow_sign)
+ {
+ self->_pc--;
+ break;
+ }
+ }
else if (ch[0] == ':')
{
if (val > LS_ADDR_MAX || val < 0)
@@ -341,6 +363,14 @@ _lex_num(ls_t * self, ls_uchar ch[2])
break;
}
+ if (radix <= 10 && ch[0] == 'E')
+ {
+ have_float = allow_sign = true;
+ digit = 0;
+ }
+ else
+ allow_sign = false;
+
ch[0] = ls_fetch(self);
if (digit >= radix)
@@ -349,6 +379,20 @@ _lex_num(ls_t * self, ls_uchar ch[2])
self->_pc++;
}
+ if (have_float)
+#ifdef LS_USE_FLOAT
+ {
+ // TODO: strtof if we have it
+ double f = strtod(self->_token.word, NULL);
+ self->_token.float_ = (ls_float_t) f;
+ return LS_TOK_FLOAT;
+ }
+#else
+ {
+ ls_throw_err(self, LS_DONT_HAVE_FLOAT);
+ }
+#endif
+
self->_token.number = val;
return tok;
}
diff --git a/lib/ls_types.h b/lib/ls_types.h
index 659b047..4ee6b0e 100644
--- a/lib/ls_types.h
+++ b/lib/ls_types.h
@@ -21,6 +21,10 @@
#include <stddef.h>
#include <inttypes.h>
+// TODO we need to figure out a good way to support build switches. For now,
+#define LS_USE_FLOAT 1
+#define LS_FLOAT_IS_DOUBLE 1
+
// --- MACROS ------------------------------------------------------------------
#define LS_IDENT_LEN 6
@@ -81,6 +85,14 @@ struct ls_s;
/// Basic integer type. This is used for integers in scripts.
typedef int32_t ls_int_t;
+#ifdef LS_USE_FLOAT
+# ifdef LS_FLOAT_IS_DOUBLE
+typedef double ls_float_t;
+# else
+typedef float ls_float_t;
+# endif
+#endif
+
/// Unsigned 8-bit char as returned from the fetcher. This is only used when
/// dealing with source at the byte level; once ls_lex parses it, it becomes
/// char.
@@ -97,6 +109,12 @@ typedef struct {
ls_int_t value;
} ls_ty_int_t;
+#ifdef LS_USE_FLOAT
+typedef struct {
+ ls_float_t value;
+} ls_ty_float_t;
+#endif
+
// --- COMPLEX TYPES -----------------------------------------------------------
/* TODO - I'm changing this to "scalar" and "vector" variables. There will be
@@ -149,6 +167,14 @@ typedef struct {
ls_int_t value;
} ls_ty_int_var_t;
+#ifdef LS_USE_FLOAT
+/// Float variable. This contains a variable name and a direct fp value.
+typedef struct {
+ char ident[LS_IDENT_LEN];
+ ls_float_t value;
+} ls_ty_float_var_t;
+#endif
+
/// Label. This points to the PC of the start of the line just after the label.
typedef struct {
char ident[LS_IDENT_LEN];
@@ -206,6 +232,9 @@ typedef enum {
LS_TY_PRISTINE = LS_TY_SIMPLE_START,
LS_TY_NOT_ALLOC,
LS_TY_INT,
+#ifdef LS_USE_FLOAT
+ LS_TY_FLOAT,
+#endif
LS_TY_STR = LS_TY_COMPLEX_START,
LS_TY_LIST,
@@ -213,6 +242,9 @@ typedef enum {
LS_TY_STR_CHUNK = LS_TY_CHILD_START,
LS_TY_INT_VAR = LS_TY_INTERNAL_START,
+#ifdef LS_USE_FLOAT
+ LS_TY_FLOAT_VAR,
+#endif
LS_TY_LABEL,
LS_TY_SCTX_CALL,
LS_TY_SCTX_FOR,
@@ -236,8 +268,14 @@ typedef struct ls_value_s {
struct ls_value_s * prev, * next;
union {
ls_ty_int_t integer;
+#ifdef LS_USE_FLOAT
+ ls_ty_float_t float_;
+#endif
ls_ty_int_var_t int_var;
+#ifdef LS_USE_FLOAT
+ ls_ty_float_var_t float_var;
+#endif
ls_ty_label_t label;
ls_ty_sctx_call_t sctx_call;
ls_ty_sctx_for_t sctx_for;
@@ -279,6 +317,7 @@ typedef enum {
LS_UNDEFINED_VARIABLE,
LS_WHILE_WEND_MISMATCH,
LS_SCANNING_STMT_IN_IF,
+ LS_DONT_HAVE_FLOAT, // a script used float but it's not compiled in
/// To be returned by a fetcher if there is no more data. This is not
/// strictly an error condition; the parser should be allowed to read
@@ -325,6 +364,8 @@ typedef enum {
LS_OP_NOT,
LS_OP_OR,
LS_OP_XOR,
+ LS_OP_FLOAT,
+ LS_OP_INT,
LS_NO_OP
} ls_op_t;
@@ -339,9 +380,12 @@ typedef enum {
typedef enum {
// --- ls_op_t here ---
- /// Integer and floating point values
+ /// Integer values
LS_TOK_NUMBER = LS_NO_OP + 1,
+ /// Floating point values
+ LS_TOK_FLOAT,
+
/// Idents
LS_TOK_WORD,
diff --git a/ls_minify.c b/ls_minify.c
index ef4601c..ff2ef6a 100644
--- a/ls_minify.c
+++ b/ls_minify.c
@@ -38,6 +38,7 @@ static void _minify(FILE * f_out);
static void _min_word_or_str_label(ls_token_t tok);
static void _min_number_or_num_label(ls_token_t tok);
+static void _min_float(ls_token_t tok);
static ls_token_t _min_keyword(ls_token_t tok);
static void _min_string(ls_token_t tok);
static void _min_operator(ls_token_t tok);
@@ -233,6 +234,10 @@ static void _minify(FILE * f_out)
_min_word_or_str_label(tok);
break;
+ case LS_TOK_FLOAT:
+ _min_float(tok);
+ break;
+
case LS_TOK_STRING:
_min_string(tok);
break;
@@ -303,6 +308,27 @@ static void _min_number_or_num_label(ls_token_t tok)
opt_un_minify ? ": " : ":");
}
+static void _min_float(ls_token_t tok)
+{
+ ls_float_t f = g_ls._token.float_;
+ ls_float_t g;
+ char buf[32];
+ int prec;
+
+ for (prec = 25; prec > 0; prec -= 1)
+ {
+ snprintf(buf, sizeof(buf), "%.*g", prec, f);
+ g = strtod(buf, NULL);
+ if (g != f)
+ break;
+ }
+
+ if (g_add_space)
+ fprintf(g_f_out, " ");
+
+ fprintf(g_f_out, "%.*g", prec + 1, f);
+}
+
static ls_token_t _min_keyword(ls_token_t tok)
{
if (tok == LS_KW_REM)