Compare commits
6 Commits
728977d241
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
b785eacf20
|
|||
|
71056896ac
|
|||
|
d6b495a7c9
|
|||
|
4ed2f5fe7c
|
|||
|
13b4b8a5f5
|
|||
|
cd0f978b09
|
15
.clang-format
Normal file
15
.clang-format
Normal file
@@ -0,0 +1,15 @@
|
||||
BasedOnStyle: GNU
|
||||
ColumnLimit: 80
|
||||
IndentWidth: 4
|
||||
TabWidth: 4
|
||||
UseTab: ForContinuationAndIndentation
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeBinaryOperators: None
|
||||
SpaceAfterCStyleCast: true
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
AlignEscapedNewlines: DontAlign
|
||||
AlignOperands: DontAlign
|
||||
AlignConsecutiveMacros: Consecutive
|
||||
AllowAllArgumentsOnNextLine: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
IndentGotoLabels: false
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -9,3 +9,5 @@
|
||||
/ell.files
|
||||
/ell.creator*
|
||||
/ell.includes
|
||||
/ell.cflags
|
||||
/ell.cxxflags
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2017, Přemysl Janouch <p@janouch.name>
|
||||
Copyright (c) 2017, Přemysl Eric Janouch <p@janouch.name>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2018, Přemysl Janouch <p@janouch.name>
|
||||
// Copyright (c) 2018, Přemysl Eric Janouch <p@janouch.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2018, Přemysl Janouch <p@janouch.name>
|
||||
// Copyright (c) 2018, Přemysl Eric Janouch <p@janouch.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted.
|
||||
|
||||
103
ell.c
103
ell.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* ell.c: an experimental little language
|
||||
*
|
||||
* Copyright (c) 2017, Přemysl Janouch <p@janouch.name>
|
||||
* Copyright (c) 2017, Přemysl Eric Janouch <p@janouch.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted.
|
||||
@@ -16,14 +16,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
#if defined __GNUC__
|
||||
#define ELL_ATTRIBUTE_PRINTF(x, y) __attribute__ ((format (printf, x, y)))
|
||||
@@ -188,9 +188,18 @@ ell_list (struct ell_v *head) {
|
||||
|
||||
// --- Lexer -------------------------------------------------------------------
|
||||
|
||||
enum ell_token { ELLT_ABORT, ELLT_LPAREN, ELLT_RPAREN,
|
||||
ELLT_LBRACKET, ELLT_RBRACKET, ELLT_LBRACE, ELLT_RBRACE,
|
||||
ELLT_STRING, ELLT_NEWLINE, ELLT_AT };
|
||||
enum ell_token {
|
||||
ELLT_ABORT,
|
||||
ELLT_LPAREN,
|
||||
ELLT_RPAREN,
|
||||
ELLT_LBRACKET,
|
||||
ELLT_RBRACKET,
|
||||
ELLT_LBRACE,
|
||||
ELLT_RBRACE,
|
||||
ELLT_STRING,
|
||||
ELLT_NEWLINE,
|
||||
ELLT_AT
|
||||
};
|
||||
|
||||
static const char *ell_token_names[] = {
|
||||
[ELLT_ABORT] = "end of input",
|
||||
@@ -240,8 +249,8 @@ ell_lexer_advance (struct ell_lexer *self) {
|
||||
static bool
|
||||
ell_lexer_hexa_escape (struct ell_lexer *self, struct ell_buffer *output) {
|
||||
const char *abc = "0123456789abcdef", *h, *l;
|
||||
if (!self->len || !(h = strchr (abc, tolower (ell_lexer_advance (self))))
|
||||
|| !self->len || !(l = strchr (abc, tolower (ell_lexer_advance (self)))))
|
||||
if (!self->len || !(h = strchr (abc, tolower (ell_lexer_advance (self)))) ||
|
||||
!self->len || !(l = strchr (abc, tolower (ell_lexer_advance (self)))))
|
||||
return false;
|
||||
|
||||
ell_buffer_append_c (output, (h - abc) << 4 | (l - abc));
|
||||
@@ -254,7 +263,8 @@ enum {
|
||||
ELL_LEXER_COMMENT = '#'
|
||||
};
|
||||
|
||||
static bool ell_lexer_is_whitespace (int c) {
|
||||
static bool
|
||||
ell_lexer_is_whitespace (int c) {
|
||||
return !c || c == ' ' || c == '\t' || c == '\r';
|
||||
}
|
||||
|
||||
@@ -326,13 +336,12 @@ ell_lexer_next (struct ell_lexer *self, const char **e) {
|
||||
enum ell_token token = ell_lexer_tokens[c];
|
||||
if (!token) {
|
||||
ell_buffer_append_c (&self->string, c);
|
||||
while (self->len && !ell_lexer_is_whitespace (*self->p)
|
||||
&& !ell_lexer_tokens[*self->p])
|
||||
while (self->len && !ell_lexer_is_whitespace (*self->p) &&
|
||||
!ell_lexer_tokens[*self->p])
|
||||
ell_buffer_append_c (&self->string, ell_lexer_advance (self));
|
||||
return ELLT_STRING;
|
||||
}
|
||||
if (token == ELLT_STRING
|
||||
&& (*e = ell_lexer_string (self, &self->string)))
|
||||
if (token == ELLT_STRING && (*e = ell_lexer_string (self, &self->string)))
|
||||
return ELLT_ABORT;
|
||||
return token;
|
||||
}
|
||||
@@ -369,8 +378,8 @@ static bool
|
||||
ell_print_string_needs_quoting (struct ell_v *s) {
|
||||
for (size_t i = 0; i < s->len; i++) {
|
||||
unsigned char c = s->string[i];
|
||||
if (ell_lexer_is_whitespace (c) || ell_lexer_tokens[c]
|
||||
|| c == ELL_LEXER_ESCAPE || c < 32)
|
||||
if (ell_lexer_is_whitespace (c) || ell_lexer_tokens[c] ||
|
||||
c == ELL_LEXER_ESCAPE || c < 32)
|
||||
return true;
|
||||
}
|
||||
return s->len == 0;
|
||||
@@ -426,8 +435,8 @@ ell_print_block (struct ell_printer *printer, struct ell_v *list) {
|
||||
|
||||
static bool
|
||||
ell_print_set (struct ell_printer *printer, struct ell_v *list) {
|
||||
if (!list->head || strcmp (list->head->string, "set")
|
||||
|| !list->head->next || list->head->next->next)
|
||||
if (!list->head || strcmp (list->head->string, "set") ||
|
||||
!list->head->next || list->head->next->next)
|
||||
return false;
|
||||
|
||||
printer->putchar (printer, '@');
|
||||
@@ -448,10 +457,10 @@ ell_print_list (struct ell_printer *printer, struct ell_v *list) {
|
||||
|
||||
static void
|
||||
ell_print_v (struct ell_printer *printer, struct ell_v *v) {
|
||||
if (ell_print_string (printer, v)
|
||||
|| ell_print_block (printer, v)
|
||||
|| ell_print_set (printer, v)
|
||||
|| ell_print_list (printer, v))
|
||||
if (ell_print_string (printer, v) ||
|
||||
ell_print_block (printer, v) ||
|
||||
ell_print_set (printer, v) ||
|
||||
ell_print_list (printer, v))
|
||||
return;
|
||||
|
||||
printer->putchar (printer, '(');
|
||||
@@ -563,7 +572,7 @@ ell_parse_prefix_list (struct ell_v *seq, const char *name) {
|
||||
return ell_list (prefix);
|
||||
}
|
||||
|
||||
static struct ell_v * ell_parse_line (struct ell_parser *p, jmp_buf out);
|
||||
static struct ell_v *ell_parse_line (struct ell_parser *p, jmp_buf out);
|
||||
|
||||
static struct ell_v *
|
||||
ell_parse_v (struct ell_parser *p, jmp_buf out) {
|
||||
@@ -576,8 +585,7 @@ ell_parse_v (struct ell_parser *p, jmp_buf out) {
|
||||
|
||||
SKIP_NL ();
|
||||
if (ACCEPT (ELLT_STRING))
|
||||
return CHECK (ell_string
|
||||
(p->lexer.string.s, p->lexer.string.len));
|
||||
return CHECK (ell_string (p->lexer.string.s, p->lexer.string.len));
|
||||
if (ACCEPT (ELLT_AT)) {
|
||||
result = ell_parse_v (p, out);
|
||||
return CHECK (ell_parse_prefix_list (result, "set"));
|
||||
@@ -706,8 +714,8 @@ static bool
|
||||
ell_scope_prepend (struct ell *ell, struct ell_v **scope, const char *name,
|
||||
struct ell_v *v) {
|
||||
struct ell_v *key, *pair;
|
||||
if (!ell_check (ell, (key = ell_string (name, strlen (name))))
|
||||
|| !ell_check (ell, (pair = ell_list (key)))) {
|
||||
if (!ell_check (ell, (key = ell_string (name, strlen (name)))) ||
|
||||
!ell_check (ell, (pair = ell_list (key)))) {
|
||||
ell_free_seq (v);
|
||||
return false;
|
||||
}
|
||||
@@ -806,8 +814,8 @@ ell_eval_args (struct ell *ell,
|
||||
for (; args; args = args->next) {
|
||||
struct ell_v *evaluated = NULL;
|
||||
// Arguments should not evaporate, default to a nil value
|
||||
if (!ell_eval_statement (ell, args, &evaluated)
|
||||
|| (!evaluated && !ell_check (ell, (evaluated = ell_list (NULL)))))
|
||||
if (!ell_eval_statement (ell, args, &evaluated) ||
|
||||
(!evaluated && !ell_check (ell, (evaluated = ell_list (NULL)))))
|
||||
goto error;
|
||||
ell_free_seq (evaluated->next);
|
||||
evaluated->next = NULL;
|
||||
@@ -889,15 +897,14 @@ ell_eval_value (struct ell *ell, const struct ell_v *body,
|
||||
}
|
||||
|
||||
static bool
|
||||
ell_eval_statement
|
||||
(struct ell *ell, const struct ell_v *statement, struct ell_v **result) {
|
||||
ell_eval_statement (struct ell *ell, const struct ell_v *statement,
|
||||
struct ell_v **result) {
|
||||
if (statement->type == ELL_STRING)
|
||||
return ell_check (ell, (*result = ell_clone (statement)));
|
||||
|
||||
// Executing a nil value results in no value. It's not very different from
|
||||
// calling a block that returns no value--it's for our callers to resolve.
|
||||
if (!statement->head
|
||||
|| ell_eval_value (ell, statement->head, result))
|
||||
if (!statement->head || ell_eval_value (ell, statement->head, result))
|
||||
return true;
|
||||
|
||||
ell_free_seq (*result);
|
||||
@@ -918,8 +925,8 @@ ell_eval_statement
|
||||
|
||||
static bool
|
||||
args_to_scope (struct ell *ell, struct ell_v *args, struct ell_v **scope) {
|
||||
if (!ell_check (ell, (args = ell_list (args)))
|
||||
|| !ell_scope_prepend (ell, scope, "args", args))
|
||||
if (!ell_check (ell, (args = ell_list (args))) ||
|
||||
!ell_scope_prepend (ell, scope, "args", args))
|
||||
return false;
|
||||
|
||||
size_t i = 0;
|
||||
@@ -927,8 +934,8 @@ args_to_scope (struct ell *ell, struct ell_v *args, struct ell_v **scope) {
|
||||
char buf[16] = "";
|
||||
(void) snprintf (buf, sizeof buf, "%zu", ++i);
|
||||
struct ell_v *copy = NULL;
|
||||
if ((args && !ell_check (ell, (copy = ell_clone (args))))
|
||||
|| !ell_scope_prepend (ell, scope, buf, copy))
|
||||
if ((args && !ell_check (ell, (copy = ell_clone (args)))) ||
|
||||
!ell_scope_prepend (ell, scope, buf, copy))
|
||||
return false;
|
||||
}
|
||||
return ell_check (ell, (*scope = ell_list (*scope)));
|
||||
@@ -1031,8 +1038,8 @@ ell_defn (ell_fn_local) {
|
||||
struct ell_v *values = names->next;
|
||||
for (names = names->head; names; names = names->next) {
|
||||
struct ell_v *value = NULL;
|
||||
if ((values && !ell_check (ell, (value = ell_clone (values))))
|
||||
|| !ell_scope_prepend (ell, scope, names->string, value))
|
||||
if ((values && !ell_check (ell, (value = ell_clone (values)))) ||
|
||||
!ell_scope_prepend (ell, scope, names->string, value))
|
||||
return false;
|
||||
if (values)
|
||||
values = values->next;
|
||||
@@ -1047,9 +1054,9 @@ ell_defn (ell_fn_set) {
|
||||
|
||||
struct ell_v *v;
|
||||
if ((v = name->next))
|
||||
return ell_check (ell, (v = ell_clone (v)))
|
||||
&& ell_check (ell, (*result = ell_clone (v)))
|
||||
&& ell_set (ell, name->string, v);
|
||||
return ell_check (ell, (v = ell_clone (v))) &&
|
||||
ell_check (ell, (*result = ell_clone (v))) &&
|
||||
ell_set (ell, name->string, v);
|
||||
|
||||
// We return an empty list for a nil value
|
||||
if (!(v = ell_get (ell, name->string)))
|
||||
@@ -1139,8 +1146,8 @@ ell_defn (ell_fn_cat) {
|
||||
else
|
||||
ell_buffer_append (&buf, args->string, args->len);
|
||||
}
|
||||
bool ok = !(ell->memory_failure |= buf.memory_failure)
|
||||
&& ell_check (ell, (*result = ell_string (buf.s, buf.len)));
|
||||
bool ok = !(ell->memory_failure |= buf.memory_failure) &&
|
||||
ell_check (ell, (*result = ell_string (buf.s, buf.len)));
|
||||
free (buf.s);
|
||||
return ok;
|
||||
}
|
||||
@@ -1178,8 +1185,8 @@ ell_defn (ell_fn_try) {
|
||||
return true;
|
||||
|
||||
struct ell_v *msg;
|
||||
if (ell->memory_failure
|
||||
|| !ell_check (ell, (msg = ell_string (ell->error, strlen (ell->error)))))
|
||||
if (ell->memory_failure ||
|
||||
!ell_check (ell, (msg = ell_string (ell->error, strlen (ell->error)))))
|
||||
return false;
|
||||
|
||||
free (ell->error); ell->error = NULL;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2018, Přemysl Janouch <p@janouch.name>
|
||||
// Copyright (c) 2018, Přemysl Eric Janouch <p@janouch.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2018, Přemysl Janouch <p@janouch.name>
|
||||
// Copyright (c) 2018, Přemysl Eric Janouch <p@janouch.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted.
|
||||
@@ -280,7 +280,7 @@ func fnMinus(ell *Ell, args []V) (result []V, ok bool) {
|
||||
|
||||
var res float64
|
||||
if n, _ := fmt.Sscan(args[0].String, &res); n < 1 {
|
||||
return ell.Errorf("invalid number: %f", args[0].String)
|
||||
return ell.Errorf("invalid number: %s", args[0].String)
|
||||
}
|
||||
if len(args) == 1 {
|
||||
res = -res
|
||||
@@ -292,7 +292,7 @@ func fnMinus(ell *Ell, args []V) (result []V, ok bool) {
|
||||
}
|
||||
var value float64
|
||||
if n, _ := fmt.Sscan(arg.String, &value); n < 1 {
|
||||
return ell.Errorf("invalid number: %f", arg.String)
|
||||
return ell.Errorf("invalid number: %s", arg.String)
|
||||
}
|
||||
res -= value
|
||||
}
|
||||
@@ -321,7 +321,7 @@ func fnDivide(ell *Ell, args []V) (result []V, ok bool) {
|
||||
|
||||
var res float64
|
||||
if n, _ := fmt.Sscan(args[0].String, &res); n < 1 {
|
||||
return ell.Errorf("invalid number: %f", args[0].String)
|
||||
return ell.Errorf("invalid number: %s", args[0].String)
|
||||
}
|
||||
for _, arg := range args[1:] {
|
||||
if arg.Type != VTypeString {
|
||||
@@ -329,7 +329,7 @@ func fnDivide(ell *Ell, args []V) (result []V, ok bool) {
|
||||
}
|
||||
var value float64
|
||||
if n, _ := fmt.Sscan(arg.String, &value); n < 1 {
|
||||
return ell.Errorf("invalid number: %f", arg.String)
|
||||
return ell.Errorf("invalid number: %s", arg.String)
|
||||
}
|
||||
res /= value
|
||||
}
|
||||
@@ -411,7 +411,7 @@ func fnEquals(ell *Ell, args []V) (result []V, ok bool) {
|
||||
}
|
||||
var first, second float64
|
||||
if n, _ := fmt.Sscan(args[0].String, &first); n < 1 {
|
||||
return ell.Errorf("invalid number: %f", args[0].String)
|
||||
return ell.Errorf("invalid number: %s", args[0].String)
|
||||
}
|
||||
res := true
|
||||
for _, arg := range args[1:] {
|
||||
@@ -419,7 +419,7 @@ func fnEquals(ell *Ell, args []V) (result []V, ok bool) {
|
||||
return ell.Errorf("arguments must be strings")
|
||||
}
|
||||
if n, _ := fmt.Sscan(arg.String, &second); n < 1 {
|
||||
return ell.Errorf("invalid number: %f", arg.String)
|
||||
return ell.Errorf("invalid number: %s", arg.String)
|
||||
}
|
||||
if res = first == second; !res {
|
||||
break
|
||||
@@ -435,7 +435,7 @@ func fnLess(ell *Ell, args []V) (result []V, ok bool) {
|
||||
}
|
||||
var first, second float64
|
||||
if n, _ := fmt.Sscan(args[0].String, &first); n < 1 {
|
||||
return ell.Errorf("invalid number: %f", args[0].String)
|
||||
return ell.Errorf("invalid number: %s", args[0].String)
|
||||
}
|
||||
res := true
|
||||
for _, arg := range args[1:] {
|
||||
@@ -443,7 +443,7 @@ func fnLess(ell *Ell, args []V) (result []V, ok bool) {
|
||||
return ell.Errorf("arguments must be strings")
|
||||
}
|
||||
if n, _ := fmt.Sscan(arg.String, &second); n < 1 {
|
||||
return ell.Errorf("invalid number: %f", arg.String)
|
||||
return ell.Errorf("invalid number: %s", arg.String)
|
||||
}
|
||||
if res = first < second; !res {
|
||||
break
|
||||
|
||||
7
go.mod
Normal file
7
go.mod
Normal file
@@ -0,0 +1,7 @@
|
||||
module janouch.name/ell
|
||||
|
||||
go 1.17
|
||||
|
||||
require github.com/peterh/liner v1.1.0
|
||||
|
||||
require github.com/mattn/go-runewidth v0.0.3 // indirect
|
||||
4
go.sum
Normal file
4
go.sum
Normal file
@@ -0,0 +1,4 @@
|
||||
github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4=
|
||||
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/peterh/liner v1.1.0 h1:f+aAedNJA6uk7+6rXsYBnhdo4Xux7ESLe+kcuVUF5os=
|
||||
github.com/peterh/liner v1.1.0/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* interpreter.c: test interpreter
|
||||
*
|
||||
* Copyright (c) 2017, Přemysl Janouch <p@janouch.name>
|
||||
* Copyright (c) 2017, Přemysl Eric Janouch <p@janouch.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted.
|
||||
@@ -65,4 +65,3 @@ main (int argc, char *argv[]) {
|
||||
ell_free (&ell);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
4
repl.c
4
repl.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* repl.c: test REPL
|
||||
*
|
||||
* Copyright (c) 2017, Přemysl Janouch <p@janouch.name>
|
||||
* Copyright (c) 2017, Přemysl Eric Janouch <p@janouch.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted.
|
||||
@@ -17,8 +17,8 @@
|
||||
*/
|
||||
|
||||
#include "ell.c"
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
#include <readline/readline.h>
|
||||
|
||||
static void
|
||||
run (struct ell *ell, struct ell_v *program) {
|
||||
|
||||
Reference in New Issue
Block a user