Compare commits

...

5 Commits

Author SHA1 Message Date
Přemysl Eric Janouch f751975cfd
Add a port to Go 2018-10-09 10:42:20 +02:00
Přemysl Eric Janouch 55a1076367
Fix an apparent memory leak 2018-10-09 08:55:03 +02:00
Přemysl Eric Janouch 7c9fb564af
Cleanup
Use inline semicolons rather then line feeds.
2018-10-09 08:54:14 +02:00
Přemysl Eric Janouch a004e91c80
Sanitize error message in "throw" 2018-10-09 08:53:39 +02:00
Přemysl Eric Janouch f452191e62
Fix typo 2018-10-09 08:53:17 +02:00
5 changed files with 1459 additions and 11 deletions

View File

@ -6,8 +6,9 @@ ell
a programming language implementable with as little code as possible while a programming language implementable with as little code as possible while
still being reasonably comfortable to use. still being reasonably comfortable to use.
This package is an implementation of said language, meant to be self-contained, This package contains two implementations of said language--one in C and
portable and reusable. Performance is specifically not an intent. another in Go--which are meant to be self-contained, portable and reusable.
Performance is specifically not an intent.
The project is currently in a "proof of concept" stage with many useful data The project is currently in a "proof of concept" stage with many useful data
operations missing but I believe it won't be a problem to implement them as operations missing but I believe it won't be a problem to implement them as

63
cmd/interpreter/main.go Normal file
View File

@ -0,0 +1,63 @@
//
// Copyright (c) 2018, Přemysl Janouch <p@janouch.name>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Program interpreter is a basic ell interpreter.
package main
import (
"fmt"
"io/ioutil"
"os"
"janouch.name/ell/ell"
)
func main() {
var script []byte
var err error
if len(os.Args) < 2 {
script, err = ioutil.ReadAll(os.Stdin)
} else {
script, err = ioutil.ReadFile(os.Args[1])
}
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
L := ell.New()
if !ell.StdInitialize(L) {
fmt.Printf("runtime library initialization failed: %s\n", L.Error)
}
program, err := ell.NewParser(script).Run()
if err != nil {
fmt.Printf("%s: %s\n", "parse error", err)
os.Exit(1)
}
var args *ell.V
tail := &args
for i := 2; i < len(os.Args); i++ {
*tail = ell.NewString([]byte(os.Args[i]))
tail = &(*tail).Next
}
var result *ell.V
if !L.EvalBlock(program, args, &result) {
fmt.Printf("%s: %s\n", "runtime error", L.Error)
}
}

91
cmd/repl/main.go Normal file
View File

@ -0,0 +1,91 @@
//
// Copyright (c) 2018, Přemysl Janouch <p@janouch.name>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Program repl is an interactive ell interpreter.
package main
import (
"fmt"
"io"
"os"
"strings"
// This library is rather simplistic but it's going to serve us fine.
"github.com/peterh/liner"
"janouch.name/ell/ell"
)
func run(L *ell.Ell, program *ell.V) {
var result *ell.V
if !L.EvalBlock(program, nil, &result) {
fmt.Printf("\x1b[31m%s: %s\x1b[0m\n", "runtime error", L.Error)
L.Error = ""
} else {
ell.PrintSeq(os.Stdout, result)
os.Stdout.WriteString("\n")
}
}
func complete(L *ell.Ell, line string) (res []string) {
// This never actually completes anything, just shows the options,
// we'd have to figure out the longest common prefix.
res = append(res, line)
line = strings.ToLower(line)
for v := L.Globals; v != nil; v = v.Next {
name := string(v.Head.String)
if strings.HasPrefix(strings.ToLower(name), line) {
res = append(res, name)
}
}
for name := range L.Native {
if strings.HasPrefix(strings.ToLower(name), line) {
res = append(res, name)
}
}
return
}
func main() {
L := ell.New()
if !ell.StdInitialize(L) {
fmt.Printf("runtime library initialization failed: %s\n", L.Error)
}
line := liner.NewLiner()
line.SetCompleter(func(line string) []string { return complete(L, line) })
line.SetMultiLineMode(true)
line.SetTabCompletionStyle(liner.TabPrints)
for {
script, err := line.Prompt("> ")
if err == nil {
line.AppendHistory(script)
p := ell.NewParser([]byte(script))
if program, err := p.Run(); err != nil {
fmt.Printf("\x1b[31m%s: %s\x1b[0m\n", "parse error", err)
} else {
run(L, program)
}
} else if err == liner.ErrPromptAborted || err == io.EOF {
break
} else {
fmt.Printf("\x1b[31m%s: %s\x1b[0m\n", "error", err)
}
}
os.Stdout.WriteString("\n")
}

17
ell.c
View File

@ -734,9 +734,8 @@ ell_set (struct ell *ell, const char *name, struct ell_v *v) {
for (struct ell_v *scope = ell->scopes; scope; scope = scope->next) { for (struct ell_v *scope = ell->scopes; scope; scope = scope->next) {
if ((place = ell_scope_find (&scope->head, name))) { if ((place = ell_scope_find (&scope->head, name))) {
ell_free_seq ((*place)->head->next); ell_free_seq ((*place)->head->next);
(*place)->head->next = NULL; (*place)->head->next = v;
return !v return true;
|| ell_check (ell, ((*place)->head->next = ell_clone (v)));
} }
} }
@ -847,7 +846,7 @@ ell_eval_native (struct ell *ell, const char *name, struct ell_v *args,
static bool static bool
ell_eval_resolved (struct ell *ell, struct ell_v *body, struct ell_v *args, ell_eval_resolved (struct ell *ell, struct ell_v *body, struct ell_v *args,
struct ell_v **result) { struct ell_v **result) {
// Resolving names ecursively could be pretty fatal, let's not do that // Resolving names recursively could be pretty fatal, let's not do that
if (body->type == ELL_STRING) if (body->type == ELL_STRING)
return ell_check (ell, (*result = ell_clone (body))); return ell_check (ell, (*result = ell_clone (body)));
struct ell_v *arguments = NULL; struct ell_v *arguments = NULL;
@ -1191,7 +1190,7 @@ ell_defn (ell_fn_throw) {
struct ell_v *message = args; struct ell_v *message = args;
if (!message || message->type != ELL_STRING) if (!message || message->type != ELL_STRING)
return ell_error (ell, "first argument must be string"); return ell_error (ell, "first argument must be string");
return ell_error (ell, message->string); return ell_error (ell, "%s", message->string);
} }
ell_defn (ell_fn_plus) { ell_defn (ell_fn_plus) {
@ -1377,10 +1376,10 @@ const char ell_std_composed[] =
"set break { throw _break }\n" "set break { throw _break }\n"
// TODO: we should be able to apply them to all arguments // TODO: we should be able to apply them to all arguments
"set ne? { not (eq? @1 @2) }\n" "set le? { ge? @2 @1 }\n" "set ne? { not (eq? @1 @2) }; set le? { ge? @2 @1 }\n"
"set ge? { not (lt? @1 @2) }\n" "set gt? { lt? @2 @1 }\n" "set ge? { not (lt? @1 @2) }; set gt? { lt? @2 @1 }\n"
"set <> { not (= @1 @2) }\n" "set <= { >= @2 @1 }\n" "set <> { not (= @1 @2) }; set <= { >= @2 @1 }\n"
"set >= { not (< @1 @2) }\n" "set > { < @2 @1 }\n"; "set >= { not (< @1 @2) }; set > { < @2 @1 }\n";
static bool static bool
ell_std_initialize (struct ell *ell) { ell_std_initialize (struct ell *ell) {

1294
ell/ell.go Normal file

File diff suppressed because it is too large Load Diff